@nanmicoder/dsh-agent-teams 0.1.2 → 0.1.4

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/lib/tools.js CHANGED
@@ -13,7 +13,7 @@ import { defineTool } from '@deepseek-ai/dsh-tools';
13
13
  import { join } from 'node:path';
14
14
  import { appendTeamEvent, captainSessionOf } from "./events.js";
15
15
  import { appendMailbox, archiveTeamDir, CAPTAIN_KEY, createMessage, createTeamDir, findTeamByCaptain, findTeamByParticipant, readMailbox, readTeam, sanitizeKey, transitionError, unsatisfiedDependencies, withTeamLock, writeTeam, } from "./state.js";
16
- import { deliverToMember, interruptMember, memberActivity, spawnMember, } from "./members.js";
16
+ import { deliverToMember, installMemberSelectionRuntime, interruptMember, memberActivity, resolveMemberLlmSelection, spawnMember, } from "./members.js";
17
17
  /** The caller agent, or a loud failure for non-agent callers. */
18
18
  function requireCaptain(exec) {
19
19
  if (!exec.agent) {
@@ -126,6 +126,7 @@ export function steerCaptainReport(captain, from, content) {
126
126
  * @param config - resolved tool config.
127
127
  */
128
128
  export function registerAgentTeamsTools(ctx, config) {
129
+ const memberSelections = installMemberSelectionRuntime(ctx, config.stateDir);
129
130
  ctx.tools.register(defineTool({
130
131
  name: 'agent_teams_create',
131
132
  description: 'Create a new AgentTeams team: you (the calling agent) become the captain. A captain leads one team at a time; create tasks and members afterwards with agent_teams_add_member and agent_teams_create_task.',
@@ -191,11 +192,12 @@ export function registerAgentTeamsTools(ctx, config) {
191
192
  }));
192
193
  ctx.tools.register(defineTool({
193
194
  name: 'agent_teams_add_member',
194
- description: 'Add a member to your team: spawns a durable continuable subagent with a member persona. The member waits for your messages and works on assigned tasks; it can message you and teammates. One team per captain, members are capped by config.',
195
+ description: 'Add a durable continuable member. By default it snapshots the captain\'s current LLM provider, model, and reasoning effort with no user prompt. Supply provider/model only for an explicitly requested role-specific route. The member waits for messages, works on assigned tasks, and can message the team.',
195
196
  parameters: {
196
197
  name: { type: 'string', required: true, description: 'Unique member name inside the team.' },
197
198
  role: { type: 'string', description: 'Role of the member (e.g. researcher, engineer, reviewer).' },
198
- model: { type: 'string', description: 'Optional model override for this member (defaults to the captain\'s model).' },
199
+ provider: { type: 'string', description: 'Optional LLM provider route. Use only when the user explicitly requests a different provider; requires model.' },
200
+ model: { type: 'string', description: 'Optional model override. Omit for the captain\'s current model (or the configured memberModel default).' },
199
201
  },
200
202
  output: {
201
203
  schema: {
@@ -204,12 +206,15 @@ export function registerAgentTeamsTools(ctx, config) {
204
206
  properties: {
205
207
  member_name: { type: 'string', required: true },
206
208
  member_id: { type: 'string', required: true },
209
+ provider: { type: 'string', required: true },
210
+ model: { type: 'string', required: true },
211
+ reasoning_effort: { type: 'string' },
207
212
  status: { type: 'string', required: true },
208
213
  },
209
214
  },
210
215
  render: (args, value) => [{
211
216
  type: 'text',
212
- text: `Member "${value.member_name}" added (subagent id ${value.member_id}, status ${value.status}).`,
217
+ text: `Member "${value.member_name}" added (subagent id ${value.member_id}, ${value.provider}/${value.model}${value.reasoning_effort === undefined ? '' : `, reasoning ${value.reasoning_effort}`}, status ${value.status}).`,
213
218
  }],
214
219
  },
215
220
  async execute(args, exec) {
@@ -232,15 +237,22 @@ export function registerAgentTeamsTools(ctx, config) {
232
237
  if (fresh.members.filter((candidate) => candidate.status !== 'removed').length >= config.maxMembers) {
233
238
  throw new Error(`team "${fresh.name}" is at its member cap (${config.maxMembers})`);
234
239
  }
240
+ const selection = await resolveMemberLlmSelection(ctx, captain, {
241
+ provider: args.provider,
242
+ model: args.model,
243
+ defaultModel: config.memberModel,
244
+ }, exec.signal);
235
245
  const member = {
236
246
  id: '',
237
247
  name: memberName,
238
248
  role: args.role,
239
- model: args.model,
249
+ provider: selection.provider,
250
+ model: selection.model,
251
+ reasoningEffort: selection.reasoningEffort,
240
252
  joinedAt: Date.now(),
241
253
  status: 'idle',
242
254
  };
243
- await spawnMember(ctx, memberRuntime(config), captain, fresh, member, config.stateDir, exec.signal);
255
+ await spawnMember(ctx, memberRuntime(config), memberSelections, selection, captain, fresh, member, config.stateDir, exec.signal);
244
256
  fresh.members.push(member);
245
257
  await writeTeam(stateRoot, fresh);
246
258
  appendTeamEvent(ctx, captainSessionOf(ctx, fresh.captainSessionId, captain.session), 'agent-teams/member-added', {
@@ -249,7 +261,16 @@ export function registerAgentTeamsTools(ctx, config) {
249
261
  name: member.name,
250
262
  ...member.role !== undefined ? { role: member.role } : {},
251
263
  });
252
- return { member_name: member.name, member_id: member.id, status: member.status };
264
+ return {
265
+ member_name: member.name,
266
+ member_id: member.id,
267
+ provider: selection.provider,
268
+ model: selection.model,
269
+ ...selection.reasoningEffort === undefined
270
+ ? {}
271
+ : { reasoning_effort: selection.reasoningEffort },
272
+ status: member.status,
273
+ };
253
274
  });
254
275
  },
255
276
  }));
@@ -620,7 +641,9 @@ export function registerAgentTeamsTools(ctx, config) {
620
641
  .map((member) => ({
621
642
  name: member.name,
622
643
  role: member.role ?? '',
644
+ provider: member.provider ?? '',
623
645
  model: member.model ?? '',
646
+ reasoning_effort: member.reasoningEffort ?? '',
624
647
  status: member.status,
625
648
  activity: member.id !== '' ? (activity.get(member.id) ?? 'unknown') : 'unspawned',
626
649
  }));
@@ -718,7 +741,6 @@ export function registerAgentTeamsTools(ctx, config) {
718
741
  function memberRuntime(config) {
719
742
  return {
720
743
  provider: config.memberProvider,
721
- model: config.memberModel,
722
744
  maxDepth: config.memberMaxDepth,
723
745
  };
724
746
  }
@@ -729,7 +751,11 @@ function renderStatus(value) {
729
751
  `Team "${team.team_name}"${team.description ? ` — ${team.description}` : ''}`,
730
752
  `Viewing as: ${team.viewer}`,
731
753
  `Members (${team.members.length}):`,
732
- ...team.members.map((member) => ` - ${member.name} [${member.role}] ${member.status}/${member.activity}`),
754
+ ...team.members.map((member) => {
755
+ const route = member.provider && member.model ? ` · ${member.provider}/${member.model}` : '';
756
+ const effort = member.reasoning_effort ? ` · reasoning ${member.reasoning_effort}` : '';
757
+ return ` - ${member.name} [${member.role}] ${member.status}/${member.activity}${route}${effort}`;
758
+ }),
733
759
  `Tasks (${team.tasks.length}):`,
734
760
  ...team.tasks.map((task) => {
735
761
  const deps = task.dependencies.length > 0 ? ` (deps: ${task.dependencies.join(',')})` : '';
@@ -12,17 +12,54 @@
12
12
  * @module dsh-agent-teams/members
13
13
  */
14
14
  import type { Context } from '@deepseek-ai/cordis';
15
- import type { Agent } from '@deepseek-ai/dsh-agent';
15
+ import { type Agent } from '@deepseek-ai/dsh-agent';
16
16
  import type { TeamMember, TeamState } from './types.ts';
17
17
  /** Runtime knobs for member spawning, resolved from plugin config. */
18
18
  export interface MemberRuntimeConfig {
19
19
  /** Registered `ctx.subagents` provider name (must support continuable + persona). */
20
20
  provider: string;
21
- /** Optional model override applied to every member. */
22
- model?: string;
23
21
  /** Child delegation depth cap (0 forbids delegation entirely). */
24
22
  maxDepth?: number;
25
23
  }
24
+ /** Durable provider/model/reasoning snapshot for one member. */
25
+ export interface MemberLlmSelection {
26
+ /** Registered LLM provider route. */
27
+ provider: string;
28
+ /** Provider-owned model id. */
29
+ model: string;
30
+ /** Adapter-owned reasoning effort, absent when the target has no explicit/default effort. */
31
+ reasoningEffort?: string;
32
+ }
33
+ /** Optional member-level route requested by the captain. */
34
+ export interface MemberLlmSelectionRequest {
35
+ /** Explicit LLM provider route; requires an explicit model. */
36
+ provider?: string;
37
+ /** Explicit model id; otherwise the plugin default or captain model is used. */
38
+ model?: string;
39
+ /** Plugin-level member model default. */
40
+ defaultModel?: string;
41
+ }
42
+ /** Process-local bridge between spawn admission and synchronous child setup. */
43
+ export interface MemberSelectionRuntime {
44
+ /** Make one selection visible while Harness materializes the fresh child. */
45
+ withPending<T>(parentSessionId: string, label: string, selection: MemberLlmSelection, operation: () => Promise<T>): Promise<T>;
46
+ }
47
+ /**
48
+ * Resolve one member's complete model selection. Ordinary members snapshot the
49
+ * captain's current request route and reasoning effort. An explicit member
50
+ * provider/model or plugin-level model replaces only that route; the current
51
+ * captain effort remains the inherited policy and is validated against the
52
+ * target model before a child is created.
53
+ */
54
+ export declare function resolveMemberLlmSelection(ctx: Context, captain: Agent, request: MemberLlmSelectionRequest, signal?: AbortSignal): Promise<MemberLlmSelection>;
55
+ /**
56
+ * Install the member selection bridge for every fresh or cold-resumed
57
+ * continuable child. Fresh creation reads the pending in-memory selection;
58
+ * cold resume restores the same selection from the owning team's durable
59
+ * record. Legacy members without a complete saved route retain Harness's
60
+ * descriptor provider/model behavior.
61
+ */
62
+ export declare function installMemberSelectionRuntime(ctx: Context, stateDir: string): MemberSelectionRuntime;
26
63
  /**
27
64
  * The member's system prompt (persona), shadowing the deployment persona for
28
65
  * that child. Self-contained: it replaces the whole persona section.
@@ -42,13 +79,15 @@ export declare function memberWelcome(team: TeamState): string;
42
79
  * `member.id` with its child session id. On failure nothing is persisted.
43
80
  * @param ctx - the plugin context (injects `subagents`).
44
81
  * @param config - member runtime knobs.
82
+ * @param selections - fresh/cold child model-selection bridge.
83
+ * @param llmSelection - resolved provider/model/reasoning snapshot.
45
84
  * @param captain - the exact live captain agent (the calling agent).
46
85
  * @param team - the team record (read-only here).
47
86
  * @param member - the member draft whose `id` is filled on success.
48
87
  * @param stateDir - configured state directory (for the persona).
49
88
  * @param signal - caller cancellation, forwarded to the start.
50
89
  */
51
- export declare function spawnMember(ctx: Context, config: MemberRuntimeConfig, captain: Agent, team: TeamState, member: TeamMember, stateDir: string, signal: AbortSignal): Promise<void>;
90
+ export declare function spawnMember(ctx: Context, config: MemberRuntimeConfig, selections: MemberSelectionRuntime, llmSelection: MemberLlmSelection, captain: Agent, team: TeamState, member: TeamMember, stateDir: string, signal: AbortSignal): Promise<void>;
52
91
  /**
53
92
  * Deliver one message to a member as its next FIFO turn. Best effort: a
54
93
  * failure (member gone or not continuable) is logged and reported as `false`
@@ -73,6 +73,16 @@ export declare function createTeamDir(stateRoot: string, state: TeamState): Prom
73
73
  * @param teamId - the team's sanitized id.
74
74
  */
75
75
  export declare function readTeam(stateRoot: string, teamId: string): Promise<TeamState | undefined>;
76
+ /**
77
+ * Synchronously read one team record while a continuable child is being
78
+ * composed. Harness requires child setup contributions to be synchronous;
79
+ * this narrow boundary lets a cold-resumed member restore its durable model
80
+ * selection before its first request can be published.
81
+ * @param stateRoot - resolved absolute state root directory.
82
+ * @param teamId - the team's sanitized id.
83
+ * @returns the team record, or `undefined` when absent.
84
+ */
85
+ export declare function readTeamSync(stateRoot: string, teamId: string): TeamState | undefined;
76
86
  /**
77
87
  * Persist one team record (inside the caller's lock).
78
88
  * @param stateRoot - resolved absolute state root directory.
@@ -39,8 +39,12 @@ export interface TeamMember {
39
39
  name: string;
40
40
  /** Role description, e.g. `researcher`, `engineer`, `reviewer`. */
41
41
  role?: string;
42
- /** Optional model override for this member. */
42
+ /** Resolved LLM provider route captured when this member was created. */
43
+ provider?: string;
44
+ /** Resolved model captured when this member was created. */
43
45
  model?: string;
46
+ /** Resolved reasoning effort captured from the captain's current session. */
47
+ reasoningEffort?: string;
44
48
  joinedAt: number;
45
49
  status: MemberStatus;
46
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanmicoder/dsh-agent-teams",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "AgentTeams for DeepSeek Harness: multi-agent team collaboration (captain, members, tasks with dependencies, messaging) driven by natural language, with a tree monitor in the web GUI",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -20,8 +20,10 @@
20
20
  "files": [
21
21
  "lib",
22
22
  "assets/agent-teams",
23
+ "assets/readme",
23
24
  "cordis.patch.yml",
24
- "README.md"
25
+ "README.md",
26
+ "README_ZH.md"
25
27
  ],
26
28
  "license": "MIT",
27
29
  "author": {
@@ -83,30 +85,44 @@
83
85
  "verify": "node scripts/verify.mjs && pnpm verify:skill",
84
86
  "prepublishOnly": "pnpm build && pnpm verify"
85
87
  },
86
- "dependencies": {},
87
88
  "peerDependencies": {
88
89
  "@deepseek-ai/cordis": "^4.0.1-rc.1",
89
- "@deepseek-ai/dsh-agent": "^0.0.1-rc.1",
90
- "@deepseek-ai/dsh-client-locale": "^0.0.1-rc.1",
91
- "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.1",
92
- "@deepseek-ai/dsh-client-ui-conversation": "^0.0.1-rc.1",
93
- "@deepseek-ai/dsh-client-ui-primitives": "^0.0.1-rc.1",
94
- "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1",
95
- "@deepseek-ai/dsh-llm": "^0.0.1-rc.1",
96
- "@deepseek-ai/dsh-session": "^0.0.1-rc.1",
97
- "@deepseek-ai/dsh-subagent": "^0.0.1-rc.1",
98
- "@deepseek-ai/dsh-system-prompt": "^0.0.1-rc.1",
99
- "@deepseek-ai/dsh-tools": "^0.0.1-rc.1",
100
- "react": "^18.2.0",
101
- "@deepseek-ai/schemastery": "^3.18.1-rc.1"
90
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
91
+ "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
92
+ "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
93
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
94
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6",
95
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
96
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
97
+ "@deepseek-ai/dsh-session": "^0.1.0-rc.6",
98
+ "@deepseek-ai/dsh-subagent": "^0.1.0-rc.6",
99
+ "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
100
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
101
+ "@deepseek-ai/schemastery": "^3.18.1-rc.1",
102
+ "react": "^18.2.0"
102
103
  },
103
104
  "devDependencies": {
105
+ "@deepseek-ai/cordis": "^4.0.1",
106
+ "@deepseek-ai/dsh-agent": "0.1.0-rc.6",
107
+ "@deepseek-ai/dsh-client-locale": "0.1.0-rc.6",
108
+ "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.6",
109
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.6",
110
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.6",
111
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6",
112
+ "@deepseek-ai/dsh-llm": "0.1.0-rc.6",
113
+ "@deepseek-ai/dsh-session": "0.1.0-rc.6",
114
+ "@deepseek-ai/dsh-subagent": "0.1.0-rc.6",
115
+ "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.6",
116
+ "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
117
+ "@deepseek-ai/dsh-workspace": "0.1.0-rc.6",
118
+ "@deepseek-ai/schemastery": "^3.18.1",
119
+ "@types/node": "^24.13.3",
104
120
  "@types/react": "~18.3.1",
105
- "@types/react-dom": "^19.2.4",
121
+ "@types/react-dom": "^18.3.7",
106
122
  "lightningcss": "^1.33.0",
107
- "tsdown": "0.22.2",
108
- "typescript": "^5.9.3",
109
123
  "react": "^18.2.0",
110
- "react-dom": "^18.2.0"
124
+ "react-dom": "^18.2.0",
125
+ "tsdown": "0.22.2",
126
+ "typescript": "^5.9.3"
111
127
  }
112
- }
128
+ }