Repository navigation
fix(agent-core-v2): align externalHooks context injection and UserPromptSubmit payloads - #4256
luyao10282 wants to merge 2 commits into
Conversation
…mptSubmit payloads - Parse hookSpecificOutput.additionalContext, systemMessage, and updatedInput in runHook - Fire UserPromptSubmit on user-slash skill and plugin-command activations and pass prompt as string instead of ContentPart[] - Inject additionalContext on SessionStart, PreToolUse, and PostToolUse as system-reminder hook_result messages while ignoring noisy plain stdout on tool hooks - Support updatedInput mutation and re-validation in BeforeToolExecuteEvent / AgentToolExecutorService - Await PostToolUse and include tool_response in stdin payload - Reload hooks index when the hooks config section changes
🦋 Changeset detectedLatest commit: fca6815 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
🟡 Changes recommended
Rewritten executions retain stale authorization, and argument propagation, prompt handling, and context preservation defects remain.
5 open findings
What changed in this PR
Addresses #4255 by improving external-hook context injection and prompt payloads in the agent engine.
Changes:
- Parses structured hook context and tool-input updates.
- Extends prompt hooks to user-invoked slash commands.
- Adds hook configuration reloads and integration coverage.
| File | Description |
|---|---|
packages/agent-core-v2/test/features/externalHooks/integration.test.ts |
Tests slash prompts and tool-hook outputs. |
packages/agent-core-v2/src/features/externalHooks/session/sessionExternalHooksService.ts |
Queues SessionStart context. |
packages/agent-core-v2/src/features/externalHooks/session/sessionExternalHooks.ts |
Exposes queued context retrieval. |
packages/agent-core-v2/src/features/externalHooks/internal/userPrompt.ts |
Extracts messages and input updates. |
packages/agent-core-v2/src/features/externalHooks/internal/types.ts |
Adds updated-input result data. |
packages/agent-core-v2/src/features/externalHooks/internal/runHook.ts |
Parses additional structured output fields. |
packages/agent-core-v2/src/features/externalHooks/app/externalHooksRunnerService.ts |
Reloads hooks after configuration changes. |
packages/agent-core-v2/src/features/externalHooks/agent/agentExternalHooksService.ts |
Integrates context, input updates, and slash prompts. |
packages/agent-core-v2/src/agent/toolExecutor/toolHooks.ts |
Extends argument-update contracts. |
packages/agent-core-v2/src/agent/toolExecutor/toolExecutorService.ts |
Revalidates and resolves rewritten executions. |
packages/agent-core-v2/src/agent/toolExecutor/beforeToolExecuteEvent.ts |
Carries argument-update decisions. |
.changeset/hooks-context-and-prompt-payload.md |
Records user-facing hook changes. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| try { | ||
| execution = await call.tool.resolveExecution(call.args); |
| signal.throwIfAborted(); | ||
| const results = await this.runner.trigger('UserPromptSubmit', { | ||
| matcherValue: input, | ||
| matcherValue: promptText !== undefined ? promptText : input, |
| if (decision?.updatedArgs !== undefined) { | ||
| const validationError = validateExecutableToolArgs(call.tool, call.args); |
| sessionId: this.sessionContext.sessionId, | ||
| inputData: this.withSessionFacts({ prompt: input, isSteer: ctx.isSteer }), | ||
| inputData: this.withSessionFacts({ | ||
| prompt: promptText ?? input, |
| message ?? | ||
| systemMessage ?? | ||
| additionalContext ?? | ||
| hookSpecificOutput?.message ?? | ||
| hookSpecificOutput?.additionalContext, |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4498f658ae
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| try { | ||
| execution = await call.tool.resolveExecution(call.args); |
There was a problem hiding this comment.
Re-run permission checks for updated tool executions
When a PreToolUse hook changes a path-bearing input (for example, changing Write.path from a workspace file to an absolute outside-workspace or .git path), this new resolveExecution happens only after fireBeforeExecute has already run every permission listener against the original execution.accesses. The updated execution is then scheduled without re-running those policies, so it can execute under an approval or UI description computed for a different target; re-evaluate permissions against the updated execution before accepting the earlier decision.
AGENTS.md reference: AGENTS.md:L122-L124
Useful? React with 👍 / 👎.
| ? `/${origin.commandName} ${args}` | ||
| : `/${origin.commandName}`; |
There was a problem hiding this comment.
Include the plugin namespace in submitted command text
For a user slash plugin command such as /demo:deploy prod, AgentPluginCommandService stores pluginId: "demo" and commandName: "deploy", and promptMetadataTextFromPluginCommand renders the canonical prompt as /demo:deploy prod; this branch instead sends /deploy prod as the matcher and stdin prompt. Hooks matching the namespaced command therefore do not run, while hooks that do run see a command the user never entered, so construct this text from both origin.pluginId and origin.commandName.
AGENTS.md reference: AGENTS.md:L114-L116
Useful? React with 👍 / 👎.
| inputData: this.withSessionFacts({ | ||
| prompt: promptText ?? input, | ||
| isSteer: ctx.isSteer, |
There was a problem hiding this comment.
Keep prompt payloads string-valued for media-only submissions
When kap-server accepts an image/video-only or empty-text prompt, turnPromptText returns undefined, so this fallback puts the original ContentPart[] back into stdin even though textual prompts now use a string. A UserPromptSubmit hook written to the new string payload contract will fail or silently allow those valid submissions; use a string fallback such as the empty string instead.
AGENTS.md reference: AGENTS.md:L114-L116
Useful? React with 👍 / 👎.
…matcher and hook context


Requirement or Bug
Resolve #4255
Bug Reproduction Steps
UserPromptSubmitorPreToolUsehook in~/.kimi-code/config.tomlthat outputs{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"context text"}}./my-skill arg1):additionalContextis silently dropped becauseHookSpecificOutputSchemaomitsadditionalContextandstructuredOutput === truesuppresses thestdoutfallback.origin.kind === 'skill_activation'),UserPromptSubmitis skipped completely, and on normal promptsparsed.prompton stdin is aContentPart[]array rather than a string.PreToolUseandPostToolUse,additionalContextandupdatedInputare ignored.Root Cause
HookSpecificOutputSchemainpackages/agent-core-v2/src/features/externalHooks/internal/runHook.tsonly extractedmessage,permissionDecision, andpermissionDecisionReason, omittingadditionalContext,systemMessage, andupdatedInput.runPromptSubmitHookinagentExternalHooksService.tsgated onorigin.kind === 'user'and passed rawContentPart[]asprompt.runPreToolUseandnotifyPostToolUsedid not collect structured hook messages orupdatedInput, andExternalHooksRunnerServicedid not subscribe toconfig.onDidSectionChange.This is a fundamental fix in
packages/agent-core-v2.Code Changes
HookSpecificOutputSchemaandHookJsonOutputSchemaininternal/runHook.tsto parseadditionalContext,systemMessage, andupdatedInput.runPromptSubmitHookto run onuser-slashskill_activationandplugin_commandorigins and serializepromptas a string viaturnPromptText.additionalContext/messagefromSessionStart,PreToolUse, andPostToolUseas<system-reminder><hook_result ...>messages (while ignoring noisy plainstdouton tool hooks), and wiredupdatedInputintoBeforeToolExecuteEvent/AgentToolExecutorServicewith schema re-validation andresolveExecutionre-evaluation.ExternalHooksRunnerServicetoconfig.onDidSectionChangeforHOOKS_SECTION.Behavior Changes and Affected Users
hookSpecificOutput.additionalContextin hook JSON outputstructuredOutput: truewithmessage: undefined)UserPromptSubmit,SessionStart,PreToolUse, andPostToolUseadditionalContextfrom hook JSON outputUserPromptSubmitonuser-slashskill/plugin commands &promptstdin fieldpromptserialized asContentPart[]arrayuser-slashskill/plugin activations;promptserialized as stringmatcheronUserPromptSubmitto filter slash commandsPreToolUseupdatedInputandPostToolUseexecutionupdatedInputignored;PostToolUsefire-and-forget withouttool_responseupdatedInputupdates and re-validates tool args;PostToolUseawaited withtool_responsestdouton tool events remain unaffected (allowPlainStdout: false)Affected modules:
packages/agent-core-v2/src/features/externalHooks,packages/agent-core-v2/src/agent/toolExecutor.Test coverage:
packages/agent-core-v2/test/features/externalHooks/integration.test.ts(142/142 tests passing).Checklist
/approve).gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.