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
@@ -0,0 +1,214 @@
1
+ const CATEGORIES = new Set(['tool', 'skill', 'resource', 'resource-template', 'task'])
2
+ const OUTCOMES = new Set(['complete', 'failed', 'cancelled', 'unknown'])
3
+ const USE_EVIDENCE = new Map([
4
+ ['tool', 'host-execution-result'], ['task', 'host-execution-result'], ['skill', 'skill-body-accepted'],
5
+ ['resource', 'resource-access'], ['resource-template', 'resource-access'],
6
+ ])
7
+
8
+ function fail(label) { throw new TypeError(`ptc-plus: invalid capability statistics ${label}`) }
9
+ function record(value, label) {
10
+ if (!value || typeof value !== 'object' || Array.isArray(value)) fail(label)
11
+ return value
12
+ }
13
+ function identity(value, label) {
14
+ if (typeof value !== 'string' || !value.trim() || value.length > 4096) fail(label)
15
+ return value
16
+ }
17
+ function integer(value, label, minimum = 0) {
18
+ if (!Number.isSafeInteger(value) || value < minimum) fail(label)
19
+ return value
20
+ }
21
+ function key(value) { return JSON.stringify(value) }
22
+ function orderSamples(a, b) { return b.eligibleOrder - a.eligibleOrder }
23
+ function freeze(value) {
24
+ if (value && typeof value === 'object') {
25
+ for (const item of Object.values(value)) freeze(item)
26
+ Object.freeze(value)
27
+ }
28
+ return value
29
+ }
30
+
31
+ /** Bounds are explicit calibration inputs; no observation, clock or process directory is inferred. */
32
+ export function createCapabilityStatistics(configuration) {
33
+ const options = record(configuration, 'configuration')
34
+ const limits = {}
35
+ for (const name of ['windowSize', 'maxFamilies', 'maxScopes', 'maxCapabilitiesPerFamily']) limits[name] = integer(options[name], name, 1)
36
+ return freeze({ schema: 'ptc-capability-statistics/v1', status: 'usable', limits, families: [], windows: [] })
37
+ }
38
+
39
+ function observation(raw) {
40
+ const value = record(raw, 'availability observation')
41
+ const category = value.category
42
+ if (!CATEGORIES.has(category)) fail('category')
43
+ if (!['full', 'on-demand', 'host'].includes(value.disclosure)) fail('availability disclosure')
44
+ const useEvidence = value.useEvidence ?? null
45
+ if (useEvidence !== null && useEvidence !== USE_EVIDENCE.get(category)) fail('owner use evidence')
46
+ const outcome = useEvidence === null ? null : value.outcome ?? 'unknown'
47
+ if (outcome !== null && !OUTCOMES.has(outcome)) fail('owner outcome evidence')
48
+ return {
49
+ workspaceId: identity(value.workspaceId, 'workspace identity'),
50
+ id: identity(value.id, 'capability identity'), revision: identity(value.revision, 'capability revision'), category,
51
+ disclosure: value.disclosure, planRevision: identity(value.planRevision, 'plan revision'),
52
+ used: useEvidence !== null, outcome,
53
+ }
54
+ }
55
+
56
+ function sampleObservation(value) {
57
+ const item = record(value, 'retained observation')
58
+ identity(item.workspaceId, 'retained workspace identity')
59
+ identity(item.id, 'retained capability identity')
60
+ identity(item.revision, 'retained capability revision')
61
+ if (!CATEGORIES.has(item.category) || typeof item.used !== 'boolean' || !Array.isArray(item.exposures) || item.exposures.length < 1) fail('retained evidence')
62
+ if (!Array.isArray(item.outcomes) || item.outcomes.some(outcome => !OUTCOMES.has(outcome))
63
+ || new Set(item.outcomes).size !== item.outcomes.length || item.used !== (item.outcomes.length > 0)) fail('retained outcomes')
64
+ for (const exposure of item.exposures) {
65
+ record(exposure, 'retained exposure')
66
+ if (!['full', 'on-demand', 'host'].includes(exposure.disclosure)) fail('retained disclosure')
67
+ identity(exposure.planRevision, 'retained plan revision')
68
+ }
69
+ }
70
+
71
+ /** Validate retained state before it can supply an automatic-allocation denominator. */
72
+ export function validateCapabilityStatistics(state) {
73
+ const value = record(state, 'state')
74
+ if (value.schema !== 'ptc-capability-statistics/v1' || !['usable', 'capacity-exceeded'].includes(value.status)) fail('state schema or status')
75
+ const template = createCapabilityStatistics(value.limits)
76
+ if (!Array.isArray(value.families) || !Array.isArray(value.windows)
77
+ || value.families.length > template.limits.maxFamilies || value.windows.length > template.limits.maxScopes) fail('state bounds')
78
+ const familyKeys = new Set()
79
+ const orders = new Set()
80
+ for (const family of value.families) {
81
+ record(family, 'retained family')
82
+ const familyKey = key([identity(family.userId, 'user identity'), identity(family.familyId, 'family identity')])
83
+ if (familyKeys.has(familyKey)) fail('duplicate family')
84
+ familyKeys.add(familyKey)
85
+ integer(family.observationRevision, 'observation revision')
86
+ if (!['active', 'settled', 'unknown'].includes(family.settlement)) fail('family settlement')
87
+ if (family.eligibleOrder !== null) {
88
+ integer(family.eligibleOrder, 'qualification order', 1)
89
+ const orderKey = key([family.userId, family.eligibleOrder])
90
+ if (orders.has(orderKey)) fail('duplicate qualification order')
91
+ orders.add(orderKey)
92
+ } else if (family.settlement === 'settled') fail('settled family without qualification order')
93
+ if (!Array.isArray(family.observations) || family.observations.length > value.limits.maxCapabilitiesPerFamily) fail('family observation bounds')
94
+ const observationKeys = new Set()
95
+ for (const item of family.observations) {
96
+ sampleObservation(item)
97
+ const observationKey = key([item.workspaceId, item.id, item.revision])
98
+ if (observationKeys.has(observationKey)) fail('duplicate observation')
99
+ observationKeys.add(observationKey)
100
+ if (item.exposures.length > value.limits.maxCapabilitiesPerFamily) fail('exposure bounds')
101
+ }
102
+ }
103
+ const windowKeys = new Set()
104
+ for (const window of value.windows) {
105
+ record(window, 'window')
106
+ identity(window.userId, 'window user identity')
107
+ if (window.workspaceId !== null) identity(window.workspaceId, 'window workspace identity')
108
+ identity(window.id, 'window capability identity')
109
+ identity(window.revision, 'window capability revision')
110
+ integer(window.floor, 'evicted qualification frontier')
111
+ const windowKey = key([window.userId, window.workspaceId, window.id, window.revision])
112
+ if (windowKeys.has(windowKey)) fail('duplicate window')
113
+ windowKeys.add(windowKey)
114
+ }
115
+ return value
116
+ }
117
+
118
+ function windowKey(userId, workspaceId, item) { return key([userId, workspaceId, item.id, item.revision]) }
119
+ function qualifies(family, window) {
120
+ return family.userId === window.userId && family.settlement === 'settled' && family.eligibleOrder > window.floor
121
+ && family.observations.some(item => item.id === window.id && item.revision === window.revision
122
+ && (window.workspaceId === null || item.workspaceId === window.workspaceId))
123
+ }
124
+
125
+ /**
126
+ * Public adapters supply actual family/workspace identities, monotone observation revisions,
127
+ * and an immutable qualification order. Observations are cumulative additions, never log guesses.
128
+ * A resumed or unknown family contributes no negative sample until owner-proven settlement.
129
+ */
130
+ export function reduceCapabilityStatistics(previous, event) {
131
+ validateCapabilityStatistics(previous)
132
+ if (previous.status !== 'usable') return previous
133
+ const value = record(event, 'event')
134
+ if (value.type !== 'family-observation') fail('event type')
135
+ const userId = identity(value.userId, 'user identity')
136
+ const familyId = identity(value.familyId, 'family identity')
137
+ const observationRevision = integer(value.observationRevision, 'observation revision')
138
+ const settlement = value.settlement
139
+ if (!['active', 'settled', 'unknown'].includes(settlement)) fail('family settlement')
140
+ const eligibleOrder = value.eligibleOrder == null ? null : integer(value.eligibleOrder, 'qualification order', 1)
141
+ if (settlement === 'settled' && eligibleOrder === null) fail('settlement qualification order')
142
+ if (!Array.isArray(value.observations) || value.observations.length > previous.limits.maxCapabilitiesPerFamily) fail('observation bounds')
143
+ const additions = value.observations.map(observation)
144
+ const state = structuredClone(previous)
145
+ let family = state.families.find(item => item.userId === userId && item.familyId === familyId)
146
+ if (family) {
147
+ if (family.eligibleOrder !== null && eligibleOrder !== family.eligibleOrder) fail('changed qualification order')
148
+ if (observationRevision <= family.observationRevision) return previous
149
+ family.observationRevision = observationRevision
150
+ family.settlement = settlement
151
+ family.eligibleOrder ??= eligibleOrder
152
+ } else {
153
+ if (eligibleOrder !== null && state.families.some(item => item.userId === userId && item.eligibleOrder === eligibleOrder)) fail('qualification order collision')
154
+ family = { userId, familyId, observationRevision, settlement, eligibleOrder, observations: [] }
155
+ state.families.push(family)
156
+ }
157
+ for (const addition of additions) {
158
+ let retained = family.observations.find(item => item.workspaceId === addition.workspaceId && item.id === addition.id && item.revision === addition.revision)
159
+ if (!retained) {
160
+ retained = { workspaceId: addition.workspaceId, id: addition.id, revision: addition.revision, category: addition.category, used: false, exposures: [], outcomes: [] }
161
+ family.observations.push(retained)
162
+ } else if (retained.category !== addition.category) fail('changed capability category')
163
+ retained.used ||= addition.used
164
+ if (addition.outcome !== null && !retained.outcomes.includes(addition.outcome)) retained.outcomes.push(addition.outcome)
165
+ const exposure = { disclosure: addition.disclosure, planRevision: addition.planRevision }
166
+ if (!retained.exposures.some(item => item.disclosure === exposure.disclosure && item.planRevision === exposure.planRevision)) retained.exposures.push(exposure)
167
+ for (const workspaceId of [addition.workspaceId, null]) {
168
+ const targetKey = windowKey(userId, workspaceId, addition)
169
+ if (!state.windows.some(item => windowKey(item.userId, item.workspaceId, item) === targetKey)) {
170
+ state.windows.push({ userId, workspaceId, id: addition.id, revision: addition.revision, floor: 0 })
171
+ }
172
+ }
173
+ }
174
+ const retainedFamilies = new Set()
175
+ for (const window of state.windows) {
176
+ const eligible = state.families.filter(item => qualifies(item, window)).sort(orderSamples)
177
+ const selected = eligible.slice(0, state.limits.windowSize)
178
+ for (const item of selected) retainedFamilies.add(key([item.userId, item.familyId]))
179
+ for (const item of eligible.slice(state.limits.windowSize)) window.floor = Math.max(window.floor, item.eligibleOrder)
180
+ }
181
+ state.families = state.families.filter(item => item.settlement !== 'settled' || retainedFamilies.has(key([item.userId, item.familyId])))
182
+ if (state.families.length > state.limits.maxFamilies || state.windows.length > state.limits.maxScopes
183
+ || state.families.some(item => item.observations.length > state.limits.maxCapabilitiesPerFamily
184
+ || item.observations.some(entry => entry.exposures.length > state.limits.maxCapabilitiesPerFamily))) {
185
+ return freeze({ ...structuredClone(previous), status: 'capacity-exceeded' })
186
+ }
187
+ validateCapabilityStatistics(state)
188
+ return freeze(state)
189
+ }
190
+
191
+ /** Per-capability eligible windows; workspace and global evidence are never added together. */
192
+ export function capabilityCoverage(state, query) {
193
+ validateCapabilityStatistics(state)
194
+ const options = record(query, 'coverage query')
195
+ const userId = identity(options.userId, 'coverage user identity')
196
+ const id = identity(options.id, 'coverage capability identity')
197
+ const revision = identity(options.revision, 'coverage capability revision')
198
+ const workspaceId = options.workspaceId === null ? null : identity(options.workspaceId, 'coverage workspace identity')
199
+ if (state.status !== 'usable') return { status: state.status, windowSize: state.limits.windowSize, eligible: null, used: null, coverage: null }
200
+ const window = state.windows.find(item => item.userId === userId && item.workspaceId === workspaceId && item.id === id && item.revision === revision)
201
+ // "Nothing recorded yet" and "recorded families are not currently settled"
202
+ // are different facts from a measured window; neither may read as a zero.
203
+ if (window === undefined) return { status: 'no-samples', windowSize: state.limits.windowSize, eligible: null, used: null, coverage: null }
204
+ const families = state.families.filter(item => qualifies(item, window)).sort(orderSamples).slice(0, state.limits.windowSize)
205
+ // A resumed family can expose a gap behind the irreversible eviction frontier.
206
+ // Surviving samples do not prove the recent settled window until it is full again.
207
+ if (window.floor > 0 && families.length < state.limits.windowSize) {
208
+ return { status: 'incomplete-window', windowSize: state.limits.windowSize, eligible: null, used: null, coverage: null }
209
+ }
210
+ if (families.length === 0) return { status: 'pending-settlement', windowSize: state.limits.windowSize, eligible: null, used: null, coverage: null }
211
+ const used = families.filter(family => family.observations.some(item => item.id === id && item.revision === revision && item.used
212
+ && (workspaceId === null || item.workspaceId === workspaceId))).length
213
+ return { status: 'usable', windowSize: state.limits.windowSize, eligible: families.length, used, coverage: families.length ? used / families.length : null }
214
+ }
@@ -1,3 +1,5 @@
1
+ import { CAPABILITY_POLICY_DEFAULT } from './capability-policy.js'
2
+
1
3
  /** Configuration fields shared by the Host schema and settings card. */
2
4
  export const MAX_TIMER_DELAY_MS = 2_147_483_647
3
5
 
@@ -11,6 +13,54 @@ export const SETTINGS_NAMESPACE = 'ptc-plus'
11
13
  * exists in both locales or in neither.
12
14
  */
13
15
  export const CONFIG_FIELDS = Object.freeze([
16
+ { key: 'capabilityContextEnabled', type: 'boolean', default: true,
17
+ label: '能力与上下文', labelEn: 'Capabilities and context',
18
+ description: '整体暂停能力规则、统计采集、说明恢复和结果预览,保留各项设置;新会话或明确刷新后生效。',
19
+ descriptionEn: 'Pauses capability rules, statistics collection, description recovery and result previews while retaining each setting. Applies to new sessions or explicit refreshes.' },
20
+ { key: 'capabilityManagementEnabled', type: 'boolean', default: false,
21
+ label: '实验性能力管理', labelEn: 'Experimental capability management',
22
+ description: '为新 PTC 会话应用可用性与说明规则;已有会话使用 /capabilities refresh 生效。收益尚未经过模型对照验收。',
23
+ descriptionEn: 'Applies availability and disclosure rules to new PTC sessions. Use /capabilities refresh for existing sessions. Model cost benefits remain unverified.' },
24
+ { key: 'capabilityPolicy', preserveOmission: true, type: 'string', default: CAPABILITY_POLICY_DEFAULT,
25
+ label: '能力规则', labelEn: 'Capability rules',
26
+ description: '分别设置可用性、说明方式以及常用集、统计和预算分配;默认、类别、MCP 来源、单项依次覆盖。',
27
+ descriptionEn: 'Choose availability, disclosure and baseline, statistics and budget allocation separately. Default, category, MCP source and item rules apply in order.' },
28
+ { key: 'capabilityRecoveryEnabled', preserveOmission: true, type: 'boolean', default: false,
29
+ label: '压缩后恢复已使用的 API 说明', labelEn: 'Recover used API descriptions after compaction',
30
+ description: '只补回已展开、仍有效且不可见的近期 API 说明,不恢复 Skill 正文。',
31
+ descriptionEn: 'Restores only expanded, current, no longer visible API descriptions from recent use; never Skill bodies.' },
32
+ { key: 'modelPreviewEnabled', preserveOmission: true, type: 'boolean', default: false,
33
+ label: '限制模型侧结果与日志展示', labelEn: 'Bound model-facing results and logs',
34
+ description: '独立使用插件预览预算;关闭时沿用宿主展示,程序值始终完整。',
35
+ descriptionEn: 'Independently selects the plugin preview budget. Otherwise retains Host presentation; program values remain complete.' },
36
+ { key: 'capabilityBaseline', type: 'string', default: '[]',
37
+ label: '初始常用能力', labelEn: 'Initial common capabilities',
38
+ description: '从能力列表勾选常用项;adaptive 选用常用集依据时参与分配,不生成使用记录。',
39
+ descriptionEn: 'Select common capabilities as an optional allocation basis for adaptive disclosure. Creates no usage records.' },
40
+ { key: 'capabilityOverrides', type: 'string', default: '{}',
41
+ label: '旧版能力选择', labelEn: 'Legacy capability selections',
42
+ description: '旧配置迁移输入;新规则由能力工作台分别保存可用性与说明方式。',
43
+ descriptionEn: 'Migration input for older settings. The workbench saves availability and disclosure separately in the policy.' },
44
+ { key: 'capabilityStatistics', type: 'string',
45
+ default: '{"enabled":false,"windowSize":100,"minimumSamples":10,"fullThreshold":0.5}',
46
+ label: '能力统计设置', labelEn: 'Capability statistics settings',
47
+ description: '独立采集使用统计,并设置样本窗口、最低样本数和完整披露阈值;关闭或不可用时保留手动基线。',
48
+ descriptionEn: 'Collect usage statistics independently and set the sample window, minimum samples and full-disclosure threshold. Disabled or unavailable statistics preserve the manual baseline.' },
49
+ { key: 'capabilityProfiles', type: 'string', default: '{"schema":"ptc-capability-profiles/v1","profiles":[]}',
50
+ label: '保存的能力配置', labelEn: 'Saved capability configurations',
51
+ description: '在能力工作台命名保存、导入导出和切换配置;已有会话需要主动刷新。',
52
+ descriptionEn: 'Name, save, import, export and switch configurations in the capability workbench. Existing sessions require explicit refresh.' },
53
+ { key: 'capabilityBudgetBytes', type: 'integer', default: 16384, min: 1, max: Number.MAX_SAFE_INTEGER,
54
+ label: '完整说明预算 (UTF-8 bytes)', labelEn: 'Full contract budget (UTF-8 bytes)',
55
+ description: '显式 full 可以超出此预算;不截断调用契约。', descriptionEn: 'Explicit full selections may exceed this budget; calling contracts are never truncated.' },
56
+ { key: 'capabilityDirectoryBytes', type: 'integer', default: 2048, min: 512, max: Number.MAX_SAFE_INTEGER,
57
+ label: '能力导航预算 (UTF-8 bytes)', labelEn: 'Capability navigation budget (UTF-8 bytes)', description: '', descriptionEn: '' },
58
+ { key: 'capabilityRecoveryBytes', type: 'integer', default: 8192, min: 1, max: Number.MAX_SAFE_INTEGER,
59
+ label: '压缩后说明恢复预算 (UTF-8 bytes)', labelEn: 'Description recovery budget (UTF-8 bytes)', description: '', descriptionEn: '' },
60
+ { key: 'modelPreviewTokens', type: 'integer', default: 4096, min: 1, max: Number.MAX_SAFE_INTEGER,
61
+ label: '模型结果预览预算 (estimated tokens)', labelEn: 'Model result preview budget (estimated tokens)',
62
+ description: '选用模型侧预览时复用 DSH spill policy;完整内容需要宿主 spill store,程序值保持完整。',
63
+ descriptionEn: 'Uses DSH spill policy when model-facing previews are selected. Complete content requires a host spill store; program values remain complete.' },
14
64
  {
15
65
  key: 'legacyBindingSettings',
16
66
  type: 'boolean',
@@ -38,6 +88,15 @@ export const CONFIG_FIELDS = Object.freeze([
38
88
  description: '开启时使用增强工具卡片;关闭后使用 DSH 原生工具卡片。',
39
89
  descriptionEn: 'Uses enhanced tool cards when enabled and DSH native tool cards when disabled.',
40
90
  },
91
+ {
92
+ key: 'toolHoverPreviewEnabled',
93
+ type: 'boolean',
94
+ default: true,
95
+ label: '悬停预览执行内容',
96
+ labelEn: 'Preview execution content on hover',
97
+ description: '在增强执行卡片上停留或键盘聚焦时显示简短预览;滚动时自动隐藏。',
98
+ descriptionEn: 'Shows a quick preview on enhanced tool cards after hovering or keyboard focus; hides while scrolling.',
99
+ },
41
100
  {
42
101
  key: 'replViewEnabled',
43
102
  type: 'boolean',
@@ -53,8 +112,8 @@ export const CONFIG_FIELDS = Object.freeze([
53
112
  default: true,
54
113
  label: '显示 PTC Plus 快捷入口',
55
114
  labelEn: 'Show the PTC Plus shortcut',
56
- description: '在输入框旁显示 PTC Plus 快捷入口:查看和启停全局绑定、编写新绑定,并直接打开插件设置窗口;隐藏后仍可使用 /binding 命令。',
57
- descriptionEn: 'Shows the PTC Plus shortcut beside the composer: global binding access and toggles, authoring, and direct access to the plugin settings dialog. Hiding it keeps /binding commands available.',
115
+ description: '在输入框旁显示星光菜单:打开插件设置、刷新当前会话能力,并在全局绑定开启时查看、启停和编写绑定;隐藏后仍可使用斜杠命令。',
116
+ descriptionEn: 'Shows the sparkle menu beside the composer: open settings, refresh current-session capabilities, and manage or author global bindings when enabled. Hiding it does not remove slash commands.',
58
117
  },
59
118
  {
60
119
  key: 'autoDescribeRunCode',
@@ -156,6 +215,15 @@ export const CONFIG_FIELDS = Object.freeze([
156
215
  description: 'worker 重启后从 session journal 重建可恢复的 REPL 状态。',
157
216
  descriptionEn: 'Rebuilds recoverable REPL state from the session journal after a worker restart.',
158
217
  },
218
+ {
219
+ key: 'officialReplPriority',
220
+ type: 'boolean',
221
+ default: false,
222
+ label: '官方 REPL 模式优先',
223
+ labelEn: 'Prioritize the official REPL mode',
224
+ description: '检测到官方持久 REPL 模式时由官方运行时接管 PTC 会话执行;PTC Plus 让出 run_code 及其进程内会话状态语义,关闭时始终由 PTC Plus 接管。',
225
+ descriptionEn: 'Lets the official runtime own PTC session execution when the official persistent REPL mode is active; PTC Plus then leaves run_code and its in-process session-state semantics to DSH. Disabled keeps PTC Plus in charge.',
226
+ },
159
227
  {
160
228
  key: 'tipsEnabled',
161
229
  type: 'boolean',
@@ -296,12 +364,6 @@ export const CONFIG_GROUPS = Object.freeze([
296
364
  labelEn: 'Plugin switch',
297
365
  fields: Object.freeze(['enabled']),
298
366
  },
299
- {
300
- key: 'calls',
301
- label: '调用容错',
302
- labelEn: 'Tool call tolerance',
303
- fields: Object.freeze(['autoDescribeRunCode', 'canonicalizeToolCalls']),
304
- },
305
367
  {
306
368
  key: 'syntax',
307
369
  label: 'REPL 语法',
@@ -314,7 +376,7 @@ export const CONFIG_GROUPS = Object.freeze([
314
376
  key: 'recovery',
315
377
  label: '状态与恢复',
316
378
  labelEn: 'State and recovery',
317
- fields: Object.freeze(['durableReplay', 'tipsEnabled', 'tipCooldownMessages', 'tipEscalationFailures']),
379
+ fields: Object.freeze(['durableReplay', 'officialReplPriority', 'tipsEnabled', 'tipCooldownMessages', 'tipEscalationFailures']),
318
380
  },
319
381
  {
320
382
  key: 'extensions',
@@ -322,11 +384,17 @@ export const CONFIG_GROUPS = Object.freeze([
322
384
  labelEn: 'Tool extensions',
323
385
  fields: Object.freeze(['userBindingsEnabled', 'cordisToolsEnabled']),
324
386
  },
387
+ {
388
+ key: 'calls',
389
+ label: '调用容错',
390
+ labelEn: 'Tool call tolerance',
391
+ fields: Object.freeze(['autoDescribeRunCode', 'canonicalizeToolCalls']),
392
+ },
325
393
  {
326
394
  key: 'interface',
327
395
  label: '界面显示',
328
396
  labelEn: 'Interface display',
329
- fields: Object.freeze(['enhancedToolView', 'replViewEnabled', 'bindingAuthorButtonVisible']),
397
+ fields: Object.freeze(['enhancedToolView', 'toolHoverPreviewEnabled', 'replViewEnabled', 'bindingAuthorButtonVisible']),
330
398
  },
331
399
  {
332
400
  key: 'limits',
@@ -344,8 +412,19 @@ export const CONFIG_GROUPS = Object.freeze([
344
412
  'maxValueBigIntDigits',
345
413
  ]),
346
414
  },
415
+ { key: 'capabilities', label: '能力与上下文', labelEn: 'Capabilities and context',
416
+ fields: Object.freeze(['capabilityContextEnabled', 'capabilityManagementEnabled', 'capabilityBaseline', 'capabilityOverrides',
417
+ 'capabilityStatistics', 'capabilityPolicy', 'capabilityRecoveryEnabled', 'modelPreviewEnabled', 'capabilityProfiles', 'capabilityBudgetBytes', 'capabilityDirectoryBytes', 'capabilityRecoveryBytes', 'modelPreviewTokens']) },
347
418
  ])
348
419
 
420
+ /** Functional modules share the same section order across every settings entry. */
421
+ export const CONFIG_MODULES = Object.freeze([
422
+ { key: 'workspace', label: '核心工作区', labelEn: 'Core workspace', groups: Object.freeze(['syntax', 'recovery']) },
423
+ { key: 'extensions', label: '扩展与容错', labelEn: 'Extensions and tolerance', groups: Object.freeze(['extensions', 'calls']) },
424
+ { key: 'presentation', label: '界面与资源', labelEn: 'Interface and resources', groups: Object.freeze(['interface', 'limits']) },
425
+ { key: 'experimental', label: '实验性', labelEn: 'Experimental', groups: Object.freeze(['capabilities']) },
426
+ ].map(Object.freeze))
427
+
349
428
  /** Resolved defaults, derived once from the field definitions. */
350
429
  export const CONFIG_DEFAULTS = Object.freeze(
351
430
  Object.fromEntries(CONFIG_FIELDS.map(field => [field.key, field.default])),
@@ -3,7 +3,6 @@ import { boundContextSummary, createUserMessage } from '@deepseek-ai/dsh-llm'
3
3
  import { validateJsonSchemaValue } from '@deepseek-ai/dsh-tools'
4
4
  import { PTC_MESSAGE_SOURCE_KIND } from './message-sources.js'
5
5
  import { canonicalizeToolCallStream } from './tool-call-canonicalizer.js'
6
- import { editRunCodeSchema } from './rejected-cell-editor.js'
7
6
  import { sessionRuntimeContexts } from './runtime-contexts.js'
8
7
  import { projectProgramSdk } from './sdk-projection.js'
9
8
  import { EDIT_RUN_CODE } from './edit-transport-owner.js'
@@ -16,11 +15,12 @@ import {
16
15
  } from './run-code-description.js'
17
16
  import { userBindingsConfiguredContext } from './user-bindings.js'
18
17
  import { BINDING_AUTHORING_SDK } from './user-binding-authoring.js'
18
+ import { executionPolicies } from './binding-update-policy.js'
19
19
 
20
- const RUN_CODE_TOOL_DESCRIPTION = 'Evaluate the next TypeScript cell in this session-bound persistent REPL, reusing earlier top-level bindings. Use `code` for the async-function body and `description` for its short UI summary. Successful image-bearing subtool results are attached after the cell.'
21
- const RUN_CODE_CODE_DESCRIPTION = 'Code for the next REPL cell, parsed as the body of an async TypeScript function.'
20
+ const RUN_CODE_TOOL_DESCRIPTION = 'Evaluate the next TypeScript cell in this session-bound persistent REPL, reusing earlier top-level bindings. Use `code` for the cell and `description` for its short UI summary. Successful image-bearing subtool results are attached after the cell.'
21
+ const RUN_CODE_CODE_DESCRIPTION = 'Code for the next PTC TypeScript cell, following the selected language conventions.'
22
22
  const RUN_CODE_DESCRIPTION_DESCRIPTION = 'Short active-voice summary of what this cell does, 5-10 words (shown in the UI).'
23
- const CODE_TRANSPORT_INSTRUCTION = '`run_code` and `edit_run_code` are the only tools callable directly. Call every native tool declared by the SDK from inside a program.'
23
+ const CODE_TRANSPORT_INSTRUCTION = '`run_code` is the only tool callable directly. Call `tools.edit_run_code` and other SDK tools from inside a program. An accepted edit runs after the current cell finishes.'
24
24
  const PTC_COLLAPSE_SECTION_NAMES = Object.freeze(['tools:ptc-only', 'tools:code-only'])
25
25
  const INVALID_TOOL_JSON_FAILURE = Object.freeze({
26
26
  code: 'MALFORMED_RESPONSE',
@@ -53,19 +53,20 @@ function adaptRunCodeSchema(tool) {
53
53
  }
54
54
  }
55
55
 
56
- function capabilitySdk(nativeSdk, userBindingsEnabled) {
57
- return `${projectProgramSdk(nativeSdk)}
56
+ function capabilitySdk(nativeSdk, userBindingsEnabled, languageSemantics) {
57
+ return `${projectProgramSdk(nativeSdk, { languageSemantics })}
58
58
 
59
59
  ## PTC Plus program capabilities
60
60
 
61
- Discover program APIs without calling them. \`tree()\` lists namespaces and members; \`inspect()\` describes selected \`namespace.member\` symbols. \`find()\` matches case-insensitive symbols or contiguous words, separating CamelCase and punctuation. Use short queries such as \`"read"\`; if none match, shorten the query or check \`tree()\`. Missing metadata is unknown.
61
+ Discover program APIs without calling them. \`search({query})\` retrieves whole indexed API descriptions by English/Chinese words; its UTF-8 JSON byte budget includes the envelope, with a continuation cursor and inspect references for oversized entries. \`tree()\` lists namespaces and members; \`find()\` matches case-insensitive symbols or contiguous words, separating CamelCase and punctuation; \`inspect()\` returns selected \`namespace.member\` symbols with an item-count budget. Missing metadata is unknown.
62
62
 
63
63
  \`\`\`ts
64
- declare class CapabilityExplorationError extends Error { readonly operation: "tree" | "find" | "inspect" }
64
+ declare class CapabilityExplorationError extends Error { readonly operation: "tree" | "find" | "inspect" | "search" }
65
65
  declare const capabilities: {
66
66
  tree(): Promise<Array<{ namespace: string; members: string[] }>>
67
67
  find(query: string): Promise<Array<{ symbol: string; description?: string; completeness: string; effect: string; replay: string }>>
68
68
  inspect(args?: { symbols?: string[]; budget?: number }): Promise<{ symbols: unknown[]; omitted: number; unknown: string[]; budget: number }>
69
+ search(args: { query: string; budgetBytes?: number; limit?: number; cursor?: string }): Promise<{ matches: Array<{ symbol: string; owner: unknown; contract: unknown }>; oversized: Array<{ symbol: string; requiredBytes: number; inspect: { symbols: string[]; budget: number } }>; total: number; remaining: number; cursor: string | null; budgetBytes: number; usedBytes: number; unit: "utf8-json-bytes" }>
69
70
  }
70
71
  \`\`\`${userBindingsEnabled ? `\n${BINDING_AUTHORING_SDK}` : ''}`
71
72
  }
@@ -118,16 +119,8 @@ function presentationState(assembly) {
118
119
  const collapse = PTC_COLLAPSE_SECTION_NAMES
119
120
  .map(name => assembly?.sections?.find(section => section?.name === name))
120
121
  .find(section => section !== undefined)
121
- if (collapse !== undefined) {
122
- const presentation = typeof collapse.text === 'string' && collapse.text.trim().length > 0
123
- ? 'ptc'
124
- : 'both'
125
- return { presentation, ownerProven: true, collapseSectionName: collapse.name }
126
- }
127
- const presentation = tools.every(tool => tool?.name === RUN_CODE || tool?.name === EDIT_RUN_CODE)
128
- ? 'ptc'
129
- : 'both'
130
- return { presentation, ownerProven: false, collapseSectionName: undefined }
122
+ return { presentation: 'ptc', ownerProven: collapse !== undefined,
123
+ collapseSectionName: collapse?.name }
131
124
  }
132
125
 
133
126
  async function* bindCallPolicies(source, policy, calls) {
@@ -465,6 +458,7 @@ export function createDirectSurfaceOwner({
465
458
  userBindingsForAgent = async () => undefined,
466
459
  setAgentPresentation = async () => {},
467
460
  sessionLogView,
461
+ capabilityEnvironment,
468
462
  }) {
469
463
  // Composition is anchored to Agent identity because DSH selects presentation
470
464
  // once per composed agent. Request signals bind the exact assembly to stream
@@ -478,6 +472,7 @@ export function createDirectSurfaceOwner({
478
472
  let disposed = false
479
473
  let currentCanonicalizeToolCalls = canonicalizeToolCalls
480
474
  let currentAutoDescribeRunCode = runtimeConfig.autoDescribeRunCode
475
+ let currentLanguageSemantics = executionPolicies(runtimeConfig).languageSemantics
481
476
  const tipConfig = {
482
477
  enabled: runtimeConfig.tipsEnabled,
483
478
  cooldownMessages: runtimeConfig.tipCooldownMessages,
@@ -563,6 +558,7 @@ export function createDirectSurfaceOwner({
563
558
 
564
559
  return Object.freeze({
565
560
  reconfigure(nextConfig) {
561
+ currentLanguageSemantics = executionPolicies(nextConfig).languageSemantics
566
562
  currentCanonicalizeToolCalls = nextConfig.canonicalizeToolCalls
567
563
  currentAutoDescribeRunCode = nextConfig.autoDescribeRunCode
568
564
  cordisRecovery.reconfigure(nextConfig.cordisToolsEnabled)
@@ -571,8 +567,11 @@ export function createDirectSurfaceOwner({
571
567
  tipConfig.escalationFailures = nextConfig.tipEscalationFailures
572
568
  },
573
569
  async assemble(initialAssembly, context, next) {
570
+ // Settings observes Host-owned catalogs without preparing a session environment.
571
+ if (context?.ptcPlusCapabilityObservation === true) return next()
574
572
  // Keep schema projection and later execution on one configuration snapshot.
575
573
  const autoDescribeRunCode = currentAutoDescribeRunCode
574
+ const languageSemantics = currentLanguageSemantics
576
575
  const agent = context?.agent
577
576
  const id = sessionId(agent)
578
577
  const requestOwner = id === undefined ? undefined : sessionOwner(id)
@@ -625,13 +624,14 @@ export function createDirectSurfaceOwner({
625
624
  if (!isCurrent()) return assembly
626
625
  const sessionPtc = presentation !== 'native' && id !== undefined
627
626
  const sessionPtcProjection = presentation === 'ptc' && id !== undefined
627
+ if (sessionPtc) await capabilityEnvironment?.prepare(agent, assembly.sections)
628
628
  const userBindings = sessionPtc ? await userBindingsForAgent(agent) : undefined
629
629
  if (!isCurrent()) return assembly
630
630
  const runCode = tools.find(tool => tool?.name === RUN_CODE)
631
- let directTools = tools
631
+ let directTools = sessionPtc ? capabilityEnvironment?.visibleSchemas(agent, tools) ?? tools : tools
632
632
  if (sessionPtcProjection) {
633
633
  editTransport.ensureInstalled(agent)
634
- directTools = [adaptRunCodeSchema(runCode), editRunCodeSchema()]
634
+ directTools = [adaptRunCodeSchema(runCode)]
635
635
  }
636
636
  const runtimeContexts = sessionPtc
637
637
  ? sessionRuntimeContexts(agent, tipConfig, {
@@ -650,9 +650,10 @@ export function createDirectSurfaceOwner({
650
650
  && assembly.sections.some(section => section?.name === 'tools:sdk'
651
651
  || (sessionPtcProjection && section?.name === completedState.collapseSectionName))
652
652
  const sections = projectsSections
653
- ? assembly.sections.map(section => {
653
+ ? (capabilityEnvironment?.projectSections(agent, assembly.sections) ?? assembly.sections).map(section => {
654
654
  if (section?.name === 'tools:sdk') {
655
- return { ...section, text: capabilitySdk(section.text, userBindings !== undefined) }
655
+ return { ...section, text: capabilitySdk(capabilityEnvironment?.sdk(agent, section.text) ?? section.text,
656
+ userBindings !== undefined, languageSemantics) }
656
657
  }
657
658
  if (sessionPtcProjection && section?.name === completedState.collapseSectionName) {
658
659
  return { ...section, text: CODE_TRANSPORT_INSTRUCTION }
@@ -662,7 +663,7 @@ export function createDirectSurfaceOwner({
662
663
  : assembly.sections
663
664
  if (id !== undefined) {
664
665
  const nativeSchemas = presentation === 'ptc'
665
- ? new Map(toolSchemasForAgent(agent)
666
+ ? new Map((capabilityEnvironment?.visibleSchemas(agent, toolSchemasForAgent(agent)) ?? toolSchemasForAgent(agent))
666
667
  .filter(schema => typeof schema?.name === 'string'
667
668
  && schema.name !== RUN_CODE && schema.name !== EDIT_RUN_CODE)
668
669
  .map(schema => [schema.name, schema]))
@@ -706,20 +707,17 @@ export function createDirectSurfaceOwner({
706
707
  return bindCallPolicies(recovered, policy, policy.owner.calls)
707
708
  },
708
709
  executionRejection(exec) {
709
- if (exec.name === EDIT_RUN_CODE && exec.parent !== undefined) {
710
- return rejection(`tool ${EDIT_RUN_CODE} is only callable directly in PTC mode; call native tools from inside run_code`)
711
- }
712
710
  const policy = dispatchPolicy(exec)
713
711
  if (exec.name === EDIT_RUN_CODE) {
714
- return policy?.presentation === 'ptc'
712
+ return exec.parent !== undefined
715
713
  ? undefined
716
- : rejection(`tool ${EDIT_RUN_CODE} is not declared for this request`)
714
+ : rejection(`tool ${EDIT_RUN_CODE} must be called through tools.${EDIT_RUN_CODE} inside run_code`)
717
715
  }
718
716
  if (exec.name === RUN_CODE || exec.parent !== undefined) {
719
717
  return undefined
720
718
  }
721
719
  if (policy?.presentation !== 'ptc') return undefined
722
- return rejection(`tool ${exec.name} is not a direct PTC tool; use run_code or edit_run_code directly, and call native tools from inside run_code`)
720
+ return rejection(`tool ${exec.name} is not a direct PTC tool; use run_code directly, and call SDK tools from inside run_code`)
723
721
  },
724
722
  executionArguments(exec) {
725
723
  const policy = dispatchPolicy(exec)
@@ -54,7 +54,7 @@ const asBindings = value => {
54
54
 
55
55
  /** Native reference operations over compiler-proved lexical and object frames. */
56
56
  export function createDynamicEnvironmentRuntime({
57
- importModule = (source, options) => import(source, options),
57
+ importModule: defaultImportModule = (source, options) => import(source, options),
58
58
  intrinsicEval = defaultIntrinsics.intrinsicEval, realmFunction = defaultIntrinsics.realmFunction, globalObject = defaultIntrinsics.globalObject,
59
59
  errors, reflect = defaultIntrinsics.reflect, object, interfaceOwner, resolveAmbient = programAmbientResolver,
60
60
  } = {}) {
@@ -173,7 +173,7 @@ export function createDynamicEnvironmentRuntime({
173
173
 
174
174
  function environment({ frames = [], varFrame = new internal.Map(), strict = false, origin, lexicalContext = {},
175
175
  getThis = () => globalObject, getNewTarget = () => undefined, allowNewTarget = false,
176
- ambient = ambientReference, createVar, declareVars, nativeAwait = false } = {}) {
176
+ ambient = ambientReference, createVar, declareVars, nativeAwait = false, importModule = defaultImportModule } = {}) {
177
177
  varFrame = asBindings(varFrame)
178
178
  const capturedFrames = []
179
179
  for (let index = 0; index < frames.length; index++) {
@@ -268,19 +268,19 @@ export function createDynamicEnvironmentRuntime({
268
268
  internal.copyArray(captured, rootIndex, captured.length, combined)
269
269
  internal.copyArray(frames, boundary, frames.length, combined)
270
270
  }
271
- return environment({ frames: combined,
271
+ return environment({ importModule, frames: combined,
272
272
  varFrame, strict: nextStrict, getThis, getNewTarget, allowNewTarget, ambient, createVar, declareVars, nativeAwait, origin, lexicalContext })
273
273
  },
274
274
  awaitActivation() {
275
275
  const local = new internal.Map()
276
- return environment({ frames: internal.copyArray(frames, 0, frames.length, [{ kind: 'lexical', bindings: local }]), varFrame: local,
276
+ return environment({ importModule, frames: internal.copyArray(frames, 0, frames.length, [{ kind: 'lexical', bindings: local }]), varFrame: local,
277
277
  strict, getThis, getNewTarget, allowNewTarget, ambient, nativeAwait: true, origin, lexicalContext })
278
278
  },
279
279
  activation(bindings, nextGetThis = getThis, nextGetNewTarget = getNewTarget, nextStrict = strict, ownFunction = true, selfBindings) {
280
280
  bindings = asBindings(bindings)
281
281
  const activationFrames = [{ kind: 'lexical', bindings }]
282
282
  if (selfBindings !== undefined) internal.appendArray(activationFrames, { kind: 'self', bindings: asBindings(selfBindings) })
283
- return environment({ frames: internal.copyArray(frames, 0, frames.length, activationFrames), varFrame: bindings,
283
+ return environment({ importModule, frames: internal.copyArray(frames, 0, frames.length, activationFrames), varFrame: bindings,
284
284
  strict: nextStrict, getThis: nextGetThis, getNewTarget: nextGetNewTarget, allowNewTarget: ownFunction || allowNewTarget, ambient, origin,
285
285
  lexicalContext: ownFunction ? { privateReferences: lexicalContext.privateReferences } : lexicalContext })
286
286
  },
@@ -294,7 +294,7 @@ export function createDynamicEnvironmentRuntime({
294
294
  }
295
295
  const parameterFrames = [{ kind: 'lexical', bindings }]
296
296
  if (selfBindings !== undefined) internal.appendArray(parameterFrames, { kind: 'self', bindings: asBindings(selfBindings) })
297
- selected = environment({ frames: internal.copyArray(frames, 0, frames.length, parameterFrames), varFrame: bindings,
297
+ selected = environment({ importModule, frames: internal.copyArray(frames, 0, frames.length, parameterFrames), varFrame: bindings,
298
298
  strict: nextStrict, getThis: nextGetThis, getNewTarget: nextGetNewTarget, allowNewTarget: ownFunction || allowNewTarget, ambient, origin, lexicalContext })
299
299
  internal.weakMapSet(parameterEnvironments, key, selected)
300
300
  }
@@ -309,15 +309,15 @@ export function createDynamicEnvironmentRuntime({
309
309
  } })
310
310
  return nativeCalls.registerCallableSourceTarget(adapter, fn)
311
311
  },
312
- context(options) { return environment({ frames, varFrame, strict: options.strict ?? strict,
312
+ context(options) { return environment({ importModule, frames, varFrame, strict: options.strict ?? strict,
313
313
  getThis: options.getThis ?? getThis, getNewTarget: options.getNewTarget ?? getNewTarget,
314
314
  allowNewTarget: options.allowNewTarget ?? allowNewTarget, ambient, createVar, declareVars, nativeAwait, origin,
315
315
  lexicalContext: { ...lexicalContext, ...options } }) },
316
- at(nextOrigin) { return environment({ frames, varFrame, strict, getThis, getNewTarget,
316
+ at(nextOrigin) { return environment({ importModule, frames, varFrame, strict, getThis, getNewTarget,
317
317
  allowNewTarget, ambient, createVar, declareVars, nativeAwait, origin: nextOrigin, lexicalContext }) },
318
318
  withObject(value) {
319
319
  if (value === null || value === undefined) throw new errors.TypeError('Cannot convert undefined or null to object')
320
- return environment({ frames: internal.copyArray(frames, 0, frames.length, [{ kind: 'object', object: object(value) }]),
320
+ return environment({ importModule, frames: internal.copyArray(frames, 0, frames.length, [{ kind: 'object', object: object(value) }]),
321
321
  varFrame, strict, getThis, getNewTarget, allowNewTarget, ambient, createVar, declareVars, nativeAwait, origin, lexicalContext })
322
322
  },
323
323
  declareEvalVars(names) {