dsh-ptc-plus 0.5.7 → 0.5.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 +4 -0
- package/README.md +4 -0
- package/docs/adr/0038-freeze-session-capability-plans.md +1 -1
- package/docs/adr/0042-persist-provider-availability-and-handback.md +8 -4
- package/docs/capability-planning-components.md +1 -1
- package/internal/capability-environment.js +7 -3
- package/internal/runtime-contexts.js +5 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -39,6 +39,8 @@ Replace `<profile>` with your profile name, restart DSH, and select **PTC mode**
|
|
|
39
39
|
|
|
40
40
|
The plugin declares the DSH compatibility range `>=0.2.1-0 <0.2.3` for the Host's version preflight; public capability checks and packed tests verify integration within that range.
|
|
41
41
|
|
|
42
|
+
When DSH exits or restarts through its built-in updater, the plugin releases computation resources without scanning historical sessions. Unloading the plugin while DSH continues running still persists session handback notices; recovery after restart uses the session records.
|
|
43
|
+
|
|
42
44
|
The session shows a **REPL** tab, and wide screens show a **PTC Plus** header indicator. The sparkle button beside the composer opens the global binding menu, the session capability workbench, and **PTC Plus settings**; **Plugins → Installed → dsh-ptc-plus → Configure** in the sidebar is the same configuration.
|
|
43
45
|
|
|
44
46
|
## Feature overview
|
|
@@ -130,6 +132,8 @@ return capabilities.search({ query: 'read file content', budgetBytes: 8192 })
|
|
|
130
132
|
|
|
131
133
|
Search uses English/Chinese lexical matching, and a miss does not prove that a tool is unavailable. Discovery grants no additional authority, DSH still validates and schedules calls, and more usage notes are in [capability discovery](docs/runtime-reference.md#capability-discovery).
|
|
132
134
|
|
|
135
|
+
With capability management enabled, interface changes after a DSH update append a notice when the conversation resumes: on-demand capabilities share one retrieval instruction and a list of names, full disclosure supplies complete replacement contracts, and withdrawn capabilities explicitly revoke their earlier contracts. With the accepted notice retained and no further interface changes, continuation or restart does not repeat it.
|
|
136
|
+
|
|
133
137
|
## Global bindings: reuse functions across sessions
|
|
134
138
|
|
|
135
139
|
Session variables serve the current task, and **global bindings** save common TypeScript functions for multiple sessions to load: after saving `textTools`, the model can call `textTools.clean(text)`. Create entries by hand, import `.ts` files, or ask the model:
|
package/README.md
CHANGED
|
@@ -39,6 +39,8 @@ dsh plugin --profile <profile> add dsh-ptc-plus
|
|
|
39
39
|
|
|
40
40
|
插件声明 DSH 兼容范围 `>=0.2.1-0 <0.2.3`,由宿主执行版本预检;范围内的接口适配仍通过公共能力检查与打包测试验证。
|
|
41
41
|
|
|
42
|
+
退出或通过内置更新重启 DSH 时,插件释放计算资源,不遍历历史会话。运行中单独卸载插件仍会持久化会话交接提示;重启后的状态恢复沿用会话记录。
|
|
43
|
+
|
|
42
44
|
界面默认显示会话里的 **REPL** 页签,宽屏顶部还有 **PTC Plus** 标志。输入框旁的星光按钮打开全局绑定菜单、会话能力工作台和 **PTC Plus 设置**,侧边栏的 **插件 → 已安装 → dsh-ptc-plus → 配置** 是同一套设置。
|
|
43
45
|
|
|
44
46
|
## 功能概览
|
|
@@ -130,6 +132,8 @@ return capabilities.search({ query: 'read file content', budgetBytes: 8192 })
|
|
|
130
132
|
|
|
131
133
|
搜索按中英文词法匹配,没匹配到不代表工具不存在。发现工具不会增加权限,实际调用仍由 DSH 校验和调度,更多用法见[能力发现](docs/runtime-reference.md#capability-discovery)。
|
|
132
134
|
|
|
135
|
+
启用能力管理时,DSH 更新造成的接口变化会在续聊时追加通知:按需能力共用一条查询提示和名称列表,完整披露项提供完整新契约,已撤下项明确撤销旧说明;已接受的通知仍保留且接口未再变化时,继续会话或重启不会重复通知。
|
|
136
|
+
|
|
133
137
|
## 全局绑定:跨会话复用常用函数
|
|
134
138
|
|
|
135
139
|
会话变量服务于当前任务,**全局绑定**把常用 TypeScript 函数保存下来供多个会话加载:保存 `textTools` 之后,模型就能调用 `textTools.clean(text)`。你可以手动创建、导入 `.ts` 文件,也可以让模型帮忙:
|
|
@@ -24,7 +24,7 @@ Automatic allocation uses session-family coverage rather than raw call volume. A
|
|
|
24
24
|
|
|
25
25
|
Allocation considers both coverage and contract length within a configurable explanation budget. The directory has its own bound; every long-tail name need not remain in the prompt. Explicit full disclosure overrides the explanation budget, with an observable overage rather than silent truncation or demotion. A budget overage does not establish that a request fits a model's physical context limit.
|
|
26
26
|
|
|
27
|
-
A session capability plan records the effective enablement and disclosure choices when the session begins. Statistics and saved configuration do not redistribute an existing session, including after restoration. A user-triggered slash command explicitly refreshes the session. Refresh synchronizes descriptions, discovery and runtime exposure and supplies a durable, model-visible notification consistent with what actually became effective. A UI receipt alone is insufficient. Refresh uses Host lifecycle boundaries and does not require a paid model request merely to execute the human command. A stored plan cannot preserve a capability that its Host owner has actually removed or forbidden. Symmetrically, a live Host member that the frozen plan has not yet allocated remains declared under the Host's own presentation — or under its explicit availability closure — until refresh folds it into the plan; a pending allocation boundary never removes the member's identity from the declaration, discovery or dispatch surfaces. The frozen choice does not freeze the calling contract. Live parameter, output, instruction-dependency and Skill invocation changes append a validated `ptc-capability-contracts/
|
|
27
|
+
A session capability plan records the effective enablement and disclosure choices when the session begins. Statistics and saved configuration do not redistribute an existing session, including after restoration. A user-triggered slash command explicitly refreshes the session. Refresh synchronizes descriptions, discovery and runtime exposure and supplies a durable, model-visible notification consistent with what actually became effective. A UI receipt alone is insufficient. Refresh uses Host lifecycle boundaries and does not require a paid model request merely to execute the human command. A stored plan cannot preserve a capability that its Host owner has actually removed or forbidden. Symmetrically, a live Host member that the frozen plan has not yet allocated remains declared under the Host's own presentation — or under its explicit availability closure — until refresh folds it into the plan; a pending allocation boundary never removes the member's identity from the declaration, discovery or dispatch surfaces. The frozen choice does not freeze the calling contract. Live parameter, output, instruction-dependency and Skill invocation changes append a validated `ptc-capability-contracts/v2` notification at an accepted step, retaining the original plan digest and current revision vector. Full choices receive complete replacement contracts; on-demand choices receive a revision notice and current discovery; removed capabilities receive a withdrawal. Saved settings and statistics still cannot reallocate that session. Unchanged views do not repeat notifications, and recovery uses visible validated notifications without rewriting the initial plan or prefix. The v2 body groups all changed on-demand identities under one instruction to retrieve each current complete contract before invocation. Full contracts, deduplicated owner dependencies and individual withdrawals retain their complete statements. Historical v1 notifications retain their exact original per-capability body; validation dispatches by the recorded generation, rejects unknown generations and modified bodies, and accepts retained v1 evidence for deduplication after an upgrade. The source snapshot and revision evidence remain in the session record; grouping changes the model-visible body, not the capability choices or calling interfaces.
|
|
28
28
|
|
|
29
29
|
Explicit session choices and saved choices have separate owners. `/capabilities set session <encoded-id> <full|on-demand|closed|automatic|inherit>` changes only the current session. `automatic` explicitly removes that identity's saved manual override from the effective input; `inherit` removes its temporary override and resumes the adopted source choice. Ordinary `/capabilities refresh` adopts current saved configuration while retaining temporary choices. `/capabilities set global <encoded-id> <full|on-demand|closed|automatic>` saves through the public revision-checked settings writer and updates this session in the same Host maintenance operation, clearing only that identity's temporary choice. Other existing sessions remain frozen. Server choices allow only `closed`, `automatic` and session `inherit`; their closure precedes child choices, and reopening preserves children.
|
|
30
30
|
|
|
@@ -14,11 +14,13 @@ Provider availability is a source-owned append-only fact, distinct from computat
|
|
|
14
14
|
|
|
15
15
|
The passive runtime-message owner publishes disablement or resumed availability only through a witnessed, accepted, non-aborted Host step. Rejected, paused or cancelled proposals do not become delivery. Existing plugin declarations or a plugin journal namespace establish prior provider participation; journal presence does not prove any binding recoverable, initialized, live or known to the model. Repetition does not append a duplicate withdrawal. Re-enablement cannot restore a capability plan preceding the last validated withdrawal.
|
|
16
16
|
|
|
17
|
-
Complete plugin release first awaits the existing runtime controller's worker and registration cleanup, then persists unloaded availability while public session services remain available. Loaded sessions use their real Session append and Host flush. Unloaded stored sessions use the public persistence list and exclusive write handle, validate their surface with public pure interpreters, and append their own new withdrawal fact with append/flush/close. No Agent is created, model awakened, effect replayed, event sequence fabricated or second session coordinator introduced.
|
|
17
|
+
Complete plugin release first awaits the existing runtime controller's worker and registration cleanup. When the application remains running, it then persists unloaded availability while public session services remain available. Loaded sessions use their real Session append and Host flush. Unloaded stored sessions use the public persistence list and exclusive write handle, validate their surface with public pure interpreters, and append their own new withdrawal fact with append/flush/close. No Agent is created, model awakened, effect replayed, event sequence fabricated or second session coordinator introduced.
|
|
18
|
+
|
|
19
|
+
Whole application shutdown has no continuing model request to hand back to a native provider. When the public Cordis root fiber is unloading, release completes runtime cleanup without enumerating loaded or stored sessions or appending withdrawals. The plugin's own unloading state does not establish application shutdown: a provider-only unload still performs durable handback. Restarted providers reconcile availability at their next accepted Host step and recover computation only from independently verified session evidence. Process shutdown does not preserve a live heap or establish recovery proof.
|
|
18
20
|
|
|
19
21
|
The lifecycle owner captures the public session, query and persistence services while they exist, rather than looking them up after removal. Loaded history uses a public immutable query cut. Offline handback is a new fact written by the releasing provider, not a Session resume: the exclusive public write handle supplies validated stored events and their next offset; `foldSurface` and `deriveEventMessage` apply installed first-party and live registered pure interpreters; `snapshotSessionEvent` validates the producer's own new `user/message` envelope; the extended surface is validated before `handle.append`, `flush` and `close`. It does not instantiate or publish an offline Session, invent a foreign resume marker, impersonate a user or command, or use deprecated synchronous readers. A loaded session whose query dependencies are already destroyed still reports that failure; retaining a service object does not recreate its dependencies.
|
|
20
22
|
|
|
21
|
-
Each session is independent. A writer conflict, damaged storage, flush refusal or unavailable public persistence ownership remains a real handback failure; cleanup continues for other sessions and aggregates these failures. The plugin never steals another process's writer. Successful in-memory append alone is not crash-durable evidence.
|
|
23
|
+
Each session is independent. A writer conflict, damaged storage, flush refusal or unavailable public persistence ownership remains a real provider-only handback failure; cleanup continues for other sessions and aggregates these failures. The plugin never steals another process's writer. Successful in-memory append alone is not crash-durable evidence. Dependency removal or external ownership can prevent persistence, so complete handback cannot be claimed in those cases. The failure must remain observable to the Host lifecycle owner; no promise is made that a destroyed dependency can still write. Runtime cleanup failures remain observable during both provider-only release and application shutdown.
|
|
22
24
|
|
|
23
25
|
User binding toggles publish a separate configuration receipt only after storage confirms the revision and entry. A public Conversation definition renders the source-validated receipt as its own visible node using current Client locale. The receipt proves saved enablement only; model catalog delivery still follows accepted-step publication and worker initialization still follows actual execution. It has no turn-trigger identity and does not wake a task. Capability command receipts similarly distinguish updated, pending first acceptance, unavailable, invalid usage, and globally saved but not applied states.
|
|
24
26
|
|
|
@@ -30,12 +32,14 @@ User binding toggles publish a separate configuration receipt only after storage
|
|
|
30
32
|
|
|
31
33
|
**Steal offline ownership or edit JSONL files.** It bypasses the Host's single-writer and validation contracts. Refusal is preserved and reported, not converted to successful handback.
|
|
32
34
|
|
|
35
|
+
**Persist archive-wide withdrawals during application shutdown.** No native request continues in that process, and historical storage size would determine whether the Host can acknowledge shutdown to its updater. Shutdown owns resource release; provider-only removal owns durable handback to a continuing Host.
|
|
36
|
+
|
|
33
37
|
**Use user/command/turn-trigger events for a binding toggle.** It confuses saved configuration with task initiation and may wake a model. A separately sourced fact and public renderer preserve both lifecycle and visibility.
|
|
34
38
|
|
|
35
39
|
## Consequences
|
|
36
40
|
|
|
37
|
-
Availability is reconstructable in native sessions after plugin removal without a plugin-specific projection interpreter. A declaration withdrawal cannot preserve the old continuous heap, prove source knowledge, provide missing Host tools or solve the independent top-level editor admission boundary. Persistence failures are explicit partial lifecycle outcomes.
|
|
41
|
+
Availability is reconstructable in native sessions after provider-only plugin removal without a plugin-specific projection interpreter. Whole application shutdown does not traverse the session archive and still awaits resource release. A declaration withdrawal cannot preserve the old continuous heap, prove source knowledge, provide missing Host tools or solve the independent top-level editor admission boundary. Persistence failures are explicit partial lifecycle outcomes.
|
|
38
42
|
|
|
39
43
|
## Evidence
|
|
40
44
|
|
|
41
|
-
`test/runtime-messages.test.js`, `test/capability-environment.test.js`, `test/user-bindings-owner.test.js` and `test/client-runtime.spec.js` exercise accepted publication, restoration, storage confirmation and locale rendering.
|
|
45
|
+
`test/runtime-messages.test.js`, `test/capability-environment.test.js`, `test/user-bindings-owner.test.js` and `test/client-runtime.spec.js` exercise accepted publication, restoration, storage confirmation and locale rendering. `test/execution-provider-owner.test.js` exercises whole-root Loader shutdown before any conversation and after a journaled computation, including actual worker exit and refusal of archive access. Current packed-Host integration evidence remains necessary for installed desktop update claims; fixtures do not stand in for that boundary.
|
|
@@ -66,7 +66,7 @@ capabilityCoverage(state, { userId, workspaceId, id, revision })
|
|
|
66
66
|
|
|
67
67
|
宿主 prompt section 的空字符串或纯空白说明不形成契约依赖,但其 section 身份仍可证明 MCP server 归属。工具签名和资源契约保持可发现;非空说明原样保留并去重,非字符串等无效依赖仍由方案校验拒绝。
|
|
68
68
|
|
|
69
|
-
工具完整契约与程序检索元数据来自公共 `ToolRuntime.schemas(scope)` 与 `ToolRuntime.get(name, scope).output.schema` 的准确 JSON 快照,包含参数及返回值 schema;输出 schema 也参与修订身份和恢复比较。新 cell 的检索与 inspect 从同一份当前 SDK 快照派生结构化 schema、签名、依赖及修订,不将冻结方案中的旧签名覆盖到新返回值类型上;冻结的是启用与披露选择。每次准备与接受步骤重新读取 live owner 契约;同名工具的参数、输出或说明依赖变化,以及 Skill 调用资格变化或能力移除,以 `capability-contracts` 的 `ptc-capability-contracts/
|
|
69
|
+
工具完整契约与程序检索元数据来自公共 `ToolRuntime.schemas(scope)` 与 `ToolRuntime.get(name, scope).output.schema` 的准确 JSON 快照,包含参数及返回值 schema;输出 schema 也参与修订身份和恢复比较。新 cell 的检索与 inspect 从同一份当前 SDK 快照派生结构化 schema、签名、依赖及修订,不将冻结方案中的旧签名覆盖到新返回值类型上;冻结的是启用与披露选择。每次准备与接受步骤重新读取 live owner 契约;同名工具的参数、输出或说明依赖变化,以及 Skill 调用资格变化或能力移除,以 `capability-contracts` 的 `ptc-capability-contracts/v2` 来源消息更新当前效力。消息记录原方案 digest、当前修订向量、变化项和当前完整说明超额,digest 与精确正文共同校验;不修改原方案。仍选完整的项提供完整新契约和去重依赖,按需项合并为一条调用前检索当前完整契约的提示及变化身份列表,移除项逐项明确撤销。历史 v1 消息保留原有逐项正文,新 v2 按分组正文核验;两代均校验 digest 与精确正文,保留的有效旧通知仍参与恢复去重,未知代际不作为证据。投递经 accepted step,拒绝与取消不提交;未变视图不重复追加,恢复按正式可见消息协调。实际可用和使用统计采用当时的契约修订,已准入调用保留捕获时的观察身份。顶层模型工具 schema、准入与调用规范化继续使用独立的 `ToolRuntime.schemas(scope)`,不向模型工具定义追加 SDK 输出字段。冻结方案只为冻结时已捕获的身份保存选择;宿主随后 live 提供且未被显式关闭的新成员保持宿主声明(工具保留 SDK 命名成员与类型化签名,Skill 保持可加载),刷新才把它折入方案,刷新边界不把成员身份从声明、检索或分派面移除。
|
|
70
70
|
|
|
71
71
|
Skill 目录的 `snapshot.complete` 由公共 registry 拥有,表示本次 provider 发现与 revision 稳定性已证明。暂时不完整不证明某个 Skill 已撤下:已有冻结环境保留最近完整观察或已校验、已接受的本会话 `capability-contracts` 更新所证明的 Skill 视图,不发布未完整证明的新 Skill,也不把这轮 Skill 观察计为新的统计资格。工具与 MCP 继续消费当前 live owner。冷恢复沿同一已接受契约来源保留确证撤下结果,不能由初始快照复活旧 Skill。新环境和显式刷新仍要求完整目录,失败保留旧环境;取消和真实 Host 错误保持原语义。session Skill projection 仅在实际输出变化时调用 registration-scoped `invalidate()`,不因每次父子会话准备使全局 revision 无意义变化。
|
|
72
72
|
|
|
@@ -496,8 +496,11 @@ const callingRevision = capability => capability === undefined || capability.eff
|
|
|
496
496
|
|
|
497
497
|
function contractUpdateMessage(snapshot, sessionId) {
|
|
498
498
|
const dependencies = new Map()
|
|
499
|
+
// Recorded generations retain their exact body for restoration and deduplication.
|
|
500
|
+
const grouped = snapshot.schema === 'ptc-capability-contracts/v2'
|
|
501
|
+
const onDemand = grouped ? snapshot.changes.filter(change => change.capability !== null && change.disclosure === 'on-demand') : []
|
|
499
502
|
const text = ['PTC Plus current calling contracts. These updates replace the earlier contracts for the listed capabilities only; session enablement and disclosure choices are unchanged.',
|
|
500
|
-
...snapshot.changes.map(change => change.capability === null
|
|
503
|
+
...snapshot.changes.filter(change => !grouped || change.capability === null || change.disclosure === 'full').map(change => change.capability === null
|
|
501
504
|
? `### ${change.id}\nThe Host no longer supplies this capability. Its earlier calling contract no longer applies.`
|
|
502
505
|
: change.disclosure === 'full'
|
|
503
506
|
? [...(change.capability.dependencies ?? []).flatMap(item => {
|
|
@@ -508,6 +511,7 @@ function contractUpdateMessage(snapshot, sessionId) {
|
|
|
508
511
|
return [`### dependency:${item.id}\n${item.text}`]
|
|
509
512
|
}), `### ${change.id}\n${change.capability.contract}`].join('\n\n')
|
|
510
513
|
: `### ${change.id}\nThe Host calling contract has changed. Retrieve the current complete contract with capabilities.search(${JSON.stringify({ query: change.id })}) before invoking it.`),
|
|
514
|
+
...(onDemand.length === 0 ? [] : [`### Updated on-demand contracts\nBefore invoking any capability below, retrieve its current complete contract with capabilities.search({query: "<capability ID>"}):\n${onDemand.map(change => `- ${change.id}`).join('\n')}`]),
|
|
511
515
|
...(snapshot.overageBytes > 0 ? [`The currently selected full contracts exceed the configured budget by ${snapshot.overageBytes} UTF-8 bytes.`] : [])].join('\n\n')
|
|
512
516
|
return createUserMessage({ source: { kind: PTC_MESSAGE_SOURCE_KIND, form: 'capability-contracts', sessionId,
|
|
513
517
|
digest: digest(snapshot), snapshot }, content: [{ type: 'text', text }] })
|
|
@@ -517,7 +521,7 @@ function readContractUpdate(message, planDigest, sessionId) {
|
|
|
517
521
|
const source = message?.source
|
|
518
522
|
const snapshot = source?.snapshot
|
|
519
523
|
if (source?.kind !== PTC_MESSAGE_SOURCE_KIND || source.form !== 'capability-contracts'
|
|
520
|
-
|| source.sessionId !== sessionId ||
|
|
524
|
+
|| source.sessionId !== sessionId || !['ptc-capability-contracts/v1', 'ptc-capability-contracts/v2'].includes(snapshot?.schema)
|
|
521
525
|
|| snapshot.planDigest !== planDigest || source.digest !== digest(snapshot)
|
|
522
526
|
|| !Array.isArray(snapshot.views) || !Array.isArray(snapshot.changes)
|
|
523
527
|
|| !Number.isSafeInteger(snapshot.overageBytes) || snapshot.overageBytes < 0) return undefined
|
|
@@ -1953,7 +1957,7 @@ export function createCapabilityEnvironment({ ctx, config, toolSchemasForAgent,
|
|
|
1953
1957
|
.filter(item => state.snapshot.capabilities.some(original => original.id === item.id)),
|
|
1954
1958
|
manual: { baseline: [], overrides: Object.fromEntries(state.snapshot.plan.entries.map(entry => [entry.id, entry.selection === 'host' ? 'full' : entry.selection])) },
|
|
1955
1959
|
navigationText: NAVIGATION, configurationRevision: state.snapshot.plan.configurationRevision })
|
|
1956
|
-
additions.push(contractUpdateMessage({ schema: 'ptc-capability-contracts/
|
|
1960
|
+
additions.push(contractUpdateMessage({ schema: 'ptc-capability-contracts/v2', planDigest, views, changes,
|
|
1957
1961
|
overageBytes: livePlan.budgets.overageBytes }, sessionId))
|
|
1958
1962
|
}
|
|
1959
1963
|
const expandedVisible = new Set([...visible, ...messages].flatMap(message => message.source?.form === 'capability-descriptions'
|
|
@@ -23,8 +23,12 @@ function hadPluginComputation(session) {
|
|
|
23
23
|
&& Object.hasOwn(event.data?.meta ?? {}, JOURNAL_KEY))
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/** Persist
|
|
26
|
+
/** Persist provider-only release through public Session and single-writer owners. */
|
|
27
27
|
export async function handBackRuntimeSessions(ctx, owners) {
|
|
28
|
+
// Cordis FiberState.UNLOADING: application teardown has no continuing model
|
|
29
|
+
// request to hand back. Archive-wide writes do not belong to process cleanup;
|
|
30
|
+
// a restarted provider reconciles availability at the next accepted step.
|
|
31
|
+
if (ctx.root?.fiber.state === 5) return
|
|
28
32
|
const service = name => owners ? owners[name] : typeof ctx.get === 'function' ? ctx.get(name) : ctx[name]
|
|
29
33
|
const sessions = service('sessions')
|
|
30
34
|
const persistence = service('sessionPersistence')
|