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.
- package/README.en.md +61 -123
- package/README.md +61 -123
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
- package/assets/ptc-plus-repl-workspace-en.png +0 -0
- package/assets/ptc-plus-repl-workspace-zh.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.cjs +42336 -0
- package/cordis.patch.yml +13 -2
- package/docs/README.md +7 -2
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +31 -179
- package/docs/adr/0008-define-agent-operating-contract.md +5 -1
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -2
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +2 -2
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +14 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0022-stage-npm-releases-with-oidc.md +4 -2
- package/docs/adr/0023-global-user-bindings.md +3 -1
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +2 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +101 -35
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +3 -3
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +1 -1
- package/docs/adr/0032-own-session-log-facts-in-a-host-only-projection.md +27 -0
- package/docs/adr/0033-gate-client-visual-usability-in-a-browser.md +23 -0
- package/docs/adr/0034-keep-program-values-separate-from-output-projections.md +29 -0
- package/docs/adr/0035-run-coverage-only-before-a-new-release.md +27 -0
- package/docs/adr/0036-separate-focused-development-from-delivery-verification.md +29 -0
- package/docs/adr/0037-keep-tool-hover-previews-passive.md +52 -0
- package/docs/adr/0038-freeze-session-capability-plans.md +71 -0
- package/docs/adr/0039-verify-documentation-without-runtime-suites.md +29 -0
- package/docs/adr/0040-separate-capability-configurations-from-session-observations.md +39 -0
- package/docs/adr/0041-record-cell-execution-directories.md +65 -0
- package/docs/adr/0042-persist-provider-availability-and-handback.md +41 -0
- package/docs/adr/0043-compose-capability-and-context-policies.md +37 -0
- package/docs/adr/README.md +6 -0
- package/docs/architecture.md +25 -27
- package/docs/binding-review-placement-design.md +29 -21
- package/docs/capability-planning-components.md +93 -0
- package/docs/capability-projection.md +7 -0
- package/docs/client-ui.md +88 -29
- package/docs/client-visual-contract.md +65 -0
- package/docs/context-management.md +22 -0
- package/docs/context-optimization-plan.md +353 -0
- package/docs/context-public-interface-coverage.md +25 -0
- package/docs/durability-design.md +31 -14
- package/docs/evaluation.md +41 -6
- package/docs/evaluations/command-goat-deepseek-v4.1-flash-20261006.md +126 -0
- package/docs/evaluations/command-goat-deepseek-v4.1-flash.md +281 -0
- package/docs/installation.md +50 -8
- package/docs/program-data-plane.md +5 -3
- package/docs/publishing.md +5 -5
- package/docs/release-coverage.md +73 -0
- package/docs/release-notes.md +1 -1
- package/docs/review-procedure.md +56 -0
- package/docs/runtime-reference.md +48 -16
- package/docs/semantic-validation.md +14 -2
- package/docs/stateful-binding-updates-proposal.md +2 -2
- package/docs/user-bindings.en.md +2 -2
- package/docs/user-bindings.md +3 -3
- package/docs/value-wire.md +11 -1
- package/docs/verification-optimization-work.md +52 -15
- package/docs/verification.md +30 -86
- package/index.js +147 -23
- package/internal/binding-toggle-receipt.js +24 -0
- package/internal/capability-command-receipt.js +29 -0
- package/internal/capability-environment.js +1247 -0
- package/internal/capability-plan.js +285 -0
- package/internal/capability-policy.js +96 -0
- package/internal/capability-statistics-store.js +180 -0
- package/internal/capability-statistics.js +210 -0
- package/internal/compiler-platform.js +5 -3
- package/internal/compiler-service.js +2 -9
- package/internal/config-spec.js +79 -9
- package/internal/cordis-tools-owner.js +22 -19
- package/internal/direct-surface-owner.js +29 -29
- package/internal/dynamic-environment-runtime.js +9 -9
- package/internal/edit-transport-owner.js +183 -139
- package/internal/execution-provider-owner.js +160 -0
- package/internal/execution-seam-compat.js +76 -120
- package/internal/isolated-worker.js +12 -8
- package/internal/kernel-worker.js +104 -32
- package/internal/native-root-dynamic.js +12 -13
- package/internal/program-bindings.js +190 -0
- package/internal/recovery-tips.js +53 -23
- package/internal/rejected-cell-editor.js +8 -8
- package/internal/repl-observation-interest.js +11 -3
- package/internal/rpc-contract.js +1 -0
- package/internal/runtime-bridge-owner.js +351 -82
- package/internal/runtime-config.js +58 -0
- package/internal/runtime-contexts.js +85 -16
- package/internal/runtime-intrinsics.js +1 -0
- package/internal/runtime-messages.js +41 -0
- package/internal/sdk-projection.js +10 -82
- package/internal/session-cell-executor.js +42 -19
- package/internal/session-events.js +74 -4
- package/internal/session-journal-recovery.js +904 -184
- package/internal/session-journal-schema.js +6 -3
- package/internal/session-journal.js +31 -2
- package/internal/session-log-view.js +189 -43
- package/internal/session-runtime.js +175 -23
- package/internal/settings-compat.js +23 -6
- package/internal/stateful-module-compiler.js +3 -2
- package/internal/stateful-module-runtime.js +7 -3
- package/internal/stateful-root-runtime.js +9 -4
- package/internal/user-binding-authoring.js +2 -7
- package/internal/user-bindings-owner.js +47 -7
- package/internal/validated-parse-repair.js +1 -1
- package/internal/value-wire.js +68 -21
- package/internal/worker-cwd-virtualization.js +20 -18
- package/internal/worker-realm-surfaces.js +3 -3
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +55 -38
- package/src/client-authoring-view.js +286 -168
- package/src/client-binding-review.js +4 -1
- package/src/client-binding-toggle.js +18 -0
- package/src/client-capabilities-view.js +1341 -0
- package/src/client-capability-sort.js +29 -0
- package/src/client-code-editor.js +6 -0
- package/src/client-copy.js +881 -8
- package/src/client-feature-gates.js +15 -5
- package/src/client-host-compat.js +23 -2
- package/src/client-indicator-view.js +4 -6
- package/src/client-primitives.js +280 -0
- package/src/client-repl-view.js +23 -16
- package/src/client-settings-view.js +412 -69
- package/src/client-styles.js +193 -41
- package/src/client-tool-hover.js +185 -0
- package/src/client-tool-view.js +45 -11
- package/src/client-transport.js +44 -9
- package/src/client-workbench.js +9 -10
- package/src/client.js +148 -68
- package/assets/ptc-plus-bindings-en.png +0 -0
- package/assets/ptc-plus-bindings-zh.png +0 -0
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-repair-zh.png +0 -0
- package/client.js +0 -31857
|
@@ -0,0 +1,210 @@
|
|
|
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
|
+
const families = window ? state.families.filter(item => qualifies(item, window)).sort(orderSamples).slice(0, state.limits.windowSize) : []
|
|
202
|
+
// A resumed family can expose a gap behind the irreversible eviction frontier.
|
|
203
|
+
// Surviving samples do not prove the recent settled window until it is full again.
|
|
204
|
+
if (window?.floor > 0 && families.length < state.limits.windowSize) {
|
|
205
|
+
return { status: 'incomplete-window', windowSize: state.limits.windowSize, eligible: null, used: null, coverage: null }
|
|
206
|
+
}
|
|
207
|
+
const used = families.filter(family => family.observations.some(item => item.id === id && item.revision === revision && item.used
|
|
208
|
+
&& (workspaceId === null || item.workspaceId === workspaceId))).length
|
|
209
|
+
return { status: 'usable', windowSize: state.limits.windowSize, eligible: families.length, used, coverage: families.length ? used / families.length : null }
|
|
210
|
+
}
|
|
@@ -20,6 +20,7 @@ const nativePathToFileURL = pathToFileURL
|
|
|
20
20
|
const nativeStripTypeScriptTypes = stripTypeScriptTypes
|
|
21
21
|
const runInContext = Script.prototype.runInContext
|
|
22
22
|
const NativeUint8Array = Uint8Array
|
|
23
|
+
const typedArrayLength = descriptor(Object.getPrototypeOf(NativeUint8Array.prototype), 'length').get
|
|
23
24
|
const NativeWeakRef = WeakRef, deref = WeakRef.prototype.deref
|
|
24
25
|
const NativeError = Error
|
|
25
26
|
const parseJson = JSON.parse
|
|
@@ -65,11 +66,12 @@ export const compilerPlatformBridge = {
|
|
|
65
66
|
fileURLToPath: input => nativeFileURLToPath(nativeUrl(input)),
|
|
66
67
|
pathToFileURL: input => ownUrl(nativePathToFileURL(input)),
|
|
67
68
|
encode(source, encoding) {
|
|
68
|
-
const
|
|
69
|
-
const
|
|
69
|
+
const size = byteLength(source, encoding)
|
|
70
|
+
const buffer = new NativeUint8Array(size)
|
|
71
|
+
const length = apply(codecs[encoding].write, buffer, [source, 0, size])
|
|
70
72
|
return { buffer, length }
|
|
71
73
|
},
|
|
72
|
-
decode: (buffer, encoding) => apply(codecs[encoding].slice, buffer, [0, buffer
|
|
74
|
+
decode: (buffer, encoding) => apply(codecs[encoding].slice, buffer, [0, apply(typedArrayLength, buffer, [])]),
|
|
73
75
|
textEncoder() {
|
|
74
76
|
const encoder = new NativeTextEncoder()
|
|
75
77
|
return { encode: source => apply(encoderEncode, encoder, [source]),
|
|
@@ -2,7 +2,6 @@ import { readFileSync } from 'node:fs'
|
|
|
2
2
|
import { constants, createContext, Script } from 'node:vm'
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
4
|
import { workerData } from 'node:worker_threads'
|
|
5
|
-
import { deserialize } from 'node:v8'
|
|
6
5
|
import { copyCompilerData } from './compiler-data.js'
|
|
7
6
|
import { PreflightError } from './cell-analysis-contract.js'
|
|
8
7
|
import { ModuleRewriteError } from './cell-error.js'
|
|
@@ -29,10 +28,7 @@ const define = compilerDescriptors.defineProperty
|
|
|
29
28
|
const keys = Object.keys
|
|
30
29
|
let compiler
|
|
31
30
|
let compilerScript
|
|
32
|
-
|
|
33
|
-
const bytecodeFile = process.env.DSH_PTC_COMPILER_BYTECODE
|
|
34
|
-
let workerCache = bytecodeFile ? deserialize(readFileSync(bytecodeFile)) : undefined
|
|
35
|
-
if (workerCache?.source !== compilerSource) workerCache = undefined
|
|
31
|
+
let workerCache
|
|
36
32
|
// Bytecode contains no compiler realm or program state. Match the complete
|
|
37
33
|
// source: V8's cache validation alone does not prove source text equality.
|
|
38
34
|
let cachedData = workerData?.compilerCache?.source === compilerSource
|
|
@@ -43,9 +39,6 @@ if (workerData !== null && typeof workerData === 'object') delete workerData.com
|
|
|
43
39
|
/** Reuse compiled compiler code across fresh workers, never their realm state. */
|
|
44
40
|
export function compilerWorkerCache() {
|
|
45
41
|
if (compilerScript === undefined) return undefined
|
|
46
|
-
// Instrumented compiler instances neither produce nor consume bytecode.
|
|
47
|
-
// Coverage's runner supplies a snapshot from its uninstrumented preparation.
|
|
48
|
-
if (workerCache === undefined && coverageEnabled) return undefined
|
|
49
42
|
workerCache ??= { source: compilerSource, data: apply(createCachedData, compilerScript, []) }
|
|
50
43
|
return { source: workerCache.source, data: new NativeUint8Array(workerCache.data) }
|
|
51
44
|
}
|
|
@@ -58,7 +51,7 @@ function compilerEntry() {
|
|
|
58
51
|
// Workers can execute host-prepared cells without compiling dynamic source.
|
|
59
52
|
// Capture source and platform before user code, but parse only on first use.
|
|
60
53
|
compilerScript = new NativeScript(compilerSource, { __proto__: null, filename, displayErrors: false,
|
|
61
|
-
cachedData
|
|
54
|
+
cachedData })
|
|
62
55
|
cachedData = undefined
|
|
63
56
|
const context = nativeCreateContext(dontContextify)
|
|
64
57
|
define(context, '__compilerBaseUrl', { value: import.meta.url })
|
package/internal/config-spec.js
CHANGED
|
@@ -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: '
|
|
57
|
-
descriptionEn: 'Shows the
|
|
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',
|
|
@@ -296,12 +355,6 @@ export const CONFIG_GROUPS = Object.freeze([
|
|
|
296
355
|
labelEn: 'Plugin switch',
|
|
297
356
|
fields: Object.freeze(['enabled']),
|
|
298
357
|
},
|
|
299
|
-
{
|
|
300
|
-
key: 'calls',
|
|
301
|
-
label: '调用容错',
|
|
302
|
-
labelEn: 'Tool call tolerance',
|
|
303
|
-
fields: Object.freeze(['autoDescribeRunCode', 'canonicalizeToolCalls']),
|
|
304
|
-
},
|
|
305
358
|
{
|
|
306
359
|
key: 'syntax',
|
|
307
360
|
label: 'REPL 语法',
|
|
@@ -322,11 +375,17 @@ export const CONFIG_GROUPS = Object.freeze([
|
|
|
322
375
|
labelEn: 'Tool extensions',
|
|
323
376
|
fields: Object.freeze(['userBindingsEnabled', 'cordisToolsEnabled']),
|
|
324
377
|
},
|
|
378
|
+
{
|
|
379
|
+
key: 'calls',
|
|
380
|
+
label: '调用容错',
|
|
381
|
+
labelEn: 'Tool call tolerance',
|
|
382
|
+
fields: Object.freeze(['autoDescribeRunCode', 'canonicalizeToolCalls']),
|
|
383
|
+
},
|
|
325
384
|
{
|
|
326
385
|
key: 'interface',
|
|
327
386
|
label: '界面显示',
|
|
328
387
|
labelEn: 'Interface display',
|
|
329
|
-
fields: Object.freeze(['enhancedToolView', 'replViewEnabled', 'bindingAuthorButtonVisible']),
|
|
388
|
+
fields: Object.freeze(['enhancedToolView', 'toolHoverPreviewEnabled', 'replViewEnabled', 'bindingAuthorButtonVisible']),
|
|
330
389
|
},
|
|
331
390
|
{
|
|
332
391
|
key: 'limits',
|
|
@@ -344,8 +403,19 @@ export const CONFIG_GROUPS = Object.freeze([
|
|
|
344
403
|
'maxValueBigIntDigits',
|
|
345
404
|
]),
|
|
346
405
|
},
|
|
406
|
+
{ key: 'capabilities', label: '能力与上下文', labelEn: 'Capabilities and context',
|
|
407
|
+
fields: Object.freeze(['capabilityContextEnabled', 'capabilityManagementEnabled', 'capabilityBaseline', 'capabilityOverrides',
|
|
408
|
+
'capabilityStatistics', 'capabilityPolicy', 'capabilityRecoveryEnabled', 'modelPreviewEnabled', 'capabilityProfiles', 'capabilityBudgetBytes', 'capabilityDirectoryBytes', 'capabilityRecoveryBytes', 'modelPreviewTokens']) },
|
|
347
409
|
])
|
|
348
410
|
|
|
411
|
+
/** Functional modules share the same section order across every settings entry. */
|
|
412
|
+
export const CONFIG_MODULES = Object.freeze([
|
|
413
|
+
{ key: 'workspace', label: '核心工作区', labelEn: 'Core workspace', groups: Object.freeze(['syntax', 'recovery']) },
|
|
414
|
+
{ key: 'extensions', label: '扩展与容错', labelEn: 'Extensions and tolerance', groups: Object.freeze(['extensions', 'calls']) },
|
|
415
|
+
{ key: 'presentation', label: '界面与资源', labelEn: 'Interface and resources', groups: Object.freeze(['interface', 'limits']) },
|
|
416
|
+
{ key: 'experimental', label: '实验性', labelEn: 'Experimental', groups: Object.freeze(['capabilities']) },
|
|
417
|
+
].map(Object.freeze))
|
|
418
|
+
|
|
349
419
|
/** Resolved defaults, derived once from the field definitions. */
|
|
350
420
|
export const CONFIG_DEFAULTS = Object.freeze(
|
|
351
421
|
Object.fromEntries(CONFIG_FIELDS.map(field => [field.key, field.default])),
|
|
@@ -149,8 +149,9 @@ function containsCompanionSkill(directory, fileExists) {
|
|
|
149
149
|
|
|
150
150
|
/** Resolve the official Cordis companion Skill across DSH preset generations. */
|
|
151
151
|
export async function resolveCompanionSkillDirectory(agentPresets, options = {}) {
|
|
152
|
-
const fileExists = options.fileExists ?? existsSync
|
|
153
152
|
const preset = await agentPresets.resolve(CORDIS_PRESET_ID)
|
|
153
|
+
const resolution = typeof options === 'function' ? options() : options
|
|
154
|
+
const fileExists = resolution.fileExists ?? existsSync
|
|
154
155
|
if (typeof preset?.broken === 'string' && preset.broken.length > 0) {
|
|
155
156
|
throw new Error(`ptc-plus: DSH cordis preset is unavailable: ${preset.broken}`)
|
|
156
157
|
}
|
|
@@ -166,7 +167,7 @@ export async function resolveCompanionSkillDirectory(agentPresets, options = {})
|
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
for (const layout of COMPANION_PACKAGE_LAYOUTS) {
|
|
169
|
-
const pkg =
|
|
170
|
+
const pkg = resolution.packageOf?.(layout.name, resolution.packageBaseUrl)
|
|
170
171
|
if (pkg === undefined) continue
|
|
171
172
|
if (pkg?.name !== layout.name || typeof pkg.dir !== 'string' || !isAbsolute(pkg.dir)) {
|
|
172
173
|
throw new Error(`ptc-plus: DSH pluginPackages returned an invalid ${layout.name} package resource`)
|
|
@@ -175,10 +176,10 @@ export async function resolveCompanionSkillDirectory(agentPresets, options = {})
|
|
|
175
176
|
if (containsCompanionSkill(directory, fileExists)) return directory
|
|
176
177
|
}
|
|
177
178
|
|
|
178
|
-
if (
|
|
179
|
+
if (resolution.packageOf === undefined && resolution.resolveLegacyPackageManifest !== undefined) {
|
|
179
180
|
let manifest
|
|
180
181
|
try {
|
|
181
|
-
manifest =
|
|
182
|
+
manifest = resolution.resolveLegacyPackageManifest('@deepseek-ai/dsh-agent-presets/package.json')
|
|
182
183
|
} catch (error) {
|
|
183
184
|
if (error?.code !== 'MODULE_NOT_FOUND' && error?.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw error
|
|
184
185
|
}
|
|
@@ -320,22 +321,24 @@ export function createCordisToolsOwner(
|
|
|
320
321
|
const cordisInspectLeases = createCordisInspectLeases(ctx)
|
|
321
322
|
const scopedCordisPlugin = cordisInspectLeases.plugin(cordisPlugin)
|
|
322
323
|
const scopedSkillPlugin = exactCompanionSkillPlugin(skillFilesystemPlugin)
|
|
323
|
-
const pluginPackages = ctx.get?.('pluginPackages')
|
|
324
324
|
const packageBaseUrl = ctx.baseUrl
|
|
325
|
-
const skillResolution = companionResolution ?? {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
:
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
: undefined
|
|
338
|
-
|
|
325
|
+
const skillResolution = companionResolution ?? (() => {
|
|
326
|
+
const pluginPackages = ctx.get?.('pluginPackages')
|
|
327
|
+
return {
|
|
328
|
+
packageOf: pluginPackages === undefined
|
|
329
|
+
? undefined
|
|
330
|
+
: (name, parentURL) => {
|
|
331
|
+
if (typeof pluginPackages?.packageOf !== 'function') {
|
|
332
|
+
throw new Error('ptc-plus: cordisToolsEnabled requires the DSH pluginPackages.packageOf API')
|
|
333
|
+
}
|
|
334
|
+
return pluginPackages.packageOf(name, parentURL)
|
|
335
|
+
},
|
|
336
|
+
packageBaseUrl,
|
|
337
|
+
resolveLegacyPackageManifest: pluginPackages === undefined && typeof packageBaseUrl === 'string'
|
|
338
|
+
? specifier => createRequire(packageBaseUrl).resolve(specifier)
|
|
339
|
+
: undefined,
|
|
340
|
+
}
|
|
341
|
+
})
|
|
339
342
|
let disposed = false
|
|
340
343
|
|
|
341
344
|
const leafErrors = error => error instanceof AggregateError
|