refactor: identify and freeze messages at creation
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
|
||||
import { Context, Service } from 'cordis'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
import type {} from '@deepseek-ai/dsh-system-prompt'
|
||||
@@ -201,7 +202,7 @@ export class PlanModeService extends Service {
|
||||
return { kind: 'success', text: 'Plan mode is already inactive.' }
|
||||
}
|
||||
this.set(agent, true)
|
||||
if (message !== '') agent.steer({ content: [{ type: 'text', text: message }], source: { kind: 'user' } })
|
||||
if (message !== '') agent.steer(createUserMessage({ content: [{ type: 'text', text: message }], source: { kind: 'user' } }))
|
||||
return {
|
||||
kind: 'success',
|
||||
text: 'Entering plan mode (applies from the next step). Use /plan off to leave.',
|
||||
@@ -331,10 +332,10 @@ export class PlanModeService extends Service {
|
||||
const text = target
|
||||
? 'The user switched this session to plan mode.'
|
||||
: 'The user switched this session back to the default mode.'
|
||||
session.append('user/message', {
|
||||
session.append('user/message', createUserMessage({
|
||||
content: [{ type: 'text', text }],
|
||||
source: { kind: 'plugin', plugin: 'plan-mode' },
|
||||
}, { surfaceOp: 'append' })
|
||||
}), { surfaceOp: 'append' })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import LlmService, { type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import LlmService, { createUserMessage, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRegistry, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
|
||||
@@ -75,7 +75,7 @@ describe('plan mode through the agent loop', () => {
|
||||
// in-turn agent/step seam, before the first assembly.
|
||||
ctx.planMode.set(agent, true)
|
||||
|
||||
agent.followup({ content: [{ type: 'text', text: 'explore the repo' }], source: { kind: 'user' } })
|
||||
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'explore the repo' }], source: { kind: 'user' } }))
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
const log = agent.session.events
|
||||
@@ -90,7 +90,7 @@ describe('plan mode through the agent loop', () => {
|
||||
// guidance alone (enforcement lives on the independent sandbox/approval
|
||||
// axes). The mode itself stays plan throughout.
|
||||
const result = findEvent(log, 'tool/result')
|
||||
expect(result.data.isError).toBe(false)
|
||||
expect(result.data.message.content[0].isError).toBe(false)
|
||||
expect(foldPlanMode(log)).toBe(true)
|
||||
expect(log.some(event => event.type === 'user/message' && event.data.source.kind === 'plugin')).toBe(false)
|
||||
})
|
||||
@@ -103,14 +103,14 @@ describe('plan mode through the agent loop', () => {
|
||||
const ctx = await harness(adapter)
|
||||
const agent = ctx.agentLoop.create(SessionId('it-plan-flip'), { provider: 'mock', model: 'mock' })
|
||||
|
||||
agent.followup({ content: [{ type: 'text', text: 'hello' }], source: { kind: 'user' } })
|
||||
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'hello' }], source: { kind: 'user' } }))
|
||||
await waitForIdle(ctx, agent)
|
||||
expect(foldPlanMode(agent.session.events)).toBe(false)
|
||||
const first = findEvent(agent.session.events, 'request/header')
|
||||
expect(first.data.header.tools?.map(tool => tool.name)).toEqual(['exit_plan_mode', 'read', 'write'])
|
||||
|
||||
ctx.planMode.set(agent, true)
|
||||
agent.followup({ content: [{ type: 'text', text: 'now plan' }], source: { kind: 'user' } })
|
||||
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'now plan' }], source: { kind: 'user' } }))
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
const log = agent.session.events
|
||||
@@ -145,7 +145,7 @@ describe('plan mode through the agent loop', () => {
|
||||
})
|
||||
|
||||
const idle = waitForIdle(ctx, agent)
|
||||
agent.followup({ content: [{ type: 'text', text: 'plan after the transient failure' }], source: { kind: 'user' } })
|
||||
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'plan after the transient failure' }], source: { kind: 'user' } }))
|
||||
await idle
|
||||
|
||||
expect(adapter.requests).toHaveLength(2)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { createUserMessage, CallId } from '@deepseek-ai/dsh-llm'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRegistry, { RUN_CODE_NAME, defineContentToolFixture } from '@deepseek-ai/dsh-tools'
|
||||
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
@@ -57,7 +57,15 @@ async function setup(config: PlanModeConfig = PLAN_CONFIG): Promise<Context> {
|
||||
async function boundary(ctx: Context, agent: Agent & { session: Session }, type: 'turn/start' | 'step/end'): Promise<void> {
|
||||
const events = agentEvents(ctx, agent)
|
||||
if (type === 'turn/start') {
|
||||
await events.waterfall('agent/prompt-submit', [{ type: 'text', text: 'boundary probe' }], { kind: 'user' }, new AbortController().signal, () => Promise.resolve({ kind: 'allow' }))
|
||||
await events.waterfall(
|
||||
'agent/prompt-submit',
|
||||
createUserMessage({
|
||||
content: [{ type: 'text', text: 'boundary probe' }],
|
||||
source: { kind: 'user' },
|
||||
}),
|
||||
new AbortController().signal,
|
||||
() => Promise.resolve({ kind: 'allow' }),
|
||||
)
|
||||
return
|
||||
}
|
||||
await events.serial('agent/step', 1, 2, new AbortController().signal)
|
||||
|
||||
Reference in New Issue
Block a user