Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions nodejs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ export type {
PermissionHandler,
PermissionRequest,
PermissionRequestResult,
PostUserPromptSubmittedHandler,
PostUserPromptSubmittedHookInput,
PostUserPromptSubmittedHookOutput,
ProviderConfig,
ProviderModelConfig,
RemoteSessionMode,
Expand Down
3 changes: 3 additions & 0 deletions nodejs/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,9 @@ export class CopilotSession {
postToolUse: this.hooks.onPostToolUse as GenericHandler | undefined,
postToolUseFailure: this.hooks.onPostToolUseFailure as GenericHandler | undefined,
userPromptSubmitted: this.hooks.onUserPromptSubmitted as GenericHandler | undefined,
postUserPromptSubmitted: this.hooks.onPostUserPromptSubmitted as
| GenericHandler
| undefined,
sessionStart: this.hooks.onSessionStart as GenericHandler | undefined,
sessionEnd: this.hooks.onSessionEnd as GenericHandler | undefined,
errorOccurred: this.hooks.onErrorOccurred as GenericHandler | undefined,
Expand Down
33 changes: 33 additions & 0 deletions nodejs/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,34 @@ export type UserPromptSubmittedHandler = (
invocation: { sessionId: string }
) => Promise<UserPromptSubmittedHookOutput | void> | UserPromptSubmittedHookOutput | void;

/**
* Input for post-user-prompt-submitted hook.
*
* This hook runs after the runtime has transformed the submitted prompt with
* generated context such as `<current_datetime>`, but before the transformed
* prompt is persisted to session history or sent to the model.
*/
export interface PostUserPromptSubmittedHookInput extends BaseHookInput {
prompt: string;
transformedPrompt: string;
}

/**
* Output for post-user-prompt-submitted hook.
*/
export interface PostUserPromptSubmittedHookOutput {
modifiedTransformedPrompt?: string;
suppressOutput?: boolean;
}

/**
* Handler for post-user-prompt-submitted hook.
*/
export type PostUserPromptSubmittedHandler = (
input: PostUserPromptSubmittedHookInput,
invocation: { sessionId: string }
) => Promise<PostUserPromptSubmittedHookOutput | void> | PostUserPromptSubmittedHookOutput | void;

/**
* Input for session-start hook
*/
Expand Down Expand Up @@ -1385,6 +1413,11 @@ export interface SessionHooks {
*/
onUserPromptSubmitted?: UserPromptSubmittedHandler;

/**
* Called after the runtime transforms a submitted prompt and before it is stored.
*/
onPostUserPromptSubmitted?: PostUserPromptSubmittedHandler;

/**
* Called when a session starts
*/
Expand Down
35 changes: 35 additions & 0 deletions nodejs/test/e2e/hooks_extended.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
Expand All @@ -7,6 +7,7 @@
import { approveAll, defineTool } from "../../src/index.js";
import type {
ErrorOccurredHookInput,
PostUserPromptSubmittedHookInput,
PostToolUseFailureHookInput,
PostToolUseHookInput,
PreToolUseHookInput,
Expand Down Expand Up @@ -149,6 +150,40 @@
await session.disconnect();
});

it("should invoke postUserPromptSubmitted hook and modify transformed prompt", async () => {

Check failure on line 153 in nodejs/test/e2e/hooks_extended.e2e.test.ts

View workflow job for this annotation

GitHub Actions / Node.js SDK Tests (macos-latest)

test/e2e/hooks_extended.e2e.test.ts > Extended session hooks > should invoke postUserPromptSubmitted hook and modify transformed prompt

Error: Test timed out in 30000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ test/e2e/hooks_extended.e2e.test.ts:153:5

Check failure on line 153 in nodejs/test/e2e/hooks_extended.e2e.test.ts

View workflow job for this annotation

GitHub Actions / Node.js SDK Tests (windows-latest)

test/e2e/hooks_extended.e2e.test.ts > Extended session hooks > should invoke postUserPromptSubmitted hook and modify transformed prompt

Error: Test timed out in 30000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ test/e2e/hooks_extended.e2e.test.ts:153:5
const inputs: PostUserPromptSubmittedHookInput[] = [];
const session = await client.createSession({
onPermissionRequest: approveAll,
hooks: {
onPostUserPromptSubmitted: async (input, invocation) => {
inputs.push(input);
expect(invocation.sessionId).toBeTruthy();
expect(input.prompt).toContain("Answer the arithmetic question above");
expect(input.transformedPrompt).toContain("Answer the arithmetic question above");
expect(input.transformedPrompt).toContain("<current_datetime>");

return {
modifiedTransformedPrompt: input.transformedPrompt.replace(
/<current_datetime>.*?<\/current_datetime>\n*/s,
"What is 19 + 23? Reply with just the number.\n"
),
};
},
},
});

const response = await session.sendAndWait({
prompt: "Answer the arithmetic question above.",
});

expect(inputs.length).toBeGreaterThan(0);
expect(inputs[0].timestamp).toBeInstanceOf(Date);
expect(inputs[0].workingDirectory).toBeDefined();
expect(response?.data.content ?? "").toContain("42");

await session.disconnect();
});

it("should invoke sessionStart hook", async () => {
const inputs: SessionStartHookInput[] = [];
const session = await client.createSession({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
models:
- claude-sonnet-4.5
conversations:
- messages:
- role: system
content: ${system}
- role: user
content: |-
What is 19 + 23? Reply with just the number.
Answer the arithmetic question above.
- role: assistant
content: "42"
Loading