test(agent): migrate remaining model selection fixtures

This commit is contained in:
_Kerman committed 2026-10-16 10:15:58 +08:00
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 -14
View File
@@ -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)
@@ -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
+8 -1
View File
@@ -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)
+6 -1
View File
@@ -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)