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
66 changes: 66 additions & 0 deletions nodejs/src/generated/rpc.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions nodejs/src/generated/session-events.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions nodejs/test/e2e/session_todos_changed.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/

import fs, { realpathSync } from "node:fs";
import os from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { approveAll, type SessionEvent } from "../../src/index.js";
import { createSdkTestContext } from "./harness/sdkTestContext.js";

/**
* E2E coverage for the runtime's `session.todos_changed` event and
* `session.plan.readSqlTodosWithDependencies` RPC. We let the agent drive the
* built-in `sql` tool (default mode = "copilot-cli") to insert known rows into
* the prompted `todos` table, then assert both that the lightweight signal
* event fired and that the structured query API returns those rows.
*/
describe("Todos changed event + readSqlTodosWithDependencies", async () => {
const baseDir = realpathSync(fs.mkdtempSync(join(os.tmpdir(), "copilot-todos-e2e-")));
const { copilotClient: client } = await createSdkTestContext({
copilotClientOptions: { baseDirectory: baseDir },
});

it(
"fires session.todos_changed and exposes rows + dependencies",
{ timeout: 120_000 },
async () => {
const session = await client.createSession({ onPermissionRequest: approveAll });

const events: SessionEvent[] = [];
session.on((event) => {
events.push(event);
});

await session.sendAndWait({
prompt:
"Use the sql tool to execute exactly these statements, in order, with no extra rows:\n" +
"1. INSERT INTO todos (id, title, status) VALUES ('alpha', 'First todo', 'pending');\n" +
"2. INSERT INTO todos (id, title, status) VALUES ('beta', 'Second todo', 'done');\n" +
"3. INSERT INTO todo_deps (todo_id, depends_on) VALUES ('beta', 'alpha');\n" +
"Then stop. Do not insert any other rows or create any other tables.",
});

const todosEvents = events.filter((e) => e.type === "session.todos_changed");
expect(todosEvents.length).toBeGreaterThanOrEqual(1);

Check failure on line 46 in nodejs/test/e2e/session_todos_changed.e2e.test.ts

View workflow job for this annotation

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

test/e2e/session_todos_changed.e2e.test.ts > Todos changed event + readSqlTodosWithDependencies > fires session.todos_changed and exposes rows + dependencies

AssertionError: expected 0 to be greater than or equal to 1 ❯ test/e2e/session_todos_changed.e2e.test.ts:46:40

Check failure on line 46 in nodejs/test/e2e/session_todos_changed.e2e.test.ts

View workflow job for this annotation

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

test/e2e/session_todos_changed.e2e.test.ts > Todos changed event + readSqlTodosWithDependencies > fires session.todos_changed and exposes rows + dependencies

AssertionError: expected 0 to be greater than or equal to 1 ❯ test/e2e/session_todos_changed.e2e.test.ts:46:40

const result = await session.rpc.plan.readSqlTodosWithDependencies();
const ids = result.rows.map((r) => r.id).filter((x): x is string => !!x).sort();
expect(ids).toEqual(["alpha", "beta"]);

const edge = result.dependencies.find(
(d) => d.todoId === "beta" && d.dependsOn === "alpha"
);
expect(edge).toBeDefined();

await session.disconnect();
}
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
models:
- claude-sonnet-4.5
conversations:
- messages:
- role: system
content: ${system}
- role: user
content: |-
Use the sql tool to execute exactly these statements, in order, with no extra rows:
1. INSERT INTO todos (id, title, status) VALUES ('alpha', 'First todo', 'pending');
2. INSERT INTO todos (id, title, status) VALUES ('beta', 'Second todo', 'done');
3. INSERT INTO todo_deps (todo_id, depends_on) VALUES ('beta', 'alpha');
Then stop. Do not insert any other rows or create any other tables.
- role: assistant
content: I'll execute those three SQL statements in order.
- role: assistant
tool_calls:
- id: toolcall_0
type: function
function:
name: sql
arguments: "{\"description\":\"Insert alpha todo\",\"query\":\"INSERT INTO todos (id, title, status) VALUES ('alpha',
'First todo', 'pending');\"}"
- role: assistant
tool_calls:
- id: toolcall_1
type: function
function:
name: sql
arguments: "{\"description\":\"Insert beta todo\",\"query\":\"INSERT INTO todos (id, title, status) VALUES ('beta',
'Second todo', 'done');\"}"
- role: assistant
tool_calls:
- id: toolcall_2
type: function
function:
name: sql
arguments: "{\"description\":\"Insert dependency\",\"query\":\"INSERT INTO todo_deps (todo_id, depends_on) VALUES
('beta', 'alpha');\"}"
- messages:
- role: system
content: ${system}
- role: user
content: |-
Use the sql tool to execute exactly these statements, in order, with no extra rows:
1. INSERT INTO todos (id, title, status) VALUES ('alpha', 'First todo', 'pending');
2. INSERT INTO todos (id, title, status) VALUES ('beta', 'Second todo', 'done');
3. INSERT INTO todo_deps (todo_id, depends_on) VALUES ('beta', 'alpha');
Then stop. Do not insert any other rows or create any other tables.
- role: assistant
content: I'll execute those three SQL statements in order.
tool_calls:
- id: toolcall_0
type: function
function:
name: sql
arguments: "{\"description\":\"Insert alpha todo\",\"query\":\"INSERT INTO todos (id, title, status) VALUES ('alpha',
'First todo', 'pending');\"}"
- id: toolcall_1
type: function
function:
name: sql
arguments: "{\"description\":\"Insert beta todo\",\"query\":\"INSERT INTO todos (id, title, status) VALUES ('beta',
'Second todo', 'done');\"}"
- id: toolcall_2
type: function
function:
name: sql
arguments: "{\"description\":\"Insert dependency\",\"query\":\"INSERT INTO todo_deps (todo_id, depends_on) VALUES
('beta', 'alpha');\"}"
- role: tool
tool_call_id: toolcall_0
content: "1 row(s) inserted. Last inserted row ID: 1."
- role: tool
tool_call_id: toolcall_1
content: "1 row(s) inserted. Last inserted row ID: 2."
- role: tool
tool_call_id: toolcall_2
content: "1 row(s) inserted. Last inserted row ID: 1."
- role: assistant
content: Done. All three statements executed successfully.
Loading