picocode-core 0.9.179 → 0.9.180
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent-transcript.js +20 -7
- package/src/config.js +6 -1
- package/src/controller.js +45 -35
- package/src/deliberation-history.js +2 -2
- package/src/deliberation.js +56 -29
- package/src/tools/index.js +1 -1
package/package.json
CHANGED
package/src/agent-transcript.js
CHANGED
|
@@ -54,6 +54,7 @@ function deliberationTranscript(agent) {
|
|
|
54
54
|
if (entry.kind === 'turn') {
|
|
55
55
|
const turn = turnFor(entry.value.role, entry.value.round)
|
|
56
56
|
turn.text = entry.value.text
|
|
57
|
+
if (entry.value.parallelGroup) turn.parallelGroup = entry.value.parallelGroup
|
|
57
58
|
turn.active = false
|
|
58
59
|
continue
|
|
59
60
|
}
|
|
@@ -61,23 +62,35 @@ function deliberationTranscript(agent) {
|
|
|
61
62
|
if (event.type === 'tool_executing') {
|
|
62
63
|
const item = toolItem(event.call || {}, event.at)
|
|
63
64
|
tools.set(item.callId, item)
|
|
64
|
-
turnFor(event.role, event.round)
|
|
65
|
+
const turn = turnFor(event.role, event.round)
|
|
66
|
+
if (event.parallelGroup) turn.parallelGroup = event.parallelGroup
|
|
67
|
+
turn.tools.push(item)
|
|
65
68
|
}
|
|
66
69
|
if (event.type === 'tool_complete' || event.type === 'tool_error') settleTool(tools, event)
|
|
67
70
|
}
|
|
68
|
-
|
|
69
|
-
const live
|
|
70
|
-
if (live?.text && live.role !== 'synthesis') {
|
|
71
|
+
const liveTurns = agent.live?.turns || (agent.live?.role ? [agent.live] : [])
|
|
72
|
+
for (const live of liveTurns.filter((turn) => turn.role !== 'synthesis')) {
|
|
71
73
|
const turn = turnFor(live.role, live.round)
|
|
72
|
-
if (
|
|
74
|
+
if (live.parallelGroup) turn.parallelGroup = live.parallelGroup
|
|
75
|
+
if (turn.text == null && live.text) turn.text = live.text
|
|
73
76
|
}
|
|
77
|
+
const liveSynthesis = liveTurns.find((turn) => turn.role === 'synthesis')
|
|
74
78
|
if (agent.result) {
|
|
75
79
|
items.push({ kind: 'deliberation-turn', role: 'synthesis', text: agent.result, tools: [], interrupted: agent.status === 'cancelled' })
|
|
76
|
-
} else if (
|
|
77
|
-
items.push({ kind: 'deliberation-turn', role: 'synthesis', text:
|
|
80
|
+
} else if (liveSynthesis?.text) {
|
|
81
|
+
items.push({ kind: 'deliberation-turn', role: 'synthesis', text: liveSynthesis.text, tools: [], active: true })
|
|
78
82
|
} else if (agent.error) {
|
|
79
83
|
items.push({ kind: 'assistant', text: agent.error, interrupted: true })
|
|
80
84
|
}
|
|
85
|
+
const order = (turn) => turn.role === 'participant-a' ? 0 : 1
|
|
86
|
+
const grouped = items.filter((turn) => turn.parallelGroup)
|
|
87
|
+
for (const group of new Set(grouped.map((turn) => turn.parallelGroup))) {
|
|
88
|
+
const sorted = grouped.filter((turn) => turn.parallelGroup === group).sort((a, b) => order(a) - order(b))
|
|
89
|
+
let index = 0
|
|
90
|
+
for (let i = 0; i < items.length; i++) {
|
|
91
|
+
if (items[i].parallelGroup === group) items[i] = sorted[index++]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
81
94
|
return items
|
|
82
95
|
}
|
|
83
96
|
|
package/src/config.js
CHANGED
|
@@ -8,7 +8,12 @@ function configFile() {
|
|
|
8
8
|
|
|
9
9
|
export async function readConfig() {
|
|
10
10
|
try {
|
|
11
|
-
|
|
11
|
+
const config = JSON.parse(await readFile(configFile(), 'utf-8'))
|
|
12
|
+
if (config.models) {
|
|
13
|
+
if (!Object.hasOwn(config.models, 'participantA')) config.models.participantA = config.models.proposer ?? null
|
|
14
|
+
if (!Object.hasOwn(config.models, 'participantB')) config.models.participantB = config.models.reviewer ?? null
|
|
15
|
+
}
|
|
16
|
+
return config
|
|
12
17
|
} catch {
|
|
13
18
|
return {}
|
|
14
19
|
}
|
package/src/controller.js
CHANGED
|
@@ -98,6 +98,8 @@ function errorText(err, limit) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
export function createController({ boot }) {
|
|
101
|
+
if (!Object.hasOwn(boot, 'participantAModel')) boot.participantAModel = boot.proposerModel ?? null
|
|
102
|
+
if (!Object.hasOwn(boot, 'participantBModel')) boot.participantBModel = boot.reviewerModel ?? null
|
|
101
103
|
const { on, emit } = createEmitter()
|
|
102
104
|
|
|
103
105
|
const state = {
|
|
@@ -238,10 +240,10 @@ export function createController({ boot }) {
|
|
|
238
240
|
rounds = options.rounds
|
|
239
241
|
const existingIds = deliberationsFromEvents(state.events).map((item) => Number(item.deliberationId)).filter(Number.isFinite)
|
|
240
242
|
const id = String(Math.max(0, ...existingIds) + 1)
|
|
241
|
-
const roleModel = (role) => (role === '
|
|
243
|
+
const roleModel = (role) => (role === 'participant-a' ? boot.participantAModel : role === 'participant-b' ? boot.participantBModel : null) || boot.deliberationModel || boot.participantAModel || boot.participantBModel
|
|
242
244
|
const roleWorkers = {}
|
|
243
245
|
const roleAuths = {}
|
|
244
|
-
for (const part of ['
|
|
246
|
+
for (const part of ['participant-a', 'participant-b', 'synthesizer']) {
|
|
245
247
|
const name = roleModel(part)
|
|
246
248
|
const found = boot.models.find((m) => m.name === name)
|
|
247
249
|
if (!found || found.available === false) throw new Error(`deliberation model unavailable: ${name}`)
|
|
@@ -250,17 +252,18 @@ export function createController({ boot }) {
|
|
|
250
252
|
}
|
|
251
253
|
const sessionId = state.session?.id
|
|
252
254
|
if (!sessionId) throw new Error('deliberation requires an active session')
|
|
253
|
-
persist(makeEvent('deliberation_start', { deliberationId: id, brief, rounds, model: roleModel('synthesizer'), models: {
|
|
255
|
+
persist(makeEvent('deliberation_start', { deliberationId: id, brief, rounds, model: roleModel('synthesizer'), models: { participantA: roleModel('participant-a'), participantB: roleModel('participant-b') } }))
|
|
254
256
|
bumpActivity()
|
|
255
|
-
const live = {
|
|
257
|
+
const live = { turns: {} }
|
|
256
258
|
liveDeliberations.set(id, live)
|
|
257
|
-
const speak = (role, round) => (event) => {
|
|
259
|
+
const speak = (role, round, parallelGroup) => (event) => {
|
|
260
|
+
const key = `${role}:${round}`
|
|
258
261
|
if (event.type === 'content') {
|
|
259
|
-
|
|
260
|
-
live.text
|
|
262
|
+
const current = live.turns[key] || { role, round, text: '', ...(parallelGroup ? { parallelGroup } : {}) }
|
|
263
|
+
live.turns[key] = { ...current, text: current.text + event.content }
|
|
261
264
|
bumpActivity()
|
|
262
|
-
} else if (event.type === 'tool_calls_ready' && live.text) {
|
|
263
|
-
live.
|
|
265
|
+
} else if (event.type === 'tool_calls_ready' && live.turns[key]?.text) {
|
|
266
|
+
live.turns[key] = { ...live.turns[key], text: '' }
|
|
264
267
|
bumpActivity()
|
|
265
268
|
}
|
|
266
269
|
}
|
|
@@ -270,7 +273,7 @@ export function createController({ boot }) {
|
|
|
270
273
|
bumpActivity()
|
|
271
274
|
}
|
|
272
275
|
|
|
273
|
-
const runWorker = async ({ history, role, tools: enabled = true, onStream }) => {
|
|
276
|
+
const runWorker = async ({ history, role, tools: enabled = true, onStream, workerSignal = signal }) => {
|
|
274
277
|
const scratchpad = ensureDir(agentScratchDir(boot.root, sessionId, `deliberation-${id}-${role}`))
|
|
275
278
|
const scan = await refreshProjectIndexes()
|
|
276
279
|
const context = await createAgentContext(boot, { userTools: scan.tools, isolated: true, instructions: participantSystemPrompt(scratchpad) })
|
|
@@ -279,7 +282,7 @@ export function createController({ boot }) {
|
|
|
279
282
|
env: { ...boot.env, PICO_SCRATCHPAD: scratchpad },
|
|
280
283
|
sessionId,
|
|
281
284
|
sessionFile: state.session?.file,
|
|
282
|
-
signal,
|
|
285
|
+
signal: workerSignal,
|
|
283
286
|
maxToolCalls: 30,
|
|
284
287
|
allowNames: enabled ? undefined : [],
|
|
285
288
|
})
|
|
@@ -291,7 +294,7 @@ export function createController({ boot }) {
|
|
|
291
294
|
effort: roleWorkers[role].effort ? 'low' : null,
|
|
292
295
|
auth: roleAuths[role],
|
|
293
296
|
system: context.system,
|
|
294
|
-
signal,
|
|
297
|
+
signal: workerSignal,
|
|
295
298
|
onStream,
|
|
296
299
|
})
|
|
297
300
|
}
|
|
@@ -300,19 +303,24 @@ export function createController({ boot }) {
|
|
|
300
303
|
brief,
|
|
301
304
|
rounds,
|
|
302
305
|
signal,
|
|
303
|
-
runParticipant: ({ history, role, round }) =>
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
306
|
+
runParticipant: ({ history, role, round, parallelGroup, signal: workerSignal }) => {
|
|
307
|
+
live.turns[`${role}:${round}`] = { role, round, text: '', ...(parallelGroup ? { parallelGroup } : {}) }
|
|
308
|
+
bumpActivity()
|
|
309
|
+
return runWorker({
|
|
310
|
+
workerSignal,
|
|
311
|
+
history,
|
|
312
|
+
role,
|
|
313
|
+
onStream: (event) => {
|
|
314
|
+
speak(role, round, parallelGroup)(event)
|
|
315
|
+
if (['tool_executing', 'tool_complete', 'tool_error'].includes(event.type)) {
|
|
316
|
+
persistDeliberation(makeEvent('deliberation_event', { deliberationId: id, role, round, parallelGroup, event }))
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
})
|
|
320
|
+
},
|
|
313
321
|
runSynthesis: ({ history }) => runWorker({ history, role: 'synthesizer', tools: false, onStream: speak('synthesis', null) }),
|
|
314
322
|
onEvent: (event) => {
|
|
315
|
-
|
|
323
|
+
delete live.turns[`${event.role}:${event.round}`]
|
|
316
324
|
persistDeliberation(makeEvent('deliberation_turn', { deliberationId: id, ...event }))
|
|
317
325
|
},
|
|
318
326
|
}).finally(() => liveDeliberations.delete(id))
|
|
@@ -608,7 +616,7 @@ export function createController({ boot }) {
|
|
|
608
616
|
sessionFile: state.session?.file,
|
|
609
617
|
wakeups: boot.wakeups,
|
|
610
618
|
agents: boot.researchModel ? agents : null,
|
|
611
|
-
deliberations: boot.deliberationModel || (boot.
|
|
619
|
+
deliberations: boot.deliberationModel || (boot.participantAModel && boot.participantBModel) ? deliberations : null,
|
|
612
620
|
onAgentsCollected: discardCollectedAgentNotes,
|
|
613
621
|
askUser,
|
|
614
622
|
signal: controller.signal,
|
|
@@ -1073,8 +1081,8 @@ export function createController({ boot }) {
|
|
|
1073
1081
|
flash('usage: /deliberate <decision>')
|
|
1074
1082
|
return true
|
|
1075
1083
|
}
|
|
1076
|
-
if (!modelAvailable(boot.
|
|
1077
|
-
if (!modelAvailable(boot.
|
|
1084
|
+
if (!modelAvailable(boot.participantAModel || boot.deliberationModel)) return false
|
|
1085
|
+
if (!modelAvailable(boot.participantBModel || boot.deliberationModel)) return false
|
|
1078
1086
|
const n = Number(rounds)
|
|
1079
1087
|
send(deliberatePrompt(decision, Number.isInteger(n) && n >= 1 && n <= MAX_DELIBERATION_ROUNDS ? n : null))
|
|
1080
1088
|
return true
|
|
@@ -1110,17 +1118,17 @@ export function createController({ boot }) {
|
|
|
1110
1118
|
changed()
|
|
1111
1119
|
}
|
|
1112
1120
|
|
|
1113
|
-
async function
|
|
1121
|
+
async function setParticipantAModel(name) {
|
|
1114
1122
|
if (name && !modelAvailable(name)) return flash(`${name} is not available`)
|
|
1115
|
-
boot.
|
|
1116
|
-
await writeConfig({ models: {
|
|
1123
|
+
boot.participantAModel = name || null
|
|
1124
|
+
await writeConfig({ models: { participantA: name || null } })
|
|
1117
1125
|
changed()
|
|
1118
1126
|
}
|
|
1119
1127
|
|
|
1120
|
-
async function
|
|
1128
|
+
async function setParticipantBModel(name) {
|
|
1121
1129
|
if (name && !modelAvailable(name)) return flash(`${name} is not available`)
|
|
1122
|
-
boot.
|
|
1123
|
-
await writeConfig({ models: {
|
|
1130
|
+
boot.participantBModel = name || null
|
|
1131
|
+
await writeConfig({ models: { participantB: name || null } })
|
|
1124
1132
|
changed()
|
|
1125
1133
|
}
|
|
1126
1134
|
|
|
@@ -1323,7 +1331,7 @@ export function createController({ boot }) {
|
|
|
1323
1331
|
function activity() {
|
|
1324
1332
|
const withLive = (item) => {
|
|
1325
1333
|
const live = liveDeliberations.get(item.deliberationId)
|
|
1326
|
-
return live
|
|
1334
|
+
return live && Object.keys(live.turns).length ? { ...item, live: { turns: Object.values(live.turns) } } : item
|
|
1327
1335
|
}
|
|
1328
1336
|
const rows = [...agents.list(), ...deliberationsFromEvents(state.events).map(withLive)]
|
|
1329
1337
|
return rows.sort((a, b) => (b.startedAt || 0) - (a.startedAt || 0))
|
|
@@ -1404,8 +1412,10 @@ export function createController({ boot }) {
|
|
|
1404
1412
|
sendInit,
|
|
1405
1413
|
setResearchModel,
|
|
1406
1414
|
setDeliberationModel,
|
|
1407
|
-
|
|
1408
|
-
|
|
1415
|
+
setParticipantAModel,
|
|
1416
|
+
setParticipantBModel,
|
|
1417
|
+
setProposerModel: setParticipantAModel,
|
|
1418
|
+
setReviewerModel: setParticipantBModel,
|
|
1409
1419
|
setDefaultModel,
|
|
1410
1420
|
previewSteer,
|
|
1411
1421
|
applySteer,
|
|
@@ -35,11 +35,11 @@ export function deliberationsFromEvents(events) {
|
|
|
35
35
|
item.updatedAt = event.at
|
|
36
36
|
|
|
37
37
|
if (event.type === 'deliberation_event') {
|
|
38
|
-
const recorded = { ...data.event, role: data.role, round: data.round, at: event.at }
|
|
38
|
+
const recorded = { ...data.event, role: data.role, round: data.round, ...(data.parallelGroup ? { parallelGroup: data.parallelGroup } : {}), at: event.at }
|
|
39
39
|
item.events.push(recorded)
|
|
40
40
|
item.timeline.push({ kind: 'event', value: recorded })
|
|
41
41
|
} else if (event.type === 'deliberation_turn') {
|
|
42
|
-
const turn = { role: data.role, round: data.round, text: data.text }
|
|
42
|
+
const turn = { role: data.role, round: data.round, text: data.text, ...(data.parallelGroup ? { parallelGroup: data.parallelGroup } : {}) }
|
|
43
43
|
item.turns.push(turn)
|
|
44
44
|
item.timeline.push({ kind: 'turn', value: turn })
|
|
45
45
|
item.usage = mergeUsage(item.usage, data.usage)
|
package/src/deliberation.js
CHANGED
|
@@ -2,6 +2,8 @@ import { resultText } from './agents.js'
|
|
|
2
2
|
|
|
3
3
|
export const DEFAULT_DELIBERATION_ROUNDS = 3
|
|
4
4
|
export const MAX_DELIBERATION_ROUNDS = 5
|
|
5
|
+
export const INITIAL_PARALLEL_GROUP = 'initial'
|
|
6
|
+
export const PARTICIPANT_ROLES = ['participant-a', 'participant-b']
|
|
5
7
|
|
|
6
8
|
export function validateDeliberation({ brief, rounds = DEFAULT_DELIBERATION_ROUNDS } = {}) {
|
|
7
9
|
if (!brief?.trim()) throw new Error('deliberation brief is required')
|
|
@@ -11,17 +13,14 @@ export function validateDeliberation({ brief, rounds = DEFAULT_DELIBERATION_ROUN
|
|
|
11
13
|
return { brief: brief.trim(), rounds }
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
const ROLE_BRIEFS = {
|
|
15
|
-
proposer: 'You are the proposer: take a position on the decision and argue for it with evidence, laying out the reasoning and the tradeoffs you accept.',
|
|
16
|
-
reviewer: 'You are the reviewer: scrutinize the proposal, test its evidence and premises, and put forward the strongest alternative where one exists.',
|
|
17
|
-
}
|
|
18
|
-
|
|
19
16
|
function participantPrompt(brief, role, rounds) {
|
|
20
|
-
|
|
17
|
+
const name = role === 'participant-a' ? 'Participant A' : 'Participant B'
|
|
18
|
+
return `You are ${name}, one of two equal participants. Independently investigate and reason about this decision. This is a ${rounds}-round deliberation about the following decision:\n\n${brief}\n\nResearch before making claims. Use the available project and web tools whenever they can replace assumption with evidence. Cite URLs and project paths in your response. Challenge weak premises and address the peer's strongest points once their work is available. Do not seek agreement for its own sake or defend a fixed position. Change your position only when evidence warrants it, and preserve material disagreement and uncertainty. Do not modify project files. Write your own argument and reasoning; never instruct the other participant what to say or conclude. Be concise.`
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
function peerMessage(role, text, round, rounds) {
|
|
24
|
-
|
|
22
|
+
const name = role === 'participant-a' ? 'Participant A' : 'Participant B'
|
|
23
|
+
return `Round ${round} of ${rounds}. ${name} replied:\n\n${text}\n\nResearch and respond to the substance of this message.`
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
function synthesisPrompt(brief, turns) {
|
|
@@ -29,38 +28,66 @@ function synthesisPrompt(brief, turns) {
|
|
|
29
28
|
return `Synthesize this deliberation into a decision for the main agent. State the recommendation, decisive evidence, unresolved uncertainty, material disagreement, and implementation constraints. Do not manufacture consensus: preserve disagreements that the evidence did not resolve. Preserve useful URLs and project paths. Do not mention the deliberation process unless disagreement remains.\n\nDecision brief:\n${brief}\n\nTranscript:\n${transcript}`
|
|
30
29
|
}
|
|
31
30
|
|
|
31
|
+
function settledTurn(result, role, round, parallelGroup) {
|
|
32
|
+
if (result.error) return { error: result.error }
|
|
33
|
+
const text = resultText(result.messages)
|
|
34
|
+
if (result.interrupted || !text) return { error: text ? null : 'deliberation participant returned no response' }
|
|
35
|
+
return { turn: { role, round, text, messages: result.messages || [], usage: result.usage || null, ...(parallelGroup ? { parallelGroup } : {}) } }
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
export async function runDeliberation({ brief, rounds, runParticipant, runSynthesis, onEvent = () => {}, signal }) {
|
|
33
39
|
const valid = validateDeliberation({ brief, rounds })
|
|
34
|
-
const participants = {
|
|
35
|
-
proposer: [{ role: 'user', content: participantPrompt(valid.brief, 'proposer', valid.rounds) }],
|
|
36
|
-
reviewer: [{ role: 'user', content: participantPrompt(valid.brief, 'reviewer', valid.rounds) }],
|
|
37
|
-
}
|
|
40
|
+
const participants = Object.fromEntries(PARTICIPANT_ROLES.map((role) => [role, [{ role: 'user', content: participantPrompt(valid.brief, role, valid.rounds) }]]))
|
|
38
41
|
const turns = []
|
|
39
|
-
let peer = null
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
if (signal?.aborted) return { turns, interrupted: true }
|
|
44
|
+
const initialController = new AbortController()
|
|
45
|
+
const abortInitial = () => initialController.abort(signal?.reason)
|
|
46
|
+
signal?.addEventListener('abort', abortInitial, { once: true })
|
|
47
|
+
let failure
|
|
48
|
+
await Promise.allSettled(PARTICIPANT_ROLES.map(async (role) => {
|
|
49
|
+
try {
|
|
50
|
+
const result = await runParticipant({
|
|
51
|
+
role, round: 1, rounds: valid.rounds, history: [...participants[role]],
|
|
52
|
+
signal: initialController.signal, parallelGroup: INITIAL_PARALLEL_GROUP,
|
|
53
|
+
})
|
|
54
|
+
const parsed = settledTurn(result, role, 1, INITIAL_PARALLEL_GROUP)
|
|
55
|
+
if (!parsed.turn) {
|
|
56
|
+
failure ??= { interrupted: true, error: parsed.error }
|
|
57
|
+
initialController.abort()
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
participants[role].push(...(result.messages || []))
|
|
61
|
+
turns.push(parsed.turn)
|
|
62
|
+
onEvent({ type: 'deliberation_turn', ...parsed.turn })
|
|
63
|
+
} catch (error) {
|
|
64
|
+
failure ??= { interrupted: true, error: error?.message || String(error) }
|
|
65
|
+
initialController.abort(error)
|
|
66
|
+
}
|
|
67
|
+
}))
|
|
68
|
+
signal?.removeEventListener('abort', abortInitial)
|
|
69
|
+
turns.sort((a, b) => PARTICIPANT_ROLES.indexOf(a.role) - PARTICIPANT_ROLES.indexOf(b.role))
|
|
70
|
+
if (failure) return { turns, ...failure }
|
|
71
|
+
if (signal?.aborted) return { turns, interrupted: true }
|
|
72
|
+
|
|
73
|
+
participants['participant-b'].push({ role: 'user', content: peerMessage('participant-a', turns[0].text, 1, valid.rounds) })
|
|
74
|
+
let peer = turns.at(-1)
|
|
75
|
+
for (let round = 2; round <= valid.rounds; round++) {
|
|
76
|
+
for (const role of PARTICIPANT_ROLES) {
|
|
43
77
|
if (signal?.aborted) return { turns, interrupted: true }
|
|
44
78
|
const history = participants[role]
|
|
45
|
-
|
|
79
|
+
history.push({ role: 'user', content: peerMessage(peer.role, peer.text, round, valid.rounds) })
|
|
46
80
|
const result = await runParticipant({ role, round, rounds: valid.rounds, history: [...history], signal })
|
|
47
81
|
history.push(...(result.messages || []))
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
peer = turn
|
|
54
|
-
onEvent({ type: 'deliberation_turn', ...turn })
|
|
82
|
+
const parsed = settledTurn(result, role, round)
|
|
83
|
+
if (!parsed.turn) return { turns, interrupted: true, error: parsed.error }
|
|
84
|
+
turns.push(parsed.turn)
|
|
85
|
+
peer = parsed.turn
|
|
86
|
+
onEvent({ type: 'deliberation_turn', ...parsed.turn })
|
|
55
87
|
}
|
|
56
88
|
}
|
|
57
89
|
|
|
90
|
+
if (signal?.aborted) return { turns, interrupted: true }
|
|
58
91
|
const synthesis = await runSynthesis({ history: [{ role: 'user', content: synthesisPrompt(valid.brief, turns) }], signal })
|
|
59
|
-
return {
|
|
60
|
-
turns,
|
|
61
|
-
result: resultText(synthesis.messages),
|
|
62
|
-
usage: synthesis.usage || null,
|
|
63
|
-
interrupted: !!synthesis.interrupted,
|
|
64
|
-
error: synthesis.error || null,
|
|
65
|
-
}
|
|
92
|
+
return { turns, result: resultText(synthesis.messages), usage: synthesis.usage || null, interrupted: !!synthesis.interrupted, error: synthesis.error || null }
|
|
66
93
|
}
|
package/src/tools/index.js
CHANGED
|
@@ -229,7 +229,7 @@ export function createToolset({ toolResult, cwd, env, tracker, skills, shells, s
|
|
|
229
229
|
schema: {
|
|
230
230
|
description: describeParam,
|
|
231
231
|
brief: { type: 'string', description: 'self-contained decision, relevant context, constraints, and desired outcome' },
|
|
232
|
-
rounds: { type: 'integer', description: 'number of
|
|
232
|
+
rounds: { type: 'integer', description: 'number of Participant A/B exchange rounds (1-5, default 3)', optional: true },
|
|
233
233
|
},
|
|
234
234
|
execute: ({ brief, rounds }) => deliberations.run({ brief, rounds, sessionId, sessionFile, signal }),
|
|
235
235
|
})
|