refactor(llm): rename CallId to ToolCallId
This commit is contained in:
1 parent
8cf2445f11
commit
a789637db6
186 files changed
+1013
-1013
No files matched your search
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import { boot, loadOverlayPatches } from '@deepseek-ai/dsh-app-boot'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type {} from '@deepseek-ai/dsh-skill'
|
||||
@@ -45,7 +45,7 @@ try {
|
||||
: undefined
|
||||
const summary = (await ctx.skills.list()).find(skill => skill.name === 'dsh-badge')
|
||||
const result = await ctx.tools.execute({
|
||||
callId: CallId('dsh-badge-snapshot'),
|
||||
callId: ToolCallId('dsh-badge-snapshot'),
|
||||
name: 'skill',
|
||||
arguments: { name: 'dsh-badge' },
|
||||
signal: new AbortController().signal,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
|
||||
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
|
||||
import { resolveSessionPreset, SETTINGS_NAMESPACE } from '@deepseek-ai/dsh-agent-presets'
|
||||
import { applyChildComposition, childSessionMeta } from '@deepseek-ai/dsh-subagent'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import type {} from '@deepseek-ai/dsh-compaction-basic'
|
||||
import type {} from '@deepseek-ai/dsh-skill'
|
||||
import type {} from '@deepseek-ai/dsh-tools'
|
||||
@@ -403,7 +403,7 @@ describe('the shipped Web composition', () => {
|
||||
|
||||
// The preset's own loader tool resolves the global-layer skill.
|
||||
const loaded = await ctx.tools.execute({
|
||||
callId: CallId('preset-skills-load'),
|
||||
callId: ToolCallId('preset-skills-load'),
|
||||
name: 'skill',
|
||||
arguments: { name: 'dsh-badge' },
|
||||
signal: new AbortController().signal,
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Browser, Page } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import { JobId } from '@deepseek-ai/dsh-jobs'
|
||||
import {
|
||||
@@ -90,7 +90,7 @@ describe.skipIf(MODE === 'record')('web e2e: background job list', () => {
|
||||
|
||||
const started = await scaffold.ctx.tools.execute({
|
||||
signal: new AbortController().signal,
|
||||
callId: CallId('background-job-list-e2e'),
|
||||
callId: ToolCallId('background-job-list-e2e'),
|
||||
name: 'bash',
|
||||
arguments: { command: COMMAND, description: 'Hold a background slot open', run_in_background: true },
|
||||
agent,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { join } from 'node:path'
|
||||
import type { Browser, Page } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
|
||||
import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import type { ReplayEntry, ReplayOverrideDoc } from '@deepseek-ai/dsh-llm-replay'
|
||||
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import {
|
||||
@@ -32,7 +32,7 @@ interface TurnSpec {
|
||||
readonly firstMarker: string
|
||||
readonly doneMarker: string
|
||||
readonly deltas: readonly string[]
|
||||
readonly callId?: ReturnType<typeof CallId>
|
||||
readonly callId?: ReturnType<typeof ToolCallId>
|
||||
readonly toolResultMarker?: string
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ function turnSpec(index: number): TurnSpec {
|
||||
firstMarker,
|
||||
doneMarker,
|
||||
deltas,
|
||||
callId: CallId(`continuous-chat-tool-${id}`),
|
||||
callId: ToolCallId(`continuous-chat-tool-${id}`),
|
||||
toolResultMarker: `CONTINUOUS_CHAT_TOOL_RESULT_${id}`,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { Browser, Page } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
||||
import type { StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import type { ReplayEntry, ReplayOverrideDoc } from '@deepseek-ai/dsh-llm-replay'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import { createChatScrollFixture, type ChatScrollFixture } from './chat-scroll-fixture.ts'
|
||||
@@ -34,7 +34,7 @@ const LIVE_TEXT_PROMPT = 'CHAT_SCROLL_LIVE_USER Continue this long conversation
|
||||
const LIVE_TEXT_FIRST = 'CHAT_SCROLL_LIVE_FIRST'
|
||||
const LIVE_TEXT_DONE = 'CHAT_SCROLL_LIVE_DONE'
|
||||
const LIVE_TOOL_PROMPT = 'CHAT_SCROLL_TOOL_USER Run the requested diagnostic and then summarize it.'
|
||||
const LIVE_TOOL_CALL_ID = CallId('chat-scroll-live-tool-call')
|
||||
const LIVE_TOOL_CALL_ID = ToolCallId('chat-scroll-live-tool-call')
|
||||
const LIVE_TOOL_RESULT = 'CHAT_SCROLL_LIVE_TOOL_RESULT'
|
||||
const LIVE_TOOL_FIRST = 'CHAT_SCROLL_TOOL_STREAM_FIRST'
|
||||
const LIVE_TOOL_DONE = 'CHAT_SCROLL_TOOL_STREAM_DONE'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// persisted conversations, while unique markers identify semantic rows
|
||||
// without depending on CSS-module names or virtualizer DOM positions.
|
||||
import {
|
||||
CallId,
|
||||
ToolCallId,
|
||||
createAssistantMessage,
|
||||
createToolResultMessage,
|
||||
createUserMessage,
|
||||
@@ -105,7 +105,7 @@ function appendToolStep(
|
||||
): void {
|
||||
const calls = [1, 2].map((index) => {
|
||||
const marker = markers.tool(turn, index)
|
||||
const callId = CallId(`chat-scroll-${suffix(turn)}-${String(index)}`)
|
||||
const callId = ToolCallId(`chat-scroll-${suffix(turn)}-${String(index)}`)
|
||||
const args = JSON.stringify({
|
||||
command: `printf '${marker}\\n'`,
|
||||
description: marker,
|
||||
|
||||
@@ -11,7 +11,7 @@ import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
||||
import type { StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import {
|
||||
CallId,
|
||||
ToolCallId,
|
||||
createAssistantMessage,
|
||||
createToolResultMessage,
|
||||
createUserMessage,
|
||||
@@ -233,7 +233,7 @@ function appendToolStep(
|
||||
toolCount: number,
|
||||
): void {
|
||||
const calls = Array.from({ length: toolCount }, (_, index) => {
|
||||
const callId = CallId(`perf-call-${String(turn)}-${String(index)}`)
|
||||
const callId = ToolCallId(`perf-call-${String(turn)}-${String(index)}`)
|
||||
const args = JSON.stringify({
|
||||
turn,
|
||||
index,
|
||||
@@ -467,7 +467,7 @@ function soakTurn(index: number): ConversationTurnSpec {
|
||||
}
|
||||
|
||||
function toolStream(index: number, marker: string): StreamChunk[] {
|
||||
const callId = CallId(`performance-tool-${marker.toLowerCase()}-${String(index)}`)
|
||||
const callId = ToolCallId(`performance-tool-${marker.toLowerCase()}-${String(index)}`)
|
||||
const args = JSON.stringify({
|
||||
command: `printf '${marker}\\n'`,
|
||||
description: `Emit performance marker ${String(index)}`,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
||||
import type { AgentHandle } from '@deepseek-ai/dsh-agent'
|
||||
import { CallId, createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type {} from '@deepseek-ai/dsh-agent-presets'
|
||||
import type {} from '@deepseek-ai/dsh-system-prompt'
|
||||
@@ -68,14 +68,14 @@ describe('minimal agent preset', () => {
|
||||
const signal = new AbortController().signal
|
||||
await scaffold.ctx.tools.execute({
|
||||
signal,
|
||||
callId: CallId('minimal-bash-state-setup'),
|
||||
callId: ToolCallId('minimal-bash-state-setup'),
|
||||
name: 'bash',
|
||||
arguments: { command: `cd ${JSON.stringify(stateDir)} && export DSH_MINIMAL_STATE=PERSISTED` },
|
||||
agent: agentHandle.agent,
|
||||
})
|
||||
const bash = await scaffold.ctx.tools.execute({
|
||||
signal,
|
||||
callId: CallId('minimal-bash-state-read'),
|
||||
callId: ToolCallId('minimal-bash-state-read'),
|
||||
name: 'bash',
|
||||
arguments: { command: 'printf \'%s:%s\n\' "$DSH_MINIMAL_STATE" "$PWD"' },
|
||||
agent: agentHandle.agent,
|
||||
@@ -84,7 +84,7 @@ describe('minimal agent preset', () => {
|
||||
await writeFile(seedPath, 'MINIMAL_EDITOR_OK\n')
|
||||
const editor = await scaffold.ctx.tools.execute({
|
||||
signal,
|
||||
callId: CallId('minimal-editor-smoke'),
|
||||
callId: ToolCallId('minimal-editor-smoke'),
|
||||
name: 'str_replace_editor',
|
||||
arguments: { command: 'view', path: seedPath },
|
||||
agent: agentHandle.agent,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import type { Browser, Page } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
|
||||
import { CallId, createAssistantMessage, createToolResultMessage, createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, createAssistantMessage, createToolResultMessage, createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { SESSION_FORMAT_VERSION, Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type {} from '@deepseek-ai/dsh-session-title'
|
||||
import {
|
||||
@@ -46,7 +46,7 @@ function mentionFixture(): string {
|
||||
session.append('step/start', { turn: 1, step: 1 })
|
||||
const calls = WRITES.map((path, index) => ({
|
||||
path,
|
||||
callId: CallId(`file-mention-${String(index)}`),
|
||||
callId: ToolCallId(`file-mention-${String(index)}`),
|
||||
args: JSON.stringify({ file_path: path, content: `content of ${path}\n` }),
|
||||
}))
|
||||
session.append('assistant/message', {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'node:url'
|
||||
import type { Browser, Page } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed, vi } from 'vitest'
|
||||
import { CallId, createAssistantMessage, createToolResultMessage, createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, createAssistantMessage, createToolResultMessage, createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { SESSION_FORMAT_VERSION, Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type {} from '@deepseek-ai/dsh-session-title'
|
||||
import {
|
||||
@@ -49,7 +49,7 @@ function producedFixture(): string {
|
||||
session.append('step/start', { turn: 1, step: 1 })
|
||||
const calls = PRODUCED.map((path, index) => ({
|
||||
path,
|
||||
callId: CallId(`produced-files-${String(index)}`),
|
||||
callId: ToolCallId(`produced-files-${String(index)}`),
|
||||
args: JSON.stringify({ file_path: path, content: `content of ${path}\n` }),
|
||||
}))
|
||||
session.append('assistant/message', {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { fileURLToPath } from 'node:url'
|
||||
import type { Browser, Page } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import {
|
||||
assertFixtureInventory, captureStableAria, compareOrRefreshGolden, fixtureUserPrompts,
|
||||
@@ -98,7 +98,7 @@ describe('web e2e: fresh round trip through the real assembly', () => {
|
||||
if (agent === undefined) throw new Error(`the settled Web agent ${settledSessionId} is no longer live`)
|
||||
const result = await scaffold.ctx.tools.execute({
|
||||
signal: AbortSignal.timeout(5_000),
|
||||
callId: CallId('web-url-probe'),
|
||||
callId: ToolCallId('web-url-probe'),
|
||||
name: 'bash',
|
||||
arguments: {
|
||||
command: 'printf \'%s\\n\' "$DSH_WEB_URL"',
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { Browser, Page } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
|
||||
import type { AgentHandle } from '@deepseek-ai/dsh-agent'
|
||||
import { CallId, createUserMessage, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, createUserMessage, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import {
|
||||
@@ -121,7 +121,7 @@ class BrowserZoneAtAdapter extends LlmAdapter {
|
||||
this.selectedAt = localAt(target, AT_BROWSER_ZONE)
|
||||
this.scheduledAt = new Date(target).toISOString()
|
||||
const argumentsJson = JSON.stringify({ prompt: AT_PROMPT, at: this.selectedAt })
|
||||
const callId = CallId('schedule-at-browser-zone')
|
||||
const callId = ToolCallId('schedule-at-browser-zone')
|
||||
yield { type: 'block-start', index: 0, blockType: 'tool-call' }
|
||||
yield {
|
||||
type: 'tool-call-delta',
|
||||
@@ -258,7 +258,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
|
||||
await workspace.attachSession(afterHandle.agent.id)
|
||||
const afterCreated = await scaffold.ctx.tools.execute({
|
||||
signal: AbortSignal.timeout(10_000),
|
||||
callId: CallId('schedule-after-create'),
|
||||
callId: ToolCallId('schedule-after-create'),
|
||||
name: 'schedule_create',
|
||||
arguments: { prompt: AFTER_PROMPT, after_seconds: 1 },
|
||||
agent: afterHandle.agent,
|
||||
@@ -314,7 +314,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
|
||||
await workspace.attachSession(everyHandle.agent.id)
|
||||
const everyListed = await scaffold.ctx.tools.execute({
|
||||
signal: AbortSignal.timeout(10_000),
|
||||
callId: CallId('schedule-every-list'),
|
||||
callId: ToolCallId('schedule-every-list'),
|
||||
name: 'schedule_list',
|
||||
arguments: {},
|
||||
agent: everyHandle.agent,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { readFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { afterEach, expect, it } from 'vitest'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import { canonicalPath, writableRoots } from '@deepseek-ai/dsh-sandbox'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
|
||||
@@ -199,7 +199,7 @@ it('lets a preset producer reach the background-job registry', async () => {
|
||||
// fails here — with every task control still listed in the catalog above.
|
||||
const started = await ctx.tools.execute({
|
||||
signal,
|
||||
callId: CallId('shipped-bash-background'),
|
||||
callId: ToolCallId('shipped-bash-background'),
|
||||
name: 'bash',
|
||||
arguments: {
|
||||
command: 'printf SHIPPED_BACKGROUND_OK',
|
||||
@@ -217,7 +217,7 @@ it('lets a preset producer reach the background-job registry', async () => {
|
||||
// owner. A per-preset registry would list nothing here even on success.
|
||||
const listed = await ctx.tools.execute({
|
||||
signal,
|
||||
callId: CallId('shipped-task-list'),
|
||||
callId: ToolCallId('shipped-task-list'),
|
||||
name: 'job_list',
|
||||
arguments: {},
|
||||
agent: handle.agent,
|
||||
@@ -231,7 +231,7 @@ it('lets a preset producer reach the background-job registry', async () => {
|
||||
// through a preset-plane control, which is the linkage the realm severed.
|
||||
const collected = await ctx.tools.execute({
|
||||
signal,
|
||||
callId: CallId('shipped-task-output'),
|
||||
callId: ToolCallId('shipped-task-output'),
|
||||
name: 'job_output',
|
||||
arguments: { job_id: 'bash-1', wait: true },
|
||||
agent: handle.agent,
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/07-into-the-harness.md
|
||||
07-into-the-harness.md: 2d3c23f9f7f7fc6bd6cabd4e7e68ebfc46e20665
|
||||
07-into-the-harness.zh.md: 0e0fe031be92c8284d2ff6331d753b9b25994ee9
|
||||
07-into-the-harness.md: 1bfe05fb511b54067b913a1a2d0903a0772a22d7
|
||||
07-into-the-harness.zh.md: 5ed4ff9d95d6e8b14402e525e1e9bb5328bfb678
|
||||
@@ -11,7 +11,7 @@ Create `greet-tool.ts` in `tmp/cordis-tutorial`:
|
||||
```ts
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
export const name = 'greet-tool'
|
||||
export const inject = ['tools']
|
||||
@@ -33,10 +33,10 @@ export function apply(ctx: Context) {
|
||||
}))
|
||||
|
||||
// Drive one call through the real execution pipeline, standing in for
|
||||
// the model. CallId brands the correlation id a provider would issue.
|
||||
// the model. ToolCallId brands the correlation id a provider would issue.
|
||||
void (async () => {
|
||||
const result = await ctx.tools.execute({
|
||||
callId: CallId('demo-1'),
|
||||
callId: ToolCallId('demo-1'),
|
||||
name: 'greet',
|
||||
arguments: { name: 'Cordis' },
|
||||
signal: new AbortController().signal,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
```ts
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
export const name = 'greet-tool'
|
||||
export const inject = ['tools']
|
||||
@@ -33,10 +33,10 @@ export function apply(ctx: Context) {
|
||||
}))
|
||||
|
||||
// Drive one call through the real execution pipeline, standing in for
|
||||
// the model. CallId brands the correlation id a provider would issue.
|
||||
// the model. ToolCallId brands the correlation id a provider would issue.
|
||||
void (async () => {
|
||||
const result = await ctx.tools.execute({
|
||||
callId: CallId('demo-1'),
|
||||
callId: ToolCallId('demo-1'),
|
||||
name: 'greet',
|
||||
arguments: { name: 'Cordis' },
|
||||
signal: new AbortController().signal,
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/persistence-catalog.md
|
||||
persistence-catalog.md: f8c10821e4b6daa8f10b0cff63f838ecb381125c
|
||||
persistence-catalog.zh.md: 4cefec2eba0a3a3fa7f4c587236fa78e476ad9d7
|
||||
persistence-catalog.md: 44a9f61b7b53670186c565efc0b4c587e216d6b8
|
||||
persistence-catalog.zh.md: 2ea0e7c236301b786a3f5aab66ad58e802f017c7
|
||||
@@ -153,12 +153,12 @@ Source: [`packages/preset/agent-presets/src/session.ts:26`](../packages/preset/a
|
||||
'approval/asked': {
|
||||
id: ApprovalRequestId
|
||||
toolName: string
|
||||
callId?: CallId
|
||||
callId?: ToolCallId
|
||||
reason?: string
|
||||
}
|
||||
```
|
||||
|
||||
Types: [CallId](subsystems/core.md)
|
||||
Types: [ToolCallId](subsystems/core.md)
|
||||
|
||||
Source: [`packages/interaction/user-approval/src/index.ts:44`](../packages/interaction/user-approval/src/index.ts)
|
||||
|
||||
@@ -799,10 +799,10 @@ Source: [`packages/core/session/src/types.ts:303`](../packages/core/session/src/
|
||||
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
||||
* call with its `tool/result`.
|
||||
*/
|
||||
'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
|
||||
'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
|
||||
```
|
||||
|
||||
Types: [CallId](subsystems/core.md)
|
||||
Types: [ToolCallId](subsystems/core.md)
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:283`](../packages/core/session/src/types.ts)
|
||||
|
||||
|
||||
@@ -155,12 +155,12 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
'approval/asked': {
|
||||
id: ApprovalRequestId
|
||||
toolName: string
|
||||
callId?: CallId
|
||||
callId?: ToolCallId
|
||||
reason?: string
|
||||
}
|
||||
```
|
||||
|
||||
类型:[CallId](subsystems/core.zh.md)
|
||||
类型:[ToolCallId](subsystems/core.zh.md)
|
||||
|
||||
来源:[`packages/interaction/user-approval/src/index.ts:44`](../packages/interaction/user-approval/src/index.ts)
|
||||
|
||||
@@ -801,10 +801,10 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
||||
* call with its `tool/result`.
|
||||
*/
|
||||
'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
|
||||
'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
|
||||
```
|
||||
|
||||
类型:[CallId](subsystems/core.zh.md)
|
||||
类型:[ToolCallId](subsystems/core.zh.md)
|
||||
|
||||
来源:[`packages/core/session/src/types.ts:283`](../packages/core/session/src/types.ts)
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/approval.md
|
||||
approval.md: 7d3d09314f8fbb151cc8a00dbbee5e2cc14e2c9a
|
||||
approval.zh.md: 22d0b0ec4242fcb2ad6fb82c29893a5914369238
|
||||
approval.md: 276cac2c00e5b781e9cdbb5da461d035e61de719
|
||||
approval.zh.md: 5fe63b5d1aa39ddced51af2c0eaf860e4df10e86
|
||||
@@ -70,7 +70,7 @@ interface ApprovalRequest {
|
||||
* The exact tool call being decided, when the asker has one — lets a UI
|
||||
* attach the prompt to the tool call it already streamed.
|
||||
*/
|
||||
readonly callId?: CallId
|
||||
readonly callId?: ToolCallId
|
||||
/** The asker's human-readable explanation of WHY it is asking. */
|
||||
readonly reason?: string
|
||||
/**
|
||||
|
||||
@@ -70,7 +70,7 @@ interface ApprovalRequest {
|
||||
* The exact tool call being decided, when the asker has one — lets a UI
|
||||
* attach the prompt to the tool call it already streamed.
|
||||
*/
|
||||
readonly callId?: CallId
|
||||
readonly callId?: ToolCallId
|
||||
/** The asker's human-readable explanation of WHY it is asking. */
|
||||
readonly reason?: string
|
||||
/**
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/compaction.md
|
||||
compaction.md: 4b6e9dee81cb30d42bb6194776457e354102b529
|
||||
compaction.zh.md: a9c57b64798be1d0361125eac68d589b3e0f1c66
|
||||
compaction.md: ebd8e6e9d7530ff5b0bb9faf4a4d5d24b00e464d
|
||||
compaction.zh.md: e51e8f7d627a339e3e5a9c079d5b5a57f384772d
|
||||
@@ -99,7 +99,7 @@ interface PrunedEntry {
|
||||
/** Newly appended pruned tool-result event. */
|
||||
readonly replacementSeq: number
|
||||
/** Tool call shared by the original and replacement. */
|
||||
readonly callId: CallId
|
||||
readonly callId: ToolCallId
|
||||
/** Original text size in Unicode code points. */
|
||||
readonly charsBefore: number
|
||||
/** Replacement text size in Unicode code points. */
|
||||
|
||||
@@ -99,7 +99,7 @@ interface PrunedEntry {
|
||||
/** Newly appended pruned tool-result event. */
|
||||
readonly replacementSeq: number
|
||||
/** Tool call shared by the original and replacement. */
|
||||
readonly callId: CallId
|
||||
readonly callId: ToolCallId
|
||||
/** Original text size in Unicode code points. */
|
||||
readonly charsBefore: number
|
||||
/** Replacement text size in Unicode code points. */
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/core.md
|
||||
core.md: 18057c9a9a308e439bb6a08f598f51f158243496
|
||||
core.zh.md: dfb1d8ad482b2cf9c6de94bde04c4dbff83a0082
|
||||
core.md: 37c185ff8c90f88aecb0b2a7e426bc8f22162578
|
||||
core.zh.md: 6559e25a5cdb534d98c3fd28cd68c506bdbaf1f4
|
||||
@@ -295,7 +295,7 @@ Two large discriminated unions are the ones consumers `switch` over most: **`Str
|
||||
|
||||
### Branded IDs
|
||||
|
||||
IDs passed between packages are **branded** — structurally strings, but non-interchangeable at the type level (a `SessionId` cannot be passed where a `CallId` is expected). Construction goes through a per-type factory; comparison, logging, and JSON behave as ordinary strings.
|
||||
IDs passed between packages are **branded** — structurally strings, but non-interchangeable at the type level (a `SessionId` cannot be passed where a `ToolCallId` is expected). Construction goes through a per-type factory; comparison, logging, and JSON behave as ordinary strings.
|
||||
|
||||
The `Branded<B>` primitive lives in its own type-only package, [dsh-brand](../../packages/util/brand) (no runtime code, no harness-package dependency), so any package can brand the ids it owns without depending on an unrelated capability package.
|
||||
|
||||
@@ -306,7 +306,7 @@ Source: [`packages/util/brand/src/index.ts`](../../packages/util/brand/src/index
|
||||
type Branded<B extends string> = string & { readonly [BRAND]: B }
|
||||
```
|
||||
|
||||
The two core IDs are `CallId` (correlates a tool call with its result; dsh-llm) and `SessionId` (the shared live agent and durable session identity; dsh-session). Capability packages brand their own ids too, such as `JobId` in [jobs.md](jobs.md).
|
||||
The two core IDs are `ToolCallId` (correlates a tool call with its result; dsh-llm) and `SessionId` (the shared live agent and durable session identity; dsh-session). Capability packages brand their own ids too, such as `JobId` in [jobs.md](jobs.md).
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
|
||||
@@ -305,7 +305,7 @@ declare module '@deepseek-ai/dsh-llm' {
|
||||
|
||||
### 品牌化 ID
|
||||
|
||||
在包之间传递的 ID 都经过**品牌化**——结构上是字符串,但在类型层面不可互换(不能把 `SessionId` 传给需要 `CallId` 的位置)。每种类型通过各自的工厂构造;比较、日志记录和 JSON 行为与普通字符串相同。
|
||||
在包之间传递的 ID 都经过**品牌化**——结构上是字符串,但在类型层面不可互换(不能把 `SessionId` 传给需要 `ToolCallId` 的位置)。每种类型通过各自的工厂构造;比较、日志记录和 JSON 行为与普通字符串相同。
|
||||
|
||||
`Branded<B>` 原语位于独立的纯类型包 [dsh-brand](../../packages/util/brand) 中(没有运行时代码,也不依赖 harness 包),因此任何包都能品牌化其拥有的 id,而无需依赖无关的能力包。
|
||||
|
||||
@@ -316,7 +316,7 @@ declare module '@deepseek-ai/dsh-llm' {
|
||||
type Branded<B extends string> = string & { readonly [BRAND]: B }
|
||||
```
|
||||
|
||||
两个核心 ID 是 `CallId`(关联工具调用及其结果;dsh-llm)和 `SessionId`(活跃 agent 与持久会话共享的标识;dsh-session)。能力包也会品牌化各自的 id,例如 [jobs.md](jobs.zh.md) 中的 `JobId`。
|
||||
两个核心 ID 是 `ToolCallId`(关联工具调用及其结果;dsh-llm)和 `SessionId`(活跃 agent 与持久会话共享的标识;dsh-session)。能力包也会品牌化各自的 id,例如 [jobs.md](jobs.zh.md) 中的 `JobId`。
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/llm-streaming.md
|
||||
llm-streaming.md: 4f322ca1024b9d74a4906e34f93fc6f8e4082cbf
|
||||
llm-streaming.zh.md: c74cabe27c1f5fdd44711ac0aae7cd6b0a7ba7dd
|
||||
llm-streaming.md: adb8fdde50c898b42f74a1562f95ab267afa6db6
|
||||
llm-streaming.zh.md: 197ae86a2182b93c3fbf4005b3fc51df96dc49b3
|
||||
@@ -28,7 +28,7 @@ interface ContentBlockMap {
|
||||
}
|
||||
```
|
||||
|
||||
The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](attachment.md)), `ToolCallBlock` (`id: CallId`, `name`, raw-JSON `arguments`), and `ToolResultBlock` (`toolCallId`, nested `content: ContentBlock[]`, `isError?`). `ContentBlock = ContentBlockMap[ContentBlockType]`. A new modality belongs in the merge-extensible map only when its adapter, UI, compaction, and durable replay paths honor it.
|
||||
The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](attachment.md)), `ToolCallBlock` (`id: ToolCallId`, `name`, raw-JSON `arguments`), and `ToolResultBlock` (`toolCallId`, nested `content: ContentBlock[]`, `isError?`). `ContentBlock = ContentBlockMap[ContentBlockType]`. A new modality belongs in the merge-extensible map only when its adapter, UI, compaction, and durable replay paths honor it.
|
||||
|
||||
Source: [`packages/llm/llm/src/message.ts`](../../packages/llm/llm/src/message.ts)
|
||||
|
||||
@@ -193,7 +193,7 @@ type StreamChunk =
|
||||
| { type: 'block-start'; index: number; blockType: ContentBlockType }
|
||||
| { type: 'text-delta'; index: number; text: string }
|
||||
| { type: 'reasoning-delta'; index: number; text: string }
|
||||
| { type: 'tool-call-delta'; index: number; id: CallId; name?: string; argumentsDelta: string }
|
||||
| { type: 'tool-call-delta'; index: number; id: ToolCallId; name?: string; argumentsDelta: string }
|
||||
| { type: 'block-end'; index: number; block: ContentBlock }
|
||||
| { type: 'usage'; usage: TokenUsage }
|
||||
| {
|
||||
|
||||
@@ -28,7 +28,7 @@ interface ContentBlockMap {
|
||||
}
|
||||
```
|
||||
|
||||
各块接口(完整字段见源码):`TextBlock`(`text`)、`ReasoningBlock`(thinking,区别于可见文本)、`ImageBlock`(一个持久的[图片附件](attachment.zh.md))、`ToolCallBlock`(`id: CallId`、`name`、原始 JSON `arguments`),以及 `ToolResultBlock`(`toolCallId`、嵌套 `content: ContentBlock[]`、`isError?`)。`ContentBlock = ContentBlockMap[ContentBlockType]`。仅当适配器、UI、压缩(compaction)和持久回放路径均支持某种新模态时,才将其纳入可合并扩展的 map。
|
||||
各块接口(完整字段见源码):`TextBlock`(`text`)、`ReasoningBlock`(thinking,区别于可见文本)、`ImageBlock`(一个持久的[图片附件](attachment.zh.md))、`ToolCallBlock`(`id: ToolCallId`、`name`、原始 JSON `arguments`),以及 `ToolResultBlock`(`toolCallId`、嵌套 `content: ContentBlock[]`、`isError?`)。`ContentBlock = ContentBlockMap[ContentBlockType]`。仅当适配器、UI、压缩(compaction)和持久回放路径均支持某种新模态时,才将其纳入可合并扩展的 map。
|
||||
|
||||
源码:[`packages/llm/llm/src/message.ts`](../../packages/llm/llm/src/message.ts)
|
||||
|
||||
@@ -193,7 +193,7 @@ type StreamChunk =
|
||||
| { type: 'block-start'; index: number; blockType: ContentBlockType }
|
||||
| { type: 'text-delta'; index: number; text: string }
|
||||
| { type: 'reasoning-delta'; index: number; text: string }
|
||||
| { type: 'tool-call-delta'; index: number; id: CallId; name?: string; argumentsDelta: string }
|
||||
| { type: 'tool-call-delta'; index: number; id: ToolCallId; name?: string; argumentsDelta: string }
|
||||
| { type: 'block-end'; index: number; block: ContentBlock }
|
||||
| { type: 'usage'; usage: TokenUsage }
|
||||
| {
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/session.md
|
||||
session.md: 96520c72b83bf713b7fe09563f7d6e44c0386bcf
|
||||
session.zh.md: 28a259226b8149dbe26c7650bc574de4aa43a740
|
||||
session.md: a23ed4c43befc1c39b110560d933ff2bb1bce941
|
||||
session.zh.md: a564ddd34705d530267f3f5bba5afe08b7dcc0ae
|
||||
@@ -71,7 +71,7 @@ interface SessionEventMap {
|
||||
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
||||
* call with its `tool/result`.
|
||||
*/
|
||||
'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
|
||||
'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
|
||||
/**
|
||||
* A completed tool call's model-facing result, optional internal failure
|
||||
* identity, and optional tool-private `meta` presentation payload. `meta` is
|
||||
|
||||
@@ -71,7 +71,7 @@ interface SessionEventMap {
|
||||
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
||||
* call with its `tool/result`.
|
||||
*/
|
||||
'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
|
||||
'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
|
||||
/**
|
||||
* A completed tool call's model-facing result, optional internal failure
|
||||
* identity, and optional tool-private `meta` presentation payload. `meta` is
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/spill.md
|
||||
spill.md: 8356fc45f8581c6776238f212592c4ea2841a96a
|
||||
spill.zh.md: 353c5fea63ff6284c922a9bbf7d6d07f124fb9bd
|
||||
spill.md: 2a3201cf2fa0d7b034a01f64d0448536bfe8d861
|
||||
spill.zh.md: d4c5b3853be46780cd93e2f826bf399bff42c405
|
||||
@@ -50,7 +50,7 @@ interface SpillSource {
|
||||
/** The tool whose result was spilled (e.g. `web_fetch`). */
|
||||
toolName: string
|
||||
/** The model-issued call id the result belongs to. */
|
||||
callId: CallId
|
||||
callId: ToolCallId
|
||||
/** A short human label for the artifact (e.g. `result`). */
|
||||
label: string
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ interface SpillSource {
|
||||
/** The tool whose result was spilled (e.g. `web_fetch`). */
|
||||
toolName: string
|
||||
/** The model-issued call id the result belongs to. */
|
||||
callId: CallId
|
||||
callId: ToolCallId
|
||||
/** A short human label for the artifact (e.g. `result`). */
|
||||
label: string
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/tools.md
|
||||
tools.md: dd89d40c1401732dd5e6d8dfb5ccb08a89c58405
|
||||
tools.zh.md: 0d3e59bc222527e66dd5bd626203f4e1bb6aad9a
|
||||
tools.md: 5ac2401d662bca52cac529c0620a39930565a7c7
|
||||
tools.zh.md: a081c58a2ba4df50b035a4dae8fa7f9ab7d9de1a
|
||||
@@ -183,12 +183,12 @@ type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]: true }
|
||||
* callers do not choose that token.
|
||||
*/
|
||||
interface ToolExecutionInput {
|
||||
readonly callId: CallId
|
||||
readonly callId: ToolCallId
|
||||
/**
|
||||
* Root model-requested call owning this execution tree. Callers omit it for
|
||||
* a root execution; nested dispatchers propagate the enclosing value.
|
||||
*/
|
||||
readonly rootCallId?: CallId
|
||||
readonly rootCallId?: ToolCallId
|
||||
readonly name: string
|
||||
/** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
|
||||
readonly arguments: unknown
|
||||
@@ -270,7 +270,7 @@ interface CodeDispatchLog {
|
||||
/** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
|
||||
readonly agent?: Agent
|
||||
/** Deterministic sub-call id (`<parent>:code:<n>`). */
|
||||
readonly subCallId: CallId
|
||||
readonly subCallId: ToolCallId
|
||||
/** The dispatched sub-tool name. */
|
||||
readonly name: string
|
||||
/** Whether the sub-call settled as an error. */
|
||||
@@ -290,7 +290,7 @@ interface CodeDispatchLog {
|
||||
*/
|
||||
interface ToolExecution extends ToolExecutionInput {
|
||||
/** Root model-requested call, resolved for every root and nested execution. */
|
||||
readonly rootCallId: CallId
|
||||
readonly rootCallId: ToolCallId
|
||||
/** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
|
||||
readonly token: ToolExecutionToken
|
||||
}
|
||||
|
||||
@@ -183,12 +183,12 @@ type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]: true }
|
||||
* callers do not choose that token.
|
||||
*/
|
||||
interface ToolExecutionInput {
|
||||
readonly callId: CallId
|
||||
readonly callId: ToolCallId
|
||||
/**
|
||||
* Root model-requested call owning this execution tree. Callers omit it for
|
||||
* a root execution; nested dispatchers propagate the enclosing value.
|
||||
*/
|
||||
readonly rootCallId?: CallId
|
||||
readonly rootCallId?: ToolCallId
|
||||
readonly name: string
|
||||
/** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
|
||||
readonly arguments: unknown
|
||||
@@ -270,7 +270,7 @@ interface CodeDispatchLog {
|
||||
/** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
|
||||
readonly agent?: Agent
|
||||
/** Deterministic sub-call id (`<parent>:code:<n>`). */
|
||||
readonly subCallId: CallId
|
||||
readonly subCallId: ToolCallId
|
||||
/** The dispatched sub-tool name. */
|
||||
readonly name: string
|
||||
/** Whether the sub-call settled as an error. */
|
||||
@@ -290,7 +290,7 @@ interface CodeDispatchLog {
|
||||
*/
|
||||
interface ToolExecution extends ToolExecutionInput {
|
||||
/** Root model-requested call, resolved for every root and nested execution. */
|
||||
readonly rootCallId: CallId
|
||||
readonly rootCallId: ToolCallId
|
||||
/** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
|
||||
readonly token: ToolExecutionToken
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/practice/llm-adapter.md
|
||||
llm-adapter.md: 882e82d880ac622cf886c6c24e75a1987e1c6702
|
||||
llm-adapter.zh.md: 27c480af2a19a68ac35900a2ce1b5e9dbdc85bf8
|
||||
llm-adapter.md: ff5fc5657e2be0a5fe0d4e10f148865e62f92b18
|
||||
llm-adapter.zh.md: b05bfad6cbc4038f54f816b212068d85e54c2100
|
||||
@@ -54,7 +54,7 @@ export function apply(ctx: Context, config: Config) {
|
||||
`stream()` yields chunks using this protocol:
|
||||
|
||||
```ts
|
||||
import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
async function* exampleChunks(): AsyncIterable<StreamChunk> {
|
||||
// 1. Start each content block with block-start.
|
||||
@@ -76,7 +76,7 @@ async function* exampleChunks(): AsyncIterable<StreamChunk> {
|
||||
yield {
|
||||
type: 'tool-call-delta',
|
||||
index: 1,
|
||||
id: CallId('call-123'),
|
||||
id: ToolCallId('call-123'),
|
||||
name: 'bash',
|
||||
argumentsDelta: '{"command":"ls"}',
|
||||
}
|
||||
@@ -85,7 +85,7 @@ async function* exampleChunks(): AsyncIterable<StreamChunk> {
|
||||
index: 1,
|
||||
block: {
|
||||
type: 'tool-call',
|
||||
id: CallId('call-123'),
|
||||
id: ToolCallId('call-123'),
|
||||
name: 'bash',
|
||||
arguments: '{"command":"ls"}',
|
||||
},
|
||||
|
||||
@@ -54,7 +54,7 @@ export function apply(ctx: Context, config: Config) {
|
||||
`stream()` 必须按以下协议生成分片:
|
||||
|
||||
```ts
|
||||
import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
async function* exampleChunks(): AsyncIterable<StreamChunk> {
|
||||
// 1. Start each content block with block-start.
|
||||
@@ -76,7 +76,7 @@ async function* exampleChunks(): AsyncIterable<StreamChunk> {
|
||||
yield {
|
||||
type: 'tool-call-delta',
|
||||
index: 1,
|
||||
id: CallId('call-123'),
|
||||
id: ToolCallId('call-123'),
|
||||
name: 'bash',
|
||||
argumentsDelta: '{"command":"ls"}',
|
||||
}
|
||||
@@ -85,7 +85,7 @@ async function* exampleChunks(): AsyncIterable<StreamChunk> {
|
||||
index: 1,
|
||||
block: {
|
||||
type: 'tool-call',
|
||||
id: CallId('call-123'),
|
||||
id: ToolCallId('call-123'),
|
||||
name: 'bash',
|
||||
arguments: '{"command":"ls"}',
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
import { boot, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
const configPath = process.argv[2]
|
||||
if (configPath === undefined) throw new Error('tool-pwsh driver requires a config path')
|
||||
@@ -21,7 +21,7 @@ try {
|
||||
|
||||
const foreground = await ctx.tools.execute({
|
||||
signal: new AbortController().signal,
|
||||
callId: CallId('loader-fg'),
|
||||
callId: ToolCallId('loader-fg'),
|
||||
name: 'pwsh',
|
||||
arguments: { command: 'Write-Output loader-ok', description: 'loader foreground' },
|
||||
})
|
||||
@@ -29,7 +29,7 @@ try {
|
||||
|
||||
const background = await ctx.tools.execute({
|
||||
signal: new AbortController().signal,
|
||||
callId: CallId('loader-bg'),
|
||||
callId: ToolCallId('loader-bg'),
|
||||
name: 'pwsh',
|
||||
arguments: {
|
||||
command: 'Start-Sleep -Milliseconds 200; Write-Output loader-bg-ok',
|
||||
@@ -46,7 +46,7 @@ try {
|
||||
while (Date.now() < deadline) {
|
||||
const read = await ctx.tools.execute({
|
||||
signal: new AbortController().signal,
|
||||
callId: CallId('loader-bg-read'),
|
||||
callId: ToolCallId('loader-bg-read'),
|
||||
name: 'job_output',
|
||||
arguments: { job_id: jobId },
|
||||
})
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
/**
|
||||
* Test adapter for the `mock-delegate` model: the first request calls the
|
||||
@@ -20,8 +20,8 @@ class MockDelegatingAdapter extends LlmAdapter {
|
||||
if (toolResultText.length === 0) {
|
||||
const args = JSON.stringify({ description: 'cwd probe', prompt: 'report your workspace' })
|
||||
yield { type: 'block-start', index: 0, blockType: 'tool-call' }
|
||||
yield { type: 'tool-call-delta', index: 0, id: CallId('call-delegate'), name: 'subagent', argumentsDelta: args }
|
||||
yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('call-delegate'), name: 'subagent', arguments: args } }
|
||||
yield { type: 'tool-call-delta', index: 0, id: ToolCallId('call-delegate'), name: 'subagent', argumentsDelta: args }
|
||||
yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('call-delegate'), name: 'subagent', arguments: args } }
|
||||
yield { type: 'usage', usage: { inputTokens: 10, outputTokens: 5 } }
|
||||
yield { type: 'finish', reason: { kind: 'tool-calls' } }
|
||||
return
|
||||
|
||||
@@ -3,7 +3,7 @@ import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import LlmRuntime, { createUserMessage, CallId, HarnessError } from '@deepseek-ai/dsh-llm'
|
||||
import LlmRuntime, { createUserMessage, ToolCallId, HarnessError } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
@@ -93,7 +93,7 @@ function runCode(
|
||||
agent?: Agent,
|
||||
): Promise<ToolExecutionResult> {
|
||||
return harness.tools.execute({
|
||||
callId: CallId(`keyless-code-${++keylessCall}`),
|
||||
callId: ToolCallId(`keyless-code-${++keylessCall}`),
|
||||
name: RUN_CODE_NAME,
|
||||
arguments: { code, description: 'Run the e2e program' },
|
||||
signal,
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import {
|
||||
CallId,
|
||||
ToolCallId,
|
||||
LlmAdapter,
|
||||
ReasoningEffortId,
|
||||
type GenerateOptions,
|
||||
@@ -37,8 +37,8 @@ class CliMockAdapter extends LlmAdapter {
|
||||
if (toolResult === undefined) {
|
||||
const args = JSON.stringify({ command: 'printf CLI_TOOL_ROUND_TRIP', description: 'Prove the CLI tool round trip.' })
|
||||
yield { type: 'block-start', index: 0, blockType: 'tool-call' }
|
||||
yield { type: 'tool-call-delta', index: 0, id: CallId('cli-smoke-call'), name: 'bash', argumentsDelta: args }
|
||||
yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('cli-smoke-call'), name: 'bash', arguments: args } }
|
||||
yield { type: 'tool-call-delta', index: 0, id: ToolCallId('cli-smoke-call'), name: 'bash', argumentsDelta: args }
|
||||
yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('cli-smoke-call'), name: 'bash', arguments: args } }
|
||||
yield { type: 'usage', usage: { inputTokens: 11, outputTokens: 3, cacheReadTokens: 2 } }
|
||||
yield { type: 'finish', reason: { kind: 'tool-calls' } }
|
||||
return
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/** Deterministic keyless Agent Teams adapter for the real headless Loader snapshot. */
|
||||
|
||||
import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
let nextCall = 0
|
||||
|
||||
@@ -38,7 +38,7 @@ function latestToolText(messages) {
|
||||
function toolChunks(specs) {
|
||||
const chunks = []
|
||||
for (const [index, spec] of specs.entries()) {
|
||||
const id = CallId(`team-fixture-${++nextCall}`)
|
||||
const id = ToolCallId(`team-fixture-${++nextCall}`)
|
||||
const args = JSON.stringify(spec.args)
|
||||
chunks.push(
|
||||
{ type: 'block-start', index, blockType: 'tool-call' },
|
||||
|
||||
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { normalizeSessionSnapshot, type NormalizeContext } from '@deepseek-ai/dsh-acp-snapshot'
|
||||
import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
|
||||
import { createUserMessage, CallId , createMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId , createMessage } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { SESSION_FORMAT_VERSION, SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session'
|
||||
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
@@ -46,7 +46,7 @@ async function seedInterruptedSession(root: string, cwd: string): Promise<string
|
||||
step: 1,
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [{ type: 'tool-call', id: CallId('unknown-outcome-call'), name: 'write_remote', arguments: '{"value":1}' }],
|
||||
content: [{ type: 'tool-call', id: ToolCallId('unknown-outcome-call'), name: 'write_remote', arguments: '{"value":1}' }],
|
||||
source: {
|
||||
kind: 'model',
|
||||
...{ provider: 'deepseek-official', model: 'deepseek-v4-flash' },
|
||||
@@ -62,7 +62,7 @@ async function seedInterruptedSession(root: string, cwd: string): Promise<string
|
||||
data: {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
callId: CallId('unknown-outcome-call'),
|
||||
callId: ToolCallId('unknown-outcome-call'),
|
||||
name: 'write_remote',
|
||||
arguments: '{"value":1}',
|
||||
},
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
/**
|
||||
* Test adapter for the `mock-delegate` model: the first request calls the
|
||||
@@ -20,8 +20,8 @@ class MockDelegatingAdapter extends LlmAdapter {
|
||||
if (toolResultText.length === 0) {
|
||||
const args = JSON.stringify({ description: 'cwd probe', prompt: 'report your workspace' })
|
||||
yield { type: 'block-start', index: 0, blockType: 'tool-call' }
|
||||
yield { type: 'tool-call-delta', index: 0, id: CallId('call-delegate'), name: 'subagent', argumentsDelta: args }
|
||||
yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('call-delegate'), name: 'subagent', arguments: args } }
|
||||
yield { type: 'tool-call-delta', index: 0, id: ToolCallId('call-delegate'), name: 'subagent', argumentsDelta: args }
|
||||
yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('call-delegate'), name: 'subagent', arguments: args } }
|
||||
yield { type: 'usage', usage: { inputTokens: 10, outputTokens: 5 } }
|
||||
yield { type: 'finish', reason: { kind: 'tool-calls' } }
|
||||
return
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { PROTOCOL_VERSION } from '@agentclientprotocol/sdk'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import ApprovalService, { type ApprovalRequest } from '@deepseek-ai/dsh-user-approval'
|
||||
@@ -21,7 +21,7 @@ describe('ACP machine permission policy', () => {
|
||||
const { sessionId } = await harness.client.newSession({ cwd: process.cwd(), mcpServers: [] })
|
||||
const agent = harness.ctx.agents.get(SessionId(sessionId))!
|
||||
agent.session.append('turn/start', { turn: 1 })
|
||||
return { agent, toolName: 'bash', callId: CallId('call-9'), ...overrides }
|
||||
return { agent, toolName: 'bash', callId: ToolCallId('call-9'), ...overrides }
|
||||
}
|
||||
|
||||
it('maps the two advertised one-shot choices', async () => {
|
||||
@@ -63,7 +63,7 @@ describe('ACP machine permission policy', () => {
|
||||
const foreign = {
|
||||
session: { id: request.agent.session.id, events: [{ type: 'turn/start' }], append: () => ({}) },
|
||||
} as unknown as Agent
|
||||
await expect(harness.ctx.approval.request({ agent: foreign, toolName: 'bash', callId: CallId('call') }))
|
||||
await expect(harness.ctx.approval.request({ agent: foreign, toolName: 'bash', callId: ToolCallId('call') }))
|
||||
.resolves.toBe('unavailable')
|
||||
expect(harness.permissionRequests).toHaveLength(0)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { PROTOCOL_VERSION } from '@agentclientprotocol/sdk'
|
||||
import { createUserMessage, CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
|
||||
import { makeBridgeHarness, textResponse, type BridgeHarness } from './harness.ts'
|
||||
@@ -8,8 +8,8 @@ import { makeBridgeHarness, textResponse, type BridgeHarness } from './harness.t
|
||||
function toolCallResponse(): StreamChunk[] {
|
||||
return [
|
||||
{ type: 'block-start', index: 0, blockType: 'tool-call' },
|
||||
{ type: 'tool-call-delta', index: 0, id: CallId('call-1'), name: 'echo', argumentsDelta: '{}' },
|
||||
{ type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('call-1'), name: 'echo', arguments: '{}' } },
|
||||
{ type: 'tool-call-delta', index: 0, id: ToolCallId('call-1'), name: 'echo', argumentsDelta: '{}' },
|
||||
{ type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('call-1'), name: 'echo', arguments: '{}' } },
|
||||
{ type: 'finish', reason: { kind: 'tool-calls' } },
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
createUserMessage,
|
||||
isTokenDelta,
|
||||
} from '@deepseek-ai/dsh-llm/message'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm/brand'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm/brand'
|
||||
import type {
|
||||
AssistantMessage,
|
||||
ContentBlock,
|
||||
@@ -62,7 +62,7 @@ function assistantMessage(content: ContentBlock[], model = 'fx-1'): AssistantMes
|
||||
}
|
||||
|
||||
function toolResultMessage(callId: string, content: ContentBlock[], isError: boolean): ToolResultMessage {
|
||||
return createToolResultMessage({ callId: CallId(callId), content, isError })
|
||||
return createToolResultMessage({ callId: ToolCallId(callId), content, isError })
|
||||
}
|
||||
|
||||
const MARKDOWN_FIXTURE = [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createUserMessage, createMessage, createToolResultMessage, CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, createMessage, createToolResultMessage, ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
// Minimal SessionEvent builders for orchestration tests (shape mirrors what the
|
||||
// host emits; only the fields the object layer reads).
|
||||
import type { ContentBlock } from '@deepseek-ai/dsh-llm/types'
|
||||
@@ -45,7 +45,7 @@ export const ev = {
|
||||
turn,
|
||||
step,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId(callId),
|
||||
callId: ToolCallId(callId),
|
||||
content: text(body),
|
||||
isError: false,
|
||||
}),
|
||||
|
||||
@@ -20,7 +20,7 @@ import type {
|
||||
import type { createChatStore } from '../stores.ts'
|
||||
import type { ComposerSubmitGesture, InputSubmitMode } from './composer-submission.ts'
|
||||
import type { ChatNode, ChatNodeKind } from './chat-nodes.ts'
|
||||
import type { CallId, SelectionTarget, ViewTab } from './views.ts'
|
||||
import type { ToolCallId, SelectionTarget, ViewTab } from './views.ts'
|
||||
|
||||
/** Browser-owned image that has not crossed the durable host boundary. */
|
||||
export interface ComposerAttachment {
|
||||
@@ -344,7 +344,7 @@ export interface InputZone {
|
||||
*/
|
||||
export interface ConvViewOwnerProps {
|
||||
/** One-shot inspect request from another view (chat's Inspect button); null when idle. */
|
||||
inspect?: { callId: CallId } | null
|
||||
inspect?: { callId: ToolCallId } | null
|
||||
/** Acknowledge the inspect request once applied (clears the store field). */
|
||||
onInspectDone?: () => void
|
||||
}
|
||||
@@ -415,11 +415,11 @@ export interface ChatNodeTurnDataInjected {
|
||||
/** Stable owner currency delivered to one keyed Chat business renderer. */
|
||||
export interface ChatNodeOwnerProps {
|
||||
/** Selected Tool call, when the shared details store names one. */
|
||||
selectedCallId?: CallId | undefined
|
||||
selectedCallId?: ToolCallId | undefined
|
||||
/** Session workspace root; Tool summaries display paths relative to it. */
|
||||
cwd?: string | undefined
|
||||
openFile: (path: string) => void
|
||||
inspectCall: (callId: CallId) => void
|
||||
inspectCall: (callId: ToolCallId) => void
|
||||
forkAt: (seq: number) => void
|
||||
/** Render a historical image group through the attachment slot. */
|
||||
renderMessageImages: RenderMessageImages
|
||||
@@ -761,7 +761,7 @@ export interface ChatViewInjected {
|
||||
/** Resolve a session-authorized historical image for inline display. */
|
||||
loadImage: (attachment: ImageAttachmentRef) => Promise<string>
|
||||
/** Hand a call off to the trajectory view: write the one-shot inspect target and switch tabs. */
|
||||
inspectCall: (callId: CallId) => void
|
||||
inspectCall: (callId: ToolCallId) => void
|
||||
/**
|
||||
* Per-session scroll memory surviving view switches (in-memory, never
|
||||
* persisted): the view saves on every scroll and restores on remount; a
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/** Shared conversation view, selection, and store-state contracts. */
|
||||
|
||||
/** Tool call identity as carried on the wire (branded upstream in connection). */
|
||||
export type CallId = string
|
||||
export type ToolCallId = string
|
||||
|
||||
/** Selection target for the details linkage channel (toolcall is the step special case). */
|
||||
export interface SelectionTarget { turnSeq: number; stepSeq?: number; callId?: CallId; toolName?: string }
|
||||
export interface SelectionTarget { turnSeq: number; stepSeq?: number; callId?: ToolCallId; toolName?: string }
|
||||
|
||||
/**
|
||||
* One conversation view tab, projected from a 'conversation.view' slot
|
||||
@@ -28,5 +28,5 @@ export interface ChatStoreState {
|
||||
* view consumes it and acknowledges by clearing. Read with `?? null` —
|
||||
* persisted snapshots from before this field rehydrate without it.
|
||||
*/
|
||||
inspect: { callId: CallId } | null
|
||||
inspect: { callId: ToolCallId } | null
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export type { IConversation } from './service.ts'
|
||||
export type { DraftAttachmentId } from './input/contract.ts'
|
||||
|
||||
export type {
|
||||
CallId, ChatStoreState, SelectionTarget, ViewTab,
|
||||
ToolCallId, ChatStoreState, SelectionTarget, ViewTab,
|
||||
} from './contract/views.ts'
|
||||
export type { ConversationKey } from './locales.ts'
|
||||
export type {
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
* The plugin creates its handle at apply time so identity follows the fiber.
|
||||
*/
|
||||
import { defineStore, type EngineStoreHandle } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { CallId, ChatStoreState, SelectionTarget } from './contract/views.ts'
|
||||
import type { ToolCallId, ChatStoreState, SelectionTarget } from './contract/views.ts'
|
||||
|
||||
/** Declared action shape used to give the exported factory a stable return type. */
|
||||
type ChatActions = {
|
||||
select: (draft: ChatStoreState, target: SelectionTarget | null) => void
|
||||
setDraft: (draft: ChatStoreState, text: string) => void
|
||||
setView: (draft: ChatStoreState, view: string) => void
|
||||
setInspect: (draft: ChatStoreState, target: { callId: CallId } | null) => void
|
||||
setInspect: (draft: ChatStoreState, target: { callId: ToolCallId } | null) => void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,7 +28,7 @@ export function createChatStore(): EngineStoreHandle<ChatStoreState, ChatActions
|
||||
select: (d, target: SelectionTarget | null) => { d.selection = target },
|
||||
setDraft: (d, text: string) => { d.draft = text },
|
||||
setView: (d, view: string) => { d.view = view },
|
||||
setInspect: (d, target: { callId: CallId } | null) => { d.inspect = target },
|
||||
setInspect: (d, target: { callId: ToolCallId } | null) => { d.inspect = target },
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
resolveTargetPolicy,
|
||||
} from '@deepseek-ai/dsh-compaction-basic/src/config.ts'
|
||||
import type { CompactionResult } from '@deepseek-ai/dsh-compaction'
|
||||
import LlmRuntime, { createUserMessage, CallId, CONTEXT_WINDOW_EXCEEDED_CODE, createToolResultMessage, LlmAdapter , createMessage } from '@deepseek-ai/dsh-llm'
|
||||
import LlmRuntime, { createUserMessage, ToolCallId, CONTEXT_WINDOW_EXCEEDED_CODE, createToolResultMessage, LlmAdapter , createMessage } from '@deepseek-ai/dsh-llm'
|
||||
import type {
|
||||
ContentBlock,
|
||||
GenerateOptions,
|
||||
@@ -142,7 +142,7 @@ function conversation(turns = 4, text = 'fixture '.repeat(40).trim()): Session {
|
||||
function toolConversation(): Session {
|
||||
const session = Session.create(SessionId('tools'))
|
||||
for (let turn = 1; turn <= 3; turn += 1) {
|
||||
const callId = CallId(`call-${turn}`)
|
||||
const callId = ToolCallId(`call-${turn}`)
|
||||
session.append('turn/start', { turn })
|
||||
session.append('user/message', createUserMessage({
|
||||
content: [{ type: 'text', text: `request ${turn} `.repeat(300) }],
|
||||
@@ -190,7 +190,7 @@ function toolConversation(): Session {
|
||||
/** One closed routed tool step followed by an open turn for rewrite events. */
|
||||
function oversizedToolResult(chars = 3_000, withCompactablePrompt = false): Session {
|
||||
const session = Session.create(SessionId(`oversized-tool-${chars}`))
|
||||
const callId = CallId('oversized')
|
||||
const callId = ToolCallId('oversized')
|
||||
session.append('turn/start', { turn: 1 })
|
||||
if (withCompactablePrompt) {
|
||||
session.append('user/message', createUserMessage({
|
||||
@@ -568,7 +568,7 @@ describe('pressure measurement and retention', () => {
|
||||
it('declines forced overflow when the whole surface is one indivisible tool pair', async () => {
|
||||
const compact = service(compactConfig)
|
||||
const session = Session.create(SessionId('single-tool-pair'))
|
||||
const callId = CallId('single-call')
|
||||
const callId = ToolCallId('single-call')
|
||||
session.append('turn/start', { turn: 1 })
|
||||
session.append('step/start', { turn: 1, step: 1 })
|
||||
session.append('request/header', {
|
||||
@@ -734,7 +734,7 @@ describe('pressure measurement and retention', () => {
|
||||
it('declines when rounding a cut would consume the only tool pair', () => {
|
||||
const ctx = createContext()
|
||||
const session = Session.create(SessionId('one-tool-pair'))
|
||||
const callId = CallId('only')
|
||||
const callId = ToolCallId('only')
|
||||
session.append('turn/start', { turn: 1 })
|
||||
session.append('step/start', { turn: 1, step: 1 })
|
||||
session.append('assistant/message', {
|
||||
@@ -1183,7 +1183,7 @@ describe('default one-shot summarizer', () => {
|
||||
const { adapter, compact } = await summarizerHarness([
|
||||
{ type: 'reasoning', text: 'private' },
|
||||
{ type: 'text', text: 'public summary' },
|
||||
{ type: 'tool-call', id: CallId('unexpected'), name: 'x', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('unexpected'), name: 'x', arguments: '{}' },
|
||||
], undefined, MODEL, {
|
||||
auto: false,
|
||||
summarizationProvider: MODEL,
|
||||
@@ -1199,7 +1199,7 @@ describe('default one-shot summarizer', () => {
|
||||
rawOutput: [
|
||||
{ type: 'reasoning', text: 'private' },
|
||||
{ type: 'text', text: 'public summary' },
|
||||
{ type: 'tool-call', id: CallId('unexpected'), name: 'x', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('unexpected'), name: 'x', arguments: '{}' },
|
||||
],
|
||||
llmStreamCall: true,
|
||||
provider: MODEL,
|
||||
@@ -1417,7 +1417,7 @@ describe('default one-shot summarizer', () => {
|
||||
it('rejects image summary output nested in a tool result', async () => {
|
||||
const { compact } = await summarizerHarness([{
|
||||
type: 'tool-result',
|
||||
toolCallId: CallId('summary-tool'),
|
||||
toolCallId: ToolCallId('summary-tool'),
|
||||
content: [{
|
||||
type: 'image',
|
||||
attachment: {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Context } from '@deepseek-ai/cordis'
|
||||
import { toolPairingBalancedAfter, toolPairingBalancedBefore } from '@deepseek-ai/dsh-compaction'
|
||||
import { createUserMessage, CONTEXT_WINDOW_EXCEEDED_CODE, LlmError, resolveRetryPolicy , createMessage } from '@deepseek-ai/dsh-llm'
|
||||
import type { ContentBlock, GenerateOptions, LlmResolvedModelInfo, ResolvedRetryPolicy, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
@@ -54,7 +54,7 @@ class StepwiseToolAdapter extends LlmAdapter {
|
||||
const n = this.calls
|
||||
this.calls += 1
|
||||
if (n < this.toolSteps) {
|
||||
const id = CallId(`c${n}`)
|
||||
const id = ToolCallId(`c${n}`)
|
||||
const args = `{"i":${n}}`
|
||||
yield { type: 'block-start', index: 0, blockType: 'text' }
|
||||
yield { type: 'block-end', index: 0, block: { type: 'text', text: `step ${n}` } }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import type { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
/** Character-budget policy for deterministic tool-result pruning. */
|
||||
export interface ToolResultPruneConfig {
|
||||
@@ -24,7 +24,7 @@ export interface PrunedEntry {
|
||||
/** Newly appended pruned tool-result event. */
|
||||
readonly replacementSeq: number
|
||||
/** Tool call shared by the original and replacement. */
|
||||
readonly callId: CallId
|
||||
readonly callId: ToolCallId
|
||||
/** Original text size in Unicode code points. */
|
||||
readonly charsBefore: number
|
||||
/** Replacement text size in Unicode code points. */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, {
|
||||
Session,
|
||||
@@ -43,7 +43,7 @@ function appendToolStep(
|
||||
content: ContentBlock[],
|
||||
extra: Record<string, unknown> = {},
|
||||
): number {
|
||||
const callId = CallId(call)
|
||||
const callId = ToolCallId(call)
|
||||
session.append('turn/start', {
|
||||
turn,
|
||||
})
|
||||
@@ -126,7 +126,7 @@ describe('ToolResultPruner content transform', () => {
|
||||
const reasoning: ContentBlock = { type: 'reasoning', text: 'private-rich-block' }
|
||||
const call: ContentBlock = {
|
||||
type: 'tool-call',
|
||||
id: CallId('nested'),
|
||||
id: ToolCallId('nested'),
|
||||
name: 'nested',
|
||||
arguments: '{}',
|
||||
}
|
||||
@@ -178,7 +178,7 @@ describe('ToolResultPruner session transaction', () => {
|
||||
expect(result.pruned).toHaveLength(1)
|
||||
expect(result.charsRemoved).toBeGreaterThan(0)
|
||||
const entry = result.pruned[0]!
|
||||
expect(entry).toMatchObject({ originalSeq, callId: CallId('one'), charsBefore: 100 })
|
||||
expect(entry).toMatchObject({ originalSeq, callId: ToolCallId('one'), charsBefore: 100 })
|
||||
expect(entry.charsAfter).toBeLessThanOrEqual(50)
|
||||
|
||||
const original = session.events[originalSeq]!
|
||||
@@ -201,7 +201,7 @@ describe('ToolResultPruner session transaction', () => {
|
||||
step: 1,
|
||||
isError: true,
|
||||
message: {
|
||||
source: { kind: 'tool', callId: CallId('one') },
|
||||
source: { kind: 'tool', callId: ToolCallId('one') },
|
||||
},
|
||||
error: { name: 'ExitError', code: 'EXIT_1' },
|
||||
meta: { diff: ['a', 'b'] },
|
||||
@@ -236,7 +236,7 @@ describe('ToolResultPruner session transaction', () => {
|
||||
const prune = service()
|
||||
const first = prune.pruneSession(session)
|
||||
const second = prune.pruneSession(session)
|
||||
expect(first.pruned.map(entry => entry.callId)).toEqual([CallId('a'), CallId('c')])
|
||||
expect(first.pruned.map(entry => entry.callId)).toEqual([ToolCallId('a'), ToolCallId('c')])
|
||||
expect(first.charsRemoved).toBe(
|
||||
first.pruned.reduce((sum, entry) => sum + entry.charsBefore - entry.charsAfter, 0),
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createUserMessage, CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { toolPairingBalancedAfter, toolPairingBalancedBefore } from '@deepseek-ai/dsh-compaction'
|
||||
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
@@ -35,7 +35,7 @@ function closedToolStep(): Session {
|
||||
step: 1,
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [{ type: 'tool-call', id: CallId('c1'), name: 'bash', arguments: '{}' }],
|
||||
content: [{ type: 'tool-call', id: ToolCallId('c1'), name: 'bash', arguments: '{}' }],
|
||||
source: {
|
||||
kind: 'model',
|
||||
...{ provider: 'mock', model: 'mock' },
|
||||
@@ -46,7 +46,7 @@ function closedToolStep(): Session {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
content: [{ type: 'text', text: 'done' }],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -70,7 +70,7 @@ describe('tool-pairing boundaries', () => {
|
||||
step: 1,
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [{ type: 'tool-call', id: CallId('open'), name: 'bash', arguments: '{}' }],
|
||||
content: [{ type: 'tool-call', id: ToolCallId('open'), name: 'bash', arguments: '{}' }],
|
||||
source: {
|
||||
kind: 'model',
|
||||
...{ provider: 'mock', model: 'mock' },
|
||||
@@ -88,8 +88,8 @@ describe('tool-pairing boundaries', () => {
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{ type: 'tool-call', id: CallId('c1'), name: 'one', arguments: '{}' },
|
||||
{ type: 'tool-call', id: CallId('c2'), name: 'two', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('c1'), name: 'one', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('c2'), name: 'two', arguments: '{}' },
|
||||
],
|
||||
source: {
|
||||
kind: 'model',
|
||||
@@ -100,7 +100,7 @@ describe('tool-pairing boundaries', () => {
|
||||
session.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -108,7 +108,7 @@ describe('tool-pairing boundaries', () => {
|
||||
session.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c2'),
|
||||
callId: ToolCallId('c2'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -125,7 +125,7 @@ describe('tool-pairing boundaries', () => {
|
||||
step: 1,
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [{ type: 'tool-call', id: CallId('c1'), name: 'bash', arguments: '{}' }],
|
||||
content: [{ type: 'tool-call', id: ToolCallId('c1'), name: 'bash', arguments: '{}' }],
|
||||
source: {
|
||||
kind: 'model',
|
||||
...{ provider: 'mock', model: 'mock' },
|
||||
@@ -139,7 +139,7 @@ describe('tool-pairing boundaries', () => {
|
||||
midStep.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -217,7 +217,7 @@ describe('tool-pairing cache refresh', () => {
|
||||
step: 1,
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [{ type: 'tool-call', id: CallId('c1'), name: 'one', arguments: '{}' }],
|
||||
content: [{ type: 'tool-call', id: ToolCallId('c1'), name: 'one', arguments: '{}' }],
|
||||
source: {
|
||||
kind: 'model',
|
||||
...{ provider: 'mock', model: 'mock' },
|
||||
@@ -231,7 +231,7 @@ describe('tool-pairing cache refresh', () => {
|
||||
data: {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -297,7 +297,7 @@ describe('tool-pairing cache refresh', () => {
|
||||
step: 1,
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [{ type: 'tool-call', id: CallId('c2'), name: 'two', arguments: '{}' }],
|
||||
content: [{ type: 'tool-call', id: ToolCallId('c2'), name: 'two', arguments: '{}' }],
|
||||
source: {
|
||||
kind: 'model',
|
||||
...{ provider: 'mock', model: 'mock' },
|
||||
@@ -311,7 +311,7 @@ describe('tool-pairing cache refresh', () => {
|
||||
data: {
|
||||
turn: 2, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c2'),
|
||||
callId: ToolCallId('c2'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -370,7 +370,7 @@ describe('tool-pairing corrupt surfaces', () => {
|
||||
session.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('orphan'),
|
||||
callId: ToolCallId('orphan'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -387,7 +387,7 @@ describe('tool-pairing corrupt surfaces', () => {
|
||||
session.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('orphan'),
|
||||
callId: ToolCallId('orphan'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
|
||||
@@ -5,7 +5,7 @@ import { describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import * as workspaceContext from '@deepseek-ai/dsh-agent-instructions'
|
||||
import LlmRuntime, { createUserMessage, CallId, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import LlmRuntime, { createUserMessage, ToolCallId, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { Session, SessionId, SESSION_FORMAT_VERSION, type SessionEvent, type UserMessage } from '@deepseek-ai/dsh-session'
|
||||
import AgentRegistry, { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
@@ -1657,7 +1657,7 @@ describe('workspace context request injection', () => {
|
||||
await write(join(root, 'AGENTS.md'), 'updated repo rule')
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-remount'),
|
||||
callId: ToolCallId('read-after-remount'),
|
||||
name: 'read',
|
||||
arguments: { file_path: 'file.txt' },
|
||||
agent,
|
||||
@@ -1854,7 +1854,7 @@ describe('workspace context request injection', () => {
|
||||
await write(join(root, 'AGENTS.md'), 'new root rule with more detail')
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-baseline-change'), name: 'read', arguments: { file_path: 'file.txt' }, agent,
|
||||
callId: ToolCallId('read-after-baseline-change'), name: 'read', arguments: { file_path: 'file.txt' }, agent,
|
||||
})
|
||||
|
||||
expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
|
||||
@@ -1883,7 +1883,7 @@ describe('workspace context request injection', () => {
|
||||
await rm(join(root, 'AGENTS.md'))
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-baseline-remove'), name: 'read', arguments: { file_path: 'file.txt' }, agent,
|
||||
callId: ToolCallId('read-after-baseline-remove'), name: 'read', arguments: { file_path: 'file.txt' }, agent,
|
||||
})
|
||||
|
||||
expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
|
||||
@@ -2499,9 +2499,9 @@ describe('dynamic nested workspace context injection', () => {
|
||||
const adapter = new MockAdapter([
|
||||
[
|
||||
{ type: 'block-start', index: 0, blockType: 'tool-call' },
|
||||
{ type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('read-before-abort'), name: 'read', arguments: '{"file_path":"pkg/deep/file.txt"}' } },
|
||||
{ type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('read-before-abort'), name: 'read', arguments: '{"file_path":"pkg/deep/file.txt"}' } },
|
||||
{ type: 'block-start', index: 1, blockType: 'tool-call' },
|
||||
{ type: 'block-end', index: 1, block: { type: 'tool-call', id: CallId('abort-after-read'), name: 'abort_step', arguments: '{}' } },
|
||||
{ type: 'block-end', index: 1, block: { type: 'tool-call', id: ToolCallId('abort-after-read'), name: 'abort_step', arguments: '{}' } },
|
||||
{ type: 'finish', reason: { kind: 'tool-calls' } },
|
||||
] satisfies StreamChunk[],
|
||||
toolCallResponse('read-after-abort', 'read', { file_path: join('pkg', 'deep', 'file.txt') }),
|
||||
@@ -2595,7 +2595,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
const reason = new Error('cancel dynamic reconciliation')
|
||||
controller.abort(reason)
|
||||
const exec = stubToolExecution({
|
||||
callId: CallId('cancelled-dynamic-read'),
|
||||
callId: ToolCallId('cancelled-dynamic-read'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'file.txt') },
|
||||
agent: stubAgent(root),
|
||||
@@ -2632,7 +2632,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-nested'),
|
||||
callId: ToolCallId('read-nested'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -2678,7 +2678,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: controller.signal,
|
||||
callId: CallId('read-before-signal-end'),
|
||||
callId: ToolCallId('read-before-signal-end'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'file.txt') },
|
||||
agent,
|
||||
@@ -2711,7 +2711,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-configured-nested-candidate'),
|
||||
callId: ToolCallId('read-configured-nested-candidate'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -2744,7 +2744,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-nested-overlay'),
|
||||
callId: ToolCallId('read-nested-overlay'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -2787,7 +2787,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-nested-overlay-disabled'),
|
||||
callId: ToolCallId('read-nested-overlay-disabled'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -2815,7 +2815,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const first = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-nested-1'),
|
||||
callId: ToolCallId('read-nested-1'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -2823,7 +2823,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
const second = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-nested-2'),
|
||||
callId: ToolCallId('read-nested-2'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -2858,12 +2858,12 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const first = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-version-fast-path'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-before-version-fast-path'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
const second = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-with-version-fast-path'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-with-version-fast-path'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
expect(first.additionalContexts).toBeUndefined()
|
||||
@@ -2895,18 +2895,18 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-same-digest-version-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-before-same-digest-version-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
fs.entries.set(instructionPath, { type: 'file', content: 'same package rule', version: FsVersion('revision-2') })
|
||||
const afterVersionChange = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-same-digest-version-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-after-same-digest-version-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await syncWorkspaceContext(ctx, agent)
|
||||
const afterRefresh = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-version-cache-refresh'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-after-version-cache-refresh'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
expect(afterVersionChange.additionalContexts).toBeUndefined()
|
||||
@@ -2941,11 +2941,11 @@ describe('dynamic nested workspace context injection', () => {
|
||||
const secondAgent = stubAgent(root)
|
||||
const first = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-from-first-session'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: firstAgent,
|
||||
callId: ToolCallId('read-from-first-session'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: firstAgent,
|
||||
})
|
||||
const second = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-from-second-session'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: secondAgent,
|
||||
callId: ToolCallId('read-from-second-session'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: secondAgent,
|
||||
})
|
||||
|
||||
expect(first.additionalContexts).toBeUndefined()
|
||||
@@ -2973,13 +2973,13 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-before-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
await write(join(root, 'pkg/AGENTS.md'), 'new package rule with more detail')
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-after-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
|
||||
@@ -3016,7 +3016,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-both-siblings'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-both-siblings'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
const firstText = blocksText(((await syncedWorkspaceContext(ctx, agent))).content)
|
||||
expect(firstText).toContain('native package rule')
|
||||
@@ -3025,7 +3025,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
await rm(join(root, 'pkg/AGENTS.md'))
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-one-sibling-removed'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-after-one-sibling-removed'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
// Removing one candidate only removes its own scope; the sibling scope is untouched.
|
||||
@@ -3054,7 +3054,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-nested-dup-siblings'), name: 'read', arguments: { file_path: join('pkg', 'deep', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-nested-dup-siblings'), name: 'read', arguments: { file_path: join('pkg', 'deep', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
|
||||
@@ -3276,7 +3276,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-dup-convergence'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-before-dup-convergence'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
const firstText = blocksText(((await syncedWorkspaceContext(ctx, agent))).content)
|
||||
expect(firstText).toContain('canonical nested rule')
|
||||
@@ -3285,7 +3285,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
await write(join(root, 'pkg/CLAUDE.md'), 'canonical nested rule')
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-dup-convergence'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-after-dup-convergence'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
const convergence = await syncedWorkspaceContext(ctx, agent)
|
||||
@@ -3313,14 +3313,14 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-earlier-converges'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-before-earlier-converges'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
// Only the earlier candidate changes; the sibling stays byte-identical but now duplicates it.
|
||||
await write(join(root, 'pkg/AGENTS.md'), 'secondary nested rule')
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-earlier-converges'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-after-earlier-converges'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
|
||||
@@ -3351,13 +3351,13 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-remove'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-before-remove'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
await rm(join(root, 'pkg/AGENTS.md'))
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-remove'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-after-remove'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
|
||||
@@ -3391,7 +3391,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-symlink-dir'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-before-symlink-dir'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
const firstText = blocksText(((await syncedWorkspaceContext(ctx, agent))).content)
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
@@ -3405,7 +3405,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
await symlink(join(root, 'pkg/elsewhere'), join(root, 'pkg/AGENTS.md'))
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-symlink-dir'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-after-symlink-dir'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
|
||||
@@ -3431,20 +3431,20 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-before-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
await rm(join(root, 'pkg/AGENTS.md'))
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-to-create-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-to-create-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
await write(join(root, 'pkg/AGENTS.md'), 'restored package rule')
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-after-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
|
||||
@@ -3476,13 +3476,13 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const first = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-provider-failure'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-before-provider-failure'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
fs.throwOnStat.add(join(root, 'pkg/AGENTS.md'))
|
||||
const duringFailure = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-during-provider-failure'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-during-provider-failure'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
expect(first.additionalContexts).toBeUndefined()
|
||||
@@ -3506,7 +3506,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
const agent = stubAgent(root)
|
||||
const first = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-resume'),
|
||||
callId: ToolCallId('read-before-resume'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -3516,7 +3516,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const afterResume = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-resume'),
|
||||
callId: ToolCallId('read-after-resume'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent: resumed,
|
||||
@@ -3542,7 +3542,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
const original = stubAgent(root)
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-offline-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: original,
|
||||
callId: ToolCallId('read-before-offline-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: original,
|
||||
})
|
||||
await appendAdditionalContexts(ctx, original)
|
||||
await write(join(root, 'pkg/AGENTS.md'), 'new nested rule after resume')
|
||||
@@ -3573,7 +3573,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
const agent = stubAgent(root)
|
||||
const first = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-before-compact'),
|
||||
callId: ToolCallId('read-before-compact'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -3581,7 +3581,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
const contextSeq = (await appendAdditionalContexts(ctx, agent))!
|
||||
const visibleBeforeCompact = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-while-visible'),
|
||||
callId: ToolCallId('read-while-visible'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -3597,7 +3597,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const afterCompact = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-compact'),
|
||||
callId: ToolCallId('read-after-compact'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -3629,7 +3629,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const whileVisible = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-visible-baseline'),
|
||||
callId: ToolCallId('read-visible-baseline'),
|
||||
name: 'read',
|
||||
arguments: { file_path: 'file.txt' },
|
||||
agent,
|
||||
@@ -3644,7 +3644,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-compacted-baseline'),
|
||||
callId: ToolCallId('read-compacted-baseline'),
|
||||
name: 'read',
|
||||
arguments: { file_path: 'file.txt' },
|
||||
agent,
|
||||
@@ -3653,7 +3653,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
await appendAdditionalContexts(ctx, agent)
|
||||
const afterRearm = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-rearmed-baseline'),
|
||||
callId: ToolCallId('read-rearmed-baseline'),
|
||||
name: 'read',
|
||||
arguments: { file_path: 'file.txt' },
|
||||
agent,
|
||||
@@ -3685,7 +3685,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
const agent = stubAgent(root)
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-package'),
|
||||
callId: ToolCallId('read-package'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'file.txt') },
|
||||
agent,
|
||||
@@ -3695,7 +3695,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-subtree'),
|
||||
callId: ToolCallId('read-subtree'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'sub', 'file.txt') },
|
||||
agent,
|
||||
@@ -3723,7 +3723,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
const agent = stubAgent(root)
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-subtree-omitting-parent'),
|
||||
callId: ToolCallId('read-subtree-omitting-parent'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'sub', 'file.txt') },
|
||||
agent,
|
||||
@@ -3733,7 +3733,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-parent-after-omit'),
|
||||
callId: ToolCallId('read-parent-after-omit'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'other.txt') },
|
||||
agent,
|
||||
@@ -3785,7 +3785,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-spoofed-state'),
|
||||
callId: ToolCallId('read-after-spoofed-state'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -3812,14 +3812,14 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const rootResult = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-root-file'),
|
||||
callId: ToolCallId('read-root-file'),
|
||||
name: 'read',
|
||||
arguments: { file_path: 'root.txt' },
|
||||
agent,
|
||||
})
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-absolute-nested-file'),
|
||||
callId: ToolCallId('read-absolute-nested-file'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join(root, 'pkg/deep/file.txt') },
|
||||
agent,
|
||||
@@ -3855,7 +3855,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-with-unreadable-nested-instruction'),
|
||||
callId: ToolCallId('read-with-unreadable-nested-instruction'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -3900,7 +3900,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-with-downstream'),
|
||||
callId: ToolCallId('read-with-downstream'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -3953,7 +3953,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-blocked-downstream'),
|
||||
callId: ToolCallId('read-blocked-downstream'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -3996,7 +3996,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const blocked = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('outer-block-first'),
|
||||
callId: ToolCallId('outer-block-first'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -4007,7 +4007,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
shouldBlock = false
|
||||
const accepted = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('outer-block-retry'),
|
||||
callId: ToolCallId('outer-block-retry'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -4043,7 +4043,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
async execute(_args, exec) {
|
||||
const nested = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId(`${exec.callId}:nested`),
|
||||
callId: ToolCallId(`${exec.callId}:nested`),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
...exec.agent === undefined ? {} : { agent: exec.agent },
|
||||
@@ -4065,7 +4065,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const blocked = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('composite-first'), name: 'composite-read', arguments: {}, agent,
|
||||
callId: ToolCallId('composite-first'), name: 'composite-read', arguments: {}, agent,
|
||||
})
|
||||
|
||||
expect(blocked.isError).toBe(true)
|
||||
@@ -4099,7 +4099,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
token: Symbol('nested-read') as ToolExecutionToken,
|
||||
parent: outerToken,
|
||||
signal: testToolSignal,
|
||||
callId: CallId('nested-read'),
|
||||
callId: ToolCallId('nested-read'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'file.txt') },
|
||||
agent,
|
||||
@@ -4108,7 +4108,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
token: Symbol('nested-non-file') as ToolExecutionToken,
|
||||
parent: outerToken,
|
||||
signal: testToolSignal,
|
||||
callId: CallId('nested-non-file'),
|
||||
callId: ToolCallId('nested-non-file'),
|
||||
name: 'search',
|
||||
arguments: {},
|
||||
agent,
|
||||
@@ -4117,7 +4117,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
token: Symbol('second-nested-read') as ToolExecutionToken,
|
||||
parent: outerToken,
|
||||
signal: testToolSignal,
|
||||
callId: CallId('second-nested-read'),
|
||||
callId: ToolCallId('second-nested-read'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'second.txt') },
|
||||
agent,
|
||||
@@ -4125,7 +4125,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
token: outerToken,
|
||||
signal: testToolSignal,
|
||||
callId: CallId('outer-code-run'),
|
||||
callId: ToolCallId('outer-code-run'),
|
||||
name: 'run_code',
|
||||
arguments: {},
|
||||
agent,
|
||||
@@ -4163,7 +4163,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-closed-step'),
|
||||
callId: ToolCallId('read-after-closed-step'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'file.txt') },
|
||||
agent,
|
||||
@@ -4185,38 +4185,38 @@ describe('dynamic nested workspace context injection', () => {
|
||||
await ctx.plugin(workspaceContext, { maxBytes: 65536 })
|
||||
const fs = ctx.fs as RecordingFileSystem
|
||||
const agent = stubAgent('/')
|
||||
const plainResult = { callId: CallId('plain'), content: [], isError: false as const, value: null }
|
||||
const plainResult = { callId: ToolCallId('plain'), content: [], isError: false as const, value: null }
|
||||
const aborted = new AbortController()
|
||||
aborted.abort(new Error('cancelled'))
|
||||
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal, callId: CallId('agentless'), name: 'read', arguments: { file_path: 'file.txt' },
|
||||
signal: testToolSignal, callId: ToolCallId('agentless'), name: 'read', arguments: { file_path: 'file.txt' },
|
||||
}), plainResult)
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal, callId: CallId('failed'), name: 'read', arguments: { file_path: 'failed/file.txt' }, agent,
|
||||
signal: testToolSignal, callId: ToolCallId('failed'), name: 'read', arguments: { file_path: 'failed/file.txt' }, agent,
|
||||
}), { content: [], isError: true, error: { message: 'failed' } })
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: aborted.signal, callId: CallId('aborted'), name: 'read', arguments: { file_path: 'aborted/file.txt' }, agent,
|
||||
signal: aborted.signal, callId: ToolCallId('aborted'), name: 'read', arguments: { file_path: 'aborted/file.txt' }, agent,
|
||||
}), plainResult)
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('null-arguments'), name: 'read', arguments: null, agent,
|
||||
callId: ToolCallId('null-arguments'), name: 'read', arguments: null, agent,
|
||||
}), plainResult)
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('missing-path'), name: 'read', arguments: {}, agent,
|
||||
callId: ToolCallId('missing-path'), name: 'read', arguments: {}, agent,
|
||||
}), plainResult)
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('non-string-path'), name: 'read', arguments: { file_path: 1 }, agent,
|
||||
callId: ToolCallId('non-string-path'), name: 'read', arguments: { file_path: 1 }, agent,
|
||||
}), plainResult)
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('blank-path'), name: 'read', arguments: { file_path: ' ' }, agent,
|
||||
callId: ToolCallId('blank-path'), name: 'read', arguments: { file_path: ' ' }, agent,
|
||||
}), plainResult)
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('non-fs'), name: 'composite', arguments: {}, agent,
|
||||
callId: ToolCallId('non-fs'), name: 'composite', arguments: {}, agent,
|
||||
}), plainResult)
|
||||
|
||||
await Promise.resolve()
|
||||
@@ -4243,7 +4243,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('projection-failure'),
|
||||
callId: ToolCallId('projection-failure'),
|
||||
name: 'read',
|
||||
arguments: { file_path: 'file.txt' },
|
||||
agent,
|
||||
@@ -4269,7 +4269,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-with-disabled-budget'),
|
||||
callId: ToolCallId('read-with-disabled-budget'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent: stubAgent(root),
|
||||
@@ -4302,12 +4302,12 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const first = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-tiny-budget-1'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-tiny-budget-1'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await syncWorkspaceContext(ctx, agent)
|
||||
const second = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-tiny-budget-2'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('read-tiny-budget-2'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await syncWorkspaceContext(ctx, agent)
|
||||
|
||||
@@ -4335,7 +4335,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-missing'),
|
||||
callId: ToolCallId('read-missing'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'missing.txt') },
|
||||
agent: stubAgent(root),
|
||||
@@ -4363,7 +4363,7 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('read-after-dispose'),
|
||||
callId: ToolCallId('read-after-dispose'),
|
||||
name: 'read',
|
||||
arguments: { file_path: join('pkg', 'deep', 'file.txt') },
|
||||
agent,
|
||||
@@ -4423,7 +4423,7 @@ describe('workspace context inbox synchronization', () => {
|
||||
const agent = stubAgent(root)
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('tiny-budget-touch'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('tiny-budget-touch'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
}), acceptedResult)
|
||||
|
||||
await syncWorkspaceContext(ctx, agent)
|
||||
@@ -4451,7 +4451,7 @@ describe('workspace context inbox synchronization', () => {
|
||||
const agent = stubAgent(root)
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('pending-v1'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('pending-v1'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await syncWorkspaceContext(ctx, agent)
|
||||
expect(blocksText(agent.inbox.nextStep[0]?.content)).toContain('pending version one')
|
||||
@@ -4464,7 +4464,7 @@ describe('workspace context inbox synchronization', () => {
|
||||
await write(join(root, 'pkg/AGENTS.md'), 'pending version two with more detail')
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('pending-v2'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('pending-v2'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await syncWorkspaceContext(ctx, agent)
|
||||
expect(agent.inbox.nextStep).toHaveLength(1)
|
||||
@@ -4475,7 +4475,7 @@ describe('workspace context inbox synchronization', () => {
|
||||
await rm(join(root, 'pkg/AGENTS.md'))
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('pending-delete'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
callId: ToolCallId('pending-delete'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
|
||||
})
|
||||
await syncWorkspaceContext(ctx, agent)
|
||||
expect(agent.inbox.nextStep).toEqual([])
|
||||
@@ -4499,7 +4499,7 @@ describe('workspace context inbox synchronization', () => {
|
||||
const agent = stubAgent(root)
|
||||
const first = stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('projected-before-abort'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent,
|
||||
callId: ToolCallId('projected-before-abort'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent,
|
||||
})
|
||||
ctx.emit('tools/result', first, acceptedResult)
|
||||
const controller = new AbortController()
|
||||
@@ -4512,7 +4512,7 @@ describe('workspace context inbox synchronization', () => {
|
||||
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('projected-after-abort'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent,
|
||||
callId: ToolCallId('projected-after-abort'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent,
|
||||
}), acceptedResult)
|
||||
await syncWorkspaceContext(ctx, agent)
|
||||
const text = blocksText(agent.inbox.nextStep[0]?.content)
|
||||
@@ -4539,11 +4539,11 @@ describe('workspace context inbox synchronization', () => {
|
||||
const agent = stubAgent(root)
|
||||
const first = stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('concurrent-a'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent,
|
||||
callId: ToolCallId('concurrent-a'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent,
|
||||
})
|
||||
const second = stubToolExecution({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('concurrent-b'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent,
|
||||
callId: ToolCallId('concurrent-b'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent,
|
||||
})
|
||||
|
||||
ctx.emit('tools/result', first, acceptedResult)
|
||||
@@ -4577,14 +4577,14 @@ describe('workspace context inbox synchronization', () => {
|
||||
const original = stubAgent(root)
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('recover-pending-a'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent: original,
|
||||
callId: ToolCallId('recover-pending-a'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent: original,
|
||||
})
|
||||
await syncWorkspaceContext(ctx, original)
|
||||
const resumed = stubAgent(root, [...original.session.events])
|
||||
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('recover-pending-b'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent: resumed,
|
||||
callId: ToolCallId('recover-pending-b'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent: resumed,
|
||||
})
|
||||
await syncWorkspaceContext(ctx, resumed)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { agentEvents, type Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { CompactionId, compactCheckpointSource } from '@deepseek-ai/dsh-compaction'
|
||||
import { createUserMessage, CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import SessionQueryEngine from '@deepseek-ai/dsh-session-query'
|
||||
import SessionReferenceResolver, {
|
||||
@@ -122,7 +122,7 @@ function appendConversation(session: Session): void {
|
||||
{
|
||||
turn: 2, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('call'),
|
||||
callId: ToolCallId('call'),
|
||||
content: [{ type: 'text', text: 'tool output' }],
|
||||
isError: false,
|
||||
}),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import { createUserMessage, CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { Session, SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import AgentRegistry, { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
|
||||
@@ -113,7 +113,7 @@ function toolCallResponse(): StreamChunk[] {
|
||||
{
|
||||
type: 'block-end',
|
||||
index: 0,
|
||||
block: { type: 'tool-call', id: CallId('tick-1'), name: 'tick', arguments: '{}' },
|
||||
block: { type: 'tool-call', id: ToolCallId('tick-1'), name: 'tick', arguments: '{}' },
|
||||
},
|
||||
{ type: 'finish', reason: { kind: 'tool-calls' } },
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { Context, type Fiber } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
import LlmRuntime, { createUserMessage, CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import LlmRuntime, { createUserMessage, ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
@@ -310,7 +310,7 @@ describe('AgentLoop initiator scope', () => {
|
||||
|
||||
const direct = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('direct'),
|
||||
callId: ToolCallId('direct'),
|
||||
name: 'agentless-probe',
|
||||
arguments: {},
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import LlmRuntime, { createUserMessage, CallId, LlmError, MessageSource, ProviderRequestId, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import LlmRuntime, { createUserMessage, ToolCallId, LlmError, MessageSource, ProviderRequestId, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { Session, SessionEvent, SessionId, TurnEndReason, type UserMessage } from '@deepseek-ai/dsh-session'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRuntime, { defineContentToolFixture, type PostToolDecision } from '@deepseek-ai/dsh-tools'
|
||||
@@ -135,9 +135,9 @@ describe('abort during tool execution ends the turn', () => {
|
||||
it('records post-tool context when a later call aborts the batch', async () => {
|
||||
const adapter = new MockAdapter([[
|
||||
{ type: 'block-start', index: 0, blockType: 'tool-call' },
|
||||
{ type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('c1'), name: 'first', arguments: '{}' } },
|
||||
{ type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('c1'), name: 'first', arguments: '{}' } },
|
||||
{ type: 'block-start', index: 1, blockType: 'tool-call' },
|
||||
{ type: 'block-end', index: 1, block: { type: 'tool-call', id: CallId('c2'), name: 'aborter', arguments: '{}' } },
|
||||
{ type: 'block-end', index: 1, block: { type: 'tool-call', id: ToolCallId('c2'), name: 'aborter', arguments: '{}' } },
|
||||
{ type: 'finish', reason: { kind: 'tool-calls' } },
|
||||
] satisfies StreamChunk[]])
|
||||
const ctx = await harness(adapter)
|
||||
@@ -160,7 +160,7 @@ describe('abort during tool execution ends the turn', () => {
|
||||
},
|
||||
}))
|
||||
ctx.on('tools/post-execute', async (exec, _result, next): Promise<PostToolDecision> => {
|
||||
if (exec.callId !== CallId('c1')) return next()
|
||||
if (exec.callId !== ToolCallId('c1')) return next()
|
||||
return {
|
||||
kind: 'accept',
|
||||
additionalContexts: [createUserMessage({
|
||||
@@ -259,9 +259,9 @@ describe('abort during tool execution ends the turn', () => {
|
||||
const adapter = new MockAdapter([
|
||||
[
|
||||
{ type: 'block-start', index: 0, blockType: 'tool-call' },
|
||||
{ type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('c1'), name: 'aborter', arguments: '{}' } },
|
||||
{ type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('c1'), name: 'aborter', arguments: '{}' } },
|
||||
{ type: 'block-start', index: 1, blockType: 'tool-call' },
|
||||
{ type: 'block-end', index: 1, block: { type: 'tool-call', id: CallId('c2'), name: 'second', arguments: '{}' } },
|
||||
{ type: 'block-end', index: 1, block: { type: 'tool-call', id: ToolCallId('c2'), name: 'second', arguments: '{}' } },
|
||||
{ type: 'finish', reason: { kind: 'tool-calls' } },
|
||||
] satisfies StreamChunk[],
|
||||
textResponse('later turn'),
|
||||
@@ -553,7 +553,7 @@ describe('discriminated SessionEvent narrows without casts', () => {
|
||||
it('narrows event.data from event.type', () => {
|
||||
const session = Session.create(SessionId('s'))
|
||||
const appended: SessionEvent = session.append('tool/call', {
|
||||
turn: 1, step: 1, callId: CallId('c1'), name: 'echo', arguments: '{}',
|
||||
turn: 1, step: 1, callId: ToolCallId('c1'), name: 'echo', arguments: '{}',
|
||||
})
|
||||
// compile-time: this switch narrows; runtime: values flow through
|
||||
switch (appended.type) {
|
||||
@@ -1069,7 +1069,7 @@ describe('tool result call identity', () => {
|
||||
// The loop must still record the tool/result under the model's authoritative
|
||||
// call.id, which is the immutable identity carried by the execution input.
|
||||
ctx.on('tools/post-execute', (exec, _result) => {
|
||||
expect(exec.callId).toBe(CallId('c1')) // the loop passed the real id in
|
||||
expect(exec.callId).toBe(ToolCallId('c1')) // the loop passed the real id in
|
||||
return Promise.resolve({ kind: 'accept', content: [{ type: 'text', text: 'ok' }] })
|
||||
}, { prepend: true })
|
||||
|
||||
@@ -1081,7 +1081,7 @@ describe('tool result call identity', () => {
|
||||
const resultEvent = [...agent.session.events].find(e => e.type === 'tool/result')
|
||||
expect(resultEvent?.type).toBe('tool/result')
|
||||
if (resultEvent?.type === 'tool/result') {
|
||||
expect(resultEvent.data.message.source.callId).toBe(CallId('c1'))
|
||||
expect(resultEvent.data.message.source.callId).toBe(ToolCallId('c1'))
|
||||
}
|
||||
|
||||
// And deriveMessages pairs the tool-result with the assistant tool-call:
|
||||
@@ -1092,7 +1092,7 @@ describe('tool result call identity', () => {
|
||||
.find(b => b.type === 'tool-result')
|
||||
expect(toolResultBlock?.type).toBe('tool-result')
|
||||
if (toolResultBlock?.type === 'tool-result') {
|
||||
expect(toolResultBlock.toolCallId).toBe(CallId('c1'))
|
||||
expect(toolResultBlock.toolCallId).toBe(ToolCallId('c1'))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import LlmRuntime, { createUserMessage, CallId, LlmError, StreamChunk, errorChain } from '@deepseek-ai/dsh-llm'
|
||||
import LlmRuntime, { createUserMessage, ToolCallId, LlmError, StreamChunk, errorChain } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { SessionId, TurnEndReason } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
@@ -47,7 +47,7 @@ describe('tool JSON parse', () => {
|
||||
// model emits tool-call with malformed arguments (not valid JSON)
|
||||
[
|
||||
{ type: 'block-start' as const, index: 0, blockType: 'tool-call' as const },
|
||||
{ type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: CallId('c1'), name: 'echo', arguments: 'not json' } },
|
||||
{ type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: ToolCallId('c1'), name: 'echo', arguments: 'not json' } },
|
||||
{ type: 'finish' as const, reason: { kind: 'tool-calls' as const } },
|
||||
] satisfies StreamChunk[],
|
||||
textResponse('done'),
|
||||
@@ -80,7 +80,7 @@ describe('tool JSON parse', () => {
|
||||
const adapter = new MockAdapter([
|
||||
[
|
||||
{ type: 'block-start' as const, index: 0, blockType: 'tool-call' as const },
|
||||
{ type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: CallId('c1'), name: 'noarg', arguments: '' } },
|
||||
{ type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: ToolCallId('c1'), name: 'noarg', arguments: '' } },
|
||||
{ type: 'finish' as const, reason: { kind: 'tool-calls' as const } },
|
||||
] satisfies StreamChunk[],
|
||||
textResponse('done'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import LlmRuntime, { createUserMessage, CallId } from '@deepseek-ai/dsh-llm'
|
||||
import LlmRuntime, { createUserMessage, ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, {
|
||||
SessionId,
|
||||
type SessionEvent,
|
||||
@@ -613,9 +613,9 @@ describe('tool additionalContexts buffering across a step', () => {
|
||||
// One assistant step with TWO tool calls; the second model response stops.
|
||||
const twoCalls = [
|
||||
{ type: 'block-start' as const, index: 0, blockType: 'tool-call' as const },
|
||||
{ type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: CallId('c1'), name: 'echo', arguments: '{"text":"a"}' } },
|
||||
{ type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: ToolCallId('c1'), name: 'echo', arguments: '{"text":"a"}' } },
|
||||
{ type: 'block-start' as const, index: 1, blockType: 'tool-call' as const },
|
||||
{ type: 'block-end' as const, index: 1, block: { type: 'tool-call' as const, id: CallId('c2'), name: 'echo', arguments: '{"text":"b"}' } },
|
||||
{ type: 'block-end' as const, index: 1, block: { type: 'tool-call' as const, id: ToolCallId('c2'), name: 'echo', arguments: '{"text":"b"}' } },
|
||||
{ type: 'usage' as const, usage: { inputTokens: 5, outputTokens: 5 } },
|
||||
{ type: 'finish' as const, reason: { kind: 'tool-calls' as const } },
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import LlmRuntime, { createUserMessage, CallId, LlmError, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import LlmRuntime, { createUserMessage, ToolCallId, LlmError, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { SessionId, TurnEndReason } from '@deepseek-ai/dsh-session'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
|
||||
@@ -1052,7 +1052,7 @@ describe('agent loop', () => {
|
||||
})
|
||||
|
||||
it('does not dispatch tool calls from a max-tokens-truncated step', async () => {
|
||||
const callId = CallId('c1')
|
||||
const callId = ToolCallId('c1')
|
||||
const adapter = new MockAdapter([[
|
||||
{ type: 'block-start', index: 0, blockType: 'tool-call' },
|
||||
{ type: 'tool-call-delta', index: 0, id: callId, name: 'echo', argumentsDelta: '{"text":"x"}' },
|
||||
@@ -1107,7 +1107,7 @@ describe('agent loop', () => {
|
||||
it('appends an empty completion anchor for a max-tokens step with no usage', async () => {
|
||||
// The truncated tool call is dropped from durable content, while the
|
||||
// successful provider call still needs an exact replay anchor.
|
||||
const callId = CallId('c1')
|
||||
const callId = ToolCallId('c1')
|
||||
const adapter = new MockAdapter([[
|
||||
{ type: 'block-start', index: 0, blockType: 'tool-call' },
|
||||
{ type: 'tool-call-delta', index: 0, id: callId, name: 'echo', argumentsDelta: '{"text":"x"}' },
|
||||
@@ -1185,7 +1185,7 @@ describe('agent loop', () => {
|
||||
})
|
||||
|
||||
it('keeps safe max-tokens assistant content while dropping truncated tool calls', async () => {
|
||||
const callId = CallId('c1')
|
||||
const callId = ToolCallId('c1')
|
||||
const adapter = new MockAdapter([[
|
||||
{ type: 'block-start', index: 0, blockType: 'text' },
|
||||
{ type: 'text-delta', index: 0, text: 'partial text' },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenerateOptions, LlmModelReasoningInfo, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
/** Helpers to write scripted responses tersely. */
|
||||
export function textResponse(text: string): StreamChunk[] {
|
||||
@@ -28,7 +28,7 @@ export function maxTokensResponse(text: string): StreamChunk[] {
|
||||
}
|
||||
|
||||
export function toolCallResponse(rawCallId: string, name: string, args: object, text?: string): StreamChunk[] {
|
||||
const callId = CallId(rawCallId)
|
||||
const callId = ToolCallId(rawCallId)
|
||||
const argumentsJson = JSON.stringify(args)
|
||||
const chunks: StreamChunk[] = []
|
||||
let index = 0
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { createUserMessage, CallId, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import LlmRuntime from '@deepseek-ai/dsh-llm'
|
||||
@@ -49,7 +49,7 @@ function multiCall(calls: { id: string; name: string; args: object }[]): StreamC
|
||||
calls.forEach((call, index) => {
|
||||
chunks.push(
|
||||
{ type: 'block-start', index, blockType: 'tool-call' },
|
||||
{ type: 'block-end', index, block: { type: 'tool-call', id: CallId(call.id), name: call.name, arguments: JSON.stringify(call.args) } },
|
||||
{ type: 'block-end', index, block: { type: 'tool-call', id: ToolCallId(call.id), name: call.name, arguments: JSON.stringify(call.args) } },
|
||||
)
|
||||
})
|
||||
chunks.push(
|
||||
@@ -196,7 +196,7 @@ describe('tool-call scheduler: grouping and barriers', () => {
|
||||
const replacement = gatedExclusiveTool('x')
|
||||
const disposeInitial = ctx.tools.register(initial.tool)
|
||||
ctx.on('tools/result', (exec) => {
|
||||
if (exec.callId !== CallId('c1')) return
|
||||
if (exec.callId !== ToolCallId('c1')) return
|
||||
disposeInitial()
|
||||
ctx.tools.register(replacement.tool)
|
||||
})
|
||||
@@ -206,7 +206,7 @@ describe('tool-call scheduler: grouping and barriers', () => {
|
||||
await until(() => initial.started.length === 2)
|
||||
initial.release('1')
|
||||
await until(() => events(agent).some(event =>
|
||||
event.type === 'tool/result' && event.data.message.source.callId === CallId('c1')))
|
||||
event.type === 'tool/result' && event.data.message.source.callId === ToolCallId('c1')))
|
||||
await new Promise(r => setTimeout(r, 5))
|
||||
expect(replacement.started).toEqual([])
|
||||
initial.release('2')
|
||||
@@ -238,7 +238,7 @@ describe('tool-call scheduler: model-order results despite out-of-order settleme
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
const results = events(agent).filter(e => e.type === 'tool/result')
|
||||
expect(results.map(e => e.data.message.source.callId)).toEqual([CallId('c1'), CallId('c2')])
|
||||
expect(results.map(e => e.data.message.source.callId)).toEqual([ToolCallId('c1'), ToolCallId('c2')])
|
||||
})
|
||||
|
||||
it('derived history pairs calls in model order regardless of tool/call log interleaving', async () => {
|
||||
@@ -257,7 +257,7 @@ describe('tool-call scheduler: model-order results despite out-of-order settleme
|
||||
|
||||
const messages = agent.session.deriveMessages()
|
||||
const toolResults = messages.flatMap(m => m.content.filter(b => b.type === 'tool-result'))
|
||||
expect(toolResults.map(b => b.toolCallId)).toEqual([CallId('c1'), CallId('c2')])
|
||||
expect(toolResults.map(b => b.toolCallId)).toEqual([ToolCallId('c1'), ToolCallId('c2')])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -316,7 +316,7 @@ describe('tool-call scheduler: rolling pool honors maxParallelToolCalls', () =>
|
||||
gated.release('4')
|
||||
await waitForIdle(ctx, agent)
|
||||
expect(events(agent).filter(e => e.type === 'tool/result').map(e => e.data.message.source.callId))
|
||||
.toEqual([CallId('c1'), CallId('c2'), CallId('c3'), CallId('c4')])
|
||||
.toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3'), ToolCallId('c4')])
|
||||
})
|
||||
|
||||
it('maxParallelToolCalls: 1 is fully serial (no second start before the first settles)', async () => {
|
||||
@@ -386,8 +386,8 @@ describe('tool-call scheduler: ordered middleware and additional contexts', () =
|
||||
gated.release('3'); gated.release('2'); gated.release('1')
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
expect(pre).toEqual([CallId('c1'), CallId('c2'), CallId('c3')].map(String))
|
||||
expect(post).toEqual([CallId('c1'), CallId('c2'), CallId('c3')].map(String))
|
||||
expect(pre).toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3')].map(String))
|
||||
expect(post).toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3')].map(String))
|
||||
})
|
||||
|
||||
it('injects additional contexts in model call order, not settlement order', async () => {
|
||||
@@ -432,8 +432,8 @@ describe('tool-call scheduler: ordered middleware and additional contexts', () =
|
||||
ctx.tools.register(gated.tool)
|
||||
const post: string[] = []
|
||||
ctx.on('tools/pre-execute', async (exec, next): Promise<PreToolDecision> => {
|
||||
if (exec.callId === CallId('c2')) return { kind: 'deny', reason: 'blocked by policy' }
|
||||
if (exec.callId === CallId('c3')) throw new Error('pre exploded')
|
||||
if (exec.callId === ToolCallId('c2')) return { kind: 'deny', reason: 'blocked by policy' }
|
||||
if (exec.callId === ToolCallId('c3')) throw new Error('pre exploded')
|
||||
return next()
|
||||
})
|
||||
ctx.on('tools/post-execute', async (exec, _result, next): Promise<PostToolDecision> => {
|
||||
@@ -450,7 +450,7 @@ describe('tool-call scheduler: ordered middleware and additional contexts', () =
|
||||
expect(gated.started).toEqual(['1'])
|
||||
expect(post).toEqual(['c1', 'c2'])
|
||||
const results = events(agent).filter(e => e.type === 'tool/result')
|
||||
expect(results.map(e => e.data.message.source.callId)).toEqual([CallId('c1'), CallId('c2'), CallId('c3')])
|
||||
expect(results.map(e => e.data.message.source.callId)).toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3')])
|
||||
expect((results[1]!.data.message.content[0].content[0] as { text: string }).text).toContain('blocked by policy')
|
||||
expect((results[2]!.data.message.content[0].content[0] as { text: string }).text).toContain('pre exploded')
|
||||
})
|
||||
@@ -477,14 +477,14 @@ describe('tool-call scheduler: abort handling', () => {
|
||||
|
||||
expect(gated.started).toEqual([])
|
||||
expect(events(agent).filter(e => e.type === 'tool/call').map(e => e.data.callId))
|
||||
.toEqual([CallId('c1'), CallId('c2')])
|
||||
.toEqual([ToolCallId('c1'), ToolCallId('c2')])
|
||||
expect(events(agent).filter(e => e.type === 'tool/result').map(e => ({
|
||||
callId: e.data.message.source.callId,
|
||||
isError: e.data.message.content[0].isError,
|
||||
error: e.data.error,
|
||||
}))).toEqual([
|
||||
{ callId: CallId('c1'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
|
||||
{ callId: CallId('c2'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
|
||||
{ callId: ToolCallId('c1'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
|
||||
{ callId: ToolCallId('c2'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
|
||||
])
|
||||
})
|
||||
|
||||
@@ -498,7 +498,7 @@ describe('tool-call scheduler: abort handling', () => {
|
||||
ctx.tools.register(gated.tool)
|
||||
const agent = ctx.agentLoop.create(SessionId('a1'), { provider: 'mock', model: 'mock' })
|
||||
ctx.on('tools/pre-execute', async (exec, next): Promise<PreToolDecision> => {
|
||||
if (exec.callId === CallId('c1')) {
|
||||
if (exec.callId === ToolCallId('c1')) {
|
||||
agent.cancel({ kind: 'user' })
|
||||
}
|
||||
return next()
|
||||
@@ -509,14 +509,14 @@ describe('tool-call scheduler: abort handling', () => {
|
||||
|
||||
expect(gated.started).toEqual([])
|
||||
expect(events(agent).filter(e => e.type === 'tool/call').map(e => e.data.callId))
|
||||
.toEqual([CallId('c1'), CallId('c2')])
|
||||
.toEqual([ToolCallId('c1'), ToolCallId('c2')])
|
||||
expect(events(agent).filter(e => e.type === 'tool/result').map(e => ({
|
||||
callId: e.data.message.source.callId,
|
||||
isError: e.data.message.content[0].isError,
|
||||
error: e.data.error,
|
||||
}))).toEqual([
|
||||
{ callId: CallId('c1'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
|
||||
{ callId: CallId('c2'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
|
||||
{ callId: ToolCallId('c1'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
|
||||
{ callId: ToolCallId('c2'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
|
||||
])
|
||||
})
|
||||
|
||||
@@ -545,9 +545,9 @@ describe('tool-call scheduler: abort handling', () => {
|
||||
|
||||
expect(gated.started).toEqual(['1', '2'])
|
||||
expect(events(agent).filter(e => e.type === 'tool/call').map(e => e.data.callId))
|
||||
.toEqual([CallId('c1'), CallId('c2'), CallId('c3'), CallId('c4')])
|
||||
.toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3'), ToolCallId('c4')])
|
||||
expect(events(agent).filter(e => e.type === 'tool/result').map(e => e.data.message.source.callId))
|
||||
.toEqual([CallId('c1'), CallId('c2'), CallId('c3'), CallId('c4')])
|
||||
.toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3'), ToolCallId('c4')])
|
||||
expect(events(agent).filter(e => e.type === 'tool/result').slice(-2).map(e => ({
|
||||
callId: e.data.message.source.callId,
|
||||
isError: e.data.message.content[0].isError,
|
||||
@@ -555,12 +555,12 @@ describe('tool-call scheduler: abort handling', () => {
|
||||
})))
|
||||
.toEqual([
|
||||
{
|
||||
callId: CallId('c3'),
|
||||
callId: ToolCallId('c3'),
|
||||
isError: true,
|
||||
error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH },
|
||||
},
|
||||
{
|
||||
callId: CallId('c4'),
|
||||
callId: ToolCallId('c4'),
|
||||
isError: true,
|
||||
error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH },
|
||||
},
|
||||
@@ -618,11 +618,11 @@ describe('tool-call scheduler: abort handling', () => {
|
||||
|
||||
expect(exclusive).toEqual([])
|
||||
expect(events(agent).filter(e => e.type === 'tool/call').map(e => e.data.callId))
|
||||
.toEqual([CallId('c1'), CallId('c2'), CallId('c3')])
|
||||
.toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3')])
|
||||
expect(events(agent).filter(e => e.type === 'tool/result').at(-1)?.data)
|
||||
.toMatchObject({
|
||||
message: {
|
||||
source: { kind: 'tool', callId: CallId('c3') },
|
||||
source: { kind: 'tool', callId: ToolCallId('c3') },
|
||||
content: [{ isError: true }],
|
||||
},
|
||||
error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH },
|
||||
@@ -651,7 +651,7 @@ describe('tool-call scheduler: failure quiescence', () => {
|
||||
let thirdPrepareEntered = false
|
||||
scheduler.prepare = async (exec) => {
|
||||
const prepared = await prepare(exec)
|
||||
if (exec.callId === CallId('c3')) {
|
||||
if (exec.callId === ToolCallId('c3')) {
|
||||
thirdPrepareEntered = true
|
||||
await prepareGate.promise
|
||||
}
|
||||
@@ -660,7 +660,7 @@ describe('tool-call scheduler: failure quiescence', () => {
|
||||
const schedulerError = new Error('scheduler exploded')
|
||||
const drainedError = new Error('sibling failed while draining')
|
||||
let rejectFirst: ((error: Error) => void) | undefined
|
||||
scheduler.dispatch = exec => exec.callId === CallId('c1')
|
||||
scheduler.dispatch = exec => exec.callId === ToolCallId('c1')
|
||||
? new Promise((_resolve, reject) => { rejectFirst = reject })
|
||||
: dispatch(exec).then(() => { throw drainedError })
|
||||
const agent = ctx.agentLoop.create(SessionId('scheduler-failure'), { provider: 'mock', model: 'mock' })
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
* @module @deepseek-ai/dsh-session/chunk-rows
|
||||
*/
|
||||
|
||||
import { CallId, assertNever } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, assertNever } from '@deepseek-ai/dsh-llm'
|
||||
import type { StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import type { SessionEvent } from './types.ts'
|
||||
|
||||
@@ -50,7 +50,7 @@ interface TextRunData extends RunDataBase {
|
||||
|
||||
/** Payload of a `tool-call-chunks` row: the run-constant call identity plus each member's raw arguments fragment. */
|
||||
interface ToolCallRunData extends RunDataBase {
|
||||
id: CallId
|
||||
id: ToolCallId
|
||||
/** Present iff every member carried it, with one uniform value (a mixed run never packs). */
|
||||
name?: string
|
||||
args: string[]
|
||||
@@ -167,7 +167,7 @@ function buildRow(kind: DeltaKind, run: readonly DeltaEvent[]): ChunkRow {
|
||||
...envelope,
|
||||
data: {
|
||||
...base,
|
||||
id: CallId(call.id),
|
||||
id: ToolCallId(call.id),
|
||||
...Object.hasOwn(call, 'name') ? { name: call.name as string } : {},
|
||||
args: run.map(event => (event.data.chunk as { argumentsDelta: string }).argumentsDelta),
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { assertNever } from '@deepseek-ai/dsh-llm'
|
||||
import type { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import type { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import type { InvariantFailure, InvariantInstaller } from '@deepseek-ai/dsh-invariants'
|
||||
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import { TOOL_NOT_STARTED } from './repair.ts'
|
||||
@@ -26,7 +26,7 @@ interface SessionTrace {
|
||||
openStep: number | null
|
||||
nextTurn: number
|
||||
nextStep: number
|
||||
pendingCalls: Set<CallId>
|
||||
pendingCalls: Set<ToolCallId>
|
||||
}
|
||||
|
||||
/** One accepted event's deferred mutation of a committed session trace. */
|
||||
@@ -34,7 +34,7 @@ interface SessionTraceTransition {
|
||||
scalars: Pick<SessionTrace, 'lastSeq' | 'openTurn' | 'openStep' | 'nextTurn' | 'nextStep'>
|
||||
pendingCalls:
|
||||
| { kind: 'none' }
|
||||
| { kind: 'add' | 'delete'; callId: CallId }
|
||||
| { kind: 'add' | 'delete'; callId: ToolCallId }
|
||||
| { kind: 'clear' }
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @module @deepseek-ai/dsh-session/repair
|
||||
*/
|
||||
|
||||
import { MessageId, freezeMessage, type CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { MessageId, freezeMessage, type ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import type { ToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import type { SessionEvent } from './types.ts'
|
||||
|
||||
@@ -29,7 +29,7 @@ export function interruptedTurnClosers(events: readonly SessionEvent[]): Session
|
||||
let openStep: number | null = null
|
||||
// Reset at each turn boundary so earlier calls cannot leak into tail repair.
|
||||
// Assistant blocks register calls; later `tool/call` events add their seqs to `sourceEventSeqs`.
|
||||
const pendingCalls = new Map<CallId, { step: number; callSeq?: number }>()
|
||||
const pendingCalls = new Map<ToolCallId, { step: number; callSeq?: number }>()
|
||||
for (const event of events) {
|
||||
switch (event.type) {
|
||||
case 'turn/start':
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Branded } from '@deepseek-ai/dsh-brand'
|
||||
import type {
|
||||
AssistantMessage,
|
||||
CallId,
|
||||
ToolCallId,
|
||||
LlmCallConfig,
|
||||
LlmCallConfigAdapterDefaults,
|
||||
LlmFailure,
|
||||
@@ -280,7 +280,7 @@ export interface SessionEventMap {
|
||||
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
||||
* call with its `tool/result`.
|
||||
*/
|
||||
'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
|
||||
'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
|
||||
/**
|
||||
* A completed tool call's model-facing result, optional internal failure
|
||||
* identity, and optional tool-private `meta` presentation payload. `meta` is
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import fc from 'fast-check'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import type { StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { decodeStorageRecord, packChunkRuns } from '@deepseek-ai/dsh-session'
|
||||
import type { ChunkRow, SessionEvent, StorageRecord } from '@deepseek-ai/dsh-session'
|
||||
@@ -43,7 +43,7 @@ describe('packChunkRuns', () => {
|
||||
it('packs reasoning and tool-call runs under their own tags', () => {
|
||||
const reasoning = deltaRun('reasoning-delta', 3)
|
||||
const toolCall = [4, 5, 6].map(seq =>
|
||||
chunkEvent(seq, 1000 + seq, { type: 'tool-call-delta', index: 1, id: CallId('c1'), name: 'write', argumentsDelta: `a${seq}` }))
|
||||
chunkEvent(seq, 1000 + seq, { type: 'tool-call-delta', index: 1, id: ToolCallId('c1'), name: 'write', argumentsDelta: `a${seq}` }))
|
||||
const packed = packChunkRuns([...reasoning, ...toolCall])
|
||||
expect(packed.map(r => (r as ChunkRow).type)).toStrictEqual(['reasoning-chunks', 'tool-call-chunks'])
|
||||
const row = packed[1] as ChunkRow & { type: 'tool-call-chunks' }
|
||||
@@ -53,7 +53,7 @@ describe('packChunkRuns', () => {
|
||||
|
||||
it('packs a name-less tool-call run and round-trips field absence', () => {
|
||||
const events = [0, 1, 2].map(seq =>
|
||||
chunkEvent(seq, 1000, { type: 'tool-call-delta', index: 0, id: CallId('c1'), argumentsDelta: `a${seq}` }))
|
||||
chunkEvent(seq, 1000, { type: 'tool-call-delta', index: 0, id: ToolCallId('c1'), argumentsDelta: `a${seq}` }))
|
||||
const packed = packChunkRuns(events)
|
||||
expect(packed).toHaveLength(1)
|
||||
expect(Object.hasOwn((packed[0] as ChunkRow).data, 'name')).toBe(false)
|
||||
@@ -91,7 +91,7 @@ describe('packChunkRuns', () => {
|
||||
|
||||
it('breaks a tool-call run on call-id or name change', () => {
|
||||
const call = (seq: number, id: string, name?: string): SessionEvent =>
|
||||
chunkEvent(seq, 1000, { type: 'tool-call-delta', index: 0, id: CallId(id), ...name !== undefined ? { name } : {}, argumentsDelta: 'a' })
|
||||
chunkEvent(seq, 1000, { type: 'tool-call-delta', index: 0, id: ToolCallId(id), ...name !== undefined ? { name } : {}, argumentsDelta: 'a' })
|
||||
const idSwitch = [call(0, 'c1', 'w'), call(1, 'c1', 'w'), call(2, 'c2', 'w')]
|
||||
expect(packChunkRuns(idSwitch)).toStrictEqual(idSwitch)
|
||||
const namePresence = [call(0, 'c1', 'w'), call(1, 'c1', 'w'), call(2, 'c1')]
|
||||
@@ -186,13 +186,13 @@ const deltaChunkArb: fc.Arbitrary<StreamChunk> = fc.oneof(
|
||||
fc.record({
|
||||
type: fc.constant<'tool-call-delta'>('tool-call-delta'),
|
||||
index: fc.nat(2),
|
||||
id: fc.constantFrom(CallId('c1'), CallId('c2')),
|
||||
id: fc.constantFrom(ToolCallId('c1'), ToolCallId('c2')),
|
||||
argumentsDelta: fc.string(),
|
||||
}),
|
||||
fc.record({
|
||||
type: fc.constant<'tool-call-delta'>('tool-call-delta'),
|
||||
index: fc.nat(2),
|
||||
id: fc.constantFrom(CallId('c1'), CallId('c2')),
|
||||
id: fc.constantFrom(ToolCallId('c1'), ToolCallId('c2')),
|
||||
name: fc.constantFrom('write', 'read'),
|
||||
argumentsDelta: fc.string(),
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { createUserMessage, CallId , createMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId , createMessage } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { Session, SessionForkError, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionEvent, TurnEndReason } from '@deepseek-ai/dsh-session'
|
||||
|
||||
@@ -268,7 +268,7 @@ describe('SessionStore.fork', () => {
|
||||
return lastSeq(session)
|
||||
}],
|
||||
['tool/call', (session) => {
|
||||
const callId = CallId('call-open')
|
||||
const callId = ToolCallId('call-open')
|
||||
session.append('turn/start', { turn: 1 })
|
||||
session.append('step/start', { turn: 1, step: 1 })
|
||||
session.append('assistant/message', {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { createScope, scopeTarget } from '@deepseek-ai/dsh-scope'
|
||||
import { createUserMessage, CallId, createMessage, createToolResultMessage, freezeMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId, createMessage, createToolResultMessage, freezeMessage } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { SessionId, TOOL_NOT_STARTED } from '@deepseek-ai/dsh-session'
|
||||
import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant'
|
||||
import InvariantRegistry, { InvariantError } from '@deepseek-ai/dsh-invariants'
|
||||
@@ -46,18 +46,18 @@ describe('session-log invariants', () => {
|
||||
step: 1,
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [{ type: 'tool-call', id: CallId('c1'), name: 'echo', arguments: '{}' }],
|
||||
content: [{ type: 'tool-call', id: ToolCallId('c1'), name: 'echo', arguments: '{}' }],
|
||||
source: {
|
||||
kind: 'model',
|
||||
...{ provider: 'mock', model: 'mock' },
|
||||
},
|
||||
}),
|
||||
}, { surfaceOp: 'append' })
|
||||
session.append('tool/call', { turn: 1, step: 1, callId: CallId('c1'), name: 'echo', arguments: '{}' })
|
||||
session.append('tool/call', { turn: 1, step: 1, callId: ToolCallId('c1'), name: 'echo', arguments: '{}' })
|
||||
session.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -223,7 +223,7 @@ describe('session-log invariants', () => {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('ghost'),
|
||||
callId: ToolCallId('ghost'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -238,7 +238,7 @@ describe('session-log invariants', () => {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('closed'),
|
||||
callId: ToolCallId('closed'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -253,7 +253,7 @@ describe('session-log invariants', () => {
|
||||
session.append('tool/call', {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
callId: CallId('rewrite'),
|
||||
callId: ToolCallId('rewrite'),
|
||||
name: 'echo',
|
||||
arguments: '{}',
|
||||
})
|
||||
@@ -261,7 +261,7 @@ describe('session-log invariants', () => {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('rewrite'),
|
||||
callId: ToolCallId('rewrite'),
|
||||
content: [{ type: 'text', text: 'original' }],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -293,7 +293,7 @@ describe('session-log invariants', () => {
|
||||
session.append('tool/call', {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
callId: CallId('rewrite'),
|
||||
callId: ToolCallId('rewrite'),
|
||||
name: 'echo',
|
||||
arguments: '{}',
|
||||
})
|
||||
@@ -301,7 +301,7 @@ describe('session-log invariants', () => {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('rewrite'),
|
||||
callId: ToolCallId('rewrite'),
|
||||
content: [{ type: 'text', text: 'original' }],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -333,7 +333,7 @@ describe('session-log invariants', () => {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('crashed'),
|
||||
callId: ToolCallId('crashed'),
|
||||
content: [],
|
||||
isError: true,
|
||||
}),
|
||||
@@ -347,7 +347,7 @@ describe('session-log invariants', () => {
|
||||
expect(() => {
|
||||
unresolved.append('turn/start', { turn: 1 })
|
||||
unresolved.append('step/start', { turn: 1, step: 1 })
|
||||
unresolved.append('tool/call', { turn: 1, step: 1, callId: CallId('c1'), name: 'echo', arguments: '{}' })
|
||||
unresolved.append('tool/call', { turn: 1, step: 1, callId: ToolCallId('c1'), name: 'echo', arguments: '{}' })
|
||||
unresolved.append('step/end', { turn: 1, step: 1 })
|
||||
unresolved.append('turn/end', { turn: 1, reason: { kind: 'error', error: { message: 'boom', code: 'UNKNOWN' } } })
|
||||
}).not.toThrow()
|
||||
@@ -358,14 +358,14 @@ describe('session-log invariants', () => {
|
||||
const session = ctx.sessions.create()
|
||||
session.append('turn/start', { turn: 1 })
|
||||
session.append('step/start', { turn: 1, step: 1 })
|
||||
session.append('tool/call', { turn: 1, step: 1, callId: CallId('c1'), name: 'echo', arguments: '{}' })
|
||||
session.append('tool/call', { turn: 1, step: 1, callId: ToolCallId('c1'), name: 'echo', arguments: '{}' })
|
||||
session.append('step/end', { turn: 1, step: 1 })
|
||||
session.append('step/start', { turn: 1, step: 2 })
|
||||
expect(() => session.append('tool/result', {
|
||||
turn: 1,
|
||||
step: 2,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import fc from 'fast-check'
|
||||
import { createUserMessage, CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionEventMap, SessionEventType, SurfaceIntent } from '@deepseek-ai/dsh-session'
|
||||
|
||||
@@ -61,7 +61,7 @@ const messageEventArb: fc.Arbitrary<Appendable> = fc.oneof(
|
||||
.map((r): Appendable => ({ type: 'tool/result', data: {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId(r.id),
|
||||
callId: ToolCallId(r.id),
|
||||
content: r.content,
|
||||
isError: r.isError,
|
||||
}),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { interruptedTurnClosers, TOOL_NOT_STARTED, TOOL_OUTCOME_UNKNOWN } from '../src/index.ts'
|
||||
import type { SessionEvent, SurfaceEvent } from '../src/index.ts'
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('interruptedTurnClosers', () => {
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{ type: 'text', text: 'calling a tool' },
|
||||
{ type: 'tool-call', id: CallId('call-1'), name: 'bash', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('call-1'), name: 'bash', arguments: '{}' },
|
||||
],
|
||||
source: {
|
||||
kind: 'model',
|
||||
@@ -75,7 +75,7 @@ describe('interruptedTurnClosers', () => {
|
||||
turn: 2,
|
||||
step: 1,
|
||||
message: {
|
||||
source: { callId: CallId('call-1') },
|
||||
source: { callId: ToolCallId('call-1') },
|
||||
content: [{ isError: true }],
|
||||
},
|
||||
error: { code: TOOL_NOT_STARTED },
|
||||
@@ -94,7 +94,7 @@ describe('interruptedTurnClosers', () => {
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{ type: 'tool-call', id: CallId('call-1'), name: 'bash', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('call-1'), name: 'bash', arguments: '{}' },
|
||||
],
|
||||
source: {
|
||||
kind: 'model',
|
||||
@@ -105,7 +105,7 @@ describe('interruptedTurnClosers', () => {
|
||||
{ type: 'tool/result', seq: 3, time: 3, data: {
|
||||
turn: 2, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('call-1'),
|
||||
callId: ToolCallId('call-1'),
|
||||
content: [{ type: 'text', text: 'ok' }],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -125,7 +125,7 @@ describe('interruptedTurnClosers', () => {
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{ type: 'tool-call', id: CallId('call-1'), name: 'bash', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('call-1'), name: 'bash', arguments: '{}' },
|
||||
],
|
||||
source: {
|
||||
kind: 'model',
|
||||
@@ -152,7 +152,7 @@ describe('interruptedTurnClosers', () => {
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{ type: 'tool-call', id: CallId('old-call'), name: 'bash', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('old-call'), name: 'bash', arguments: '{}' },
|
||||
],
|
||||
source: {
|
||||
kind: 'model',
|
||||
@@ -163,7 +163,7 @@ describe('interruptedTurnClosers', () => {
|
||||
{ type: 'tool/result', seq: 3, time: 3, data: {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('old-call'),
|
||||
callId: ToolCallId('old-call'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -177,7 +177,7 @@ describe('interruptedTurnClosers', () => {
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{ type: 'tool-call', id: CallId('new-call'), name: 'bash', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('new-call'), name: 'bash', arguments: '{}' },
|
||||
],
|
||||
source: {
|
||||
kind: 'model',
|
||||
@@ -201,8 +201,8 @@ describe('interruptedTurnClosers', () => {
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{ type: 'tool-call', id: CallId('call-a'), name: 'bash', arguments: '{}' },
|
||||
{ type: 'tool-call', id: CallId('call-b'), name: 'bash', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('call-a'), name: 'bash', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('call-b'), name: 'bash', arguments: '{}' },
|
||||
],
|
||||
source: {
|
||||
kind: 'model',
|
||||
@@ -214,7 +214,7 @@ describe('interruptedTurnClosers', () => {
|
||||
{ type: 'tool/result', seq: 3, time: 3, data: {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('call-a'),
|
||||
callId: ToolCallId('call-a'),
|
||||
content: [],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -235,7 +235,7 @@ describe('interruptedTurnClosers', () => {
|
||||
message: createMessage({
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{ type: 'tool-call', id: CallId('call-1'), name: 'bash', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('call-1'), name: 'bash', arguments: '{}' },
|
||||
],
|
||||
source: {
|
||||
kind: 'model',
|
||||
@@ -243,7 +243,7 @@ describe('interruptedTurnClosers', () => {
|
||||
},
|
||||
}),
|
||||
} },
|
||||
{ type: 'tool/call', seq: 3, time: 3, data: { turn: 1, step: 1, callId: CallId('call-1'), name: 'bash', arguments: '{}' } },
|
||||
{ type: 'tool/call', seq: 3, time: 3, data: { turn: 1, step: 1, callId: ToolCallId('call-1'), name: 'bash', arguments: '{}' } },
|
||||
]
|
||||
const closers = interruptedTurnClosers(events)
|
||||
expect(closers.map(e => e.type)).toEqual(['tool/result', 'step/end', 'turn/end'])
|
||||
@@ -266,7 +266,7 @@ describe('interruptedTurnClosers', () => {
|
||||
const events: SessionEvent[] = [
|
||||
userTurnStart(1, 0),
|
||||
{ type: 'step/start', seq: 1, time: 1, data: { turn: 1, step: 1 } },
|
||||
{ type: 'tool/call', seq: 2, time: 2, data: { turn: 1, step: 1, callId: CallId('orphan'), name: 'bash', arguments: '{}' } },
|
||||
{ type: 'tool/call', seq: 2, time: 2, data: { turn: 1, step: 1, callId: ToolCallId('orphan'), name: 'bash', arguments: '{}' } },
|
||||
]
|
||||
const closers = interruptedTurnClosers(events)
|
||||
// No pending calls → no synthetic tool/result, just step/end + turn/end.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, expectTypeOf, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { createUserMessage, CallId, createMessage, createToolResultMessage, MessageId, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId, createMessage, createToolResultMessage, MessageId, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, {
|
||||
adoptSessionEvent,
|
||||
SESSION_FORMAT_VERSION,
|
||||
@@ -33,7 +33,7 @@ describe('Session', () => {
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{ type: 'text', text: 'let me check' },
|
||||
{ type: 'tool-call', id: CallId('c1'), name: 'echo', arguments: '{}' },
|
||||
{ type: 'tool-call', id: ToolCallId('c1'), name: 'echo', arguments: '{}' },
|
||||
],
|
||||
source: {
|
||||
kind: 'model',
|
||||
@@ -44,7 +44,7 @@ describe('Session', () => {
|
||||
session.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
content: [{ type: 'text', text: 'ok' }],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -55,7 +55,7 @@ describe('Session', () => {
|
||||
expect(messages.map(m => m.role)).toEqual(['user', 'assistant', 'user'])
|
||||
// raw chunks must NOT appear in derived history
|
||||
expect(messages[1]!.content).toHaveLength(2)
|
||||
expect(messages[2]!.content[0]).toMatchObject({ type: 'tool-result', toolCallId: CallId('c1') })
|
||||
expect(messages[2]!.content[0]).toMatchObject({ type: 'tool-result', toolCallId: ToolCallId('c1') })
|
||||
})
|
||||
|
||||
it('accepts and round-trips a max-tokens turn/end reason', () => {
|
||||
@@ -442,7 +442,7 @@ describe('Session', () => {
|
||||
session.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
content: [{ type: 'text', text: 'tool out' }],
|
||||
isError: false,
|
||||
}),
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
createToolResultMessage,
|
||||
createUserMessage,
|
||||
freezeMessage,
|
||||
CallId,
|
||||
ToolCallId,
|
||||
MessageId,
|
||||
} from '@deepseek-ai/dsh-llm'
|
||||
|
||||
@@ -68,7 +68,7 @@ function toolResultEvent(
|
||||
turn: 1,
|
||||
step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId(callId),
|
||||
callId: ToolCallId(callId),
|
||||
content: [{ type: 'text', text: `result ${seq}` }],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -175,7 +175,7 @@ describe('foldSurface tool-result rewrites', () => {
|
||||
})
|
||||
|
||||
it.each([
|
||||
['toolCallId', { toolCallId: CallId('changed') }],
|
||||
['toolCallId', { toolCallId: ToolCallId('changed') }],
|
||||
['isError', { isError: true }],
|
||||
] as const)('rejects a replacement that changes the result block %s', (_field, patch) => {
|
||||
const original = toolResultEvent(0, 'original')
|
||||
@@ -460,7 +460,7 @@ describe('SurfaceManager', () => {
|
||||
s.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
content: [{ type: 'text', text: 'ok' }],
|
||||
isError: false,
|
||||
}),
|
||||
@@ -474,7 +474,7 @@ describe('SurfaceManager', () => {
|
||||
original.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
content: [{ type: 'text', text: 'ok' }],
|
||||
isError: false,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @module @deepseek-ai/dsh-tools/src/code-mode
|
||||
*/
|
||||
|
||||
import { CallId, createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId, createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm'
|
||||
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
|
||||
import type { CodeBindingFunction, CodeRunResult, CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
|
||||
import { snapshotJsonValue } from '@deepseek-ai/dsh-session'
|
||||
@@ -469,7 +469,7 @@ export function createRunCodeTool(registry: ToolRuntime, options: RunCodeBridgeO
|
||||
}
|
||||
const normalized = jsonNormalizeArgs(rawArgs)
|
||||
const n = ++dispatches
|
||||
const subCallId = CallId(`${String(exec.callId)}:code:${n}`)
|
||||
const subCallId = ToolCallId(`${String(exec.callId)}:code:${n}`)
|
||||
const input = {
|
||||
callId: subCallId,
|
||||
rootCallId: exec.rootCallId,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Context, Service } from '@deepseek-ai/cordis'
|
||||
import z from '@deepseek-ai/schemastery'
|
||||
import { AnonymousEntries, NamedEntries, ScopedLayers, scopeOf, scopeTarget } from '@deepseek-ai/dsh-scope'
|
||||
import type { ScopeKey, ScopeLayer, Scoped } from '@deepseek-ai/dsh-scope'
|
||||
import type { CallId, ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm'
|
||||
import type { ToolCallId, ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm'
|
||||
import { assertNever, deepFreeze, HarnessError } from '@deepseek-ai/dsh-llm'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { snapshotJsonValue } from '@deepseek-ai/dsh-session'
|
||||
@@ -312,12 +312,12 @@ export type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]:
|
||||
* callers do not choose that token.
|
||||
*/
|
||||
export interface ToolExecutionInput {
|
||||
readonly callId: CallId
|
||||
readonly callId: ToolCallId
|
||||
/**
|
||||
* Root model-requested call owning this execution tree. Callers omit it for
|
||||
* a root execution; nested dispatchers propagate the enclosing value.
|
||||
*/
|
||||
readonly rootCallId?: CallId
|
||||
readonly rootCallId?: ToolCallId
|
||||
readonly name: string
|
||||
/** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
|
||||
readonly arguments: unknown
|
||||
@@ -360,7 +360,7 @@ export interface CodeDispatchLog {
|
||||
/** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
|
||||
readonly agent?: Agent
|
||||
/** Deterministic sub-call id (`<parent>:code:<n>`). */
|
||||
readonly subCallId: CallId
|
||||
readonly subCallId: ToolCallId
|
||||
/** The dispatched sub-tool name. */
|
||||
readonly name: string
|
||||
/** Whether the sub-call settled as an error. */
|
||||
@@ -378,7 +378,7 @@ export interface CodeDispatchLog {
|
||||
*/
|
||||
export interface ToolExecution extends ToolExecutionInput {
|
||||
/** Root model-requested call, resolved for every root and nested execution. */
|
||||
readonly rootCallId: CallId
|
||||
readonly rootCallId: ToolCallId
|
||||
/** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
|
||||
readonly token: ToolExecutionToken
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
* @module @deepseek-ai/dsh-tools/types
|
||||
*/
|
||||
|
||||
import type { CallId } from '@deepseek-ai/dsh-llm/brand'
|
||||
import type { ToolCallId } from '@deepseek-ai/dsh-llm/brand'
|
||||
import type { ContentBlock } from '@deepseek-ai/dsh-llm/types'
|
||||
|
||||
/** Payload recorded when one nested Code Mode Tool dispatch starts. */
|
||||
export interface CodeDispatchStartEventData {
|
||||
rootCallId: CallId
|
||||
parentCallId: CallId
|
||||
subCallId: CallId
|
||||
rootCallId: ToolCallId
|
||||
parentCallId: ToolCallId
|
||||
subCallId: ToolCallId
|
||||
name: string
|
||||
arguments: unknown
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { createUserMessage, CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import { createScope } from '@deepseek-ai/dsh-scope'
|
||||
import type { Scope } from '@deepseek-ai/dsh-scope'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
@@ -107,7 +107,7 @@ async function runCode(
|
||||
): Promise<ToolExecutionResult> {
|
||||
return ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('call-1'),
|
||||
callId: ToolCallId('call-1'),
|
||||
name: RUN_CODE_NAME,
|
||||
arguments: { code, description: extras.description ?? 'Run the test program' },
|
||||
...extras.agent ? { agent: extras.agent } : {},
|
||||
@@ -1624,9 +1624,9 @@ describe('the run_code dispatch bridge', () => {
|
||||
content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' },
|
||||
}), { surfaceOp: 'append' })
|
||||
session.append('tool/code-dispatch', {
|
||||
rootCallId: CallId('p1'),
|
||||
parentCallId: CallId('p1'),
|
||||
subCallId: CallId('p1:code:1'),
|
||||
rootCallId: ToolCallId('p1'),
|
||||
parentCallId: ToolCallId('p1'),
|
||||
subCallId: ToolCallId('p1:code:1'),
|
||||
name: 'echo',
|
||||
arguments: { value: 'x' },
|
||||
isError: false,
|
||||
@@ -1666,7 +1666,7 @@ describe('the run_code dispatch bridge', () => {
|
||||
registerEcho(ctx, 'write')
|
||||
const result = await registry.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('call-1'),
|
||||
callId: ToolCallId('call-1'),
|
||||
name: 'write',
|
||||
arguments: { text: 'hello' },
|
||||
})
|
||||
@@ -1688,7 +1688,7 @@ describe('the run_code dispatch bridge', () => {
|
||||
aborted.abort()
|
||||
const result = await registry.execute({
|
||||
signal: aborted.signal,
|
||||
callId: CallId('call-1'),
|
||||
callId: ToolCallId('call-1'),
|
||||
name: 'write',
|
||||
arguments: { text: 'hello' },
|
||||
})
|
||||
@@ -1720,7 +1720,7 @@ describe('per-agent presentation', () => {
|
||||
// mode is its own rather than the deployment's.
|
||||
const denied = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('coded-direct'),
|
||||
callId: ToolCallId('coded-direct'),
|
||||
name: 'echo',
|
||||
arguments: { value: 'coded' },
|
||||
agent,
|
||||
@@ -1756,14 +1756,14 @@ describe('per-agent presentation', () => {
|
||||
// `dsh-agent-tool-presentation` produces.
|
||||
expect(ctx.tools.executionMode({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('preset-coded-schedule'),
|
||||
callId: ToolCallId('preset-coded-schedule'),
|
||||
name: 'echo',
|
||||
arguments: { value: 'joined' },
|
||||
agent: joined.agent,
|
||||
})).toEqual({ kind: 'exclusive' })
|
||||
const denied = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('preset-coded-direct'),
|
||||
callId: ToolCallId('preset-coded-direct'),
|
||||
name: 'echo',
|
||||
arguments: { value: 'joined' },
|
||||
agent: joined.agent,
|
||||
@@ -1776,7 +1776,7 @@ describe('per-agent presentation', () => {
|
||||
expect(native.tools.map(tool => tool.name)).toEqual(['echo'])
|
||||
const allowed = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('native-sibling-direct'),
|
||||
callId: ToolCallId('native-sibling-direct'),
|
||||
name: 'echo',
|
||||
arguments: { value: 'loner' },
|
||||
agent: loner.agent,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { describe, expect, expectTypeOf, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRuntime, {
|
||||
defineContentToolFixture,
|
||||
@@ -21,7 +21,7 @@ async function setup() {
|
||||
}
|
||||
|
||||
function exec(name: string, args: unknown): ToolExecutionInput {
|
||||
return { signal: testToolSignal, callId: CallId('c1'), name, arguments: args }
|
||||
return { signal: testToolSignal, callId: ToolCallId('c1'), name, arguments: args }
|
||||
}
|
||||
|
||||
describe('ToolRuntime.executionMode', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expectTypeOf, it } from 'vitest'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
import type {
|
||||
ToolDispatchExecution,
|
||||
@@ -15,7 +15,7 @@ function inputAndExecutionContracts(
|
||||
run: ToolRunContext,
|
||||
): void {
|
||||
// @ts-expect-error -- every typed invocation must supply a caller-owned signal.
|
||||
const missingSignal: ToolExecutionInput = { callId: CallId('missing'), name: 'probe', arguments: {} }
|
||||
const missingSignal: ToolExecutionInput = { callId: ToolCallId('missing'), name: 'probe', arguments: {} }
|
||||
void missingSignal
|
||||
|
||||
// @ts-expect-error -- caller input is readonly after construction.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { scopeTarget } from '@deepseek-ai/dsh-scope'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { ToolExecution, ToolExecutionResult, ToolExecutionToken } from '@deepseek-ai/dsh-tools'
|
||||
import * as ToolsInvariant from '@deepseek-ai/dsh-tools/invariant'
|
||||
@@ -19,12 +19,12 @@ async function setup(): Promise<Context> {
|
||||
|
||||
const execution = (overrides: Partial<ToolExecution> = {}): ToolExecution => ({
|
||||
token: Symbol('tool') as ToolExecutionToken,
|
||||
callId: CallId('call-1'),
|
||||
callId: ToolCallId('call-1'),
|
||||
name: 'echo',
|
||||
arguments: Object.freeze({ text: 'hi' }),
|
||||
...overrides,
|
||||
signal: overrides.signal ?? testToolSignal,
|
||||
rootCallId: overrides.rootCallId ?? overrides.callId ?? CallId('call-1'),
|
||||
rootCallId: overrides.rootCallId ?? overrides.callId ?? ToolCallId('call-1'),
|
||||
})
|
||||
|
||||
const outcome = (): ToolExecutionResult => Object.freeze({
|
||||
@@ -55,7 +55,7 @@ describe('tool-pipeline invariants', () => {
|
||||
Object.freeze(dispatched)
|
||||
emitResult(ctx, dispatched, outcome())
|
||||
|
||||
const denied = execution({ callId: CallId('call-2') })
|
||||
const denied = execution({ callId: ToolCallId('call-2') })
|
||||
await stage(ctx, 'tools/pre-execute', denied)
|
||||
await ctx.waterfall(ctx as never, 'tools/post-execute', denied, outcome(), () => Promise.resolve({ kind: 'accept' as const }))
|
||||
Object.freeze(denied)
|
||||
@@ -69,7 +69,7 @@ describe('tool-pipeline invariants', () => {
|
||||
await stage(ctx, 'tools/pre-execute', exec)
|
||||
await expect(stage(ctx, 'tools/pre-execute', exec)).rejects.toThrow(/repeated/)
|
||||
|
||||
const noPre = execution({ callId: CallId('call-2') })
|
||||
const noPre = execution({ callId: ToolCallId('call-2') })
|
||||
await expect(stage(ctx, 'tools/execute', noPre)).rejects.toThrow(/must follow tools\/pre-execute/)
|
||||
expect(() => ctx.waterfall(
|
||||
ctx as never, 'tools/post-execute', noPre, outcome(),
|
||||
@@ -93,9 +93,9 @@ describe('tool-pipeline invariants', () => {
|
||||
const ctx = await setup()
|
||||
const session = ctx.sessions.create()
|
||||
const data = {
|
||||
rootCallId: CallId('parent'),
|
||||
parentCallId: CallId('parent'),
|
||||
subCallId: CallId('child'),
|
||||
rootCallId: ToolCallId('parent'),
|
||||
parentCallId: ToolCallId('parent'),
|
||||
subCallId: ToolCallId('child'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
}
|
||||
@@ -109,18 +109,18 @@ describe('tool-pipeline invariants', () => {
|
||||
const ctx = await setup()
|
||||
const session = ctx.sessions.create()
|
||||
expect(() => session.append('tool/code-dispatch-start', {
|
||||
rootCallId: CallId('rejected-root'),
|
||||
parentCallId: CallId('rejected-root'),
|
||||
subCallId: CallId('reused-child'),
|
||||
rootCallId: ToolCallId('rejected-root'),
|
||||
parentCallId: ToolCallId('rejected-root'),
|
||||
subCallId: ToolCallId('reused-child'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
})).toThrow(/outside any open turn/)
|
||||
|
||||
session.append('turn/start', { turn: 1 })
|
||||
expect(() => session.append('tool/code-dispatch-start', {
|
||||
rootCallId: CallId('accepted-root'),
|
||||
parentCallId: CallId('accepted-root'),
|
||||
subCallId: CallId('reused-child'),
|
||||
rootCallId: ToolCallId('accepted-root'),
|
||||
parentCallId: ToolCallId('accepted-root'),
|
||||
subCallId: ToolCallId('reused-child'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
})).not.toThrow()
|
||||
@@ -131,24 +131,24 @@ describe('tool-pipeline invariants', () => {
|
||||
const session = ctx.sessions.create()
|
||||
session.append('turn/start', { turn: 1 })
|
||||
session.append('tool/code-dispatch-start', {
|
||||
rootCallId: CallId('root'),
|
||||
parentCallId: CallId('root'),
|
||||
subCallId: CallId('child'),
|
||||
rootCallId: ToolCallId('root'),
|
||||
parentCallId: ToolCallId('root'),
|
||||
subCallId: ToolCallId('child'),
|
||||
name: 'run_code',
|
||||
arguments: {},
|
||||
})
|
||||
session.append('tool/code-dispatch-start', {
|
||||
rootCallId: CallId('root'),
|
||||
parentCallId: CallId('child'),
|
||||
subCallId: CallId('grandchild'),
|
||||
rootCallId: ToolCallId('root'),
|
||||
parentCallId: ToolCallId('child'),
|
||||
subCallId: ToolCallId('grandchild'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
})
|
||||
|
||||
expect(() => session.append('tool/code-dispatch-start', {
|
||||
rootCallId: CallId('another-root'),
|
||||
parentCallId: CallId('child'),
|
||||
subCallId: CallId('invalid-grandchild'),
|
||||
rootCallId: ToolCallId('another-root'),
|
||||
parentCallId: ToolCallId('child'),
|
||||
subCallId: ToolCallId('invalid-grandchild'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
})).toThrow(/parentCallId child does not belong to rootCallId another-root/)
|
||||
@@ -161,24 +161,24 @@ describe('tool-pipeline invariants', () => {
|
||||
const session = ctx.sessions.create()
|
||||
session.append('turn/start', { turn: 1 })
|
||||
expect(() => session.append('tool/code-dispatch-start', {
|
||||
rootCallId: CallId(''),
|
||||
parentCallId: CallId('root'),
|
||||
subCallId: CallId('child'),
|
||||
rootCallId: ToolCallId(''),
|
||||
parentCallId: ToolCallId('root'),
|
||||
subCallId: ToolCallId('child'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
})).toThrow(/must carry non-empty rootCallId/)
|
||||
|
||||
session.append('tool/code-dispatch-start', {
|
||||
rootCallId: CallId('root'),
|
||||
parentCallId: CallId('root'),
|
||||
subCallId: CallId('child'),
|
||||
rootCallId: ToolCallId('root'),
|
||||
parentCallId: ToolCallId('root'),
|
||||
subCallId: ToolCallId('child'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
})
|
||||
expect(() => session.append('tool/code-dispatch-start', {
|
||||
rootCallId: CallId('other-root'),
|
||||
parentCallId: CallId('other-root'),
|
||||
subCallId: CallId('child'),
|
||||
rootCallId: ToolCallId('other-root'),
|
||||
parentCallId: ToolCallId('other-root'),
|
||||
subCallId: ToolCallId('child'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
})).toThrow(/changed rootCallId for subCallId child/)
|
||||
@@ -194,9 +194,9 @@ describe('tool-pipeline invariants', () => {
|
||||
seq: 1,
|
||||
time: 1,
|
||||
data: {
|
||||
rootCallId: CallId('root'),
|
||||
parentCallId: CallId('root'),
|
||||
subCallId: CallId('child'),
|
||||
rootCallId: ToolCallId('root'),
|
||||
parentCallId: ToolCallId('root'),
|
||||
subCallId: ToolCallId('child'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
},
|
||||
@@ -210,9 +210,9 @@ describe('tool-pipeline invariants', () => {
|
||||
const session = ctx.sessions.create()
|
||||
session.append('turn/start', { turn: 1 })
|
||||
session.append('tool/code-dispatch', {
|
||||
rootCallId: CallId('parent'),
|
||||
parentCallId: CallId('parent'),
|
||||
subCallId: CallId('child'),
|
||||
rootCallId: ToolCallId('parent'),
|
||||
parentCallId: ToolCallId('parent'),
|
||||
subCallId: ToolCallId('child'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
isError: false,
|
||||
@@ -227,9 +227,9 @@ describe('tool-pipeline invariants', () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
ctx.sessions.create().append('tool/code-dispatch-start', {
|
||||
rootCallId: CallId('parent'),
|
||||
parentCallId: CallId('parent'),
|
||||
subCallId: CallId('child'),
|
||||
rootCallId: ToolCallId('parent'),
|
||||
parentCallId: ToolCallId('parent'),
|
||||
subCallId: ToolCallId('child'),
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ import ToolRuntime from '@deepseek-ai/dsh-tools'
|
||||
import type { PreToolDecision, ToolDefinition, ToolExecution, ToolExecutionInput, ToolExecutionToken } from '@deepseek-ai/dsh-tools'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-session'
|
||||
|
||||
const testToolSignal = new AbortController().signal
|
||||
@@ -49,7 +49,7 @@ function tool(name: string, reply = `ran:${name}`): ToolDefinition {
|
||||
async function run(ctx: Context, name: string, agent?: Agent): Promise<string> {
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
name,
|
||||
arguments: {},
|
||||
...agent ? { agent } : {},
|
||||
@@ -418,7 +418,7 @@ describe('scoped execution dispatch', () => {
|
||||
const callerArguments = { source: true }
|
||||
const safeResult = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('safe-call'),
|
||||
callId: ToolCallId('safe-call'),
|
||||
name: 'safe',
|
||||
arguments: callerArguments,
|
||||
agent: key,
|
||||
@@ -461,7 +461,7 @@ describe('scoped execution dispatch', () => {
|
||||
if (exec.name === 'parent') parent = exec.token
|
||||
return next()
|
||||
})
|
||||
await ctx.tools.execute({ signal: testToolSignal, callId: CallId('parent'), name: 'parent', arguments: {} })
|
||||
await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('parent'), name: 'parent', arguments: {} })
|
||||
stopCapture()
|
||||
policyCalls = 0
|
||||
const signal = new AbortController().signal
|
||||
@@ -477,7 +477,7 @@ describe('scoped execution dispatch', () => {
|
||||
const callerArguments = { invalid: () => undefined }
|
||||
|
||||
const scopedResult = await ctx.tools.execute({
|
||||
callId: CallId('non-cloneable'),
|
||||
callId: ToolCallId('non-cloneable'),
|
||||
name: 't',
|
||||
arguments: callerArguments,
|
||||
agent: key,
|
||||
@@ -486,7 +486,7 @@ describe('scoped execution dispatch', () => {
|
||||
})
|
||||
const subjectlessResult = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('non-cloneable-subjectless'),
|
||||
callId: ToolCallId('non-cloneable-subjectless'),
|
||||
name: 't',
|
||||
arguments: { invalid: () => undefined },
|
||||
})
|
||||
@@ -525,7 +525,7 @@ describe('scoped execution dispatch', () => {
|
||||
const forged = { fake: true } as unknown as ToolExecutionToken
|
||||
let parentReads = 0
|
||||
const input = {
|
||||
callId: CallId('stateful-parent'),
|
||||
callId: ToolCallId('stateful-parent'),
|
||||
name: 't',
|
||||
arguments: {},
|
||||
signal: testToolSignal,
|
||||
@@ -553,14 +553,14 @@ describe('scoped execution dispatch', () => {
|
||||
if (exec.name === 'parent') parent = exec.token
|
||||
return next()
|
||||
})
|
||||
await ctx.tools.execute({ signal: testToolSignal, callId: CallId('parent'), name: 'parent', arguments: {} })
|
||||
await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('parent'), name: 'parent', arguments: {} })
|
||||
stopCapture()
|
||||
const acceptedSignal = new AbortController().signal
|
||||
const driftSignal = new AbortController().signal
|
||||
const forged = { fake: true } as unknown as ToolExecutionToken
|
||||
const reads = { callId: 0, name: 0, arguments: 0, agent: 0, parent: 0, signal: 0 }
|
||||
const input = {
|
||||
get callId() { reads.callId += 1; return CallId('unstable-error') },
|
||||
get callId() { reads.callId += 1; return ToolCallId('unstable-error') },
|
||||
get name() { reads.name += 1; return 't' },
|
||||
get arguments(): unknown { reads.arguments += 1; return { invalid: () => undefined } },
|
||||
get agent() { reads.agent += 1; return reads.agent === 1 ? key : driftAgent },
|
||||
@@ -578,7 +578,7 @@ describe('scoped execution dispatch', () => {
|
||||
expect(reads).toEqual({ callId: 1, name: 1, arguments: 1, agent: 1, parent: 1, signal: 1 })
|
||||
expect(scopedObserved).toBe(1)
|
||||
expect(observed).toMatchObject({
|
||||
callId: CallId('unstable-error'),
|
||||
callId: ToolCallId('unstable-error'),
|
||||
name: 't',
|
||||
agent: key,
|
||||
parent,
|
||||
@@ -598,7 +598,7 @@ describe('scoped execution dispatch', () => {
|
||||
expect(result.isError).toBe(true)
|
||||
})
|
||||
const input = {
|
||||
callId: CallId('throwing-arguments'),
|
||||
callId: ToolCallId('throwing-arguments'),
|
||||
name: 't',
|
||||
signal: testToolSignal,
|
||||
get arguments(): unknown {
|
||||
@@ -642,7 +642,7 @@ describe('scoped execution dispatch', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('bad-arguments'), name: 't', arguments: argumentsValue,
|
||||
callId: ToolCallId('bad-arguments'), name: 't', arguments: argumentsValue,
|
||||
})
|
||||
|
||||
expect(result.isError).toBe(true)
|
||||
@@ -663,7 +663,7 @@ describe('scoped execution dispatch', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('unstable-arguments'), name: 't', arguments: argumentsValue,
|
||||
callId: ToolCallId('unstable-arguments'), name: 't', arguments: argumentsValue,
|
||||
})
|
||||
|
||||
expect(reads).toBe(1)
|
||||
@@ -705,7 +705,7 @@ describe('scoped execution dispatch', () => {
|
||||
ctx.on('tools/result', () => Promise.reject(new Error('async observer failure')) as never)
|
||||
ctx.on('tools/result', (_exec, result) => { seen.push(result.isError) })
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('final'), name: 't', arguments: {}, agent: key })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('final'), name: 't', arguments: {}, agent: key })
|
||||
await Promise.resolve()
|
||||
expect(result).toMatchObject({ isError: true, content: [{ type: 'text', text: 'outer failure' }] })
|
||||
expect(seen).toEqual([true, true])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, expectTypeOf, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { createUserMessage, CallId, HarnessError, type ContentBlock } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, ToolCallId, HarnessError, type ContentBlock } from '@deepseek-ai/dsh-llm'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import ApprovalService, { type ApprovalOutcome, type ApprovalRequest } from '@deepseek-ai/dsh-user-approval'
|
||||
@@ -88,7 +88,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.tools.register(echoTool)
|
||||
let observed: ToolExecutionResult | undefined
|
||||
ctx.on('tools/result', (_exec, result) => { observed = result })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
expect(result).toEqual({ content: [{ type: 'text', text: 'hi' }], isError: false, value: 'hi' })
|
||||
expect(observed).toEqual(result)
|
||||
})
|
||||
@@ -106,7 +106,7 @@ describe('ToolRuntime', () => {
|
||||
return 'ok'
|
||||
},
|
||||
})
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'meta-tool', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'meta-tool', arguments: {} })
|
||||
expect(result).toEqual({
|
||||
content: [{ type: 'text', text: 'ok' }],
|
||||
isError: false,
|
||||
@@ -124,7 +124,7 @@ describe('ToolRuntime', () => {
|
||||
return 'ok'
|
||||
},
|
||||
})
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'no-meta-tool', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'no-meta-tool', arguments: {} })
|
||||
expect(result).toEqual({ content: [{ type: 'text', text: 'ok' }], isError: false, value: 'ok' })
|
||||
expect('meta' in result).toBe(false)
|
||||
})
|
||||
@@ -147,7 +147,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('bad-meta'), name: 'bad-meta', arguments: {},
|
||||
callId: ToolCallId('bad-meta'), name: 'bad-meta', arguments: {},
|
||||
})
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]?.type === 'text' && result.content[0].text).toContain('Error:')
|
||||
@@ -185,7 +185,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('throwing-meta'), name: 'throwing-meta', arguments: {},
|
||||
callId: ToolCallId('throwing-meta'), name: 'throwing-meta', arguments: {},
|
||||
})
|
||||
|
||||
expect(result.isError).toBe(true)
|
||||
@@ -210,7 +210,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('throwing-finalizer'), name: 'throwing-finalizer', arguments: {},
|
||||
callId: ToolCallId('throwing-finalizer'), name: 'throwing-finalizer', arguments: {},
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -251,8 +251,8 @@ describe('ToolRuntime', () => {
|
||||
execute: async () => 42 as unknown as string,
|
||||
}))
|
||||
|
||||
const lossy = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('lossy'), name: 'lossy-output', arguments: {} })
|
||||
const mismatch = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('mismatch'), name: 'wrong-output', arguments: {} })
|
||||
const lossy = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('lossy'), name: 'lossy-output', arguments: {} })
|
||||
const mismatch = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('mismatch'), name: 'wrong-output', arguments: {} })
|
||||
expect(lossy.error).toMatchObject({ info: { name: 'ToolOutputError', code: 'INVALID_TOOL_OUTPUT' } })
|
||||
expect(lossy.content[0]?.type === 'text' ? lossy.content[0].text : '').toContain('not lossless JSON')
|
||||
expect(mismatch.error).toMatchObject({ info: { name: 'ToolOutputError', code: 'INVALID_TOOL_OUTPUT' } })
|
||||
@@ -275,7 +275,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('hostile-body'), name: 'hostile-body', arguments: {},
|
||||
callId: ToolCallId('hostile-body'), name: 'hostile-body', arguments: {},
|
||||
})
|
||||
expect(result.error?.message).toContain('value snapshot failed: body snapshot getter exploded')
|
||||
expect(result.error?.info).toEqual({ name: 'ToolOutputError', code: 'INVALID_TOOL_OUTPUT' })
|
||||
@@ -301,7 +301,7 @@ describe('ToolRuntime', () => {
|
||||
execute: async () => 'ok',
|
||||
}))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId(projector), name: `throwing-${projector}`, arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId(projector), name: `throwing-${projector}`, arguments: {} })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.error?.message)
|
||||
.toContain(projector === 'render' ? 'renderer exploded' : 'metadata exploded')
|
||||
@@ -333,7 +333,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId(`hostile-${projector}`), name: `hostile-${projector}`, arguments: {},
|
||||
callId: ToolCallId(`hostile-${projector}`), name: `hostile-${projector}`, arguments: {},
|
||||
})
|
||||
expect(result.error?.message).toContain('snapshot getter exploded')
|
||||
expect(result.error?.info).toEqual({ name: 'ToolOutputError', code: 'INVALID_TOOL_OUTPUT' })
|
||||
@@ -370,9 +370,9 @@ describe('ToolRuntime', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const content = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('content'), name: 'projected', arguments: {} })
|
||||
const content = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('content'), name: 'projected', arguments: {} })
|
||||
replacement = 'value'
|
||||
const value = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('value'), name: 'projected', arguments: {} })
|
||||
const value = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('value'), name: 'projected', arguments: {} })
|
||||
|
||||
expect(content).toEqual({
|
||||
isError: false,
|
||||
@@ -402,7 +402,7 @@ describe('ToolRuntime', () => {
|
||||
value: 'replacement',
|
||||
content: [{ type: 'text', text: 'also replacement' }],
|
||||
} as unknown as PostToolDecision))
|
||||
const bothResult = await both.tools.execute({ signal: testToolSignal, callId: CallId('both'), name: 'echo', arguments: {} })
|
||||
const bothResult = await both.tools.execute({ signal: testToolSignal, callId: ToolCallId('both'), name: 'echo', arguments: {} })
|
||||
expect(bothResult).toMatchObject({
|
||||
isError: true,
|
||||
error: { message: 'tools/post-execute accept decision cannot replace both value and content' },
|
||||
@@ -411,7 +411,7 @@ describe('ToolRuntime', () => {
|
||||
const invalid = await setup()
|
||||
invalid.tools.register(echoTool)
|
||||
invalid.on('tools/post-execute', async () => ({ kind: 'accept', value: 1 }))
|
||||
const invalidResult = await invalid.tools.execute({ signal: testToolSignal, callId: CallId('invalid'), name: 'echo', arguments: {} })
|
||||
const invalidResult = await invalid.tools.execute({ signal: testToolSignal, callId: ToolCallId('invalid'), name: 'echo', arguments: {} })
|
||||
expect(invalidResult.error).toMatchObject({ info: { code: 'INVALID_TOOL_OUTPUT' } })
|
||||
expect('value' in invalidResult).toBe(false)
|
||||
})
|
||||
@@ -424,7 +424,7 @@ describe('ToolRuntime', () => {
|
||||
feedback: [{ type: 'text', text: 'blocked by policy' }],
|
||||
}))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('block'), name: 'echo', arguments: { text: 'secret' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('block'), name: 'echo', arguments: { text: 'secret' } })
|
||||
expect(result).toEqual({
|
||||
isError: true,
|
||||
error: { message: 'blocked by policy' },
|
||||
@@ -438,7 +438,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.tools.register(echoTool)
|
||||
ctx.on('tools/post-execute', async () => ({ kind: 'accept', value: 'replacement' }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('replace-value'), name: 'echo', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('replace-value'), name: 'echo', arguments: {} })
|
||||
expect(result).toEqual({
|
||||
isError: false,
|
||||
value: 'replacement',
|
||||
@@ -454,7 +454,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.tools.register(echoTool)
|
||||
ctx.on('tools/post-execute', async () => ({ kind: 'block', feedback: [...feedback] }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('block-message'), name: 'echo', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('block-message'), name: 'echo', arguments: {} })
|
||||
expect(result.error?.message).toBe(message)
|
||||
})
|
||||
|
||||
@@ -466,7 +466,7 @@ describe('ToolRuntime', () => {
|
||||
feedback: [{ type: 'text', text: 'blocked', invalid: () => undefined } as never],
|
||||
}))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('invalid-block'), name: 'echo', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('invalid-block'), name: 'echo', arguments: {} })
|
||||
expect(result).toMatchObject({
|
||||
isError: true,
|
||||
error: { message: 'tool result must be losslessly JSON-serializable' },
|
||||
@@ -484,7 +484,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('failed-replace'), name: 'throw-before-replace', arguments: {},
|
||||
callId: ToolCallId('failed-replace'), name: 'throw-before-replace', arguments: {},
|
||||
})
|
||||
expect(result.error?.message).toBe('tools/post-execute cannot replace the value of a failed result')
|
||||
})
|
||||
@@ -497,7 +497,7 @@ describe('ToolRuntime', () => {
|
||||
return { kind: 'accept', value: 'replacement' }
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('post-disposed'), name: 'echo', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('post-disposed'), name: 'echo', arguments: {} })
|
||||
expect(result.error).toEqual({
|
||||
message: 'unknown tool "echo"',
|
||||
info: { name: 'ToolNotFoundError', code: 'UNKNOWN_TOOL' },
|
||||
@@ -517,7 +517,7 @@ describe('ToolRuntime', () => {
|
||||
})],
|
||||
}))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('wrapper-failure'), name: 'echo', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('wrapper-failure'), name: 'echo', arguments: {} })
|
||||
expect(result).toEqual({
|
||||
isError: true,
|
||||
error: { message: 'wrapped failure' },
|
||||
@@ -540,7 +540,7 @@ describe('ToolRuntime', () => {
|
||||
return { isError: false, value: 'replacement', content: [] }
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('wrapper-disposed'), name: 'echo', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('wrapper-disposed'), name: 'echo', arguments: {} })
|
||||
expect(result.error).toEqual({
|
||||
message: 'unknown tool "echo"',
|
||||
info: { name: 'ToolNotFoundError', code: 'UNKNOWN_TOOL' },
|
||||
@@ -554,10 +554,10 @@ describe('ToolRuntime', () => {
|
||||
name: 'meta-suppression',
|
||||
output: { ...echoTool.output, presentationMeta: () => ({ card: true }) },
|
||||
})
|
||||
const direct = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('direct'), name: 'meta-suppression', arguments: {} })
|
||||
const direct = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('direct'), name: 'meta-suppression', arguments: {} })
|
||||
const nested = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('nested'),
|
||||
callId: ToolCallId('nested'),
|
||||
name: 'meta-suppression',
|
||||
arguments: {},
|
||||
parent: Symbol('outer') as ToolExecutionToken,
|
||||
@@ -588,7 +588,7 @@ describe('ToolRuntime', () => {
|
||||
async execute(_args, exec) {
|
||||
call += 1
|
||||
const nested = await ctx.tools.execute({
|
||||
signal: exec.signal, callId: CallId(`nested-${call}`), name: 'terminal-nested', arguments: {}, parent: exec.token,
|
||||
signal: exec.signal, callId: ToolCallId(`nested-${call}`), name: 'terminal-nested', arguments: {}, parent: exec.token,
|
||||
})
|
||||
if (nested.concludesTurn) exec.concludeTurn()
|
||||
return nested.isError ? 'nested failed, composite recovered' : 'nested succeeded'
|
||||
@@ -602,7 +602,7 @@ describe('ToolRuntime', () => {
|
||||
return { kind: 'block', feedback: [{ type: 'text', text: 'nested success rejected' }] }
|
||||
})
|
||||
const recovered = await ctx.tools.execute({
|
||||
signal: testToolSignal, callId: CallId('composite-vetoed'), name: 'composite', arguments: {},
|
||||
signal: testToolSignal, callId: ToolCallId('composite-vetoed'), name: 'composite', arguments: {},
|
||||
})
|
||||
expect(recovered.isError).toBe(false)
|
||||
expect(recovered.concludesTurn).toBeUndefined()
|
||||
@@ -611,7 +611,7 @@ describe('ToolRuntime', () => {
|
||||
// The same nested call succeeding carries the marker; the composite
|
||||
// forwards it onto its own successful result.
|
||||
const concluded = await ctx.tools.execute({
|
||||
signal: testToolSignal, callId: CallId('composite-ok'), name: 'composite', arguments: {},
|
||||
signal: testToolSignal, callId: ToolCallId('composite-ok'), name: 'composite', arguments: {},
|
||||
})
|
||||
expect(concluded.isError).toBe(false)
|
||||
expect(concluded.concludesTurn).toBe(true)
|
||||
@@ -627,7 +627,7 @@ describe('ToolRuntime', () => {
|
||||
},
|
||||
})
|
||||
|
||||
const unknown = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'nope', arguments: {} })
|
||||
const unknown = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'nope', arguments: {} })
|
||||
expect(unknown.isError).toBe(true)
|
||||
expect(unknown.content[0]).toMatchObject({ text: 'Error: unknown tool "nope"' })
|
||||
// An unknown tool is a routable failure class, same as a tool-thrown one.
|
||||
@@ -636,7 +636,7 @@ describe('ToolRuntime', () => {
|
||||
info: { name: 'ToolNotFoundError', code: 'UNKNOWN_TOOL' },
|
||||
})
|
||||
|
||||
const thrown = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c2'), name: 'boom', arguments: {} })
|
||||
const thrown = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c2'), name: 'boom', arguments: {} })
|
||||
expect(thrown.isError).toBe(true)
|
||||
expect(thrown.content[0]).toMatchObject({ text: 'Error: exploded' })
|
||||
})
|
||||
@@ -657,7 +657,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
await expect(ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('hostile'), name: 'hostile-throw', arguments: {},
|
||||
callId: ToolCallId('hostile'), name: 'hostile-throw', arguments: {},
|
||||
})).resolves.toMatchObject({
|
||||
isError: true,
|
||||
content: [{ type: 'text', text: 'Error: <unprintable thrown value>' }],
|
||||
@@ -688,7 +688,7 @@ describe('ToolRuntime', () => {
|
||||
return next()
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: denied by policy' })
|
||||
expect(postSawFrozen).toBe(true)
|
||||
@@ -701,7 +701,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> =>
|
||||
({ kind: 'ask', reason: 'needs approval' }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: needs approval' })
|
||||
})
|
||||
@@ -712,7 +712,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: tool "echo" requires approval (not yet supported)' })
|
||||
})
|
||||
@@ -749,7 +749,7 @@ describe('ToolRuntime', () => {
|
||||
({ kind: 'ask', reason: 'hook wants a human' }))
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' }, agent, signal: controller.signal,
|
||||
callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' }, agent, signal: controller.signal,
|
||||
})
|
||||
|
||||
expect(result).toMatchObject({ isError: false, content: [{ type: 'text', text: 'hi' }] })
|
||||
@@ -763,7 +763,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.on('approval/request', () => Promise.resolve<ApprovalOutcome>('rejected'))
|
||||
ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: the user rejected tool "echo"' })
|
||||
})
|
||||
@@ -773,7 +773,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.on('approval/request', () => Promise.resolve<ApprovalOutcome>('cancelled'))
|
||||
ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: approval for tool "echo" was cancelled' })
|
||||
})
|
||||
@@ -795,7 +795,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('approval-cancelled'),
|
||||
callId: ToolCallId('approval-cancelled'),
|
||||
name: 'approval-probe',
|
||||
arguments: {},
|
||||
agent: fakeAgent(),
|
||||
@@ -817,7 +817,7 @@ describe('ToolRuntime', () => {
|
||||
const ctx = await approvalSetup()
|
||||
ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: tool "echo" requires approval, but no approval channel is available' })
|
||||
})
|
||||
@@ -831,7 +831,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: {} })
|
||||
expect(asked).toBe(false)
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: tool "echo" requires approval, but the call has no agent to route it through' })
|
||||
@@ -846,7 +846,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.provide('approval', { request: () => Promise.resolve('yolo') } as unknown as ApprovalService)
|
||||
ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
|
||||
expect(result.isError).toBe(true)
|
||||
const text = result.content[0]?.type === 'text' ? result.content[0].text : ''
|
||||
expect(text).toContain('unreachable')
|
||||
@@ -860,7 +860,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.on('tools/post-execute', async (_exec, _result, _next): Promise<PostToolDecision> =>
|
||||
({ kind: 'accept', content: [{ type: 'text', text: 'rewritten' }] }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
expect(result.isError).toBe(false)
|
||||
expect(result.content[0]).toMatchObject({ text: 'rewritten' })
|
||||
})
|
||||
@@ -872,7 +872,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.on('tools/post-execute', async (_exec, _result, _next): Promise<PostToolDecision> =>
|
||||
({ kind: 'block', feedback: [{ type: 'text', text: 'output rejected: try again' }] }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'output rejected: try again' })
|
||||
})
|
||||
@@ -895,7 +895,7 @@ describe('ToolRuntime', () => {
|
||||
throw new HarnessError('policy failed', 'POLICY_FAILED')
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('bounded'), name: 'bounded', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('bounded'), name: 'bounded', arguments: {} })
|
||||
|
||||
expect(result).toEqual({
|
||||
content: [{ type: 'text', text: 'bounded failure' }],
|
||||
@@ -919,7 +919,7 @@ describe('ToolRuntime', () => {
|
||||
},
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('identity-finalizer'), name: 'identity-finalizer', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('identity-finalizer'), name: 'identity-finalizer', arguments: {} })
|
||||
|
||||
expect(result.isError).toBe(false)
|
||||
expect(result.content).toEqual([{ type: 'text', text: '' }])
|
||||
@@ -939,7 +939,7 @@ describe('ToolRuntime', () => {
|
||||
})],
|
||||
}))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'rejected' })
|
||||
expect(result.additionalContexts).toMatchObject([{ content: [{ text: 'why it was rejected' }], source: { kind: 'plugin', plugin: 'test' } }])
|
||||
@@ -954,7 +954,7 @@ describe('ToolRuntime', () => {
|
||||
content: [{ type: 'text', text: 'fyi' }], source: { kind: 'plugin', plugin: 'test' },
|
||||
})] }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
expect(result.additionalContexts).toMatchObject([{ content: [{ text: 'fyi' }], source: { kind: 'plugin', plugin: 'test' } }])
|
||||
})
|
||||
|
||||
@@ -999,7 +999,7 @@ describe('ToolRuntime', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('composite'), name: 'composite', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('composite'), name: 'composite', arguments: {} })
|
||||
|
||||
expect(result.additionalContexts?.map(context => context.source)).toEqual([
|
||||
{ kind: 'plugin', plugin: 'nested-1' },
|
||||
@@ -1023,7 +1023,7 @@ describe('ToolRuntime', () => {
|
||||
},
|
||||
}))
|
||||
|
||||
const failed = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('failed'), name: 'failing-composite', arguments: {} })
|
||||
const failed = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('failed'), name: 'failing-composite', arguments: {} })
|
||||
expect(failed.isError).toBe(true)
|
||||
expect(failed.additionalContexts?.map(context => context.source)).toEqual([{ kind: 'plugin', plugin: 'nested' }])
|
||||
|
||||
@@ -1034,7 +1034,7 @@ describe('ToolRuntime', () => {
|
||||
content: [{ type: 'text', text: 'block-only' }], source: { kind: 'plugin', plugin: 'blocker' },
|
||||
})],
|
||||
}))
|
||||
const blocked = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('blocked'), name: 'failing-composite', arguments: {} })
|
||||
const blocked = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('blocked'), name: 'failing-composite', arguments: {} })
|
||||
expect(blocked.isError).toBe(true)
|
||||
expect(blocked.additionalContexts?.map(context => context.source)).toEqual([{ kind: 'plugin', plugin: 'blocker' }])
|
||||
})
|
||||
@@ -1057,7 +1057,7 @@ describe('ToolRuntime', () => {
|
||||
return decision
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'x' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'x' } })
|
||||
expect(result.isError).toBe(false)
|
||||
// pre runs fully (gate) before dispatch, then post runs over the result.
|
||||
expect(order).toEqual(['pre:before', 'pre:after', 'post:before', 'post:after'])
|
||||
@@ -1085,7 +1085,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
ctx.on('tools/post-execute', async (_exec, _result, next) => { order.push('post'); return next() })
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'traced', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'traced', arguments: { text: 'hi' } })
|
||||
expect(result).toEqual({ content: [{ type: 'text', text: 'hi' }], isError: false, value: [{ type: 'text', text: 'hi' }] })
|
||||
// The around-dispatch extension point wraps dispatch; pre gates before it, post runs over its result.
|
||||
expect(order).toEqual(['pre', 'execute:before', 'dispatch', 'execute:after', 'post'])
|
||||
@@ -1109,7 +1109,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('cancelled-in-pre'), name: 'must-not-run', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('cancelled-in-pre'), name: 'must-not-run', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
await entered.promise
|
||||
controller.abort('cancelled in policy')
|
||||
@@ -1140,7 +1140,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('denied-after-cancel'), name: 'denied-after-cancel', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('denied-after-cancel'), name: 'denied-after-cancel', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
|
||||
await entered.promise
|
||||
@@ -1173,7 +1173,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('cancelled-pre-error'), name: 'must-not-run', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('cancelled-pre-error'), name: 'must-not-run', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
await entered.promise
|
||||
controller.abort('cancelled in policy')
|
||||
@@ -1212,7 +1212,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('cancelled-in-around'), name: 'must-not-run', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('cancelled-in-around'), name: 'must-not-run', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
await entered.promise
|
||||
controller.abort('cancelled in wrapper')
|
||||
@@ -1246,7 +1246,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const controller = new AbortController()
|
||||
const result = await ctx.tools.execute({
|
||||
callId: CallId('cancelled-wrapper'), name: 'must-not-run', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('cancelled-wrapper'), name: 'must-not-run', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
|
||||
expect(result.error).toEqual({
|
||||
@@ -1281,7 +1281,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('cancelled-short-circuit'),
|
||||
callId: ToolCallId('cancelled-short-circuit'),
|
||||
name: 'short-circuited',
|
||||
arguments: {},
|
||||
signal: controller.signal,
|
||||
@@ -1323,7 +1323,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('cancelled-after-body'), name: 'completed-before-wrapper', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('cancelled-after-body'), name: 'completed-before-wrapper', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
await entered.promise
|
||||
controller.abort('cancelled while wrapper settled')
|
||||
@@ -1366,7 +1366,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('cancelled-in-post'), name: 'completed-before-post', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('cancelled-in-post'), name: 'completed-before-post', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
await entered.promise
|
||||
controller.abort('cancelled while post policy waits')
|
||||
@@ -1400,7 +1400,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('wrapper-failure'), name: 'wrapper-failure', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('wrapper-failure'), name: 'wrapper-failure', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
|
||||
await entered.promise
|
||||
@@ -1432,7 +1432,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('tool-failure'), name: 'tool-failure', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('tool-failure'), name: 'tool-failure', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
|
||||
await entered.promise
|
||||
@@ -1457,7 +1457,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('post-failure'), name: 'echo', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('post-failure'), name: 'echo', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
|
||||
await entered.promise
|
||||
@@ -1500,7 +1500,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('cancelled-body'), name: 'cooperative', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('cancelled-body'), name: 'cooperative', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
await entered.promise
|
||||
expect(bodySignal).not.toBe(controller.signal)
|
||||
@@ -1534,7 +1534,7 @@ describe('ToolRuntime', () => {
|
||||
const controller = new AbortController()
|
||||
|
||||
await ctx.tools.execute({
|
||||
callId: CallId('restored-signal'), name: 'echo', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('restored-signal'), name: 'echo', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
|
||||
expect(postSignal).toBe(controller.signal)
|
||||
@@ -1558,7 +1558,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
const controller = new AbortController()
|
||||
const pending = ctx.tools.execute({
|
||||
callId: CallId('drain-body'), name: 'uncooperative', arguments: {}, signal: controller.signal,
|
||||
callId: ToolCallId('drain-body'), name: 'uncooperative', arguments: {}, signal: controller.signal,
|
||||
})
|
||||
await entered.promise
|
||||
controller.abort('must still drain')
|
||||
@@ -1605,7 +1605,7 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
callId: CallId('pre-aborted'),
|
||||
callId: ToolCallId('pre-aborted'),
|
||||
name: 'domain-abort',
|
||||
get arguments() { argumentReads += 1; return callerArguments },
|
||||
signal: callerSignal,
|
||||
@@ -1637,7 +1637,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.on('tools/result', () => { observed += 1 })
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
callId: CallId('invalid-pre-aborted'),
|
||||
callId: ToolCallId('invalid-pre-aborted'),
|
||||
name: 'missing',
|
||||
arguments: { invalid: () => undefined },
|
||||
signal: AbortSignal.abort('already cancelled'),
|
||||
@@ -1662,7 +1662,7 @@ describe('ToolRuntime', () => {
|
||||
return next()
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: nope' })
|
||||
expect(entered).toBe(false) // A denied call never enters the around-dispatch extension point.
|
||||
@@ -1685,7 +1685,7 @@ describe('ToolRuntime', () => {
|
||||
return result
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'boom', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'boom', arguments: {} })
|
||||
expect(seen).toEqual({
|
||||
isError: true,
|
||||
error: { message: 'kaboom', info: { name: 'HarnessError', code: 'BOOM' } },
|
||||
@@ -1710,7 +1710,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('frozen-canonical'), name: 'echo', arguments: { text: 'original' },
|
||||
callId: ToolCallId('frozen-canonical'), name: 'echo', arguments: { text: 'original' },
|
||||
})
|
||||
expect(mutationAttempts).toEqual([false, false])
|
||||
expect(result.isError ? undefined : result.value).toBe('original')
|
||||
@@ -1731,7 +1731,7 @@ describe('ToolRuntime', () => {
|
||||
return next()
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'boom', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'boom', arguments: {} })
|
||||
expect(postSaw).toBe(true) // the normalized isError still flows through post-execute
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: exploded' })
|
||||
@@ -1759,7 +1759,7 @@ describe('ToolRuntime', () => {
|
||||
return next()
|
||||
})
|
||||
|
||||
await ctx.tools.execute({ callId: CallId('c1'), name: 'signal-probe', arguments: {}, signal: upstream })
|
||||
await ctx.tools.execute({ callId: ToolCallId('c1'), name: 'signal-probe', arguments: {}, signal: upstream })
|
||||
expect(seenSignal).toBeDefined()
|
||||
expect(seenSignal).not.toBe(upstream)
|
||||
expect(seenSignal).not.toBe(replacement)
|
||||
@@ -1777,7 +1777,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.on('tools/execute', async (_exec: ToolDispatchExecution, _next: () => Promise<ToolExecutionResult>): Promise<ToolExecutionResult> =>
|
||||
({ content: [{ type: 'text', text: 'ignored authored content' }], isError: false, value: 'short-circuited' }))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'never-runs', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'never-runs', arguments: {} })
|
||||
expect(dispatched).toBe(false) // returning without next() skips core dispatch
|
||||
expect(result.content[0]).toMatchObject({ text: 'short-circuited' })
|
||||
})
|
||||
@@ -1817,10 +1817,10 @@ describe('ToolRuntime', () => {
|
||||
})
|
||||
|
||||
const first = await ctx.tools.execute({
|
||||
signal: testToolSignal, callId: CallId('cached-first'), name: 'string-output', arguments: {},
|
||||
signal: testToolSignal, callId: ToolCallId('cached-first'), name: 'string-output', arguments: {},
|
||||
})
|
||||
const second = await ctx.tools.execute({
|
||||
signal: testToolSignal, callId: CallId('cached-second'), name: 'object-output', arguments: {},
|
||||
signal: testToolSignal, callId: ToolCallId('cached-second'), name: 'object-output', arguments: {},
|
||||
})
|
||||
|
||||
expect(first.isError ? undefined : first.value).toBe('cached')
|
||||
@@ -1846,7 +1846,7 @@ describe('ToolRuntime', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('around-context'), name: 'echo', arguments: {},
|
||||
callId: ToolCallId('around-context'), name: 'echo', arguments: {},
|
||||
})
|
||||
expect(result.additionalContexts).toEqual([{
|
||||
id: expect.any(String) as unknown,
|
||||
@@ -1861,7 +1861,7 @@ describe('ToolRuntime', () => {
|
||||
ctx.tools.register(echoTool)
|
||||
ctx.on('tools/execute', async () => { throw new Error('wrapper broke') })
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
expect(result).toEqual({
|
||||
content: [{ type: 'text', text: 'Error: wrapper broke' }],
|
||||
error: { message: 'wrapper broke' },
|
||||
@@ -1876,7 +1876,7 @@ describe('ToolRuntime', () => {
|
||||
throw new Error('permission hook broke')
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
|
||||
expect(result).toEqual({
|
||||
content: [{ type: 'text', text: 'Error: permission hook broke' }],
|
||||
@@ -1892,7 +1892,7 @@ describe('ToolRuntime', () => {
|
||||
throw new Error('post hook broke')
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
|
||||
expect(result).toEqual({
|
||||
content: [{ type: 'text', text: 'Error: post hook broke' }],
|
||||
@@ -1908,7 +1908,7 @@ describe('ToolRuntime', () => {
|
||||
throw new HarnessError('denied', 'DENIED')
|
||||
})
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
|
||||
|
||||
expect(result).toMatchObject({
|
||||
isError: true,
|
||||
@@ -2136,7 +2136,7 @@ describe('defineTool / schema DSL', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
name: 'typed-echo',
|
||||
arguments: { text: 'hello', uppercase: true },
|
||||
})
|
||||
@@ -2195,7 +2195,7 @@ describe('defineTool / schema DSL', () => {
|
||||
// Execution round-trip
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
name: 'roundtrip',
|
||||
arguments: { req: 'hello' },
|
||||
})
|
||||
@@ -2232,7 +2232,7 @@ describe('defineTool / schema DSL', () => {
|
||||
|
||||
const result = await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('c1'),
|
||||
callId: ToolCallId('c1'),
|
||||
name: 'raw-tool',
|
||||
arguments: { path: '/tmp' },
|
||||
})
|
||||
@@ -2409,7 +2409,7 @@ describe('schema DSL optional and nested contracts', () => {
|
||||
throw { message: 'denied by object' }
|
||||
},
|
||||
})
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'object-thrower', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'object-thrower', arguments: {} })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: denied by object' })
|
||||
})
|
||||
@@ -2424,7 +2424,7 @@ describe('schema DSL optional and nested contracts', () => {
|
||||
throw 'kaboom'
|
||||
},
|
||||
})
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'string-thrower', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'string-thrower', arguments: {} })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: kaboom' })
|
||||
})
|
||||
@@ -2439,7 +2439,7 @@ describe('schema DSL optional and nested contracts', () => {
|
||||
throw { code: 500 }
|
||||
},
|
||||
})
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'object-no-message', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'object-no-message', arguments: {} })
|
||||
expect(result.isError).toBe(true)
|
||||
const firstContent = result.content[0]!
|
||||
expect(firstContent.type).toBe('text')
|
||||
@@ -2578,7 +2578,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
|
||||
},
|
||||
}))
|
||||
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'reader', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'reader', arguments: {} })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.content[0]).toMatchObject({
|
||||
text: 'Error: invalid arguments: missing required property "path"',
|
||||
@@ -2595,7 +2595,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
|
||||
return [{ type: 'text', text: `read ${args.path}` }]
|
||||
},
|
||||
}))
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'reader', arguments: { path: '/x' } })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'reader', arguments: { path: '/x' } })
|
||||
expect(result).toEqual({
|
||||
content: [{ type: 'text', text: 'read /x' }],
|
||||
isError: false,
|
||||
@@ -2622,7 +2622,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
|
||||
return [{ type: 'text', text: args.path }]
|
||||
},
|
||||
}))
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'reader', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'reader', arguments: {} })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.error).toEqual({
|
||||
message: 'invalid arguments: missing required property "path"',
|
||||
@@ -2640,7 +2640,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
|
||||
throw new HarnessError('disk full', 'ENOSPC')
|
||||
},
|
||||
})
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'coded', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'coded', arguments: {} })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.error).toEqual({ message: 'disk full', info: { name: 'HarnessError', code: 'ENOSPC' } })
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: disk full' })
|
||||
@@ -2655,7 +2655,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
|
||||
throw new Error('just a message')
|
||||
},
|
||||
})
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'plain', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'plain', arguments: {} })
|
||||
expect(result.isError).toBe(true)
|
||||
expect(result.error).toEqual({ message: 'just a message' })
|
||||
expect(result.content[0]).toMatchObject({ text: 'Error: just a message' })
|
||||
@@ -2678,7 +2678,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
|
||||
})
|
||||
// Missing the "required" path — but raw tools validate their own input, so
|
||||
// this reaches execute rather than being rejected by the harness.
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'raw', arguments: {} })
|
||||
const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'raw', arguments: {} })
|
||||
expect(result.isError).toBe(false)
|
||||
})
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
|
||||
import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
|
||||
import {
|
||||
createUserMessage,
|
||||
CallId,
|
||||
ToolCallId,
|
||||
LlmAdapter,
|
||||
LlmError,
|
||||
resolveRetryPolicy,
|
||||
@@ -605,8 +605,8 @@ describe('dsh-agent-spine-demo bundle', () => {
|
||||
const execution: ToolExecution = {
|
||||
signal: testToolSignal,
|
||||
token: Symbol('agent-core-dsh-home-test') as ToolExecution['token'],
|
||||
callId: CallId('agent-core-dsh-home'),
|
||||
rootCallId: CallId('agent-core-dsh-home'),
|
||||
callId: ToolCallId('agent-core-dsh-home'),
|
||||
rootCallId: ToolCallId('agent-core-dsh-home'),
|
||||
name: 'bash',
|
||||
arguments: { command: 'true' },
|
||||
}
|
||||
@@ -685,7 +685,7 @@ describe('dsh-agent-spine-demo bundle', () => {
|
||||
const wait = vi.spyOn(ctx.jobs, 'wait')
|
||||
await ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('task-config-forwarding'),
|
||||
callId: ToolCallId('task-config-forwarding'),
|
||||
name: 'job_output',
|
||||
arguments: { job_id: id, wait: true },
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
|
||||
import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
|
||||
import * as FsPolicy from '@deepseek-ai/dsh-fs-observation-policy'
|
||||
import SandboxedFileSystem from '@deepseek-ai/dsh-fs-sandbox'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
|
||||
import { bwrapProfileArgs, seatbeltProfileArgs } from '@deepseek-ai/dsh-sandbox-local/src/profiles.ts'
|
||||
import SandboxPolicyService from '@deepseek-ai/dsh-sandbox-policy'
|
||||
@@ -85,22 +85,22 @@ describe('one-context multi-project sandbox', () => {
|
||||
const { active, agentA, agentB } = await agents()
|
||||
const [aOwn, bOwn, aCross, bCross] = await Promise.all([
|
||||
active.tools.execute({
|
||||
callId: CallId('bash-a-own'), name: 'bash', agent: agentA,
|
||||
callId: ToolCallId('bash-a-own'), name: 'bash', agent: agentA,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { command: 'printf a > a-owned.txt', description: 'Write project A marker' },
|
||||
}),
|
||||
active.tools.execute({
|
||||
callId: CallId('bash-b-own'), name: 'bash', agent: agentB,
|
||||
callId: ToolCallId('bash-b-own'), name: 'bash', agent: agentB,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { command: 'printf b > b-owned.txt', description: 'Write project B marker' },
|
||||
}),
|
||||
active.tools.execute({
|
||||
callId: CallId('bash-a-cross'), name: 'bash', agent: agentA,
|
||||
callId: ToolCallId('bash-a-cross'), name: 'bash', agent: agentA,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { command: `printf cross > ../${basename(projectB)}/from-a.txt`, description: 'Attempt project B write' },
|
||||
}),
|
||||
active.tools.execute({
|
||||
callId: CallId('bash-b-cross'), name: 'bash', agent: agentB,
|
||||
callId: ToolCallId('bash-b-cross'), name: 'bash', agent: agentB,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { command: `printf cross > ../${basename(projectA)}/from-b.txt`, description: 'Attempt project A write' },
|
||||
}),
|
||||
@@ -122,22 +122,22 @@ describe('one-context multi-project sandbox', () => {
|
||||
const { active, agentA, agentB } = await agents()
|
||||
const [aOwn, bOwn, aCross, bCross] = await Promise.all([
|
||||
active.tools.execute({
|
||||
callId: CallId('fs-a-own'), name: 'write', agent: agentA,
|
||||
callId: ToolCallId('fs-a-own'), name: 'write', agent: agentA,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { file_path: 'a-owned.txt', content: 'a' },
|
||||
}),
|
||||
active.tools.execute({
|
||||
callId: CallId('fs-b-own'), name: 'write', agent: agentB,
|
||||
callId: ToolCallId('fs-b-own'), name: 'write', agent: agentB,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { file_path: 'b-owned.txt', content: 'b' },
|
||||
}),
|
||||
active.tools.execute({
|
||||
callId: CallId('fs-a-cross'), name: 'write', agent: agentA,
|
||||
callId: ToolCallId('fs-a-cross'), name: 'write', agent: agentA,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { file_path: join(projectB, 'from-a.txt'), content: 'cross' },
|
||||
}),
|
||||
active.tools.execute({
|
||||
callId: CallId('fs-b-cross'), name: 'write', agent: agentB,
|
||||
callId: ToolCallId('fs-b-cross'), name: 'write', agent: agentB,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { file_path: join(projectA, 'from-b.txt'), content: 'cross' },
|
||||
}),
|
||||
@@ -171,22 +171,22 @@ describe('one-context multi-project sandbox', () => {
|
||||
|
||||
const [bashOwn, bashLexical, fsOwn, fsLexical] = await Promise.all([
|
||||
active.tools.execute({
|
||||
callId: CallId('bash-symlink-own'), name: 'bash', agent: handle.agent,
|
||||
callId: ToolCallId('bash-symlink-own'), name: 'bash', agent: handle.agent,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { command: 'printf bash > bash-owned.txt', description: 'Write physical workspace marker' },
|
||||
}),
|
||||
active.tools.execute({
|
||||
callId: CallId('bash-symlink-lexical'), name: 'bash', agent: handle.agent,
|
||||
callId: ToolCallId('bash-symlink-lexical'), name: 'bash', agent: handle.agent,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { command: `printf escaped > ${join(lexicalRoot, 'bash-escaped.txt')}`, description: 'Attempt lexical workspace write' },
|
||||
}),
|
||||
active.tools.execute({
|
||||
callId: CallId('fs-symlink-own'), name: 'write', agent: handle.agent,
|
||||
callId: ToolCallId('fs-symlink-own'), name: 'write', agent: handle.agent,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { file_path: 'fs-owned.txt', content: 'fs' },
|
||||
}),
|
||||
active.tools.execute({
|
||||
callId: CallId('fs-symlink-lexical'), name: 'write', agent: handle.agent,
|
||||
callId: ToolCallId('fs-symlink-lexical'), name: 'write', agent: handle.agent,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { file_path: join(lexicalRoot, 'fs-escaped.txt'), content: 'escaped' },
|
||||
}),
|
||||
@@ -222,12 +222,12 @@ describe('one-context multi-project sandbox', () => {
|
||||
|
||||
const [bashRead, fsRead] = await Promise.all([
|
||||
active.tools.execute({
|
||||
callId: CallId('bash-symlink-parent-read'), name: 'bash', agent: handle.agent,
|
||||
callId: ToolCallId('bash-symlink-parent-read'), name: 'bash', agent: handle.agent,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { command: 'cat ../shared.txt', description: 'Read through the physical parent' },
|
||||
}),
|
||||
active.tools.execute({
|
||||
callId: CallId('fs-symlink-parent-read'), name: 'read', agent: handle.agent,
|
||||
callId: ToolCallId('fs-symlink-parent-read'), name: 'read', agent: handle.agent,
|
||||
signal: new AbortController().signal,
|
||||
arguments: { file_path: '../shared.txt' },
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Context } from '@deepseek-ai/cordis'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import { scopeOf } from '@deepseek-ai/dsh-scope'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
@@ -68,7 +68,7 @@ function execute(
|
||||
signal: AbortSignal = SIGNAL,
|
||||
) {
|
||||
return ctx.tools.execute({
|
||||
callId: CallId(`team-call-${++callNumber}`),
|
||||
callId: ToolCallId(`team-call-${++callNumber}`),
|
||||
name,
|
||||
arguments: args,
|
||||
signal,
|
||||
|
||||
@@ -216,7 +216,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
|
||||
},
|
||||
],
|
||||
ownerProps: [
|
||||
'/** Stable owner currency delivered to one keyed Chat business renderer. */\nexport interface ChatNodeOwnerProps {\n /** Selected Tool call, when the shared details store names one. */\n selectedCallId?: CallId | undefined\n /** Session workspace root; Tool summaries display paths relative to it. */\n cwd?: string | undefined\n openFile: (path: string) => void\n inspectCall: (callId: CallId) => void\n forkAt: (seq: number) => void\n /** Render a historical image group through the attachment slot. */\n renderMessageImages: RenderMessageImages\n fileMentions: (owner: TurnTailOwnerProps) => MarkdownFileMentions | undefined\n}',
|
||||
'/** Stable owner currency delivered to one keyed Chat business renderer. */\nexport interface ChatNodeOwnerProps {\n /** Selected Tool call, when the shared details store names one. */\n selectedCallId?: ToolCallId | undefined\n /** Session workspace root; Tool summaries display paths relative to it. */\n cwd?: string | undefined\n openFile: (path: string) => void\n inspectCall: (callId: ToolCallId) => void\n forkAt: (seq: number) => void\n /** Render a historical image group through the attachment slot. */\n renderMessageImages: RenderMessageImages\n fileMentions: (owner: TurnTailOwnerProps) => MarkdownFileMentions | undefined\n}',
|
||||
],
|
||||
ownerPropsReferences: [
|
||||
'MarkdownFileMentions',
|
||||
@@ -1119,7 +1119,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
|
||||
},
|
||||
],
|
||||
ownerProps: [
|
||||
'/**\n * View-slot owner share: the cross-view inspect handoff (otherwise views need\n * nothing from the render site — sessionId and the snapshot hook arrive as\n * framework-standard props; tool rows go through each view\'s own declared\n * toolview hole).\n */\nexport interface ConvViewOwnerProps {\n /** One-shot inspect request from another view (chat\'s Inspect button); null when idle. */\n inspect?: { callId: CallId } | null\n /** Acknowledge the inspect request once applied (clears the store field). */\n onInspectDone?: () => void\n}',
|
||||
'/**\n * View-slot owner share: the cross-view inspect handoff (otherwise views need\n * nothing from the render site — sessionId and the snapshot hook arrive as\n * framework-standard props; tool rows go through each view\'s own declared\n * toolview hole).\n */\nexport interface ConvViewOwnerProps {\n /** One-shot inspect request from another view (chat\'s Inspect button); null when idle. */\n inspect?: { callId: ToolCallId } | null\n /** Acknowledge the inspect request once applied (clears the store field). */\n onInspectDone?: () => void\n}',
|
||||
],
|
||||
ownerPropsReferences: [],
|
||||
standardProps: [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import Timer from '@deepseek-ai/cordis-plugin-timer'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRegistry from '@deepseek-ai/dsh-tools'
|
||||
import type { ToolDefinition, ToolExecutionResult } from '@deepseek-ai/dsh-tools'
|
||||
@@ -147,7 +147,7 @@ let callCounter = 0
|
||||
export function call(ctx: Context, name: string, args: unknown): Promise<ToolExecutionResult> {
|
||||
return ctx.tools.execute({
|
||||
signal: new AbortController().signal,
|
||||
callId: CallId(`call-${++callCounter}`),
|
||||
callId: ToolCallId(`call-${++callCounter}`),
|
||||
name,
|
||||
arguments: args,
|
||||
})
|
||||
|
||||
@@ -2894,7 +2894,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'ApprovalRequest',
|
||||
declaration: 'export interface ApprovalRequest {\n readonly agent: Agent;\n readonly toolName: string;\n readonly callId?: CallId;\n readonly reason?: string;\n readonly signal?: AbortSignal;\n}',
|
||||
declaration: 'export interface ApprovalRequest {\n readonly agent: Agent;\n readonly toolName: string;\n readonly callId?: ToolCallId;\n readonly reason?: string;\n readonly signal?: AbortSignal;\n}',
|
||||
},
|
||||
{
|
||||
name: 'ApprovalService',
|
||||
@@ -3038,7 +3038,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'CodeDispatchLog',
|
||||
declaration: 'export interface CodeDispatchLog {\n readonly exec: ToolExecution;\n readonly agent?: Agent;\n readonly subCallId: CallId;\n readonly name: string;\n readonly isError: boolean;\n readonly content: ContentBlock[];\n}',
|
||||
declaration: 'export interface CodeDispatchLog {\n readonly exec: ToolExecution;\n readonly agent?: Agent;\n readonly subCallId: ToolCallId;\n readonly name: string;\n readonly isError: boolean;\n readonly content: ContentBlock[];\n}',
|
||||
},
|
||||
{
|
||||
name: 'CodeJsonValue',
|
||||
@@ -3874,7 +3874,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'PrunedEntry',
|
||||
declaration: 'export interface PrunedEntry {\n readonly originalSeq: number;\n readonly replacementSeq: number;\n readonly callId: CallId;\n readonly charsBefore: number;\n readonly charsAfter: number;\n}',
|
||||
declaration: 'export interface PrunedEntry {\n readonly originalSeq: number;\n readonly replacementSeq: number;\n readonly callId: ToolCallId;\n readonly charsBefore: number;\n readonly charsAfter: number;\n}',
|
||||
},
|
||||
{
|
||||
name: 'PruneResult',
|
||||
@@ -4066,7 +4066,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'SessionEventMap',
|
||||
declaration: 'export interface SessionEventMap {\n \'turn/start\': {\n turn: number;\n };\n \'turn/end\': {\n turn: number;\n reason: TurnEndReason;\n };\n \'step/start\': {\n turn: number;\n step: number;\n };\n \'step/end\': {\n turn: number;\n step: number;\n };\n \'user/message\': UserMessage;\n \'assistant/chunk\': {\n turn: number;\n step: number;\n chunk: StreamChunk;\n };\n \'assistant/message\': {\n turn: number;\n step: number;\n message: AssistantMessage;\n usage?: TokenUsage;\n interrupted?: true;\n };\n \'tool/call\': {\n turn: number;\n step: number;\n callId: CallId;\n name: string;\n arguments: string;\n };\n \'tool/result\': {\n turn: number;\n step: number;\n message: ToolResultMessage;\n error?: {\n name: string;\n code: string;\n };\n meta?: JsonValue;\n };\n \'todo/write\': {\n todos: TodoItem[];\n };\n \'request/header\': {\n header: EpochHeader;\n reason: RequestHeaderReason;\n };\n \'request/context\': RequestContext;\n \'session/end-seed\': Record<string, never>;\n}',
|
||||
declaration: 'export interface SessionEventMap {\n \'turn/start\': {\n turn: number;\n };\n \'turn/end\': {\n turn: number;\n reason: TurnEndReason;\n };\n \'step/start\': {\n turn: number;\n step: number;\n };\n \'step/end\': {\n turn: number;\n step: number;\n };\n \'user/message\': UserMessage;\n \'assistant/chunk\': {\n turn: number;\n step: number;\n chunk: StreamChunk;\n };\n \'assistant/message\': {\n turn: number;\n step: number;\n message: AssistantMessage;\n usage?: TokenUsage;\n interrupted?: true;\n };\n \'tool/call\': {\n turn: number;\n step: number;\n callId: ToolCallId;\n name: string;\n arguments: string;\n };\n \'tool/result\': {\n turn: number;\n step: number;\n message: ToolResultMessage;\n error?: {\n name: string;\n code: string;\n };\n meta?: JsonValue;\n };\n \'todo/write\': {\n todos: TodoItem[];\n };\n \'request/header\': {\n header: EpochHeader;\n reason: RequestHeaderReason;\n };\n \'request/context\': RequestContext;\n \'session/end-seed\': Record<string, never>;\n}',
|
||||
},
|
||||
{
|
||||
name: 'SessionEventMetadataFilter',
|
||||
@@ -4426,7 +4426,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'SpillSource',
|
||||
declaration: 'export interface SpillSource {\n toolName: string;\n callId: CallId;\n label: string;\n}',
|
||||
declaration: 'export interface SpillSource {\n toolName: string;\n callId: ToolCallId;\n label: string;\n}',
|
||||
},
|
||||
{
|
||||
name: 'StorageBackend',
|
||||
@@ -4442,7 +4442,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'StreamChunk',
|
||||
declaration: 'export type StreamChunk = {\n type: \'block-start\';\n index: number;\n blockType: ContentBlockType;\n} | {\n type: \'text-delta\';\n index: number;\n text: string;\n} | {\n type: \'reasoning-delta\';\n index: number;\n text: string;\n} | {\n type: \'tool-call-delta\';\n index: number;\n id: CallId;\n name?: string;\n argumentsDelta: string;\n} | {\n type: \'block-end\';\n index: number;\n block: ContentBlock;\n} | {\n type: \'usage\';\n usage: TokenUsage;\n} | {\n type: \'finish\';\n reason: FinishReason;\n replayState?: ReplayEnvelope;\n};',
|
||||
declaration: 'export type StreamChunk = {\n type: \'block-start\';\n index: number;\n blockType: ContentBlockType;\n} | {\n type: \'text-delta\';\n index: number;\n text: string;\n} | {\n type: \'reasoning-delta\';\n index: number;\n text: string;\n} | {\n type: \'tool-call-delta\';\n index: number;\n id: ToolCallId;\n name?: string;\n argumentsDelta: string;\n} | {\n type: \'block-end\';\n index: number;\n block: ContentBlock;\n} | {\n type: \'usage\';\n usage: TokenUsage;\n} | {\n type: \'finish\';\n reason: FinishReason;\n replayState?: ReplayEnvelope;\n};',
|
||||
},
|
||||
{
|
||||
name: 'SubagentCapabilities',
|
||||
@@ -4750,7 +4750,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'ToolExecution',
|
||||
declaration: 'export interface ToolExecution extends ToolExecutionInput {\n readonly rootCallId: CallId;\n readonly token: ToolExecutionToken;\n}',
|
||||
declaration: 'export interface ToolExecution extends ToolExecutionInput {\n readonly rootCallId: ToolCallId;\n readonly token: ToolExecutionToken;\n}',
|
||||
},
|
||||
{
|
||||
name: 'ToolExecutionFailure',
|
||||
@@ -4758,7 +4758,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'ToolExecutionInput',
|
||||
declaration: 'export interface ToolExecutionInput {\n readonly callId: CallId;\n readonly rootCallId?: CallId;\n readonly name: string;\n readonly arguments: unknown;\n readonly agent?: Agent;\n readonly parent?: ToolExecutionToken;\n readonly signal: AbortSignal;\n}',
|
||||
declaration: 'export interface ToolExecutionInput {\n readonly callId: ToolCallId;\n readonly rootCallId?: ToolCallId;\n readonly name: string;\n readonly arguments: unknown;\n readonly agent?: Agent;\n readonly parent?: ToolExecutionToken;\n readonly signal: AbortSignal;\n}',
|
||||
},
|
||||
{
|
||||
name: 'ToolExecutionMode',
|
||||
@@ -4786,7 +4786,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'ToolMessageSource',
|
||||
declaration: 'export interface ToolMessageSource {\n kind: \'tool\';\n callId: CallId;\n}',
|
||||
declaration: 'export interface ToolMessageSource {\n kind: \'tool\';\n callId: ToolCallId;\n}',
|
||||
},
|
||||
{
|
||||
name: 'ToolOutputDefinition',
|
||||
@@ -4810,7 +4810,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'ToolResultBlock',
|
||||
declaration: 'export interface ToolResultBlock {\n type: \'tool-result\';\n toolCallId: CallId;\n content: ContentBlock[];\n isError?: boolean;\n}',
|
||||
declaration: 'export interface ToolResultBlock {\n type: \'tool-result\';\n toolCallId: ToolCallId;\n content: ContentBlock[];\n isError?: boolean;\n}',
|
||||
},
|
||||
{
|
||||
name: 'ToolResultMessage',
|
||||
|
||||
@@ -15,7 +15,7 @@ import { mkdir, mkdtemp, rm, utimes, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRuntime, { TOOL_ABORTED_BEFORE_DISPATCH } from '@deepseek-ai/dsh-tools'
|
||||
import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
|
||||
@@ -30,7 +30,7 @@ let callCounter = 0
|
||||
function call(name: string, args: unknown, agentObj?: object) {
|
||||
return ctx.tools.execute({
|
||||
signal: testToolSignal,
|
||||
callId: CallId(`it-${++callCounter}`),
|
||||
callId: ToolCallId(`it-${++callCounter}`),
|
||||
name,
|
||||
arguments: args,
|
||||
...agentObj ? { agent: agentObj as never } : {},
|
||||
@@ -180,7 +180,7 @@ describe('search tools over the real subprocess service + the packaged rg', () =
|
||||
const controller = new AbortController()
|
||||
controller.abort()
|
||||
const result = await ctx.tools.execute({
|
||||
callId: CallId(`it-${++callCounter}`),
|
||||
callId: ToolCallId(`it-${++callCounter}`),
|
||||
name: 'grep',
|
||||
arguments: { pattern: 'x' },
|
||||
signal: controller.signal,
|
||||
|
||||
Loaded 100 of 186 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user