|
|
@@ -8,7 +8,7 @@ import * as workspaceContext from '@deepseek-ai/dsh-agent-instructions'
|
|
|
import LlmRuntime, { createUserMessage, ToolCallId, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
|
|
import SessionStore, { SessionId, type SessionEvent, type UserMessage } from '@deepseek-ai/dsh-session'
|
|
|
import AgentRegistry, { agentEvents, type Agent } from '@deepseek-ai/dsh-agent'
|
|
|
-import AgentLoop, { ReactLoopInbox, turnBoundaryProjectionDefinition } from '@deepseek-ai/dsh-agent-loop'
|
|
|
+import AgentLoop, { turnBoundaryProjectionDefinition } from '@deepseek-ai/dsh-agent-loop'
|
|
|
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
|
|
import { FileSystem, FsTargetKey, FsVersion } from '@deepseek-ai/dsh-fs'
|
|
|
import type {
|
|
|
@@ -43,7 +43,10 @@ import {
|
|
|
import { resolveConfig } from '../src/config.ts'
|
|
|
import { candidateScopeKey, renderInstructionChanges, renderWorkspaceInstructionSet, USER_GLOBAL_DIRECTORY, USER_GLOBAL_FILE } from '../src/render.ts'
|
|
|
import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
|
|
|
-import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit'
|
|
|
+import {
|
|
|
+ createInboxFixture,
|
|
|
+ type InboxFixture,
|
|
|
+} from '@deepseek-ai/dsh-agent-loop-testkit'
|
|
|
|
|
|
/** Per-candidate reconciliation scope key: directory paired with the file name. */
|
|
|
const sk = (directory: string, candidateName: string): string => candidateScopeKey(directory, candidateName)
|
|
|
@@ -55,8 +58,14 @@ await isolatedInboxCtx.plugin(SessionProjectionRegistry)
|
|
|
await isolatedInboxCtx.plugin(AgentRegistry)
|
|
|
let nextStubSession = 1
|
|
|
|
|
|
-interface TestAgent extends Agent {
|
|
|
- readonly inbox: ReactLoopInbox
|
|
|
+type TestAgent = Agent
|
|
|
+const inboxFixtures = new WeakMap<Agent, InboxFixture>()
|
|
|
+
|
|
|
+/** Return the loop-driver operations paired with one structural test Agent. */
|
|
|
+function inboxFixture(agent: Agent): InboxFixture {
|
|
|
+ const fixture = inboxFixtures.get(agent)
|
|
|
+ if (fixture === undefined) throw new Error('agent Inbox fixture is unavailable')
|
|
|
+ return fixture
|
|
|
}
|
|
|
const requestTimeoutMs = process.platform === 'win32' ? 5_000 : 1_000
|
|
|
|
|
|
@@ -205,12 +214,13 @@ function stubAgent(cwd?: string, seed: readonly SessionEvent[] = []): TestAgent
|
|
|
seed,
|
|
|
...cwd === undefined ? {} : { meta: { createdAt: 0, cwd } },
|
|
|
})
|
|
|
- const agent: Agent = {
|
|
|
+ const fixture = createInboxFixture(agentCtx.sessionProjections, session)
|
|
|
+ const agent: TestAgent = {
|
|
|
ctx: agentCtx,
|
|
|
id: SessionId('a1'),
|
|
|
options: {},
|
|
|
session,
|
|
|
- inbox: unsupportedInbox(),
|
|
|
+ inbox: fixture.inbox,
|
|
|
status: 'idle',
|
|
|
send: () => {},
|
|
|
followup: () => {},
|
|
|
@@ -220,9 +230,8 @@ function stubAgent(cwd?: string, seed: readonly SessionEvent[] = []): TestAgent
|
|
|
runMaintenance: task => task(new AbortController().signal),
|
|
|
whenIdle: () => Promise.resolve(),
|
|
|
}
|
|
|
- return Object.assign(agent, {
|
|
|
- inbox: new ReactLoopInbox(agentCtx.sessionProjections, session, agentEvents(agentCtx, agent)),
|
|
|
- })
|
|
|
+ inboxFixtures.set(agent, fixture)
|
|
|
+ return agent
|
|
|
}
|
|
|
|
|
|
function stubToolExecution(
|
|
|
@@ -273,7 +282,7 @@ function baselineEvents(agent: Agent): SessionEvent[] {
|
|
|
async function appendAdditionalContexts(ctx: Context, agent: TestAgent): Promise<number | undefined> {
|
|
|
await syncedWorkspaceContext(ctx, agent)
|
|
|
let lastSeq: number | undefined
|
|
|
- for (const claimed of agent.inbox.claim('next-step', 1)) {
|
|
|
+ for (const claimed of inboxFixture(agent).claim('next-step')) {
|
|
|
if (claimed.source.kind !== 'agent-instructions') continue
|
|
|
const event = agent.session.append('user/message', claimed, { surfaceOp: 'append' })
|
|
|
ctx.emit('session/event', agent.session, event)
|
|
|
@@ -291,7 +300,7 @@ async function composeBaselinePrefix(ctx: Context, agent: TestAgent): Promise<Me
|
|
|
{ messages: [], turn: 1, step: 1, signal },
|
|
|
() => Promise.resolve({ kind: 'enter' as const, messages: [] }),
|
|
|
)
|
|
|
- const claimed = agent.inbox.claim('next-step', 1)
|
|
|
+ const claimed = inboxFixture(agent).claim('next-step')
|
|
|
const decision = await agentEvents(ctx, agent).waterfall(
|
|
|
'agent/pre-step',
|
|
|
{ messages: claimed, turn: 1, step: 2, signal },
|
|
|
@@ -1404,7 +1413,7 @@ describe('workspace context request injection', () => {
|
|
|
await mountWorkspaceContextPlugin(ctx, { dshHome: home, maxBytes: 65536 })
|
|
|
const resumed = stubAgent(root, original.session.snapshotEvents())
|
|
|
agentEvents(ctx, resumed).emit('agent/session-start', { source: 'resume' })
|
|
|
- const claimed = resumed.inbox.claim('next-step', 1)
|
|
|
+ const claimed = inboxFixture(resumed).claim('next-step')
|
|
|
const decision = await agentEvents(ctx, resumed).waterfall(
|
|
|
'agent/pre-step',
|
|
|
{ messages: claimed, turn: 1, step: 1, signal: AbortSignal.timeout(requestTimeoutMs) },
|
|
|
@@ -1450,7 +1459,7 @@ describe('workspace context request injection', () => {
|
|
|
await mountWorkspaceContextPlugin(ctx, { dshHome: home, maxBytes: 65536 })
|
|
|
const resumed = stubAgent(root, original.session.snapshotEvents())
|
|
|
agentEvents(ctx, resumed).emit('agent/session-start', { source: 'resume' })
|
|
|
- const staleClaim = resumed.inbox.claim('next-step', 1)
|
|
|
+ const staleClaim = inboxFixture(resumed).claim('next-step')
|
|
|
const staleDecision = await agentEvents(ctx, resumed).waterfall(
|
|
|
'agent/pre-step',
|
|
|
{ messages: staleClaim, turn: 1, step: 1, signal: AbortSignal.timeout(requestTimeoutMs) },
|
|
|
@@ -1503,7 +1512,7 @@ describe('workspace context request injection', () => {
|
|
|
await mountWorkspaceContextPlugin(resumedCtx, { dshHome: home, maxBytes })
|
|
|
const resumed = stubAgent(root, original.session.snapshotEvents())
|
|
|
agentEvents(resumedCtx, resumed).emit('agent/session-start', { source: 'resume' })
|
|
|
- const claimed = resumed.inbox.claim('next-step', 1)
|
|
|
+ const claimed = inboxFixture(resumed).claim('next-step')
|
|
|
const decision = await agentEvents(resumedCtx, resumed).waterfall(
|
|
|
'agent/pre-step',
|
|
|
{ messages: claimed, turn: 1, step: 1, signal: AbortSignal.timeout(requestTimeoutMs) },
|
|
|
@@ -4658,7 +4667,7 @@ describe('workspace context inbox synchronization', () => {
|
|
|
await mountFileToolsAndWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
|
|
|
const agent = stubAgent(join(root, 'pkg'))
|
|
|
await syncedWorkspaceContext(ctx, agent)
|
|
|
- const claimed = agent.inbox.claim('next-step', 1)
|
|
|
+ const claimed = inboxFixture(agent).claim('next-step')
|
|
|
await write(join(root, 'pkg/AGENTS.md'), 'new claimed rule with more detail')
|
|
|
const downstream = { kind: 'enter' as const, messages: claimed }
|
|
|
|