@chatpanel/events 0.82.1 → 0.83.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/team-run.js +11 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chatpanel/events",
3
- "version": "0.82.1",
3
+ "version": "0.83.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
@@ -122,6 +122,11 @@ export async function runTeam({
122
122
  return (appoint ? appoint(r, { exclude }) : null) || (r.model && !exclude?.has(r.model) ? { model: r.model, mode: r.mode } : null);
123
123
  };
124
124
  const MAX_APPOINTMENTS = 3;
125
+ // A model that was not there for one member is not there for the next: what failed as
126
+ // unavailable anywhere in this run is skipped by every later appointment. Two members
127
+ // each spent two minutes finding out the same agent was down.
128
+ const runExclude = new Set();
129
+ const excluding = (local) => new Set([...runExclude, ...(local || [])]);
125
130
  const stopped = () => !!signal?.aborted;
126
131
 
127
132
  say(resume ? 'run.resumed' : 'run.started', { team: t.name, request: String(request || ''), budget: budget.cap, roles: t.roles.map((r) => r.id), ...(resume ? { carried: [...carried] } : {}) });
@@ -215,7 +220,7 @@ export async function runTeam({
215
220
  const exclude = new Set();
216
221
  let lastErr = '';
217
222
  for (let attempt = 1; ; attempt++) {
218
- const m = modelFor(role, exclude);
223
+ const m = modelFor(role, excluding(exclude));
219
224
  if (!m?.model) throw new Error(exclude.size ? `no model left for role "${role.id}" after ${[...exclude].join(', ')}` : `no model for role "${role.id}"`);
220
225
  if (attempt > 1) say('task.reappointed', { taskId: task.id, role: role.id, model: m.model, after: [...exclude], error: lastErr });
221
226
  // Who is doing this task, for a ledger that shows the lanes — said per attempt.
@@ -236,7 +241,7 @@ export async function runTeam({
236
241
  if (res?.ok && String(res?.text || '').trim()) { text = String(res.text); break; }
237
242
  const err = res?.ok ? 'the model returned no answer' : (res?.error || 'the model did not answer');
238
243
  if (attempt >= MAX_APPOINTMENTS || stopped() || !isModelUnavailable(err)) throw new Error(err);
239
- exclude.add(m.model); lastErr = err;
244
+ exclude.add(m.model); runExclude.add(m.model); lastErr = err;
240
245
  }
241
246
  }
242
247
  } catch (e) {
@@ -259,7 +264,8 @@ export async function runTeam({
259
264
  budgetAsked = true;
260
265
  const left = tasks.filter((x) => !tasksOut.some((y) => y.id === x.id)).length;
261
266
  const spent = budget.snapshot().spent;
262
- const a = await askPerson({ type: 'budget', text: `The team has used its budget (${Object.entries(spent).filter(([k]) => budget.cap[k] !== undefined).map(([k, v]) => `${k} ${v} of ${budget.cap[k]}`).join(', ')}) with ${left} task${left === 1 ? '' : 's'} left. Raise it by half, or stop here with what it has?`, options: ['Raise by half', 'Stop here'] });
267
+ const what = left ? `${left} task${left === 1 ? '' : 's'} and the merge left` : 'only the merge left';
268
+ const a = await askPerson({ type: 'budget', text: `The team has used its budget (${Object.entries(spent).filter(([k]) => budget.cap[k] !== undefined).map(([k, v]) => `${k} ${v} of ${budget.cap[k]}`).join(', ')}) with ${what}. Raise it by half, or stop here with what it has?`, options: ['Raise by half', 'Stop here'] });
263
269
  if (a && /raise|allow|yes|more|continue/i.test(a.text)) { budget.raise(1.5); overBudget = false; }
264
270
  }
265
271
  }
@@ -293,7 +299,7 @@ export async function runTeam({
293
299
  const excl = new Set();
294
300
  let judgeErr = '';
295
301
  for (let attempt = 1; attempt <= MAX_APPOINTMENTS; attempt++) {
296
- const mm = attempt === 1 ? m : modelFor(judge, excl);
302
+ const mm = attempt === 1 ? (runExclude.has(m?.model) ? modelFor(judge, excluding(excl)) : m) : modelFor(judge, excluding(excl));
297
303
  if (!mm?.model) break;
298
304
  if (attempt > 1) say('task.reappointed', { taskId: 'merge', role: judge.id, model: mm.model, after: [...excl], error: judgeErr });
299
305
  say('task.model', { taskId: 'merge', role: judge.id, model: mm.model, attempt });
@@ -302,7 +308,7 @@ export async function runTeam({
302
308
  if (res?.ok && String(res.text || '').trim()) break;
303
309
  const err = res?.ok ? 'the model returned no answer' : (res?.error || 'the model did not answer');
304
310
  if (stopped() || !isModelUnavailable(err)) break;
305
- excl.add(mm.model); judgeErr = err;
311
+ excl.add(mm.model); runExclude.add(mm.model); judgeErr = err;
306
312
  }
307
313
  const judged = res?.ok && String(res.text || '').trim();
308
314
  say(judged ? 'task.done' : 'task.failed', { taskId: 'merge', role: judge.id, status: judged ? 'ok' : 'failed', error: judged ? null : (res?.error || 'the judge did not answer'), findings: 0 });