Repository navigation
Conversation
ikliml666
force-pushed
the
fix/490-upstream
branch
2 times, most recently
from
October 30, 2026 16:01
947ab3d to
847be0d
Compare
… probe A host that emits `tools/change` without a scope payload (the official 0.2.1-rc.4 contract is deliberately unfiltered) made every live session re-probe, each probe costing a full prompt assembly. At 198 live sessions this measured 23,143ms and 1,393 assemblies per session creation. Two changes: - publication/changes.ts: read the scope payload newer hosts attach, falling back to our own `withToolChange` window when the payload is empty or absent. An empty payload must not override our attribution with "global", or our own publications would be misread as global; a list payload is not a scope identity and must stay conservative rather than silently dropping the change. - discovery/controller.ts: on an unattributed change, probe one representative session (satisfying the probe-memo "at least once" contract) and lazily invalidate the rest. `system-prompt/change` takes the same path, since a prompt change moves presentation only. Publication-set changes (modes, runtime, configuration) keep using `refresh`, which must visit every entry to withdraw stale registrations. The representative is chosen through the same `coordinable` predicate the reconcile loop uses, so the "at least one probe" contract cannot silently degrade to zero probes — including on an external-only mount, where no sandbox policy is injected. Refs T-Auto#490
ikliml666
force-pushed
the
fix/490-upstream
branch
from
October 30, 2026 17:27
847be0d to
b6e09cc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #490
What was wrong
新建会话时,宿主在建立会话作用域期间会发出未归属(无 scope 载荷)的
tools/change。官方契约就是如此 ——packages/core/tools/src/index.ts:201-209的签名是'tools/change'(): void,注释写明 "deliberately not scope-filtered dispatch"。dsh-ops 把这个信号当作「所有会话表面都可能变了」:
而
refresh()会对每个存活会话重新装配一次完整提示词。装配成本几乎全在tools:sdk段(渲染全部工具的 TypeScript 声明),实测约 0.23 ms/KB,而 SDK 字节由「工具数 × schema 复杂度」决定。在官方
dsh-v0.2.1-rc.4、150 个近生产 schema 下实测:装配次数 = 存活会话数 + 3,严格线性 —— 与 issue 报告的 2.7 s 中位数 / 3.9 s 最差吻合。The change
两处,都在
tools/change的归属处理上。src/tool-changes.ts— 新增hostScope(args),读取较新宿主在载荷里附带的{ scope }:载荷为空对象或缺失时回退到插件自己的
withToolChange窗口,而不是覆盖成[](全局)。这一点是必须的:dsh-ops 自己的发布也会被宿主包成空载荷,若判成全局就丢了自己的归属,test/discovery-probe-memo.test.mjs会立刻变红。src/discovery/index.ts— 未归属变更只探测一个代表会话,其余惰性失效:if (!scopes?.length) { - refresh() + const representative = [...entries].find(([agent, entry]) => + entry.ctx && ownership?.owns(agent) && permitted(agent)) + if (representative) { + invalidate(representative[1]) + void reconcile([representative[0]]) + } + for (const [agent, entry] of entries) + if (agent !== representative?.[0]) invalidate(entry) return }代表会话必须从本组件真正拥有的会话里选(
owns + permitted + has ctx三者都满足)。取entries插入序首位的话,若那个会话不属本组件,reconcile会把它过滤掉,「至少探测一次」的约定就静默退化成零次。刻意没做的
test/ptc-sdk.test.mjs:199-205有第三方system-prompt/assemblehook 能在装配阶段把某个 scope 翻成 PTC-only,且不产生任何 registry 事件。只失效不探测会漏掉它 —— 代表会话这一次探测正是保守性的下限。src/ptc/index.ts的未归属分支。它只标记 queued、不自己装配,实测探针数为 0,不是放大源。src/official-tool-hiding.ts:189的ctx.on('tools/change', reconcile)。默认禁用,且成本约 4 ms/会话,远小于装配成本。Validation
Every new assertion was checked in both directions — reverting the implementation turns it red:
staleAllback torefreshonsystem-prompt/changeAssertionError: a system-prompt/change must probe exactly one representative session/8 !== 1hostScopepayload readAssertionError: a scope-attributed change must not re-probe sibling sessionsArray.isArray(scope)guardAssertionError: a list payload is not a scope identity and must stay conservative/0 !== 1coordinableexternal branchdiscovery-external-lazy/0 !== 1Measurement on official
v0.2.1-rc.4150 near-production schemas, each round creating one session plus one third-party registration and one unattributed
tools/change:Zero live sessions are unchanged, because no sibling assembly exists to save. The remaining linear growth comes from the PTC path, which still probes every entry on an unattributed change: bounding it requires per-session presentation verdicts without a full assembly, and the three approaches tried (single representative,
surfaceOfcomparison,run_codevisibility) each either broketest/discovery.test.mjsor failed to actually short-circuit. It is recorded on the issue rather than guessed at here.