dsh-ptc-plus 0.4.2 → 0.4.8
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 +170 -76
- package/README.md +171 -77
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
- package/assets/ptc-plus-icon.svg +5 -0
- package/client.js +599 -310
- package/compiler-core.cjs +162 -160
- package/compiler-core.cjs.map +3 -3
- package/docs/README.md +4 -1
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +49 -11
- package/docs/adr/0008-define-agent-operating-contract.md +2 -2
- package/docs/adr/0010-session-log-derived-recovery-tips.md +7 -0
- package/docs/adr/0016-archive-resolved-review-findings.md +3 -3
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +5 -1
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +3 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +3 -1
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +14 -3
- package/docs/adr/0023-global-user-bindings.md +1 -1
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +23 -7
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +4 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +26 -6
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +2 -2
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +29 -0
- package/docs/adr/README.md +4 -4
- package/docs/architecture.md +8 -5
- package/docs/binding-review-placement-design.md +1 -1
- package/docs/client-ui.md +8 -4
- package/docs/durability-design.md +7 -7
- package/docs/installation.md +48 -12
- package/docs/publishing.md +4 -4
- package/docs/release-notes.md +109 -0
- package/docs/runtime-reference.md +25 -12
- package/docs/semantic-validation.md +7 -3
- package/docs/user-bindings.en.md +7 -6
- package/docs/user-bindings.md +6 -5
- package/docs/value-wire.md +1 -1
- package/docs/verification.md +43 -2
- package/index.js +39 -34
- package/internal/async-local-storage-intrinsics.js +12 -7
- package/internal/callable-source-facts.js +37 -1
- package/internal/cell-analysis.js +64 -9
- package/internal/config-spec.js +1 -1
- package/internal/cordis-tools-owner.js +81 -15
- package/internal/direct-surface-owner.js +331 -7
- package/internal/edit-transport-owner.js +80 -20
- package/internal/execution-seam-compat.js +192 -30
- package/internal/kernel-worker.js +162 -72
- package/internal/message-sources.js +14 -0
- package/internal/module-compilation.js +2 -1
- package/internal/rejected-cell-editor.js +11 -21
- package/internal/repl-memory-projection.js +10 -0
- package/internal/repl-scope-normalizer.js +72 -35
- package/internal/repl-value-observer.js +141 -45
- package/internal/runtime-config.js +62 -2
- package/internal/runtime-intrinsics.js +3 -0
- package/internal/runtime-messages.js +6 -4
- package/internal/session-cell-executor.js +24 -1
- package/internal/session-journal-recovery.js +267 -67
- package/internal/session-journal.js +466 -40
- package/internal/session-log-view.js +5 -4
- package/internal/session-runtime.js +45 -21
- package/internal/session-state.js +7 -29
- package/internal/settings-compat.js +66 -1
- package/internal/source-position-map.js +19 -0
- package/internal/user-binding-console-worker.js +23 -9
- package/internal/user-binding-draft-projection.js +3 -2
- package/internal/user-bindings-owner.js +2 -1
- package/internal/user-bindings.js +6 -1
- package/internal/validated-parse-repair.js +6 -2
- package/internal/value-wire.js +95 -30
- package/internal/worker-client.js +99 -8
- package/internal/worker-cwd-virtualization.js +10 -9
- package/internal/worker-output-capture.js +187 -0
- package/internal/worker-output-fence.js +26 -0
- package/internal/worker-realm-surfaces.js +197 -0
- package/internal/worker-repl-realm.js +10 -4
- package/package.json +42 -4
- package/src/client-activity.js +4 -1
- package/src/client-authoring-view.js +121 -62
- package/src/client-console.js +4 -2
- package/src/client-copy.js +4 -2
- package/src/client-feature-gates.js +7 -9
- package/src/client-host-compat.js +104 -27
- package/src/client-repl-view.js +3 -1
- package/src/client-styles.js +12 -1
- package/src/client-tool-view.js +3 -1
- package/src/client-transport.js +2 -2
- package/src/client-workbench.js +10 -7
- package/src/client.js +56 -17
|
@@ -43,6 +43,16 @@ function callableSourceStart(source, node) {
|
|
|
43
43
|
return start + tokens.getToken().start
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function removeCallableSourceMarks(source, prefix, suffix, sources, options) {
|
|
47
|
+
const tree = parse(prefix + source + suffix, options)
|
|
48
|
+
const markers = new Set(sources.entries.map(entry => entry.marker))
|
|
49
|
+
const offset = prefix.length
|
|
50
|
+
const edits = tree.comments.filter(comment => markers.has(comment.value)
|
|
51
|
+
&& comment.start >= offset && comment.end <= offset + source.length)
|
|
52
|
+
.map(comment => ({ start: comment.start - offset, end: comment.end - offset, text: '' }))
|
|
53
|
+
return edits.length === 0 ? source : applySourceEdits(source, identitySourceMap(source.length), edits).code
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
export function callableMarkerPosition(node, source, comments) {
|
|
47
57
|
if (node.type !== 'ArrowFunctionExpression') return node.body.start + 1
|
|
48
58
|
let start = source.lastIndexOf('=>', node.body.start - 1)
|
|
@@ -129,6 +139,7 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
|
|
|
129
139
|
const nativeFunctions = typedRecords.map(record => markedNodes.get(record.marker))
|
|
130
140
|
.filter(node => t.isFunction(node) && !isMethod(node)).sort((left, right) => left.start - right.start)
|
|
131
141
|
const nativeRecipes = new Map()
|
|
142
|
+
const aliasMarkers = []
|
|
132
143
|
const nativeFunctionIndex = start => {
|
|
133
144
|
let low = 0, high = nativeFunctions.length
|
|
134
145
|
while (low < high) {
|
|
@@ -175,7 +186,17 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
|
|
|
175
186
|
original = lowered.source
|
|
176
187
|
const entries = [...originals].map(([key, text]) => [key, key === marker ? original : text])
|
|
177
188
|
for (const [node, fact] of markedCallableOwners(lowered.emitted, entries)) {
|
|
178
|
-
|
|
189
|
+
const generated = lowered.emittedSource.slice(node.start, node.end)
|
|
190
|
+
aliasMarkers.push([generated, fact.marker])
|
|
191
|
+
const generatedMethod = isMethod(node)
|
|
192
|
+
const markerFree = removeCallableSourceMarks(
|
|
193
|
+
generated,
|
|
194
|
+
generatedMethod ? 'class Source {' : '(',
|
|
195
|
+
generatedMethod ? '}' : ')',
|
|
196
|
+
sources,
|
|
197
|
+
options,
|
|
198
|
+
)
|
|
199
|
+
if (markerFree !== generated) aliasMarkers.push([markerFree, fact.marker])
|
|
179
200
|
}
|
|
180
201
|
}
|
|
181
202
|
// Prefer source-preserving erasure. Runtime TypeScript constructs such as
|
|
@@ -202,6 +223,7 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
|
|
|
202
223
|
} catch {
|
|
203
224
|
lowerCallable()
|
|
204
225
|
}
|
|
226
|
+
original = removeCallableSourceMarks(original, prefix, suffix, sources, options)
|
|
205
227
|
entry.buffer = sources.buffers.length
|
|
206
228
|
sources.buffers.push(original)
|
|
207
229
|
entry.start = 0
|
|
@@ -209,6 +231,8 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
|
|
|
209
231
|
originals.set(marker, original)
|
|
210
232
|
if (!method && t.isFunction(node)) nativeRecipes.set(node, { node, start, end: node.end, text: original })
|
|
211
233
|
}
|
|
234
|
+
const finalFacts = factMap(sources)
|
|
235
|
+
for (const [generated, marker] of aliasMarkers) sources.aliases.push([generated, originalText(finalFacts.get(marker))])
|
|
212
236
|
return { ...marked, callableSources: sources, nativeJavaScript }
|
|
213
237
|
}
|
|
214
238
|
|
|
@@ -273,6 +297,18 @@ export function collectCallableSources(code, sources, parserOptions = {}, transf
|
|
|
273
297
|
}).concat(sources.aliases ?? [])
|
|
274
298
|
}
|
|
275
299
|
|
|
300
|
+
/** Restore only callable marks whose transformed starts have exact source-map ownership. */
|
|
301
|
+
export function restoreMappedCallableSourceMarks(mapped, input, transformation, sources, parserOptions = {}) {
|
|
302
|
+
if (sources === undefined || sources.length === 0) return mapped
|
|
303
|
+
const { tree, owners } = callableSourceOwners(mapped.code, sources, parserOptions,
|
|
304
|
+
{ code: input, map: transformation.map })
|
|
305
|
+
const edits = [...owners].map(([node, fact]) => {
|
|
306
|
+
const start = callableMarkerPosition(node, mapped.code, tree.comments)
|
|
307
|
+
return { start, end: start, text: `/*${fact.marker}*/` }
|
|
308
|
+
})
|
|
309
|
+
return edits.length === 0 ? mapped : applySourceEdits(mapped.code, mapped.sourceMap, edits)
|
|
310
|
+
}
|
|
311
|
+
|
|
276
312
|
/** Emission ranges come from typed nodes and exact generator mappings. */
|
|
277
313
|
export function regionCallableRanges(input, generated, sourceRanges, parserOptions) {
|
|
278
314
|
if (!sourceRanges?.length) return []
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { applySourceEdits, mapSourcePosition, mapSourceSpan, sourceRangeHasOriginalText } from './source-position-map.js'
|
|
21
21
|
import { SKIP_AST_CHILDREN, walkAst } from './ast-traversal.js'
|
|
22
22
|
import { compileStatefulRoot } from './stateful-root-compiler.js'
|
|
23
|
-
import { CELL_PARSER_PLUGINS, bindCompilerIntrinsics, lowerStatefulDecorators, lowerStatefulResources, lowerNativeLanguageSource, normalizeStatefulScopes } from './repl-scope-normalizer.js'
|
|
23
|
+
import { CELL_PARSER_PLUGINS, bindCompilerIntrinsics, lowerStatefulDecorators, lowerStatefulResources, lowerNativeLanguageSource, normalizationFailureInput, normalizeStatefulScopes } from './repl-scope-normalizer.js'
|
|
24
24
|
import { markCallableSources, collectCallableSources } from './callable-source-facts.js'
|
|
25
25
|
import { LegacyPreflightError, prepareLegacyProgram } from './legacy-cell-analysis.js'
|
|
26
26
|
import { adaptLegacyModuleImports } from './managed-module-operations.js'
|
|
@@ -220,6 +220,49 @@ function isFunction(node) {
|
|
|
220
220
|
|| node.type === 'ArrowFunctionExpression'
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
function lowerCellImportMeta(result, importMeta, originalSource) {
|
|
224
|
+
if (!result.code.includes('import')) return result
|
|
225
|
+
const tree = parseSource(result.code, {
|
|
226
|
+
sourceType: 'unambiguous',
|
|
227
|
+
errorRecovery: true,
|
|
228
|
+
allowAwaitOutsideFunction: true,
|
|
229
|
+
allowImportExportEverywhere: true,
|
|
230
|
+
allowReturnOutsideFunction: true,
|
|
231
|
+
allowUndeclaredExports: true,
|
|
232
|
+
plugins: CELL_PARSER_PLUGINS,
|
|
233
|
+
}).program
|
|
234
|
+
const references = []
|
|
235
|
+
walkAst(tree, (node) => {
|
|
236
|
+
if (node.type === 'MetaProperty' && node.meta?.name === 'import' && node.property?.name === 'meta') {
|
|
237
|
+
references.push(node)
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
if (references.length === 0) return result
|
|
241
|
+
if (importMeta === null || typeof importMeta !== 'object'
|
|
242
|
+
|| typeof importMeta.url !== 'string' || typeof importMeta.filename !== 'string'
|
|
243
|
+
|| typeof importMeta.dirname !== 'string') {
|
|
244
|
+
const node = references[0]
|
|
245
|
+
throw new ModuleRewriteError('import.meta requires the session module base', mapSourcePosition({
|
|
246
|
+
line: node.loc.start.line,
|
|
247
|
+
column: node.loc.start.column + 1,
|
|
248
|
+
}, result.code, originalSource, result.sourceMap))
|
|
249
|
+
}
|
|
250
|
+
const binding = createGeneratedNameAllocator(tree)('import_meta')
|
|
251
|
+
const directiveEnd = tree.directives.at(-1)?.end ?? tree.interpreter?.end ?? 0
|
|
252
|
+
const declaration = `\nconst ${binding}={__proto__:null,url:${JSON.stringify(importMeta.url)},filename:${JSON.stringify(importMeta.filename)},dirname:${JSON.stringify(importMeta.dirname)}};\n`
|
|
253
|
+
const edits = [
|
|
254
|
+
{ start: directiveEnd, end: directiveEnd, text: declaration },
|
|
255
|
+
...references.map(node => ({
|
|
256
|
+
start: node.start,
|
|
257
|
+
end: node.end,
|
|
258
|
+
text: binding,
|
|
259
|
+
mappings: [{ generatedStart: 0, generatedEnd: binding.length,
|
|
260
|
+
originalStart: node.start, originalEnd: node.end }],
|
|
261
|
+
})),
|
|
262
|
+
]
|
|
263
|
+
return { ...result, ...applySourceEdits(result.code, result.sourceMap, edits), importMetaBinding: binding }
|
|
264
|
+
}
|
|
265
|
+
|
|
223
266
|
|
|
224
267
|
function staticModuleClassification(moduleLoads) {
|
|
225
268
|
const reasons = new Map()
|
|
@@ -282,14 +325,16 @@ export function classifyDurability(code, knownBindings = new Set(), {
|
|
|
282
325
|
const reasons = new Map()
|
|
283
326
|
const addReason = reason => reasons.set(renderDurabilityReason(reason), reason)
|
|
284
327
|
for (const reason of staticModuleClassification(moduleLoads)) addReason(reason)
|
|
285
|
-
const classifyModule = (source) => {
|
|
328
|
+
const classifyModule = (source, form) => {
|
|
286
329
|
if (source?.type !== 'Literal' || typeof source.value !== 'string') {
|
|
287
330
|
addReason(DYNAMIC_MODULE_REASON)
|
|
288
331
|
return
|
|
289
332
|
}
|
|
290
333
|
const classification = classifyModuleSource(source.value)
|
|
291
334
|
if (classification.status === 'forbidden') {
|
|
292
|
-
|
|
335
|
+
const span = sourceMap === undefined || originalSource === undefined ? declarationSpan(source)
|
|
336
|
+
: mapSourceSpan(declarationSpan(source), code, originalSource, sourceMap)
|
|
337
|
+
throw new PreflightError(`cell ${form} of ${source.value} is forbidden because it exposes kernel control`, undefined, span)
|
|
293
338
|
}
|
|
294
339
|
if (classification.reason !== undefined) addReason(classification.reason)
|
|
295
340
|
}
|
|
@@ -327,7 +372,7 @@ export function classifyDurability(code, knownBindings = new Set(), {
|
|
|
327
372
|
nestedScopes = [...scopes, loopBindings(node)]
|
|
328
373
|
}
|
|
329
374
|
if (node.type === 'ImportExpression') {
|
|
330
|
-
classifyModule(node.source)
|
|
375
|
+
classifyModule(node.source, 'import')
|
|
331
376
|
}
|
|
332
377
|
const sourceOwnedThis = sourceMap === undefined
|
|
333
378
|
|| sourceRangeHasOriginalText(sourceMap, code, originalSource, node.start, node.end)
|
|
@@ -336,7 +381,7 @@ export function classifyDurability(code, knownBindings = new Set(), {
|
|
|
336
381
|
}
|
|
337
382
|
if (node.type === 'CallExpression' && node.callee?.type === 'Identifier' && node.callee.name === 'require'
|
|
338
383
|
&& !isBound('require', nestedScopes)) {
|
|
339
|
-
classifyModule(node.arguments[0])
|
|
384
|
+
classifyModule(node.arguments[0], 'require')
|
|
340
385
|
}
|
|
341
386
|
if (node.type === 'Identifier' && isReferenceIdentifier(node, parent, parentKey)
|
|
342
387
|
&& !isBound(node.name, nestedScopes) && AMBIENT_GLOBALS.has(node.name)
|
|
@@ -462,21 +507,31 @@ export function prepareProgram(program, options = {}) {
|
|
|
462
507
|
let normalized
|
|
463
508
|
let prepared
|
|
464
509
|
let callableSources
|
|
510
|
+
let diagnosticInput
|
|
465
511
|
const intrinsicContext = { bindings: new Set() }
|
|
466
512
|
try {
|
|
467
513
|
const marked = markCallableSources(program, undefined, { plugins: CELL_PARSER_PLUGINS, allowImportExportEverywhere: true, allowUndeclaredExports: true },
|
|
468
514
|
{ lowerNativeSource: lowerNativeLanguageSource, nativeUsing: options.nativeUsing })
|
|
515
|
+
const moduleMetadata = lowerCellImportMeta(marked, options.importMeta, program)
|
|
516
|
+
diagnosticInput = moduleMetadata
|
|
469
517
|
callableSources = marked.callableSources
|
|
470
|
-
normalized = normalizeStatefulScopes(
|
|
471
|
-
nativeJavaScript: marked.nativeJavaScript,
|
|
518
|
+
normalized = normalizeStatefulScopes(moduleMetadata.code, moduleMetadata.sourceMap, { mode: options.languageSemantics,
|
|
519
|
+
callableSources, nativeJavaScript: marked.nativeJavaScript,
|
|
520
|
+
deferDecorators: true, deferResources: true, intrinsicContext })
|
|
521
|
+
if (moduleMetadata.importMetaBinding !== undefined) {
|
|
522
|
+
normalized.internalBindings = new Set(normalized.internalBindings)
|
|
523
|
+
normalized.internalBindings.add(moduleMetadata.importMetaBinding)
|
|
524
|
+
}
|
|
525
|
+
diagnosticInput = normalized
|
|
472
526
|
prepared = compileStatefulRoot(normalized.code, { ...options, sourceMap: normalized.sourceMap,
|
|
473
527
|
internalBindings: normalized.internalBindings, dynamicBindings: normalized.dynamicBindings,
|
|
474
528
|
privateBindings: normalized.privateBindings, originalSource: program })
|
|
475
529
|
} catch (error) {
|
|
476
530
|
if (error instanceof ModuleRewriteError) throw error
|
|
477
531
|
const position = error.loc === undefined ? undefined : { line: error.loc.line, column: error.loc.column + 1 }
|
|
478
|
-
|
|
479
|
-
|
|
532
|
+
const failureInput = normalizationFailureInput(error) ?? diagnosticInput
|
|
533
|
+
throw new ModuleRewriteError(error.message, failureInput === undefined ? position
|
|
534
|
+
: mapSourcePosition(position, failureInput.code, program, failureInput.sourceMap))
|
|
480
535
|
}
|
|
481
536
|
prepared = { ...prepared, deferredHelpers: normalized.deferredHelpers }
|
|
482
537
|
intrinsicContext.expression = `this[${JSON.stringify(prepared.rootRuntimeName)}].intrinsics`
|
package/internal/config-spec.js
CHANGED
|
@@ -96,7 +96,7 @@ export const CONFIG_FIELDS = Object.freeze([
|
|
|
96
96
|
{
|
|
97
97
|
key: 'userBindingsEnabled',
|
|
98
98
|
type: 'boolean',
|
|
99
|
-
default:
|
|
99
|
+
default: true,
|
|
100
100
|
label: '启用全局用户 Binding',
|
|
101
101
|
labelEn: 'Enable Global User Bindings',
|
|
102
102
|
description: '管理 TypeScript helper,并将已启用条目作为跨会话的默认 REPL 绑定。',
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as CordisTools from '@deepseek-ai/dsh-tool-cordis'
|
|
2
2
|
import * as CordisSkillFilesystem from '@deepseek-ai/dsh-skill-filesystem'
|
|
3
|
+
import { existsSync } from 'node:fs'
|
|
4
|
+
import { createRequire } from 'node:module'
|
|
3
5
|
import { dirname, isAbsolute, join } from 'node:path'
|
|
4
6
|
import { isDeepStrictEqual } from 'node:util'
|
|
5
7
|
import { RUN_CODE } from './runtime-bridge-owner.js'
|
|
@@ -8,6 +10,16 @@ const CORDIS_PRESET_ID = 'cordis'
|
|
|
8
10
|
const CORDIS_SKILL_NAME = 'cordis-plugin-development'
|
|
9
11
|
const CORDIS_SKILL_PROVIDER = 'ptc-plus-cordis'
|
|
10
12
|
const COMPANION_SKILL_TOOL = 'skill'
|
|
13
|
+
const COMPANION_PACKAGE_LAYOUTS = Object.freeze([
|
|
14
|
+
Object.freeze({
|
|
15
|
+
name: '@deepseek-ai/dsh-agent-preset',
|
|
16
|
+
skillRoot: directory => join(directory, 'skills'),
|
|
17
|
+
}),
|
|
18
|
+
Object.freeze({
|
|
19
|
+
name: '@deepseek-ai/dsh-agent-presets',
|
|
20
|
+
skillRoot: directory => join(directory, 'presets', CORDIS_PRESET_ID, 'skills'),
|
|
21
|
+
}),
|
|
22
|
+
])
|
|
11
23
|
|
|
12
24
|
function exactCompanionCandidates(observation) {
|
|
13
25
|
if (Array.isArray(observation)) {
|
|
@@ -131,15 +143,51 @@ function requireCompanionServices(agent) {
|
|
|
131
143
|
return { agentPresets, skills }
|
|
132
144
|
}
|
|
133
145
|
|
|
134
|
-
|
|
146
|
+
function containsCompanionSkill(directory, fileExists) {
|
|
147
|
+
return fileExists(join(directory, CORDIS_SKILL_NAME, 'SKILL.md'))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Resolve the official Cordis companion Skill across DSH preset generations. */
|
|
151
|
+
export async function resolveCompanionSkillDirectory(agentPresets, options = {}) {
|
|
152
|
+
const fileExists = options.fileExists ?? existsSync
|
|
135
153
|
const preset = await agentPresets.resolve(CORDIS_PRESET_ID)
|
|
136
|
-
if (typeof preset?.
|
|
137
|
-
throw new Error('ptc-plus: DSH cordis preset did not expose an absolute composition path')
|
|
138
|
-
}
|
|
139
|
-
if (typeof preset.broken === 'string' && preset.broken.length > 0) {
|
|
154
|
+
if (typeof preset?.broken === 'string' && preset.broken.length > 0) {
|
|
140
155
|
throw new Error(`ptc-plus: DSH cordis preset is unavailable: ${preset.broken}`)
|
|
141
156
|
}
|
|
142
|
-
|
|
157
|
+
if (preset?.path !== undefined) {
|
|
158
|
+
if (typeof preset.path !== 'string' || !isAbsolute(preset.path)) {
|
|
159
|
+
throw new Error('ptc-plus: DSH cordis preset did not expose an absolute composition path')
|
|
160
|
+
}
|
|
161
|
+
const directory = join(dirname(preset.path), 'skills')
|
|
162
|
+
if (!containsCompanionSkill(directory, fileExists)) {
|
|
163
|
+
throw new Error(`ptc-plus: DSH cordis preset does not contain ${CORDIS_SKILL_NAME}/SKILL.md beside its composition`)
|
|
164
|
+
}
|
|
165
|
+
return directory
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for (const layout of COMPANION_PACKAGE_LAYOUTS) {
|
|
169
|
+
const pkg = options.packageOf?.(layout.name, options.packageBaseUrl)
|
|
170
|
+
if (pkg === undefined) continue
|
|
171
|
+
if (pkg?.name !== layout.name || typeof pkg.dir !== 'string' || !isAbsolute(pkg.dir)) {
|
|
172
|
+
throw new Error(`ptc-plus: DSH pluginPackages returned an invalid ${layout.name} package resource`)
|
|
173
|
+
}
|
|
174
|
+
const directory = layout.skillRoot(pkg.dir)
|
|
175
|
+
if (containsCompanionSkill(directory, fileExists)) return directory
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (options.packageOf === undefined && options.resolveLegacyPackageManifest !== undefined) {
|
|
179
|
+
let manifest
|
|
180
|
+
try {
|
|
181
|
+
manifest = options.resolveLegacyPackageManifest('@deepseek-ai/dsh-agent-presets/package.json')
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (error?.code !== 'MODULE_NOT_FOUND' && error?.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw error
|
|
184
|
+
}
|
|
185
|
+
if (manifest !== undefined) {
|
|
186
|
+
const directory = join(dirname(manifest), 'presets', CORDIS_PRESET_ID, 'skills')
|
|
187
|
+
if (containsCompanionSkill(directory, fileExists)) return directory
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
throw new Error(`ptc-plus: the active DSH installation does not publish a loadable ${CORDIS_SKILL_NAME} companion Skill through its package-resolution surface`)
|
|
143
191
|
}
|
|
144
192
|
|
|
145
193
|
async function requireCompanionSkill(agent, skills) {
|
|
@@ -260,6 +308,7 @@ export function createCordisToolsOwner(
|
|
|
260
308
|
ctx,
|
|
261
309
|
cordisPlugin = CordisTools,
|
|
262
310
|
skillFilesystemPlugin = CordisSkillFilesystem,
|
|
311
|
+
companionResolution = undefined,
|
|
263
312
|
) {
|
|
264
313
|
if (typeof ctx?.agents?.list !== 'function') {
|
|
265
314
|
throw new Error('ptc-plus: cordisToolsEnabled requires the DSH agents.list API')
|
|
@@ -271,6 +320,22 @@ export function createCordisToolsOwner(
|
|
|
271
320
|
const cordisInspectLeases = createCordisInspectLeases(ctx)
|
|
272
321
|
const scopedCordisPlugin = cordisInspectLeases.plugin(cordisPlugin)
|
|
273
322
|
const scopedSkillPlugin = exactCompanionSkillPlugin(skillFilesystemPlugin)
|
|
323
|
+
const pluginPackages = ctx.get?.('pluginPackages')
|
|
324
|
+
const packageBaseUrl = ctx.baseUrl
|
|
325
|
+
const skillResolution = companionResolution ?? {
|
|
326
|
+
packageOf: pluginPackages === undefined
|
|
327
|
+
? undefined
|
|
328
|
+
: (name, parentURL) => {
|
|
329
|
+
if (typeof pluginPackages?.packageOf !== 'function') {
|
|
330
|
+
throw new Error('ptc-plus: cordisToolsEnabled requires the DSH pluginPackages.packageOf API')
|
|
331
|
+
}
|
|
332
|
+
return pluginPackages.packageOf(name, parentURL)
|
|
333
|
+
},
|
|
334
|
+
packageBaseUrl,
|
|
335
|
+
resolveLegacyPackageManifest: pluginPackages === undefined && typeof packageBaseUrl === 'string'
|
|
336
|
+
? specifier => createRequire(packageBaseUrl).resolve(specifier)
|
|
337
|
+
: undefined,
|
|
338
|
+
}
|
|
274
339
|
let disposed = false
|
|
275
340
|
|
|
276
341
|
const leafErrors = error => error instanceof AggregateError
|
|
@@ -329,6 +394,7 @@ export function createCordisToolsOwner(
|
|
|
329
394
|
|
|
330
395
|
const installAgent = (agent) => {
|
|
331
396
|
if (disposed) return Promise.resolve()
|
|
397
|
+
if (withdrawn.has(agent)) return Promise.resolve()
|
|
332
398
|
const mounted = mounts.get(agent)
|
|
333
399
|
if (mounted !== undefined) {
|
|
334
400
|
return mounted.withdrawn === true ? Promise.resolve() : mounted.activation
|
|
@@ -353,13 +419,14 @@ export function createCordisToolsOwner(
|
|
|
353
419
|
mounts.set(agent, mount)
|
|
354
420
|
mount.activation = Promise.resolve().then(async () => {
|
|
355
421
|
const { agentPresets, skills } = requireCompanionServices(agent)
|
|
356
|
-
const skillDirectory = await
|
|
422
|
+
const skillDirectory = await resolveCompanionSkillDirectory(agentPresets, skillResolution)
|
|
357
423
|
if (mount.disposed) return
|
|
358
424
|
|
|
359
425
|
const skillFiber = agent.ctx.plugin(scopedSkillPlugin, {
|
|
360
426
|
providerName: CORDIS_SKILL_PROVIDER,
|
|
361
427
|
includeDefaultRoots: false,
|
|
362
|
-
|
|
428
|
+
bundledSkillDir: skillDirectory,
|
|
429
|
+
watch: false,
|
|
363
430
|
})
|
|
364
431
|
if (typeof skillFiber?.dispose !== 'function') {
|
|
365
432
|
throw new Error('ptc-plus: DSH Context.plugin did not return a disposable Cordis Skill fiber')
|
|
@@ -416,11 +483,11 @@ export function createCordisToolsOwner(
|
|
|
416
483
|
/**
|
|
417
484
|
* Install for a call the host makes on its own behalf.
|
|
418
485
|
*
|
|
419
|
-
*
|
|
420
|
-
* when a listener rejects, so a scope this owner cannot
|
|
421
|
-
* instead of failing an operation another plugin asked
|
|
422
|
-
* enumeration stays strict: there the plugin speaks for
|
|
423
|
-
* have published, and `ready` reports the same failure.
|
|
486
|
+
* The public host lifecycle announces `agent/created` serially and rejects
|
|
487
|
+
* `agents.create()` when a listener rejects, so a scope this owner cannot
|
|
488
|
+
* serve must withdraw instead of failing an operation another plugin asked
|
|
489
|
+
* for. Install-time enumeration stays strict: there the plugin speaks for
|
|
490
|
+
* what it claims to have published, and `ready` reports the same failure.
|
|
424
491
|
*/
|
|
425
492
|
const installForHost = (agent) => {
|
|
426
493
|
let activation
|
|
@@ -451,8 +518,7 @@ export function createCordisToolsOwner(
|
|
|
451
518
|
if (disposed) return
|
|
452
519
|
for (const agent of [...pending]) {
|
|
453
520
|
void Promise.resolve().then(() => installAgent(agent)).catch(error => {
|
|
454
|
-
|
|
455
|
-
ctx.logger?.warn?.('ptc-plus: deferred Cordis activation failed', error)
|
|
521
|
+
withdraw(agent, error)
|
|
456
522
|
})
|
|
457
523
|
}
|
|
458
524
|
})
|