跳到內容
Crystal CodeDocs
繁繁體中文

Hooks, raw hooks, and loading

本頁內容尚未翻譯。

Hooks run in plugin load order. The next hook sees the previous replacement. The same pipeline wraps built-in tools, other plugins, and external tools. A hook that throws is skipped with an English note.

Hook May May not
OnSessionStartedAsync / OnSessionEndedAsync Read workspace root, session id, and approval mode Prompt the operator, or write config.json
OnPrompt Append text to the instruction block Replace Work, Plan, or Review
OnUserMessageAsync Replace the user message before it is stored Leave the stored message blank, or run for a side question
OnTurnStartedAsync / OnTurnFinishedAsync Read the stored user text, mode, and, when the turn ends, the stop reason Change the transcript
TransformModelAsync Change user, assistant, or tool-result text for one outbound call, or drop image references already on an item Reorder, add, or drop items, edit a system message, add an image, or write the archive
OnModelResponseAsync Read the purpose, finish reason, returned items, and usage of one completed work, plan, side, or compaction call Change the transcript, the archive, or the response the host uses
OnToolCallAsync Replace the name or arguments Change the call id, skip the call, or skip approval
OnToolResultAsync Replace the text result and, on an image-capable turn, its images Mark an approval as passed
OnApproval Raise risk, or require another prompt Lower the host risk, or auto-pass
OnCompaction Append facts to the summary prompt or the summary body Drop history, or replace the compactor

A rewritten tool call is classified and approved again before it runs. Text-only turns ignore images returned by OnToolResultAsync. Image types are image/png, image/jpeg, image/gif, and image/webp.

TransformModelAsync sees the model transcript for a work, plan, or side request, and the older turns about to be summarized, after every raw hook has run. It does not see the approval review. Returned image lists may only name attachments already on that item. The stored transcript and the archive stay as they were, except for text replaced by OnUserMessageAsync.

OnModelResponseAsync runs after one of those calls returns and before the host commits that candidate or runs its tools. Approval review does not call it. The host keeps the candidate it received.

TransformModelAsync changes only the items sent on that one call. The stored transcript and the archive stay as they were. Rewriting user, assistant, or tool-result text can make the model answer from a conversation the operator did not store. A replacement that reorders, adds, or drops items, changes a role or a tool call, or edits a system message is skipped with an English note. Structural changes belong to raw hooks.

Session start runs after the plugin load, and again when another coding session becomes active. Session end runs when the session closes, when /clear, /resume, or /fork replaces it, and before a /cd or /space reload. The end callback still carries the workspace and session being left. A resume that also changes workspace is one end and one start. crystal run closes the session when the process finishes.

A raw hook is a privileged, low-level extension point. IPluginRawHook is a separate interface. A plugin registers raw hooks on PluginContribution.RawHooks, apart from Hooks. The operator does not approve them. The host writes one note for each plugin that registers one, for example Plugin 'Acme' registered a raw hook.

Raw hooks run in plugin load order. The next raw hook sees the previous replacement. A raw hook that throws is skipped with an English note. The host does not hold a raw hook to the rules that bind ordinary hooks. It refuses only what it cannot represent.

Raw hook May May not
RebuildModelAsync For one outbound call, drop, reorder, add, or rewrite items of any kind. That includes the live system prompt, roles, tool calls, tool results, and reasoning, and it may split a call from its result Repeat an item id, return empty reasoning text, name an image the session does not hold, add an image to a request that cannot carry images, or write the archive
RewritePrompt Replace the composed system text for work, plan, review, or compaction with any string, including an empty one. Work and plan text becomes the live system message and is archived Write ~/.crystal/prompts
RewriteCompaction Replace the compaction user prompt, or the summary body that is stored, with any string, including an empty one Choose which history is folded, or replace the compactor
RewriteApproval After ordinary approval hooks, set any risk, any authority, any summary, and either prompt requirement. The approval policy then uses that classification Skip that policy

RebuildModelAsync sees the same requests as TransformModelAsync and runs first. The stored transcript and the archive stay as they were.

  • Ids. An item keeps the id the host gave it. A new item needs an id the request does not use. An id that returns with a different kind of item is a new item.
  • New items. A new tool result ignores its Name, which the host derives from the call. New reasoning carries text only, with no provider state. Rewriting the text of existing reasoning drops that state too.
  • Images. The host stores image bytes in the session and references them by marker. A raw hook can keep, drop, or move an attachment that is already in the request, with the same media type. It cannot create an image. PluginModelImage carries no bytes. Support for new images is planned. Only user messages and tool results carry images to the model. Images on assistant and system messages become plain text. PluginModelRequest.AcceptsImages is true only for a work or plan call on an image-capable model. Text-only turns, side questions, and compaction never send images, so the host skips a replacement that adds one there.
  • Provider rules. The host does not check what a provider accepts. A request that splits a tool call from its result, or moves the system prompt, can be rejected by the provider. When a work or plan call fails after a raw hook changed its request, the host writes a note such as Raw hook 'Acme' changed this model request. The failure may be related. The note is a hint, not a diagnosis.
  • Prompt text. OnPrompt still only appends. RewritePrompt then replaces the composed system text for that mode. Any returned string is used, including an empty one. Work and plan text is what the session stores. The files under ~/.crystal/prompts stay as the operator saved them. RebuildModelAsync can still change the system item of one outbound call after that, without writing the archive.
  • Compaction text. OnCompaction still only appends. RewriteCompaction then replaces the full compaction user prompt or the stored summary body. An empty replacement is kept.
  • Approval. OnApproval can still only raise risk or require another prompt. RewriteApproval then replaces the classification and may lower risk, change authority, change the summary, or clear the prompt requirement. The approval policy uses the result.

More hook methods will be added later. A new method joins IPluginHook or IPluginRawHook when its behavior is defined. This build does not reserve empty methods for them. Whether plugins declare dependencies on one another, and related loading questions, are under consideration and are not decided.

Each plugin gets one non-collectible AssemblyLoadContext.

Shared assemblies come from the host: Crystal, Crystal.Tools, CrystalCode.Tools, and CrystalCode.Plugins. Other dependencies must resolve inside the plugin directory. The host does not probe the NuGet cache or the workspace.

CrystalCode, CrystalCode.Engine, CrystalCode.Display, and CrystalCode.Providers are refused, including as the entry assembly. The context is not collectible. There is no hot reload, signing check, or marketplace.

  • MCP.
  • Collectible unload and hot reload.
  • Replacing a built-in protocol, built-in tool, or built-in slash verb.
  • Hooks on external tool sets.
  • A module system below plugins and raw hooks. A later one may reach the host through reflection or another mechanism. Its behavior and ownership are not defined, and this build does not reserve types for it.