summaryrefslogtreecommitdiffhomepage
path: root/packages/kernel/src/runtime
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-05 01:35:50 +0900
committerAdam Malczewski <[email protected]>2026-06-05 01:35:50 +0900
commit94dd5334b0277f3cf3b0588150a6615af86a32b3 (patch)
tree53b1b327790ae43bd3b7cbabe555b832f3e27248 /packages/kernel/src/runtime
parent977ca522736bba53172e010494de5ac59fdb2a4a (diff)
downloaddispatch-94dd5334b0277f3cf3b0588150a6615af86a32b3.tar.gz
dispatch-94dd5334b0277f3cf3b0588150a6615af86a32b3.zip
refactor(kernel): rename tabId → conversationId across contracts + consumers (218 tests)
Step 4 of the post-MVP backlog: resolve the last vocab drift. The canonical term for a thread of turns is `conversationId` (GLOSSARY), but `AgentEvent` variants and `RunTurnInput` still used the legacy `tabId` from the old frontend "tab" concept, with session-orchestrator bridging `conversationId → tabId`. Atomic, type-driven rename across the full 10-file consumer set: - contracts/events.ts: all 11 AgentEvent variants tabId → conversationId - contracts/runtime.ts: RunTurnInput.tabId → conversationId - runtime/{events,run-turn,dispatch}.ts: factory params, ctx field, locals - session-orchestrator: drop the redundant `tabId: conversationId` bridge line - transport-http: emit wiring; external /chat field + X-Conversation-Id header unchanged (already canonical) — only the emitted NDJSON event field flips - tests (run-turn, app, logic): inputs + assertions now use conversationId Pure rename, zero behavior change: typecheck clean, 218 tests pass (unchanged count), biome clean, `grep tabId packages/` → zero matches. Verified live: multi-turn curl emits conversationId-keyed NDJSON and threads history correctly. GLOSSARY drift note removed. Closes the post-MVP backlog (Steps 1–4).
Diffstat (limited to 'packages/kernel/src/runtime')
-rw-r--r--packages/kernel/src/runtime/dispatch.ts15
-rw-r--r--packages/kernel/src/runtime/events.ts34
-rw-r--r--packages/kernel/src/runtime/run-turn.test.ts38
-rw-r--r--packages/kernel/src/runtime/run-turn.ts39
4 files changed, 77 insertions, 49 deletions
diff --git a/packages/kernel/src/runtime/dispatch.ts b/packages/kernel/src/runtime/dispatch.ts
index c6c5f8e..626b333 100644
--- a/packages/kernel/src/runtime/dispatch.ts
+++ b/packages/kernel/src/runtime/dispatch.ts
@@ -13,7 +13,7 @@ export async function executeToolCall(
tool: ToolContract | undefined,
signal: AbortSignal,
emit: EventEmitter,
- tabId: string,
+ conversationId: string,
turnId: string,
): Promise<ToolResult> {
if (tool === undefined) {
@@ -26,7 +26,7 @@ export async function executeToolCall(
toolCallId: call.id,
signal,
onOutput: (data, stream) => {
- emit(toolOutputEvent(tabId, turnId, call.id, data, stream));
+ emit(toolOutputEvent(conversationId, turnId, call.id, data, stream));
},
};
try {
@@ -48,7 +48,7 @@ export function createStepDispatcher(
policy: ToolDispatchPolicy,
signal: AbortSignal,
emit: EventEmitter,
- tabId: string,
+ conversationId: string,
turnId: string,
): StepDispatcher {
let activeCount = 0;
@@ -78,7 +78,14 @@ export function createStepDispatcher(
}
async function runAndResolve(entry: QueueEntry): Promise<void> {
- const result = await executeToolCall(entry.call, entry.tool, signal, emit, tabId, turnId);
+ const result = await executeToolCall(
+ entry.call,
+ entry.tool,
+ signal,
+ emit,
+ conversationId,
+ turnId,
+ );
activeCount--;
if (entry.tool?.concurrencySafe === false) unsafeRunning = false;
entry.resolve(result);
diff --git a/packages/kernel/src/runtime/events.ts b/packages/kernel/src/runtime/events.ts
index 62218be..2a92008 100644
--- a/packages/kernel/src/runtime/events.ts
+++ b/packages/kernel/src/runtime/events.ts
@@ -1,57 +1,61 @@
import type { AgentEvent } from "../contracts/events.js";
import type { Usage } from "../contracts/provider.js";
-export function textDeltaEvent(tabId: string, turnId: string, delta: string): AgentEvent {
- return { type: "text-delta", tabId, turnId, delta };
+export function textDeltaEvent(conversationId: string, turnId: string, delta: string): AgentEvent {
+ return { type: "text-delta", conversationId, turnId, delta };
}
-export function reasoningDeltaEvent(tabId: string, turnId: string, delta: string): AgentEvent {
- return { type: "reasoning-delta", tabId, turnId, delta };
+export function reasoningDeltaEvent(
+ conversationId: string,
+ turnId: string,
+ delta: string,
+): AgentEvent {
+ return { type: "reasoning-delta", conversationId, turnId, delta };
}
export function toolCallEvent(
- tabId: string,
+ conversationId: string,
turnId: string,
toolCallId: string,
toolName: string,
input: unknown,
): AgentEvent {
- return { type: "tool-call", tabId, turnId, toolCallId, toolName, input };
+ return { type: "tool-call", conversationId, turnId, toolCallId, toolName, input };
}
export function toolResultEvent(
- tabId: string,
+ conversationId: string,
turnId: string,
toolCallId: string,
toolName: string,
content: string,
isError: boolean,
): AgentEvent {
- return { type: "tool-result", tabId, turnId, toolCallId, toolName, content, isError };
+ return { type: "tool-result", conversationId, turnId, toolCallId, toolName, content, isError };
}
export function toolOutputEvent(
- tabId: string,
+ conversationId: string,
turnId: string,
toolCallId: string,
data: string,
stream: "stdout" | "stderr",
): AgentEvent {
- return { type: "tool-output", tabId, turnId, toolCallId, data, stream };
+ return { type: "tool-output", conversationId, turnId, toolCallId, data, stream };
}
-export function usageEvent(tabId: string, turnId: string, usage: Usage): AgentEvent {
- return { type: "usage", tabId, turnId, usage };
+export function usageEvent(conversationId: string, turnId: string, usage: Usage): AgentEvent {
+ return { type: "usage", conversationId, turnId, usage };
}
export function errorEvent(
- tabId: string,
+ conversationId: string,
turnId: string,
message: string,
code?: string,
): AgentEvent {
if (code !== undefined) {
- return { type: "error", tabId, turnId, message, code };
+ return { type: "error", conversationId, turnId, message, code };
}
- return { type: "error", tabId, turnId, message };
+ return { type: "error", conversationId, turnId, message };
}
diff --git a/packages/kernel/src/runtime/run-turn.test.ts b/packages/kernel/src/runtime/run-turn.test.ts
index 1ea6406..696a385 100644
--- a/packages/kernel/src/runtime/run-turn.test.ts
+++ b/packages/kernel/src/runtime/run-turn.test.ts
@@ -52,7 +52,7 @@ const userMessage: ChatMessage = {
};
describe("runTurn", () => {
- it("emits events with the tabId and turnId from input", async () => {
+ it("emits events with the conversationId and turnId from input", async () => {
const provider = createFakeProvider([
[
{ type: "text-delta", delta: "hi" },
@@ -68,14 +68,14 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "conv-42",
+ conversationId: "conv-42",
turnId: "turn-99",
emit,
});
expect(events.length).toBeGreaterThan(0);
for (const event of events) {
- expect(event.tabId).toBe("conv-42");
+ expect(event.conversationId).toBe("conv-42");
if (event.type !== "status") {
expect(event.turnId).toBe("turn-99");
}
@@ -100,7 +100,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit,
});
@@ -143,7 +143,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [tool],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit,
});
@@ -201,7 +201,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [tool],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit: () => {},
});
@@ -250,7 +250,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [toolA, toolB],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit: () => {},
});
@@ -294,7 +294,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [toolA, toolB],
dispatch: { maxConcurrent: 2, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit: () => {},
});
@@ -352,7 +352,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [toolA, toolB, toolC],
dispatch: { maxConcurrent: 0, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit: () => {},
});
@@ -413,7 +413,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [tool],
dispatch: { maxConcurrent: 1, eager: true },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit: () => {},
});
@@ -464,7 +464,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [tool],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit: () => {},
});
@@ -513,7 +513,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [tool],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit,
signal: ac.signal,
@@ -545,7 +545,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit: () => {},
signal: ac.signal,
@@ -583,7 +583,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [tool],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit,
});
@@ -657,7 +657,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [unsafeTool, safeTool],
dispatch: { maxConcurrent: 5, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit: () => {},
});
@@ -691,7 +691,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit,
});
@@ -724,7 +724,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit,
});
@@ -759,7 +759,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [tool],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit: () => {},
});
@@ -797,7 +797,7 @@ describe("runTurn", () => {
messages: [userMessage],
tools: [tool],
dispatch: { maxConcurrent: 1, eager: false },
- tabId: "tab-test",
+ conversationId: "tab-test",
turnId: "turn-test",
emit,
});
diff --git a/packages/kernel/src/runtime/run-turn.ts b/packages/kernel/src/runtime/run-turn.ts
index 919491a..9421d86 100644
--- a/packages/kernel/src/runtime/run-turn.ts
+++ b/packages/kernel/src/runtime/run-turn.ts
@@ -74,7 +74,7 @@ interface StepContext {
readonly dispatch: RunTurnInput["dispatch"];
readonly emit: EventEmitter;
readonly signal: AbortSignal;
- readonly tabId: string;
+ readonly conversationId: string;
readonly turnId: string;
}
@@ -96,11 +96,11 @@ function processEvent(
switch (event.type) {
case "text-delta":
appendTextDelta(chunks, event.delta);
- ctx.emit(textDeltaEvent(ctx.tabId, ctx.turnId, event.delta));
+ ctx.emit(textDeltaEvent(ctx.conversationId, ctx.turnId, event.delta));
break;
case "reasoning-delta":
appendThinkingDelta(chunks, event.delta);
- ctx.emit(reasoningDeltaEvent(ctx.tabId, ctx.turnId, event.delta));
+ ctx.emit(reasoningDeltaEvent(ctx.conversationId, ctx.turnId, event.delta));
break;
case "tool-call": {
const call: ToolCall = {
@@ -115,14 +115,22 @@ function processEvent(
toolName: event.toolName,
input: event.input,
});
- ctx.emit(toolCallEvent(ctx.tabId, ctx.turnId, event.toolCallId, event.toolName, event.input));
+ ctx.emit(
+ toolCallEvent(
+ ctx.conversationId,
+ ctx.turnId,
+ event.toolCallId,
+ event.toolName,
+ event.input,
+ ),
+ );
if (ctx.dispatch.eager) {
dispatcher.submit(call);
}
break;
}
case "usage":
- ctx.emit(usageEvent(ctx.tabId, ctx.turnId, event.usage));
+ ctx.emit(usageEvent(ctx.conversationId, ctx.turnId, event.usage));
break;
case "finish":
break;
@@ -132,7 +140,7 @@ function processEvent(
} else {
chunks.push({ type: "error", message: event.message });
}
- ctx.emit(errorEvent(ctx.tabId, ctx.turnId, event.message, event.code));
+ ctx.emit(errorEvent(ctx.conversationId, ctx.turnId, event.message, event.code));
break;
}
}
@@ -148,7 +156,7 @@ async function executeStep(ctx: StepContext): Promise<StepResult> {
ctx.dispatch,
ctx.signal,
ctx.emit,
- ctx.tabId,
+ ctx.conversationId,
ctx.turnId,
);
@@ -167,7 +175,7 @@ async function executeStep(ctx: StepContext): Promise<StepResult> {
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
chunks.push({ type: "error", message });
- ctx.emit(errorEvent(ctx.tabId, ctx.turnId, message));
+ ctx.emit(errorEvent(ctx.conversationId, ctx.turnId, message));
finishReason = "error";
}
@@ -184,7 +192,16 @@ async function executeStep(ctx: StepContext): Promise<StepResult> {
const result = results.get(call.id);
if (result !== undefined) {
const isError = result.isError ?? false;
- ctx.emit(toolResultEvent(ctx.tabId, ctx.turnId, call.id, call.name, result.content, isError));
+ ctx.emit(
+ toolResultEvent(
+ ctx.conversationId,
+ ctx.turnId,
+ call.id,
+ call.name,
+ result.content,
+ isError,
+ ),
+ );
toolMessages.push({
role: "tool",
chunks: [
@@ -217,7 +234,7 @@ export async function runTurn(input: RunTurnInput): Promise<RunTurnResult> {
toolMap.set(tool.name, tool);
}
- const tabId = input.tabId;
+ const conversationId = input.conversationId;
const turnId = input.turnId;
const signal = input.signal ?? new AbortController().signal;
@@ -235,7 +252,7 @@ export async function runTurn(input: RunTurnInput): Promise<RunTurnResult> {
dispatch: input.dispatch,
emit: input.emit,
signal,
- tabId,
+ conversationId,
turnId,
});