@chatpanel/events 0.79.0 → 0.80.0
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/cowriter-router.js +5 -2
- package/package.json +1 -1
- package/team-run.js +8 -1
- package/team-tool.js +15 -8
package/cowriter-router.js
CHANGED
|
@@ -48,9 +48,12 @@ export function appoint(role, candidates, { overrides = {}, exclude = null } = {
|
|
|
48
48
|
if (m) return withTierAndMode(m);
|
|
49
49
|
}
|
|
50
50
|
const want = TIER_RANK[role.prefer] ?? 1;
|
|
51
|
+
// Ties go to ROSTER ORDER, not to the alphabet: the host lists what it trusts first (the
|
|
52
|
+
// model the person is already chatting with, installed agents), and over a gateway that
|
|
53
|
+
// lists eight hundred models the alphabet picks a provider nobody has used.
|
|
51
54
|
const best = usable
|
|
52
|
-
.map((c) => ({ c: withTierAndMode(c), d: Math.abs((TIER_RANK[classifyModel(c.model)] ?? 1) - want) }))
|
|
53
|
-
.sort((a, b) => a.d - b.d ||
|
|
55
|
+
.map((c, i) => ({ c: withTierAndMode(c), d: Math.abs((TIER_RANK[classifyModel(c.model)] ?? 1) - want), i }))
|
|
56
|
+
.sort((a, b) => a.d - b.d || a.i - b.i)[0];
|
|
54
57
|
return best.c;
|
|
55
58
|
}
|
|
56
59
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/events",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.80.0",
|
|
4
4
|
"description": "The canonical ChatPanel event-log and capability contracts \u2014 typed durable facts, clock-free deterministic linearization, schema upcasting, and the invariants the replay harness asserts. Pure, dependency-free ESM shared by the ChatPanel extension, gateway and bridge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
package/team-run.js
CHANGED
|
@@ -171,7 +171,14 @@ export async function runTeam({
|
|
|
171
171
|
usage = res?.usage || null;
|
|
172
172
|
if (usage) budget.charge(usage);
|
|
173
173
|
if (res?.aborted) { status = 'stopped'; break; }
|
|
174
|
-
if (res?.ok) {
|
|
174
|
+
if (res?.ok) {
|
|
175
|
+
text = String(res?.text || '');
|
|
176
|
+
// A turn that ended with nothing to say — an agent that exited, a stream that
|
|
177
|
+
// died after its tool calls — is not a done task. Three members "completed"
|
|
178
|
+
// empty once, the run merged nothing, and the caller ran the team again.
|
|
179
|
+
if (!text.trim()) throw new Error('the model returned no answer');
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
175
182
|
const err = res?.error || 'the model did not answer';
|
|
176
183
|
if (attempt >= MAX_APPOINTMENTS || stopped() || !isModelUnavailable(err)) throw new Error(err);
|
|
177
184
|
exclude.add(m.model);
|
package/team-tool.js
CHANGED
|
@@ -72,6 +72,10 @@ const json = (v) => JSON.stringify(v);
|
|
|
72
72
|
export function teamToolProvider({ teams = [], run = null, appoint = null, confirmSave = null, saveTeam = null } = {}) {
|
|
73
73
|
const byName = new Map(usable(teams).map((t) => [t.name, t]));
|
|
74
74
|
let bound = null;
|
|
75
|
+
// One run per team+request per turn. A run that failed, answered with nothing, or ran
|
|
76
|
+
// out of budget comes back as a result the model must REPORT — asking for it again in the
|
|
77
|
+
// same turn is the circle a person had to break by hand.
|
|
78
|
+
const ran = new Map();
|
|
75
79
|
return {
|
|
76
80
|
id: 'team',
|
|
77
81
|
specs: [teamToolSpec(teams)],
|
|
@@ -112,18 +116,21 @@ export function teamToolProvider({ teams = [], run = null, appoint = null, confi
|
|
|
112
116
|
if (action === 'run') {
|
|
113
117
|
if (!request) return json({ error: 'run needs a request — what should the team do?' });
|
|
114
118
|
if (typeof run !== 'function') return json({ error: 'This surface cannot run a team.' });
|
|
119
|
+
const key = `${team.name}\n${request}`;
|
|
120
|
+
const prior = ran.get(key);
|
|
121
|
+
if (prior) return json({ error: `The "${team.name}" team already ran this request in this turn (run ${prior.runId}, ${prior.status}). Do not run it again: tell the user what happened — ${prior.summary} — and ask how to proceed.`, runId: prior.runId, status: prior.status, tasks: prior.tasks });
|
|
115
122
|
const dry = dryRunTeam(team, request, { appoint });
|
|
116
123
|
if (!dry.ok) return json({ error: `No model is available for role(s): ${dry.missing.join(', ')}.`, roles: dry.roles });
|
|
117
124
|
const result = await run({ team, request, toolset: bound });
|
|
118
125
|
const findings = (result.board || []).map((f) => ({ role: f.role, kind: f.kind, text: f.text, refs: f.refs }));
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
});
|
|
126
|
+
const tasks = (result.tasks || []).map((x) => ({ id: x.id, role: x.role, status: x.status, ms: x.ms, findings: (x.findings || []).length, error: x.error || undefined }));
|
|
127
|
+
const failed = tasks.filter((x) => x.status !== 'ok');
|
|
128
|
+
const summary = failed.length ? failed.map((x) => `${x.role} ${x.status}${x.error ? ` (${x.error})` : ''}`).join('; ') : `${findings.length} findings`;
|
|
129
|
+
ran.set(key, { runId: result.runId, status: result.status, summary, tasks });
|
|
130
|
+
const hint = result.status === 'over-budget' ? 'The team stopped at its budget; the proposal is what it had. Say so.'
|
|
131
|
+
: result.status === 'failed' ? `The run FAILED — ${summary}. Do not run the team again this turn. Tell the user exactly which role failed and why, and ask whether to retry, change the team\'s models in Settings → Teams, or answer without the team.`
|
|
132
|
+
: failed.length ? `Some roles did not finish — ${summary}. Say so alongside the proposal.` : undefined;
|
|
133
|
+
return json({ name: team.name, runId: result.runId, status: result.status, proposal: result.proposal, tasks, findings, usage: result.usage, hint });
|
|
127
134
|
}
|
|
128
135
|
return json({ error: `Unknown action "${action}". Use run, dry_run or save.` });
|
|
129
136
|
},
|