Repository navigation
Expand file tree
/
Copy pathactor.test.mjs
More file actions
157 lines (142 loc) · 6.36 KB
/
Copy pathactor.test.mjs
File metadata and controls
157 lines (142 loc) · 6.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
* actor preset tests — replay the system-prompt/assemble waterfall contract
* against the real plugin code (zero dependencies, node:test).
*/
import test from 'node:test'
import assert from 'node:assert/strict'
import { apply, name } from './preset/actor-bootstrap.mjs'
// The rc.8 Standard assembly this preset governs: identity first, the
// persona slot second (PERSONA_ORDER = 0), plan-mode and tool guidance after.
const SECTIONS = [
{ name: 'harness:identity', text: 'You are an AI agent powered by DeepSeek Harness.' },
{ name: 'deployment:persona', text: 'You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.' },
{ name: 'plan-mode', text: 'You are in plan mode.' },
{ name: 'tool-guidance:fs', text: 'Filesystem tool guidance.' },
]
const CONTEXTS = [{ name: 'runtime-context', text: 'Current runtime context.' }]
function baseAssembled(extraTools = []) {
return {
sections: SECTIONS.map((section) => ({ ...section })),
contexts: CONTEXTS.map((context) => ({ ...context })),
tools: [
...['bash', 'read', 'write', 'edit', 'glob', 'grep', 'todo', 'web', 'subagent'].map((tool) => ({ name: tool })),
...extraTools.map((tool) => ({ name: tool })),
],
variables: { model: 'deepseek-v4-flash', cwd: 'D:/work' },
}
}
function makeSession(events = [], header = {}) {
return { id: 's1', events, header }
}
function makeHarness(config = {}) {
const handlers = new Map()
const ctx = {
on: (topic, fn) => handlers.set(topic, fn),
effect: (fn) => fn(),
}
apply(ctx, config)
return {
async assemble(assembled, context) {
const handler = handlers.get('system-prompt/assemble')
assert.ok(handler, 'system-prompt/assemble handler registered')
// `next()` returns the same object: a promoted assembly must be that
// exact reference (returned untouched).
return handler(assembled, context, async () => assembled)
},
}
}
test('plugin identity', () => {
assert.equal(name, 'actor-bootstrap')
})
test('first request: the doer persona is the ENTIRE prompt (minimal-style), core tools, no contexts', async () => {
const h = makeHarness()
const agent = { session: makeSession(), options: { provider: 'p', model: 'm' } }
const assembled = await h.assemble(baseAssembled(), { agent, scope: agent })
// Minimal-style: one section only — no harness identity, no plan-mode,
// no tool guidance; the persona text is the whole system prompt.
assert.deepEqual(
assembled.sections,
[{ name: 'actor-persona', text: assembled.sections[0].text }],
)
assert.equal(assembled.sections.length, 1)
assert.deepEqual(assembled.contexts, [])
assert.deepEqual(new Set(assembled.tools.map((tool) => tool.name)), new Set(['read', 'write', 'edit', 'bash']))
})
test('persona text is the router react persona, verbatim', async () => {
const h = makeHarness()
const agent = { session: makeSession(), options: {} }
const assembled = await h.assemble(baseAssembled(), { agent, scope: agent })
assert.equal(assembled.sections.length, 1)
assert.equal(
assembled.sections[0].text,
'You are a hands-on software engineer who delivers working output fast.\n'
+ 'Work directly: write or edit code, then verify it by reading and running. '
+ 'Keep the loop tight — produce, verify, fix — and do not build test '
+ 'harnesses, scaffolding, or ceremony the user did not ask for. '
+ 'Finish with a usable deliverable and a short summary.',
)
})
test('promotion: after the first assistant/message the assembly returns untouched', async () => {
const h = makeHarness()
const agent = {
session: makeSession([
{ type: 'user/message', data: {} },
{ type: 'assistant/message', data: { interrupted: false } },
]),
options: { provider: 'p', model: 'm' },
}
const original = baseAssembled()
const assembled = await h.assemble(original, { agent, scope: agent })
// Strict identity: the Standard persona, full catalog, and runtime
// contexts all come back exactly as the composition produced them.
assert.equal(assembled, original)
assert.equal(original.sections[1].name, 'deployment:persona')
assert.equal(original.tools.length, 9)
assert.equal(original.contexts.length, 1)
})
test('interrupted first response still promotes (thinking ended)', async () => {
const h = makeHarness()
const agent = {
session: makeSession([{ type: 'assistant/message', data: { interrupted: true } }]),
options: {},
}
const original = baseAssembled()
assert.equal(await h.assemble(original, { agent, scope: agent }), original)
})
test('unlock is thinking-based, not tool-based: tool/call alone does not promote', async () => {
const h = makeHarness()
const agent = {
session: makeSession([{ type: 'user/message', data: {} }, { type: 'tool/call', data: {} }]),
options: {},
}
const assembled = await h.assemble(baseAssembled(), { agent, scope: agent })
// The deliberate difference from router-standard: a tool call without a
// completed response keeps the minimal doer surface.
assert.equal(assembled.sections.length, 1)
assert.equal(assembled.sections[0].name, 'actor-persona')
assert.deepEqual(new Set(assembled.tools.map((tool) => tool.name)), new Set(['read', 'write', 'edit', 'bash']))
})
test('pwsh is preferred over bash when both are present', async () => {
const h = makeHarness()
const agent = { session: makeSession(), options: {} }
const assembled = await h.assemble(baseAssembled(['pwsh']), { agent, scope: agent })
assert.deepEqual(new Set(assembled.tools.map((tool) => tool.name)), new Set(['read', 'write', 'edit', 'pwsh']))
})
test('no platform shell in the catalog fails loud', async () => {
const h = makeHarness()
const agent = { session: makeSession(), options: {} }
const base = baseAssembled()
base.tools = base.tools.filter((tool) => tool.name !== 'bash')
await assert.rejects(() => h.assemble(base, { agent, scope: agent }), /no platform shell/)
})
test('subagent sessions are untouched', async () => {
const h = makeHarness()
const agent = { session: makeSession([], { parentSession: 'root-session' }), options: {} }
const original = baseAssembled()
assert.equal(await h.assemble(original, { agent, scope: agent }), original)
})
test('assembly without an agent is untouched', async () => {
const h = makeHarness()
const original = baseAssembled()
assert.equal(await h.assemble(original, {}), original)
})