@nanmicoder/dsh-agent-teams 0.1.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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +72 -0
  3. package/assets/agent-teams/action-celebrating.png +0 -0
  4. package/assets/agent-teams/action-reporting.png +0 -0
  5. package/assets/agent-teams/action-sending.png +0 -0
  6. package/assets/agent-teams/action-sleeping.png +0 -0
  7. package/assets/agent-teams/action-thinking.png +0 -0
  8. package/assets/agent-teams/action-working.png +0 -0
  9. package/assets/agent-teams/data-analyst.png +0 -0
  10. package/assets/agent-teams/designer.png +0 -0
  11. package/assets/agent-teams/docs-coordinator.png +0 -0
  12. package/assets/agent-teams/engineer.png +0 -0
  13. package/assets/agent-teams/qa-engineer.png +0 -0
  14. package/assets/agent-teams/researcher.png +0 -0
  15. package/assets/agent-teams/security-reviewer.png +0 -0
  16. package/assets/agent-teams/team-lead.png +0 -0
  17. package/cordis.patch.yml +21 -0
  18. package/lib/client/ActivityPanel.js +340 -0
  19. package/lib/client/AgentTeamsCard.js +74 -0
  20. package/lib/client/activity-model.js +70 -0
  21. package/lib/client/agent-teams-card-definition.js +85 -0
  22. package/lib/client/artwork.js +40 -0
  23. package/lib/client/index.js +33 -0
  24. package/lib/client.js +1235 -0
  25. package/lib/client.js.map +1 -0
  26. package/lib/event-types.js +12 -0
  27. package/lib/events.js +60 -0
  28. package/lib/index.js +172 -0
  29. package/lib/members.js +168 -0
  30. package/lib/snapshot.js +155 -0
  31. package/lib/state.js +461 -0
  32. package/lib/tools.js +749 -0
  33. package/lib/types/client/ActivityPanel.d.ts +64 -0
  34. package/lib/types/client/AgentTeamsCard.d.ts +24 -0
  35. package/lib/types/client/activity-model.d.ts +31 -0
  36. package/lib/types/client/agent-teams-card-definition.d.ts +44 -0
  37. package/lib/types/client/artwork.d.ts +19 -0
  38. package/lib/types/client/index.d.ts +11 -0
  39. package/lib/types/event-types.d.ts +103 -0
  40. package/lib/types/events.d.ts +37 -0
  41. package/lib/types/index.d.ts +42 -0
  42. package/lib/types/members.d.ts +86 -0
  43. package/lib/types/snapshot.d.ts +83 -0
  44. package/lib/types/state.d.ts +144 -0
  45. package/lib/types/tools.d.ts +40 -0
  46. package/lib/types/types.d.ts +73 -0
  47. package/lib/types.js +11 -0
  48. package/package.json +108 -0
@@ -0,0 +1,70 @@
1
+ /** Pure relationship projections used by the AgentTeams activity panel. */
2
+ /**
3
+ * Whether an expanded activity panel still belongs to the current session.
4
+ *
5
+ * The panel is mounted through a body portal, so React does not remount it
6
+ * when the conversation route changes. Ownership keeps an expanded panel
7
+ * from leaking onto the new-session screen (or another conversation) while
8
+ * its local open state is being reset.
9
+ */
10
+ export function activityPanelExpandedForSession(open, owner, current) {
11
+ return open && owner !== undefined && owner === current;
12
+ }
13
+ /** Group tasks by their precomputed dependency depth. */
14
+ export function taskStages(tasks) {
15
+ const byDepth = new Map();
16
+ for (const task of tasks) {
17
+ const depth = Number.isFinite(task.depth) ? Math.max(0, Math.floor(task.depth)) : 0;
18
+ const stage = byDepth.get(depth) ?? [];
19
+ stage.push(task);
20
+ byDepth.set(depth, stage);
21
+ }
22
+ return [...byDepth.entries()]
23
+ .sort(([left], [right]) => left - right)
24
+ .map(([depth, stageTasks]) => ({
25
+ depth,
26
+ tasks: stageTasks.slice().sort((left, right) => left.id.localeCompare(right.id, 'en', { numeric: true })),
27
+ }));
28
+ }
29
+ /**
30
+ * Return the complete upstream/downstream chain around one task.
31
+ *
32
+ * Traversal uses both dependency directions and remains cycle-safe, so the UI
33
+ * can highlight every handoff related to the focused task even if malformed
34
+ * durable data contains a cycle.
35
+ */
36
+ export function relatedTaskIds(taskId, tasks) {
37
+ const byId = new Map(tasks.map((task) => [task.id, task]));
38
+ if (!byId.has(taskId))
39
+ return new Set();
40
+ const dependents = new Map();
41
+ for (const task of tasks) {
42
+ for (const dependency of task.dependencies) {
43
+ const targets = dependents.get(dependency) ?? [];
44
+ targets.push(task.id);
45
+ dependents.set(dependency, targets);
46
+ }
47
+ }
48
+ const related = new Set();
49
+ const upstreamSeen = new Set();
50
+ const downstreamSeen = new Set();
51
+ const visitUpstream = (id) => {
52
+ if (upstreamSeen.has(id))
53
+ return;
54
+ upstreamSeen.add(id);
55
+ related.add(id);
56
+ for (const dependency of byId.get(id)?.dependencies ?? [])
57
+ visitUpstream(dependency);
58
+ };
59
+ const visitDownstream = (id) => {
60
+ if (downstreamSeen.has(id))
61
+ return;
62
+ downstreamSeen.add(id);
63
+ related.add(id);
64
+ for (const dependent of dependents.get(id) ?? [])
65
+ visitDownstream(dependent);
66
+ };
67
+ visitUpstream(taskId);
68
+ visitDownstream(taskId);
69
+ return related;
70
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * AgentTeams conversation card: a lightweight in-conversation summary shown
3
+ * when a team is created — the captain's name, the member roster with whale
4
+ * avatars, and an entry point that re-activates the top-right activity
5
+ * panel (useful after the floater was closed, or when re-opening an old
6
+ * session for review).
7
+ *
8
+ * The fold anchors to the Harness's durable `tool/call` + `tool/result`
9
+ * records for `agent_teams_create`. Those are first-party session events, so
10
+ * the card survives restarts without writing an out-of-repo event type.
11
+ * @module dsh-agent-teams/client/card
12
+ */
13
+ /** Parse the only create-call fields the historic card owns. */
14
+ export function parseAgentTeamsCreateArgs(value) {
15
+ try {
16
+ const parsed = JSON.parse(value);
17
+ if (typeof parsed !== 'object' || parsed === null || !('name' in parsed) || typeof parsed.name !== 'string') {
18
+ return undefined;
19
+ }
20
+ const name = parsed.name.trim();
21
+ if (name === '')
22
+ return undefined;
23
+ const cleaned = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
24
+ return { teamId: cleaned === '' ? 'team' : cleaned, name };
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
30
+ /** Durable first-party tool events folded into one keyed Chat node. */
31
+ export const agentTeamsCardDefinition = {
32
+ kind: 'agent-teams',
33
+ target: 'chat',
34
+ match: (event) => {
35
+ if (event.type === 'tool/call' && event.data.name === 'agent_teams_create') {
36
+ return parseAgentTeamsCreateArgs(event.data.arguments) === undefined
37
+ ? null
38
+ : { id: String(event.data.callId), role: 'start' };
39
+ }
40
+ if (event.type === 'tool/result' && event.data.message.source.kind === 'tool') {
41
+ return { id: String(event.data.message.source.callId), role: 'update' };
42
+ }
43
+ return null;
44
+ },
45
+ start: (_context, match) => {
46
+ if (match.event.type !== 'tool/call') {
47
+ throw new Error('agent-teams card start requires agent_teams_create tool/call');
48
+ }
49
+ const parsed = parseAgentTeamsCreateArgs(match.event.data.arguments);
50
+ if (parsed === undefined)
51
+ throw new Error('agent-teams card start requires valid create arguments');
52
+ return { ...parsed, accepted: false };
53
+ },
54
+ update: (context, match) => {
55
+ if (match.event.type !== 'tool/result')
56
+ return context.state;
57
+ const failed = match.event.data.error !== undefined
58
+ || match.event.data.message.content.some((block) => block.type === 'tool-result' && block.isError === true);
59
+ if (failed)
60
+ return context.state;
61
+ return { ...context.state, accepted: true };
62
+ },
63
+ buildViewNode: (context) => {
64
+ if (context.start === undefined)
65
+ return null;
66
+ const state = context.state;
67
+ if (!state.accepted)
68
+ return null;
69
+ return {
70
+ key: context.key,
71
+ kind: 'agent-teams',
72
+ id: context.id,
73
+ target: 'chat',
74
+ anchorSeq: context.start.event.seq,
75
+ location: context.start.location,
76
+ visibility: 'visible',
77
+ data: {
78
+ teamId: state.teamId,
79
+ captainSessionId: '',
80
+ teamName: state.name,
81
+ members: [],
82
+ },
83
+ };
84
+ },
85
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Shared whale artwork lookup for the activity panel and the conversation
3
+ * card: role keywords map to the packaged role images; the captain always
4
+ * uses the lead whale.
5
+ * @module dsh-agent-teams/client/artwork
6
+ */
7
+ /** Artwork route prefix served by the plugin host half. */
8
+ export const ART_BASE = '/plugins/dsh-agent-teams/assets/';
9
+ /** Whale role artwork per role keyword. */
10
+ const ROLE_ART = [
11
+ [/resear|analys|investig|explor|data|study|研究|分析|数据|调查|探索|调研/, 'researcher.png'],
12
+ [/engineer|dev\b|server|backend|\bapi\b|runtime|watcher|contract|工程|后端|服务|接口|开发|代码|编程/, 'engineer.png'],
13
+ [/\bqa\b|test|verif|quality|测试|质量/, 'qa-engineer.png'],
14
+ [/design|\bui\b|\bux\b|front|theme|accessib|设计|前端|主题/, 'designer.png'],
15
+ [/secur|audit|risk|threat|review|安全|审计|审查|风险/, 'security-reviewer.png'],
16
+ [/docs|writer|product|spec|coordin|撰写|文案|写作|文档|协调/, 'docs-coordinator.png'],
17
+ [/release|\bbuild\b|deploy|\bops\b|\bci\b|ship|发布|构建|部署/, 'engineer.png'],
18
+ ];
19
+ /** Captain artwork (always the lead whale). */
20
+ export const LEAD_ART = `${ART_BASE}team-lead.png`;
21
+ /** Status action artwork per member activity. */
22
+ export const ACTION_ART = {
23
+ working: `${ART_BASE}action-working.png`,
24
+ idle: `${ART_BASE}action-sleeping.png`,
25
+ unknown: `${ART_BASE}action-thinking.png`,
26
+ };
27
+ /**
28
+ * Member artwork URL, or null when no role matches (initial-letter fallback).
29
+ * @param name - the member's display name.
30
+ * @param role - the member's role text.
31
+ * @returns the artwork URL, or null when unmatched.
32
+ */
33
+ export function memberArtUrl(name, role) {
34
+ const identity = `${name} ${role}`.toLowerCase();
35
+ for (const [pattern, art] of ROLE_ART) {
36
+ if (pattern.test(identity))
37
+ return `${ART_BASE}${art}`;
38
+ }
39
+ return null;
40
+ }
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createRoot } from 'react-dom/client';
3
+ import { ActivityPanel } from "./ActivityPanel.js";
4
+ import { AgentTeamsCard } from "./AgentTeamsCard.js";
5
+ import { agentTeamsCardDefinition } from "./agent-teams-card-definition.js";
6
+ /** Required services: conversation nodes, slots, and sessions navigation. */
7
+ export const inject = ['conversationEvents', 'slots', 'sessions'];
8
+ /**
9
+ * Mount the floater through a body portal (the web shell has no top-right
10
+ * slot) and register the in-conversation team card, whose "activity panel"
11
+ * button re-activates the floater via a window event — the recovery path
12
+ * for a closed floater or a re-opened session.
13
+ */
14
+ export function apply(ctx) {
15
+ const host = document.createElement('div');
16
+ host.dataset.agentTeamsHost = '';
17
+ document.body.appendChild(host);
18
+ const root = createRoot(host);
19
+ root.render(_jsx(ActivityPanel, { sessionsList: ctx.sessions.list, openSession: (id) => { ctx.sessions.open(id); } }));
20
+ ctx.effect(() => () => {
21
+ root.unmount();
22
+ host.remove();
23
+ }, 'agent-teams: activity panel');
24
+ ctx.conversationEvents.register(agentTeamsCardDefinition);
25
+ ctx.slots.inject('conversation.chat.node', () => ctx.slots.register({
26
+ name: 'conversation.chat.node',
27
+ key: 'agent-teams',
28
+ inject: () => ({
29
+ openSession: (id) => { ctx.sessions.open(id); },
30
+ currentSessionId: () => ctx.sessions.list.getSnapshot().current,
31
+ }),
32
+ }, AgentTeamsCard));
33
+ }