test(agent): migrate remaining model selection fixtures
This commit is contained in:
1 parent
76acba476b
commit
e7dd79a476
17 files changed
+99
-56
No files matched your search
@@ -5,7 +5,7 @@ import { createUserMessage, createSystemMessage, CONTEXT_WINDOW_EXCEEDED_CODE, L
|
||||
import type { ContentBlock, GenerateOptions, LlmResolvedModelInfo, ResolvedRetryPolicy, StreamChunk } 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 { recordModelSelection, 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 { BasicCompactionEngine } from '@deepseek-ai/dsh-compaction-basic'
|
||||
@@ -205,16 +205,14 @@ function overflowHistorySeed(): readonly SessionEvent[] {
|
||||
}
|
||||
|
||||
describe('CBR-001: a real-loop checkpoint is a valid boundary on both sides', () => {
|
||||
it('uses the model actually routed by agent/request for post-step pressure', async () => {
|
||||
it('uses the recorded model selection for post-step pressure', async () => {
|
||||
const { ctx } = await harness(8)
|
||||
ctx.on('agent/request', async (_payload, next) => ({
|
||||
...await next(), provider: 'mock', model: 'mock',
|
||||
}))
|
||||
try {
|
||||
const agent = await ctx.agentLoop.create(SessionId('routed-pressure'), {
|
||||
provider: 'unconfigured-agent-fallback',
|
||||
model: 'unconfigured-agent-fallback',
|
||||
})
|
||||
recordModelSelection(ctx.sessionProjections, agent, { provider: 'mock', model: 'mock' })
|
||||
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'do a routed multi-step task' }], source: { kind: 'user' } }))
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
@@ -380,9 +378,6 @@ describe('context-overflow recovery across the real loop and compaction-basic',
|
||||
await ctx.plugin(AgentLoop, { agents: [] })
|
||||
await ctx.plugin(TokenMeter)
|
||||
ctx.llm.registerAdapter(['mock'], adapter)
|
||||
ctx.on('agent/request', async (_payload, next) => ({
|
||||
...await next(), provider: 'mock', model: 'mock',
|
||||
}))
|
||||
await ctx.plugin(BasicCompactionEngine, {
|
||||
headroomTokens: 0,
|
||||
thresholdRatio: 1,
|
||||
@@ -402,6 +397,7 @@ describe('context-overflow recovery across the real loop and compaction-basic',
|
||||
},
|
||||
})
|
||||
|
||||
recordModelSelection(ctx.sessionProjections, agent, { provider: 'mock', model: 'mock' })
|
||||
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'continue from history' }], source: { kind: 'user' } }))
|
||||
await agent.whenIdle()
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ async function harness(): Promise<Context> {
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SystemPrompt, { personaPrefix: '' })
|
||||
await ctx.plugin(ToolRegistry)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalFileSystem)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(WorkingDirectory)
|
||||
return ctx
|
||||
}
|
||||
@@ -64,6 +64,7 @@ describe('LocalFileReferenceService', () => {
|
||||
it('defers guidance until optional prompt and tool services become available', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
ctx.provide('workingDirectory', { ensure: vi.fn<WorkingDirectory['ensure']>() })
|
||||
await ctx.plugin(LocalFileReferenceService)
|
||||
|
||||
@@ -15,6 +15,7 @@ import Llm, { LlmAdapter, ToolCallId, createUserMessage } from '@deepseek-ai/dsh
|
||||
import type { GenerateOptions, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import Sessions, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import * as JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import Agents from '@deepseek-ai/dsh-agent'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
@@ -148,6 +149,7 @@ it('waits for SystemPrompt and ToolRuntime before reserving the provider', async
|
||||
const ctx = new Context()
|
||||
contexts.push(ctx)
|
||||
await ctx.plugin(BrowserUse)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(Agents)
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
const tools = ctx.plugin(Tools)
|
||||
|
||||
@@ -6,6 +6,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import Include from '@deepseek-ai/cordis-plugin-include'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import type { Agent, AgentStatus } from '@deepseek-ai/dsh-agent'
|
||||
import CommandRuntime from '@deepseek-ai/dsh-commands'
|
||||
@@ -56,6 +57,7 @@ describe('/feedback real Loader composition through cordis.yml', () => {
|
||||
vi.stubEnv('DSH_HOME', root)
|
||||
const configPath = join(root, 'cordis.yml')
|
||||
await writeFile(configPath, [
|
||||
"- name: '@deepseek-ai/dsh-session-projection'",
|
||||
"- name: '@deepseek-ai/dsh-agent'",
|
||||
"- name: '@deepseek-ai/dsh-session'",
|
||||
"- name: '@deepseek-ai/dsh-commands'",
|
||||
@@ -68,6 +70,7 @@ describe('/feedback real Loader composition through cordis.yml', () => {
|
||||
await context.plugin(Loader)
|
||||
context.loader.builtins.include = Include
|
||||
const modules = new Map<string, unknown>([
|
||||
['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry],
|
||||
['@deepseek-ai/dsh-agent', AgentRegistry],
|
||||
['@deepseek-ai/dsh-session', SessionStore],
|
||||
['@deepseek-ai/dsh-commands', CommandRuntime],
|
||||
|
||||
@@ -76,13 +76,11 @@ async function setup(
|
||||
contexts.push(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
if (options.sandboxMode === undefined) {
|
||||
await ctx.plugin(LocalFileSystem, { cwd: root })
|
||||
} else {
|
||||
// SandboxPolicy declares the registry as a required injection; mount it
|
||||
// before the policy activates.
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(SandboxPolicy, { mode: options.sandboxMode, workspaceRoot: root })
|
||||
await ctx.plugin(SandboxedFileSystem, { cwd: root })
|
||||
}
|
||||
@@ -616,6 +614,7 @@ describe('tool-str-replace-editor', () => {
|
||||
contexts.push(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalFileSystem, { cwd: root })
|
||||
Object.defineProperty(ctx.fs, 'sandboxMode', { value: 'read-only' })
|
||||
|
||||
@@ -198,6 +198,7 @@ describe('UserQuestionService', () => {
|
||||
|
||||
it('rejects a live runtime-owned agent before reaching the provider', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(UserQuestionService)
|
||||
const p = { ask: vi.fn(async () => ({ answers: [] })) }
|
||||
@@ -220,6 +221,7 @@ describe('UserQuestionService', () => {
|
||||
|
||||
it('reaches the provider for a lineage-bearing session resumed as a runtime root', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(UserQuestionService)
|
||||
const p = provider('yes')
|
||||
@@ -250,6 +252,7 @@ describe('UserQuestionService', () => {
|
||||
|
||||
it('rejects a stale agent object that reuses a live id', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(UserQuestionService)
|
||||
const p = { ask: vi.fn(async () => ({ answers: [] })) }
|
||||
@@ -389,8 +392,8 @@ function liveAgent(id: string): LiveAgent {
|
||||
|
||||
async function timedContext(): Promise<Context> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(UserQuestionService)
|
||||
return ctx
|
||||
}
|
||||
@@ -832,16 +835,15 @@ describe('late replies', () => {
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
it('answers nothing when the composition carries no Session projections', async () => {
|
||||
it('rejects a late reply without a live Agent registry', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(UserQuestionService)
|
||||
const agent = liveAgent('late-no-projections')
|
||||
ctx.agents.enter(agent, undefined)
|
||||
askInLog(agent, timedCallId)
|
||||
continueInLog(agent, timedCallId)
|
||||
|
||||
expect(ctx.userQuestions.answer(agent, timedCallId, { answers: [] })).toBe(false)
|
||||
expect(() => ctx.userQuestions.answer(agent, timedCallId, { answers: [] }))
|
||||
.toThrow(expect.objectContaining({ code: 'CALLER_NOT_LIVE' }))
|
||||
expect(agent.steer).not.toHaveBeenCalled()
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ import { afterEach, expect, it, vi } from 'vitest'
|
||||
import { installAccountTaskCancellation, type DeepSeekAccount } from '@deepseek-ai/dsh-deepseek-account'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import LlmRuntime, { createUserMessage, LlmAdapter, type GenerateOptions, type StreamChunk, ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
|
||||
import AgentRegistry, { recordModelSelection, type Agent } from '@deepseek-ai/dsh-agent'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
@@ -95,7 +95,7 @@ it.each(['deepseek-account', 'deepseek-official'])('cancels only the active acco
|
||||
? { kind: 'hook', reason: 'deepseek-account/signed-out' } : { kind: 'user' } } })
|
||||
})
|
||||
|
||||
it.each(['tool', 'retry'])('replaces the active provider only after the next request binds: %s', async (transition) => {
|
||||
it.each(['tool', 'retry'])('keeps the active provider until a later step binds a new selection: %s', async (transition) => {
|
||||
const ctx = await harness()
|
||||
const preparing = Promise.withResolvers<undefined>()
|
||||
const proceed = Promise.withResolvers<undefined>()
|
||||
@@ -106,11 +106,18 @@ it.each(['tool', 'retry'])('replaces the active provider only after the next req
|
||||
if (++requests === 1) return config
|
||||
preparing.resolve(undefined)
|
||||
await proceed.promise
|
||||
return { ...config, provider: 'deepseek-official' }
|
||||
return config
|
||||
})
|
||||
const selectOfficial = (agent: Agent) => {
|
||||
recordModelSelection(ctx.sessionProjections, agent, { provider: 'deepseek-official', model: 'deepseek-v4-flash' })
|
||||
}
|
||||
ctx.on('agent/request-error', async ({ agent }, next) => {
|
||||
await next()
|
||||
selectOfficial(agent)
|
||||
return { kind: 'retry' }
|
||||
})
|
||||
ctx.on('agent/request-error', async (_event, next) => { await next(); return { kind: 'retry' } })
|
||||
ctx.on('llm/stream', async function* (request) {
|
||||
if (request.provider === 'deepseek-account') {
|
||||
if (requests === 1) {
|
||||
if (transition === 'retry') {
|
||||
yield { type: 'finish', reason: { kind: 'error', failure: { code: 'TRANSPORT', message: 'retry fixture' } } }
|
||||
return
|
||||
@@ -122,22 +129,29 @@ it.each(['tool', 'retry'])('replaces the active provider only after the next req
|
||||
}
|
||||
const signal = request.signal!
|
||||
streaming.resolve(signal)
|
||||
await new Promise<void>((resolve) => { signal.addEventListener('abort', () => { resolve() }, { once: true }) })
|
||||
if (!signal.aborted) await new Promise<void>((resolve) => { signal.addEventListener('abort', () => { resolve() }, { once: true }) })
|
||||
signal.throwIfAborted()
|
||||
})
|
||||
ctx.tools.register(defineContentToolFixture({ name: 'continue', description: '', parameters: {}, execute: async () => [{ type: 'text', text: 'done' }] }))
|
||||
ctx.tools.register(defineContentToolFixture({ name: 'continue', description: '', parameters: {}, execute: async () => {
|
||||
selectOfficial(agent)
|
||||
return [{ type: 'text', text: 'done' }]
|
||||
} }))
|
||||
const agent = await ctx.agentLoop.create(SessionId('switch-provider'), { provider: 'deepseek-account', model: 'deepseek-v4-flash' })
|
||||
expect(agent.session.requestContext()?.provider).toBeUndefined()
|
||||
send(agent)
|
||||
await preparing.promise
|
||||
expect(agent.session.requestContext()?.provider).toBe('deepseek-account')
|
||||
proceed.resolve(undefined)
|
||||
const signal = await streaming.promise
|
||||
expect(agent.session.requestContext()?.provider).toBe('deepseek-official')
|
||||
ctx.emit('deepseek-account/signed-out')
|
||||
expect(signal.aborted).toBe(false)
|
||||
agent.cancel({ kind: 'user' })
|
||||
await agent.whenIdle()
|
||||
try {
|
||||
send(agent)
|
||||
await preparing.promise
|
||||
expect(agent.session.requestContext()?.provider).toBe('deepseek-account')
|
||||
proceed.resolve(undefined)
|
||||
const signal = await streaming.promise
|
||||
expect(agent.session.requestContext()?.provider).toBe(transition === 'tool' ? 'deepseek-official' : 'deepseek-account')
|
||||
ctx.emit('deepseek-account/signed-out')
|
||||
expect(signal.aborted).toBe(transition === 'retry')
|
||||
} finally {
|
||||
proceed.resolve(undefined)
|
||||
agent.cancel({ kind: 'user' })
|
||||
await agent.whenIdle()
|
||||
}
|
||||
expect(agent.status).toBe('idle')
|
||||
})
|
||||
|
||||
@@ -230,6 +244,7 @@ it('cancels an account child without stopping its API-key parent', async () => {
|
||||
})
|
||||
|
||||
it('lists each DeepSeek route only when its own credential is present', async () => {
|
||||
vi.stubEnv('DEEPSEEK_BASE_URL', 'https://api.deepseek.com')
|
||||
vi.stubEnv('DEEPSEEK_API_KEY', '')
|
||||
vi.stubGlobal('fetch', vi.fn((input: string | URL | Request) => Promise.resolve(listingRequest(input) ?? new Response(null, { status: 500 }))))
|
||||
const ctx = await harness()
|
||||
@@ -264,13 +279,14 @@ it('ignores idle account history but uses the last bound route during a new turn
|
||||
const cancel = vi.spyOn(agent, 'cancel')
|
||||
ctx.emit('deepseek-account/signed-out')
|
||||
expect(cancel).not.toHaveBeenCalled()
|
||||
recordModelSelection(ctx.sessionProjections, agent, { provider: 'deepseek-official', model: 'deepseek-v4-flash' })
|
||||
const preparing = Promise.withResolvers<undefined>()
|
||||
const proceed = Promise.withResolvers<undefined>()
|
||||
ctx.on('agent/request', async (_event, next) => {
|
||||
const config = await next()
|
||||
preparing.resolve(undefined)
|
||||
await proceed.promise
|
||||
return { ...config, provider: 'deepseek-official' }
|
||||
return config
|
||||
})
|
||||
try {
|
||||
send(agent)
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Context } from '@deepseek-ai/cordis'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import Include from '@deepseek-ai/cordis-plugin-include'
|
||||
import LlmRuntime from '@deepseek-ai/dsh-llm'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
||||
@@ -58,6 +59,8 @@ async function loadComposition(
|
||||
" name: '@deepseek-ai/dsh-llm'",
|
||||
'- id: session',
|
||||
" name: '@deepseek-ai/dsh-session'",
|
||||
'- id: session-projection',
|
||||
" name: '@deepseek-ai/dsh-session-projection'",
|
||||
'- id: agents',
|
||||
" name: '@deepseek-ai/dsh-agent'",
|
||||
'- id: deepseek-llm-api-extensions',
|
||||
@@ -93,6 +96,7 @@ async function loadComposition(
|
||||
const modules = new Map<string, unknown>([
|
||||
['@deepseek-ai/dsh-llm', LlmRuntime],
|
||||
['@deepseek-ai/dsh-session', SessionStore],
|
||||
['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry],
|
||||
['@deepseek-ai/dsh-agent', AgentRegistry],
|
||||
['@deepseek-ai/dsh-deepseek-llm-api-extensions', DeepSeekLlmApiExtensionRegistry],
|
||||
['@deepseek-ai/dsh-session-log-deepseek', SessionLogDeepSeek],
|
||||
|
||||
@@ -17,7 +17,7 @@ import type { SessionEvent, SessionEventMap } from '@deepseek-ai/dsh-session'
|
||||
import type { LlmRetryEventData } from '@deepseek-ai/dsh-llm-retry/types'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import AgentRegistry, { recordModelSelection } from '@deepseek-ai/dsh-agent'
|
||||
import type { Agent, RequestErrorAction } from '@deepseek-ai/dsh-agent'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
import * as retry from '../src/index.ts'
|
||||
@@ -524,7 +524,7 @@ describe('provider-routed retry policy', () => {
|
||||
expect(adapter.requests.map(request => request.provider)).toEqual(['mock', 'other', 'other'])
|
||||
})
|
||||
|
||||
it('selects an always policy from the provider chosen by agent/request', async () => {
|
||||
it('selects an always policy from the recorded provider', async () => {
|
||||
vi.useFakeTimers()
|
||||
const adapter = new ScriptedAdapter([
|
||||
new LlmError('rerouted auth failed', 'AUTH'),
|
||||
@@ -532,16 +532,12 @@ describe('provider-routed retry policy', () => {
|
||||
])
|
||||
;({ ctx: context } = await harness(adapter, {
|
||||
other: alwaysConfig({ initialDelayMs: 1, maxDelayMs: 1, jitterRatio: 0 }),
|
||||
}, (ctx) => {
|
||||
ctx.on('agent/request', async (_payload, next) => ({
|
||||
...await next(),
|
||||
provider: 'other',
|
||||
}))
|
||||
}))
|
||||
const agent = await context.agentLoop.create(SessionId('retry-provider-rerouted'), {
|
||||
provider: 'mock',
|
||||
model: 'mock',
|
||||
})
|
||||
recordModelSelection(context.sessionProjections, agent, { provider: 'other', model: 'mock' })
|
||||
const scheduled = waitForRetry(context, agent, 1)
|
||||
|
||||
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'reroute' }], source: { kind: 'user' } }))
|
||||
@@ -553,10 +549,15 @@ describe('provider-routed retry policy', () => {
|
||||
expect(adapter.requests.map(request => request.provider)).toEqual(['other', 'other'])
|
||||
})
|
||||
|
||||
it('keeps finite retry budgets scoped to the failed provider', async () => {
|
||||
it('starts a fresh retry budget when the next step selects another provider', async () => {
|
||||
vi.useFakeTimers()
|
||||
const adapter = new ScriptedAdapter([
|
||||
new LlmError('mock failed', 'SERVER'),
|
||||
[
|
||||
{ type: 'block-start', index: 0, blockType: 'tool-call' },
|
||||
{ type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('switch'), name: 'switch_provider', arguments: '{}' } },
|
||||
{ type: 'finish', reason: { kind: 'tool-calls' } },
|
||||
],
|
||||
new LlmError('other failed', 'SERVER'),
|
||||
textResponse('other recovered'),
|
||||
])
|
||||
@@ -569,16 +570,18 @@ describe('provider-routed retry policy', () => {
|
||||
maxRetries: 1,
|
||||
backoff: { initialDelayMs: 1, maxDelayMs: 1 },
|
||||
}),
|
||||
}, (ctx) => {
|
||||
ctx.on('agent/request', async (_payload, next) => ({
|
||||
...await next(),
|
||||
provider: adapter.requests.length === 0 ? 'mock' : 'other',
|
||||
}))
|
||||
}))
|
||||
const agent = await context.agentLoop.create(SessionId('retry-provider-budgets'), {
|
||||
provider: 'mock',
|
||||
model: 'mock',
|
||||
})
|
||||
context.tools.register(defineContentToolFixture({
|
||||
name: 'switch_provider', description: '', parameters: {},
|
||||
execute: async () => {
|
||||
recordModelSelection(agent.ctx.sessionProjections, agent, { provider: 'other', model: 'mock' })
|
||||
return [{ type: 'text', text: 'switched' }]
|
||||
},
|
||||
}))
|
||||
const idle = waitForIdle(context, agent)
|
||||
|
||||
agent.followup(createUserMessage({
|
||||
@@ -588,7 +591,7 @@ describe('provider-routed retry policy', () => {
|
||||
await vi.runAllTimersAsync()
|
||||
await idle
|
||||
|
||||
expect(adapter.requests.map(request => request.provider)).toEqual(['mock', 'other', 'other'])
|
||||
expect(adapter.requests.map(request => request.provider)).toEqual(['mock', 'mock', 'other', 'other'])
|
||||
expect(agent.session.snapshotEvents().filter(event => event.type === 'llm/retry').map(event => ({
|
||||
provider: event.data.provider,
|
||||
retry: event.data.retry,
|
||||
|
||||
@@ -48,8 +48,8 @@ async function harness(
|
||||
await ctx.plugin(Loader)
|
||||
if (packageService) await ctx.plugin(PluginPackages)
|
||||
ctx.loader.builtins.include = Include
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(AgentPresets, { default: 'fixture' })
|
||||
await ctx.plugin(DeepSeekLlmApiExtensionRegistry)
|
||||
const inventory = enabled === undefined
|
||||
@@ -260,6 +260,7 @@ describe('DeepSeek plugin package inventory', () => {
|
||||
const ctx = new Context()
|
||||
contexts.push(ctx)
|
||||
await ctx.plugin(Loader)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(DeepSeekLlmApiExtensionRegistry)
|
||||
await ctx.plugin(PluginInventory)
|
||||
|
||||
+3
@@ -7,6 +7,7 @@ import { afterEach, expect, it } from 'vitest'
|
||||
import { Context, FiberState } from '@deepseek-ai/cordis'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import Include from '@deepseek-ai/cordis-plugin-include'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import DeepSeekLlmApiExtensionRegistry from '@deepseek-ai/dsh-deepseek-llm-api-extensions'
|
||||
import * as PluginInventory from '../src/index.ts'
|
||||
@@ -40,6 +41,7 @@ it.each([true, false])('reports an unversioned package by name alongside a versi
|
||||
const siblingUrl = pathToFileURL(join(siblingDir, 'plugin.js')).href
|
||||
const configPath = join(root, 'cordis.yml')
|
||||
await writeFile(configPath, [
|
||||
'- name: cordis:session-projections',
|
||||
'- name: cordis:agents',
|
||||
'- name: cordis:deepseek-extensions',
|
||||
'- name: cordis:plugin-inventory',
|
||||
@@ -53,6 +55,7 @@ it.each([true, false])('reports an unversioned package by name alongside a versi
|
||||
ctx.baseUrl = pathToFileURL(configPath).href
|
||||
await ctx.plugin(Loader)
|
||||
ctx.loader.builtins.include = Include
|
||||
ctx.loader.builtins['session-projections'] = SessionProjectionRegistry
|
||||
ctx.loader.builtins.agents = AgentRegistry
|
||||
ctx.loader.builtins['deepseek-extensions'] = DeepSeekLlmApiExtensionRegistry
|
||||
ctx.loader.builtins['plugin-inventory'] = PluginInventory
|
||||
|
||||
@@ -51,6 +51,7 @@ async function setup() {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalSubprocessRuntime)
|
||||
;(ctx.subprocess as LocalSubprocessRuntime).internals = { spillDir }
|
||||
@@ -66,6 +67,7 @@ async function setupWithJobs() {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(ToolJobs)
|
||||
@@ -214,10 +216,10 @@ async function setupSandboxed(withApproval = false) {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(ToolJobs)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
ctx.sessionProjections.register(turnBoundaryProjectionDefinition)
|
||||
await ctx.plugin(SandboxPolicyService, {})
|
||||
await ctx.plugin(RecordingSandboxExecutor)
|
||||
@@ -507,6 +509,7 @@ describe('bash tool', () => {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(LocalSubprocessRuntime)
|
||||
@@ -526,6 +529,7 @@ describe('the background surface follows the job registry', () => {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalSubprocessRuntime)
|
||||
;(ctx.subprocess as LocalSubprocessRuntime).internals = { spillDir }
|
||||
@@ -637,6 +641,7 @@ describe('background execution through the job runtime', () => {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(ToolJobs)
|
||||
@@ -667,6 +672,7 @@ describe('background execution through the job runtime', () => {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(CountingStartExecutor)
|
||||
@@ -1337,6 +1343,7 @@ describe('the model-facing bash tool builds its request from named args only (no
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(ToolJobs)
|
||||
|
||||
@@ -152,6 +152,7 @@ async function setup(toolConfig: Partial<ToolPwsh.Config> = {}, dshHome?: string
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(BashEnvPlugin, dshHome === undefined ? {} : { dshHome })
|
||||
await ctx.plugin(FakeBash)
|
||||
@@ -166,6 +167,7 @@ async function setupWithJobs(toolConfig: Partial<ToolPwsh.Config> = {}, dshHome?
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(ToolJobs)
|
||||
@@ -227,11 +229,11 @@ async function setupSandboxed(withApproval = false) {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(ToolJobs)
|
||||
await ctx.plugin(BashEnvPlugin)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
// The loop's turnBoundary unit (the open-turn fold) is not mounted in this
|
||||
// bench — the loop itself is not composed. Register its open-turn fold so
|
||||
// the approval service's turn-enclosure gate reads the seeded log shape.
|
||||
@@ -618,6 +620,7 @@ describe('per-call sandbox policy resolution', () => {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(BashEnvPlugin)
|
||||
await ctx.plugin(ConfiningFakeBash)
|
||||
@@ -914,6 +917,7 @@ describe('background execution through the job runtime', () => {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(BashEnvPlugin)
|
||||
@@ -933,6 +937,7 @@ describe('the background surface follows the job registry', () => {
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(BashEnvPlugin)
|
||||
await ctx.plugin(FakeBash)
|
||||
|
||||
@@ -19,7 +19,7 @@ function parentStore(ctx: Context): Promise<WeakMap<Agent, Agent>> {
|
||||
onTestFinished(() => ctx.fiber.dispose())
|
||||
const ready = (async () => {
|
||||
if (ctx.get('sessions') === undefined) await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
if (ctx.get('sessionProjections') === undefined) await ctx.plugin(SessionProjectionRegistry)
|
||||
if (ctx.get('agents') === undefined) await ctx.plugin(AgentRegistry)
|
||||
return new WeakMap<Agent, Agent>()
|
||||
})()
|
||||
|
||||
@@ -449,6 +449,7 @@ describe('dsh-tool-workflow', () => {
|
||||
onTestFinished(async () => { await ctx.fiber.dispose() })
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(ToolTasks)
|
||||
@@ -659,10 +660,10 @@ describe('dsh-tool-workflow', () => {
|
||||
onTestFinished(async () => { await ctx.fiber.dispose() })
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(ToolTasks)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SubagentRuntime)
|
||||
ctx.subagents.registerProvider({
|
||||
|
||||
@@ -34,8 +34,8 @@ async function setup(execute?: (bindings: HostBindings, spec: PtcRunSpec) => Pro
|
||||
const ctx = new Context()
|
||||
onTestFinished(async () => { await ctx.fiber.dispose() })
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(SessionProjections)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(SandboxPolicy, { mode: 'read-only' })
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
await ctx.plugin(SubagentRuntime)
|
||||
|
||||
@@ -37,8 +37,8 @@ export async function mountWorkflowRuntime(
|
||||
options: { cwd?: string; mode?: SandboxMode; runtimeConfig?: NodeRuntimeConfig } = {},
|
||||
): Promise<NodePtcRuntime> {
|
||||
if (!ctx.get('sessions')) await ctx.plugin(SessionStore)
|
||||
if (!ctx.get('agents')) await ctx.plugin(AgentRegistry)
|
||||
if (!ctx.get('sessionProjections')) await ctx.plugin(SessionProjections)
|
||||
if (!ctx.get('agents')) await ctx.plugin(AgentRegistry)
|
||||
if (!ctx.get('fs')) await ctx.plugin(FileSystem)
|
||||
if (!ctx.get('subprocess')) await ctx.plugin(Subprocess)
|
||||
if (!ctx.get('sandbox')) await ctx.plugin(Sandbox)
|
||||
|
||||
Reference in new issue
Block a user