fix(web): address the second review round on context provenance

- An empty replacement catalog is a real catalog: `renderCatalogUpdate()`
  publishes zero entries when the last skill disappears, and falling back
  would hide that every earlier name was retired.
- The opaque fallback keeps a `form` declaration this version cannot present.
  It is the one place a newer or foreign log's declared shape would otherwise
  vanish from the UI entirely, since the row marker is also absent there.
- An instruction change with an unrecognized `action` disqualifies the record.
  The action decides the word the row shows, so an unknown one would be
  presented as loaded or updated.
- The catalog list bounds itself and reports the withheld count. Entry count
  is unbounded and the scrollport bounds height, not node count.
- A catalog message keeps content blocks this version does not know, instead
  of dropping model-visible content the extensible union may carry.
- `core.md` defines `ContextFormed`, the interface actually carrying the
  optional field, beside `ContextForm`.
- The superseded-in-part bullet states the affected clauses as one rule rather
  than enumerating them; two rounds of enumeration each missed some, which is
  the shape being fragile rather than the list being wrong.
- The note records the one migration case that does not self-heal: an
  old-format catalog as the only one, with an empty current view, leaves a
  stale catalog nothing replaces.
This commit is contained in:
creatixchu
2026-08-05 16:51:01 +08:00
parent aeb718688e
commit ccd27f3775
16 changed files with 160 additions and 59 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/skill/tool-skill/README.md
README.md: deccda0ece1ffe2cbdb576a8af3801f28695d8d5
README.zh.md: eaf442d9e123a2d56bf09abd9faab47a001e01b9
README.md: c8c8b0d18665893104bc8ac65c1a90cae7816724
README.zh.md: 696e18955bf6f622251a0ca875f53e66419ec944

View File

@@ -10,7 +10,7 @@ Requires `ctx.agents`, `ctx.tools`, and `ctx.skills` (`inject: ['agents', 'tools
At every `agent/step`, the plugin calls `ctx.skills.snapshot()` for the calling session's cwd, forwards the step abort signal to discovery, applies exact `skill` tool visibility, and renders the ordered `name` and `description` entries. When no prior catalog exists and that view is non-empty, it injects an initial durable user-role `<system-reminder>` before the request. Catalog messages contain only those summaries; skill bodies, paths, sources, providers, and `whenToUse` hints remain outside the catalog.
Every catalog message carries the `skill-catalog` source: a `catalog`-form context whose `entries` record exactly the `name` and `description` pairs it published, plus `update` on a replacement. The digest covers those durable entries, not the rendered prose, so the surrounding `<system-reminder>` framing — written for the model — cannot decide whether a republish is needed, and a consumer presenting the list never re-parses the `<available_skills>` block. The plugin scans durable session events backwards without copying them and derives the comparison baseline from the newest visible `skill-catalog` message. When the digest changes, `agent.inject()` records a durable user-role message containing the complete replacement catalog; an empty replacement explicitly retires earlier names. If no catalog remains visible but a recognizable historical catalog exists, compaction hid it and the next complete observation re-establishes the current catalog. An incomplete provider snapshot emits nothing and preserves the last-good model view for retry on the next step. If no prior catalog exists and the current view is empty, no tombstone is necessary.
Every catalog message carries the `skill-catalog` source: a `catalog`-form context whose `entries` record exactly the `name` and `description` pairs it published, plus `update` on a replacement. The digest covers those durable entries, not the rendered prose, so the surrounding `<system-reminder>` framing — written for the model — cannot decide whether a republish is needed, and a consumer presenting the list never re-parses the `<available_skills>` block. The plugin scans durable session events backwards without copying them and derives the comparison baseline from the newest visible `skill-catalog` message it can read; an unreadable record is skipped like any foreign one. When the digest changes, `agent.inject()` records a durable user-role message containing the complete replacement catalog; an empty replacement explicitly retires earlier names. If no catalog remains visible but a recognizable historical catalog exists, compaction hid it and the next complete observation re-establishes the current catalog. An incomplete provider snapshot emits nothing and preserves the last-good model view for retry on the next step. If no prior catalog exists and the current view is empty, no tombstone is necessary.
The catalog is omitted when no model-invocable skills are initially available, and also when that agent's tool view restricts away the shipped `skill` tool or resolves a same-name scoped shadow instead. Visibility changes participate in the digest, keeping prompt guidance, model-visible schema, and executable dispatch aligned.

View File

@@ -10,7 +10,7 @@
每次 `agent/step`,该插件都会使用调用会话的 cwd 调用 `ctx.skills.snapshot()`,将步骤中止信号转发到发现流程,应用 `skill` 工具的精确可见性,并按顺序渲染 `name``description` 条目。如果先前不存在目录且该视图非空,插件会在请求之前注入初始的持久用户角色 `<system-reminder>`。目录消息只包含这些摘要skill 正文、路径、来源、提供方和 `whenToUse` 提示仍位于目录之外。
每条目录消息都携带 `skill-catalog` 来源:一份 `catalog` 形态的上下文,其 `entries` 精确记录本次发布的 `name``description` 对,替换目录另带 `update`。digest 覆盖的是这些持久条目而非渲染出的散文,因此为模型书写的 `<system-reminder>` 包装无法左右是否需要重新发布,消费方展示该列表时也不必再解析 `<available_skills>` 块。插件从后向前扫描持久会话事件且不复制,并以最新一条仍可见的 `skill-catalog` 消息作为比较基线。digest 变化时,`agent.inject()` 会记录一条包含完整替换目录的持久用户角色消息空替换会显式停用较早的名称。如果没有目录仍然可见但历史中存在可识别目录则说明压缩compaction已将其遮蔽下一次完整观察会重新建立当前目录。提供方快照不完整时插件不会发送任何内容并会保留最后一次完整的模型视图以便在下一步骤重试。若不存在先前目录且当前视图为空则不需要 tombstone。
每条目录消息都携带 `skill-catalog` 来源:一份 `catalog` 形态的上下文,其 `entries` 精确记录本次发布的 `name``description` 对,替换目录另带 `update`。digest 覆盖的是这些持久条目而非渲染出的散文,因此为模型书写的 `<system-reminder>` 包装无法左右是否需要重新发布,消费方展示该列表时也不必再解析 `<available_skills>` 块。插件从后向前扫描持久会话事件且不复制,并以最新一条仍可见且可读`skill-catalog` 消息作为比较基线;不可读的记录与外来记录一样被跳过。digest 变化时,`agent.inject()` 会记录一条包含完整替换目录的持久用户角色消息空替换会显式停用较早的名称。如果没有目录仍然可见但历史中存在可识别目录则说明压缩compaction已将其遮蔽下一次完整观察会重新建立当前目录。提供方快照不完整时插件不会发送任何内容并会保留最后一次完整的模型视图以便在下一步骤重试。若不存在先前目录且当前视图为空则不需要 tombstone。
如果最初没有模型可调用 skill则省略目录如果该 agent智能体的工具视图排除了随附的 `skill` 工具,或解析出同名的作用域内遮蔽项,也会省略目录。可见性变更参与 digest 计算,使提示词指引、模型可见 schema 和可执行分派保持对齐。