@chatpanel/events 0.100.0 → 0.102.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/index.js +1 -1
- package/package.json +1 -1
- package/slash-commands.js +4 -1
- package/team-org.js +133 -2
package/index.js
CHANGED
|
@@ -238,7 +238,7 @@ export { workLogFor, workLogText, workLogEvidence, describeCall, WORKLOG_KINDS }
|
|
|
238
238
|
export { normalizeRequest, subtaskFromRequest, takeUp, takeUpLine, holdsGrants, jobFromSubtask, extendDependents, taskTree, threadRows, MAX_SUBTASKS, MAX_DEPTH, MIN_TAKEUP_FIT } from './team-subtask.js';
|
|
239
239
|
export { teamLine, teamLanes } from './team-trail.js';
|
|
240
240
|
// The org, derived (F8 §17): roles as cards, starters whole, a team's health and shape, the roster, one colour per agent.
|
|
241
|
-
export { promoteRoles, starterTeam, missingStarters, teamHealth, teamShape, describeTeamShape, whereItWorks, rosterRows, agentKind, agentHue, agentColor, agentInitials, roleCardId, cardNumbers, upsertAgents, TEAM_SHAPES, ROSTER_KINDS } from './team-org.js';
|
|
241
|
+
export { promoteRoles, starterTeam, missingStarters, teamHealth, teamShape, describeTeamShape, whereItWorks, rosterRows, agentKind, agentHue, agentColor, agentInitials, roleCardId, cardNumbers, upsertAgents, soloTeam, teamsWithSolos, builtinOrg, isBuiltin, grantChoices, grantsFromChoices, skillChoices, GRANT_INFO, SOLO_BUDGET, TEAM_SHAPES, ROSTER_KINDS } from './team-org.js';
|
|
242
242
|
export { observeInbox, observeStrip, runLanes, spendRows, engineStrip } from './team-observe.js';
|
|
243
243
|
export { mcpDispatchProvider, MCP_TOOL_NAME } from './mcp-dispatch.js';
|
|
244
244
|
export { createManifest, ManifestError, SOURCES } from './manifest.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/events",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.102.0",
|
|
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/slash-commands.js
CHANGED
|
@@ -40,7 +40,9 @@ function recipeItem(recipe) {
|
|
|
40
40
|
// A saved TEAM answers to a slash the same way: `/research <request>` is a request to run
|
|
41
41
|
// it, and the `team` tool does the rest.
|
|
42
42
|
function teamItem(team) {
|
|
43
|
-
|
|
43
|
+
// A solo team (team-org.js soloTeam) IS an agent, invokable on its own: drawn as one.
|
|
44
|
+
const solo = !!team.origin?.agent;
|
|
45
|
+
return { type: 'team', command: team.name || '', icon: solo ? '🧑💻' : '🧑🤝🧑', description: team.description || (solo ? 'Agent' : 'Agent team'), team, ...(solo ? { agent: team.origin.agent } : {}) };
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
/** Skills that are switched on. Absence of the flag means enabled (older records have none). */
|
|
@@ -119,6 +121,7 @@ export function matchSlashTeam(text, teams = []) {
|
|
|
119
121
|
/** What the model receives for a team command: a request to run it, never a prompt expansion. */
|
|
120
122
|
export function teamInvocationText(team, args = '') {
|
|
121
123
|
const a = String(args || '').trim();
|
|
124
|
+
if (team.origin?.agent) return `Run the agent "${team.name}" (a one-role team of that name)${a ? ` on this request: ${a}` : ''}. Use the team tool; if the request is unclear, ask first.`;
|
|
122
125
|
return `Run the saved team "${team.name}"${a ? ` on this request: ${a}` : ''}. Use the team tool; if the request is unclear, ask first.`;
|
|
123
126
|
}
|
|
124
127
|
|
package/team-org.js
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
// Nothing here renders. A client maps `columns` to boxes and arrows, `hue` to a colour, and
|
|
14
14
|
// `kind` to a word; the SVG is its own.
|
|
15
15
|
|
|
16
|
-
import { normalizeTeam, validateTeam, starterTeams, TeamError } from './team.js';
|
|
17
|
-
import { normalizeAgent, engineOf, starterAgents, STARTER_AGENTS, ASSISTANT_ID } from './agent.js';
|
|
16
|
+
import { normalizeTeam, validateTeam, starterTeams, TeamError, GRANT_RE } from './team.js';
|
|
17
|
+
import { normalizeAgent, engineOf, starterAgents, assistantAgent, STARTER_AGENTS, ASSISTANT_ID } from './agent.js';
|
|
18
18
|
|
|
19
19
|
const isRecord = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
20
20
|
const poolList = (pool) => (Array.isArray(pool) ? pool : []).filter((a) => a && a.id);
|
|
@@ -309,3 +309,134 @@ export function upsertAgents(pool, cards) {
|
|
|
309
309
|
const add = (Array.isArray(cards) ? cards : []).filter((c) => c && c.id);
|
|
310
310
|
return [...list.map((a) => add.find((c) => c.id === a.id) || a), ...add.filter((c) => !list.some((a) => a.id === c.id))];
|
|
311
311
|
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* An agent, INVOKABLE on its own: a one-role team named after it (`/researcher`, "ask the
|
|
315
|
+
* researcher to…"), the role standing for the card, the answer the role's own (`merge:
|
|
316
|
+
* first`), under a default budget since a team without one does not run (O1). `origin.agent`
|
|
317
|
+
* marks it so a client can draw it as an agent, not a team. Never stored — derived from the
|
|
318
|
+
* pool every time, so a card edit lands at once and a deleted card takes its command with it.
|
|
319
|
+
*/
|
|
320
|
+
export const SOLO_BUDGET = Object.freeze({ tokens: 40000, ms: 300000 });
|
|
321
|
+
export function soloTeam(agent, { budget = SOLO_BUDGET } = {}) {
|
|
322
|
+
const a = agent && agent.id ? agent : null;
|
|
323
|
+
if (!a || a.enabled === false) return null;
|
|
324
|
+
return normalizeTeam({
|
|
325
|
+
name: String(a.id).toLowerCase(),
|
|
326
|
+
description: `Just ${a.name || a.id}${a.purpose ? ` — ${a.purpose}` : ''}`,
|
|
327
|
+
plan: 'fixed', merge: 'first',
|
|
328
|
+
roles: [{ id: String(a.id).toLowerCase().slice(0, 32), agent: a.id }],
|
|
329
|
+
budget: { ...budget },
|
|
330
|
+
origin: { agent: a.id },
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* The teams a chat can run: the saved ones, then a solo team per enabled pool agent whose
|
|
337
|
+
* id no saved team already claims. What the slash menu, the `team` tool and "run …" all read.
|
|
338
|
+
*/
|
|
339
|
+
export function teamsWithSolos(teams = [], pool = [], opts = {}) {
|
|
340
|
+
const org = builtinOrg(teams, pool);
|
|
341
|
+
const saved = org.teams;
|
|
342
|
+
const taken = new Set(saved.map((t) => String(t.name).toLowerCase()));
|
|
343
|
+
const solos = [];
|
|
344
|
+
for (const a of org.pool) {
|
|
345
|
+
if (taken.has(String(a.id).toLowerCase()) || !(Array.isArray(a.appliesTo) ? a.appliesTo : ['jobs']).includes('jobs')) continue;
|
|
346
|
+
const t = soloTeam(a, opts);
|
|
347
|
+
if (t) { solos.push(t); taken.add(t.name); }
|
|
348
|
+
}
|
|
349
|
+
// The Assistant too: `/assistant <request>` runs the chat's own model as a one-role team —
|
|
350
|
+
// on the board, on a scorecard — where a plain turn is neither.
|
|
351
|
+
if (!taken.has(ASSISTANT_ID)) { const t = soloTeam(assistantAgent(), opts); if (t) solos.push({ ...t, builtin: true }); }
|
|
352
|
+
return [...saved, ...solos];
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* THE BUILT-IN ORG — the standing agents and the starter teams ship with the product, present
|
|
357
|
+
* and runnable without a click (the mock's "built-in" chips), and DERIVED, never stored: the
|
|
358
|
+
* `agents` and `teams` sections hold only what a person made, edited or switched off. A saved
|
|
359
|
+
* record with a built-in's id or name REPLACES it (an edit, or `enabled: false`), so the
|
|
360
|
+
* sections stay small and a product update reaches every unedited built-in.
|
|
361
|
+
*
|
|
362
|
+
* • every starter team a person has not saved is here, promoted (its roles as cards) and
|
|
363
|
+
* marked `builtin`, with the cards it stands on;
|
|
364
|
+
* • every starter agent not in the pool is here, marked `builtin`;
|
|
365
|
+
* • the Assistant stays the fixed card it is (agent.js assistantAgent).
|
|
366
|
+
* Returns `{ teams, pool }` — what every list, the slash menu and the runner read.
|
|
367
|
+
*/
|
|
368
|
+
export function builtinOrg(teams = [], pool = []) {
|
|
369
|
+
const savedTeams = (Array.isArray(teams) ? teams : []).filter((t) => t && t.name);
|
|
370
|
+
const savedNames = new Set(savedTeams.map((t) => String(t.name).toLowerCase()));
|
|
371
|
+
const outPool = [...poolList(pool)];
|
|
372
|
+
const have = () => new Set(outPool.map((a) => String(a.id)));
|
|
373
|
+
const outTeams = [...savedTeams];
|
|
374
|
+
for (const st of starterTeams()) {
|
|
375
|
+
if (savedNames.has(st.name)) continue;
|
|
376
|
+
const { team, agents } = starterTeam(st.name, outPool, { now: () => 0 });
|
|
377
|
+
outTeams.push({ ...team, builtin: true });
|
|
378
|
+
const ids = have();
|
|
379
|
+
for (const a of agents) if (!ids.has(a.id)) { const { createdAt, ...card } = a; outPool.push({ ...card, builtin: true }); }
|
|
380
|
+
}
|
|
381
|
+
const ids = have();
|
|
382
|
+
for (const a of starterAgents()) if (!ids.has(a.id)) outPool.push({ ...a, builtin: true });
|
|
383
|
+
return { teams: outTeams, pool: outPool };
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/** Is this record the product's, not a person's — drawn with the built-in chip, toggled rather than deleted. */
|
|
387
|
+
export const isBuiltin = (rec) => !!rec?.builtin;
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* The GRANTS a person can give, as choices — not a free field of ids nobody has seen. Grouped
|
|
391
|
+
* the way they read: what the agent may reach, which connected servers, what work it may do
|
|
392
|
+
* (only an agent-tool engine can use those). `servers` are the connected MCP servers
|
|
393
|
+
* (`{ id, name? }`), each its own `mcp:<id>` choice under the umbrella `mcp`.
|
|
394
|
+
*/
|
|
395
|
+
export const GRANT_INFO = Object.freeze({
|
|
396
|
+
data: ['Your data', 'notes, meetings and past chats — search and read'],
|
|
397
|
+
web: ['Web', 'search the web and fetch pages'],
|
|
398
|
+
history: ['Chat history', 'search past chats only'],
|
|
399
|
+
mcp: ['Every connected server', 'all MCP servers you connected; pick servers below to narrow'],
|
|
400
|
+
shell: ['Run commands', 'a shell in its working directory'],
|
|
401
|
+
'fs:write': ['Write files', 'edit files in its working directory'],
|
|
402
|
+
'scm:read': ['Read the repository', 'the checkout and its hub'],
|
|
403
|
+
'scm:push': ['Push its branch', 'its own cp/<project>/<job> branch, never main'],
|
|
404
|
+
'scm:pr': ['Open a pull request', ''],
|
|
405
|
+
'scm:merge': ['Merge', 'only where the org\'s gate allows it'],
|
|
406
|
+
});
|
|
407
|
+
export function grantChoices({ servers = [] } = {}) {
|
|
408
|
+
const item = (id) => ({ id, label: GRANT_INFO[id]?.[0] || id, hint: GRANT_INFO[id]?.[1] || '' });
|
|
409
|
+
const groups = [
|
|
410
|
+
{ id: 'reach', label: 'May reach', items: ['data', 'web', 'history', 'mcp'].map(item) },
|
|
411
|
+
];
|
|
412
|
+
const srv = (Array.isArray(servers) ? servers : []).filter((s) => s && s.id);
|
|
413
|
+
if (srv.length) groups.push({ id: 'servers', label: 'Connected servers', items: srv.map((s) => ({ id: `mcp:${s.id}`, label: s.name || s.id, hint: 'this server only' })) });
|
|
414
|
+
groups.push({ id: 'work', label: 'May do (agent-tool engines only)', items: ['shell', 'fs:write', 'scm:read', 'scm:push', 'scm:pr', 'scm:merge'].map(item) });
|
|
415
|
+
return groups;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** Grants from checked ids: nothing checked is `none`; `mcp` swallows the per-server picks. */
|
|
419
|
+
export function grantsFromChoices(checked = []) {
|
|
420
|
+
const set = new Set((Array.isArray(checked) ? checked : []).filter((g) => g && g !== 'none' && GRANT_RE.test(String(g))));
|
|
421
|
+
if (set.has('mcp')) for (const g of [...set]) if (g.startsWith('mcp:')) set.delete(g);
|
|
422
|
+
return set.size ? [...set] : ['none'];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* The SKILLS a person can attach, from the skills they have: the shared `skills` section
|
|
427
|
+
* (written or installed, `{ command, name?, description?, enabled? }`) and any names an
|
|
428
|
+
* agent already carries that are not there (kept, marked `missing`, so a card never loses a
|
|
429
|
+
* skill the list forgot). A skill's id is its command — what `skill_open` and `fit` match on.
|
|
430
|
+
*/
|
|
431
|
+
export function skillChoices(skills = [], { current = [] } = {}) {
|
|
432
|
+
const out = [];
|
|
433
|
+
const seen = new Set();
|
|
434
|
+
for (const s of Array.isArray(skills) ? skills : []) {
|
|
435
|
+
const id = String(s?.command || s?.name || '').trim();
|
|
436
|
+
if (!id || seen.has(id) || s?.enabled === false) continue;
|
|
437
|
+
seen.add(id);
|
|
438
|
+
out.push({ id, label: s.name && s.name !== id ? `${s.name} (/${id})` : `/${id}`, hint: String(s.description || '').slice(0, 120) });
|
|
439
|
+
}
|
|
440
|
+
for (const id of Array.isArray(current) ? current : []) if (id && !seen.has(id)) { seen.add(id); out.push({ id, label: id, hint: 'not among your skills now', missing: true }); }
|
|
441
|
+
return out;
|
|
442
|
+
}
|