@chatpanel/events 0.97.0 → 0.98.1
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/model-picker.js +2 -2
- package/package.json +1 -1
- package/recruit.js +3 -3
- package/team-board.js +24 -2
- package/team-record.js +1 -1
- package/team-trail.js +2 -0
package/model-picker.js
CHANGED
|
@@ -102,9 +102,9 @@ export function groupModels(models, { selectedId = '' } = {}) {
|
|
|
102
102
|
if (agents.length) {
|
|
103
103
|
sections.push({
|
|
104
104
|
key: 'agents',
|
|
105
|
-
// Naming
|
|
105
|
+
// Naming (naming-revamp.md): the CLI coding agents are AGENT TOOLS — a runtime one
|
|
106
106
|
// of the user's own agents can be given as its engine. The key and kind are code.
|
|
107
|
-
label: '
|
|
107
|
+
label: 'Agent Tools',
|
|
108
108
|
kind: 'agent',
|
|
109
109
|
items: agents.slice().sort(availableFirst).map(decorate),
|
|
110
110
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/events",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.98.1",
|
|
4
4
|
"description": "The canonical ChatPanel event-log and capability contracts — 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/recruit.js
CHANGED
|
@@ -143,7 +143,7 @@ export function needForJob(job, { reach = 'any' } = {}) {
|
|
|
143
143
|
const capabilities = [];
|
|
144
144
|
const why = [];
|
|
145
145
|
if (tools.length || grants.length) { capabilities.push('tools'); why.push('the job uses tools'); }
|
|
146
|
-
if (harness) why.push(`a work grant (${grants.filter((g) => WORK_GRANTS.includes(g)).join(', ')}) needs
|
|
146
|
+
if (harness) why.push(`a work grant (${grants.filter((g) => WORK_GRANTS.includes(g)).join(', ')}) needs an agent tool`);
|
|
147
147
|
const r = REACH_RANK[reach] != null ? reach : 'any';
|
|
148
148
|
if (r !== 'any') why.push(`reach ≤ ${r} (the project's privacy setting)`);
|
|
149
149
|
return { capabilities, harness, reach: r, why };
|
|
@@ -155,7 +155,7 @@ const meets = (row, need, policy) => {
|
|
|
155
155
|
const why = [];
|
|
156
156
|
if (row.available === false) why.push('unavailable right now');
|
|
157
157
|
if (REACH_RANK[row.reach] > REACH_RANK[need.reach]) why.push(`reach ${row.reach} exceeds ${need.reach}`);
|
|
158
|
-
if (need.harness && row.engine.kind !== 'harness') why.push('not
|
|
158
|
+
if (need.harness && row.engine.kind !== 'harness') why.push('not an agent tool');
|
|
159
159
|
const missing = need.capabilities.filter((c) => !row.capabilities.includes(c));
|
|
160
160
|
// A harness brings its own tools; the capability list of a bridge agent is the host's guess.
|
|
161
161
|
if (missing.length && !(row.engine.kind === 'harness' && missing.every((c) => c === 'tools'))) why.push(`lacks ${missing.join(', ')}`);
|
|
@@ -222,7 +222,7 @@ export function routeFor(agent, job, { rows = [], summary = null, need = null, r
|
|
|
222
222
|
const policy = spec.policy || {};
|
|
223
223
|
const rejected = [];
|
|
224
224
|
const cleared = list.filter((r) => { const why = meets(r, n, policy); if (why.length) rejected.push(`${r.key}: ${why.join('; ')}`); return !why.length; });
|
|
225
|
-
if (!cleared.length) return none([`no engine clears ${n.harness ? '
|
|
225
|
+
if (!cleared.length) return none([`no engine clears ${n.harness ? 'an agent tool with ' : ''}${n.capabilities.join(', ') || 'the requirements'}${n.reach !== 'any' ? ` within reach ${n.reach}` : ''}${policy.prefer ? ` under ${policy.prefer}` : ''}`, ...rejected.slice(0, 4)]);
|
|
226
226
|
const ordered = orderByPolicy(cleared, policy.prefer || 'balanced', summary);
|
|
227
227
|
let pick = ordered[0];
|
|
228
228
|
let exploration = false;
|
package/team-board.js
CHANGED
|
@@ -104,6 +104,7 @@ export function foldBoard(state, ev) {
|
|
|
104
104
|
if (type === 'board.thread' && p.thread?.id) {
|
|
105
105
|
if (!s.threads.some((t) => t.id === p.thread.id)) s.threads.push({ ...p.thread });
|
|
106
106
|
} else if (type === 'board.post' && p.post?.id) {
|
|
107
|
+
if (Array.isArray(s.removed) && s.removed.includes(p.post.threadId)) return s;
|
|
107
108
|
if (!s.posts.some((x) => x.id === p.post.id)) s.posts.push({ ...p.post });
|
|
108
109
|
const t = s.threads.find((x) => x.id === p.post.threadId);
|
|
109
110
|
if (t) { t.lastAt = p.post.at; t.lastBy = p.post.by; t.posts = (t.posts || 0) + 1; }
|
|
@@ -113,6 +114,13 @@ export function foldBoard(state, ev) {
|
|
|
113
114
|
} else if (type === 'board.thread-status' && p.threadId) {
|
|
114
115
|
const t = s.threads.find((x) => x.id === p.threadId);
|
|
115
116
|
if (t) { t.status = p.status; if (p.status !== 'waiting') t.waitingOn = null; if (p.holder) t.holder = p.holder; }
|
|
117
|
+
} else if (type === 'board.thread-removed' && p.threadId) {
|
|
118
|
+
// A person took the thread off the board: it and its posts go, and its id is kept so a
|
|
119
|
+
// post a member still makes in it (its runner did not see the removal) lands nowhere.
|
|
120
|
+
s.threads = s.threads.filter((x) => x.id !== p.threadId);
|
|
121
|
+
s.posts = s.posts.filter((x) => x.threadId !== p.threadId);
|
|
122
|
+
if (!Array.isArray(s.removed)) s.removed = [];
|
|
123
|
+
if (!s.removed.includes(p.threadId)) s.removed.push(p.threadId);
|
|
116
124
|
}
|
|
117
125
|
return s;
|
|
118
126
|
}
|
|
@@ -123,7 +131,7 @@ export function foldBoard(state, ev) {
|
|
|
123
131
|
* stays: a finding is a post of kind `finding` in its task's thread.
|
|
124
132
|
*/
|
|
125
133
|
export function createBoard({ now = () => Date.now(), newId = null, state = null, onEvent = null } = {}) {
|
|
126
|
-
const st = state && Array.isArray(state.threads) ? { threads: state.threads.map((t) => ({ ...t })), posts: (state.posts || []).map((x) => ({ ...x })) } : emptyBoardState();
|
|
134
|
+
const st = state && Array.isArray(state.threads) ? { threads: state.threads.map((t) => ({ ...t })), posts: (state.posts || []).map((x) => ({ ...x })), ...(Array.isArray(state.removed) && state.removed.length ? { removed: [...state.removed] } : {}) } : emptyBoardState();
|
|
127
135
|
const listeners = new Set();
|
|
128
136
|
let seq = st.posts.length + st.threads.length;
|
|
129
137
|
const mk = (prefix) => (newId ? newId(prefix) : `${prefix}_${(++seq).toString(36)}${Math.random().toString(36).slice(2, 6)}`);
|
|
@@ -181,6 +189,20 @@ export function createBoard({ now = () => Date.now(), newId = null, state = null
|
|
|
181
189
|
say('board.thread-status', { threadId, status, ...extra });
|
|
182
190
|
return t;
|
|
183
191
|
},
|
|
192
|
+
/**
|
|
193
|
+
* A person takes a thread off the board (from either client — the store appends the
|
|
194
|
+
* event, the running client's tail applies it here). Its posts go with it; a member's
|
|
195
|
+
* later post in it lands nowhere. A waiting ask is not removed: answer it or stop the run.
|
|
196
|
+
*/
|
|
197
|
+
removeThread(threadId, { by = PERSON } = {}) {
|
|
198
|
+
const t = threadOf(threadId);
|
|
199
|
+
if (!t || (t.kind === 'ask' && t.status === 'waiting')) return null;
|
|
200
|
+
st.threads = st.threads.filter((x) => x.id !== threadId);
|
|
201
|
+
st.posts = st.posts.filter((x) => x.threadId !== threadId);
|
|
202
|
+
(st.removed ||= []).push(threadId);
|
|
203
|
+
say('board.thread-removed', { threadId, by });
|
|
204
|
+
return t;
|
|
205
|
+
},
|
|
184
206
|
/**
|
|
185
207
|
* A member is stuck: open an ask thread (status `waiting`) with the question as its
|
|
186
208
|
* first post. The runner waits on it; a person answers from either client.
|
|
@@ -208,7 +230,7 @@ export function createBoard({ now = () => Date.now(), newId = null, state = null
|
|
|
208
230
|
/** Every ask still waiting — what a client pins at the top. */
|
|
209
231
|
waiting: () => st.threads.filter((t) => t.kind === 'ask' && t.status === 'waiting').map((t) => ({ ...t })),
|
|
210
232
|
/** The whole board, for the run record and for a resume. */
|
|
211
|
-
state: () => ({ threads: st.threads.map((t) => ({ ...t })), posts: st.posts.map((x) => ({ ...x })) }),
|
|
233
|
+
state: () => ({ threads: st.threads.map((t) => ({ ...t })), posts: st.posts.map((x) => ({ ...x })), ...(st.removed?.length ? { removed: [...st.removed] } : {}) }),
|
|
212
234
|
|
|
213
235
|
// ── findings, as before: a finding is a post in its task's thread ──
|
|
214
236
|
add(list, { by = null } = {}) {
|
package/team-record.js
CHANGED
|
@@ -99,7 +99,7 @@ export function foldRun(run, ev) {
|
|
|
99
99
|
if (p.checkpoint) run.checkpoint = p.checkpoint;
|
|
100
100
|
break;
|
|
101
101
|
case 'run.stop-requested': run.stopRequested = at; break;
|
|
102
|
-
case 'board.thread': case 'board.post': case 'board.decision': case 'board.thread-status':
|
|
102
|
+
case 'board.thread': case 'board.post': case 'board.decision': case 'board.thread-status': case 'board.thread-removed':
|
|
103
103
|
run.threads = foldBoard(run.threads || emptyBoardState(), ev);
|
|
104
104
|
if (type === 'board.thread-status' && p.status !== 'waiting' && run.status === 'waiting' && !(run.threads.threads || []).some((t) => t.kind === 'ask' && t.status === 'waiting')) run.status = 'answered';
|
|
105
105
|
break;
|
package/team-trail.js
CHANGED
|
@@ -16,6 +16,7 @@ export function teamLine(ev) {
|
|
|
16
16
|
case 'run.resumed': return { type: 'status', text: `team ${ev.team} resumed${(ev.carried || []).length ? ` (${ev.carried.length} task${ev.carried.length === 1 ? '' : 's'} carried over)` : ''}` };
|
|
17
17
|
case 'board.post': return ev.post && ev.post.kind !== 'finding' ? { type: 'status', text: `${ev.post.by} ${ev.post.replyTo ? 'replied' : 'posted'} (${ev.post.kind}): ${String(ev.post.text || '').slice(0, 120)}` } : null;
|
|
18
18
|
case 'board.decision': return { type: 'status', text: `${ev.by || 'someone'} ${ev.status} a post` };
|
|
19
|
+
case 'board.thread-removed': return { type: 'status', text: `${ev.by || 'someone'} removed a thread from the board` };
|
|
19
20
|
case 'task.note': return { type: 'status', text: `${role}: ${ev.text}` };
|
|
20
21
|
case 'task.handoff': return { type: 'status', text: `${role} handed off ${ev.from ? `from ${ev.from} ` : ''}to ${ev.to} by ${ev.by || 'person'}${ev.reason ? ` — ${ev.reason}` : ''}` };
|
|
21
22
|
case 'task.step': return null;
|
|
@@ -63,6 +64,7 @@ export function teamLanes(prev, ev) {
|
|
|
63
64
|
case 'task.waiting': if (lanes.tasks[ev.taskId]) lanes.tasks[ev.taskId] = { ...lanes.tasks[ev.taskId], status: 'waiting', waitingOn: ev.threadId }; lanes.waiting = [...(lanes.waiting || []), ev.threadId]; break;
|
|
64
65
|
case 'task.done': case 'task.failed': if (lanes.tasks[ev.taskId]) lanes.tasks[ev.taskId] = { ...lanes.tasks[ev.taskId], status: ev.status || 'ok', ms: ev.ms }; break;
|
|
65
66
|
case 'board.thread-status': if (ev.status !== 'waiting' && lanes.waiting) lanes.waiting = lanes.waiting.filter((x) => x !== ev.threadId); break;
|
|
67
|
+
case 'board.thread-removed': if (lanes.waiting) lanes.waiting = lanes.waiting.filter((x) => x !== ev.threadId); break;
|
|
66
68
|
case 'run.waiting': lanes.waiting = [...(lanes.waiting || []), ev.threadId]; break;
|
|
67
69
|
case 'run.done': lanes.status = ev.status; lanes.usage = ev.usage; break;
|
|
68
70
|
default: break;
|