@wowyuarm/dsh-agent-team 0.1.1 → 0.1.2

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 (61) hide show
  1. package/package.json +31 -23
  2. package/packages/agent-team/lib/index.js +51 -62
  3. package/packages/agent-team/lib/ledger.js +63 -39
  4. package/packages/agent-team/lib/spec.js +10 -4
  5. package/packages/agent-team/lib/typert.host.js +60 -73
  6. package/packages/agent-team/lib/typert.remote-client.d.ts +3 -3
  7. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  8. package/packages/agent-team/lib/typert.remote-client.js +41 -56
  9. package/packages/agent-team/lib/types/index.d.ts +10 -13
  10. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  11. package/packages/agent-team/lib/types/ledger.d.ts +11 -11
  12. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  13. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  14. package/packages/agent-team/lib/types/types.d.ts +20 -12
  15. package/packages/agent-team/lib/types/types.d.ts.map +1 -1
  16. package/packages/agent-team/preset/team-member/agent.cordis.yml +2 -2
  17. package/packages/client-agent-team/README.md +3 -2
  18. package/packages/client-agent-team/README.zh.md +3 -2
  19. package/packages/client-agent-team/lib/client.js +758 -184
  20. package/packages/client-agent-team/lib/client.js.map +1 -1
  21. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +1 -2
  22. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  23. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +20 -17
  24. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +3 -1
  25. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  26. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +38 -2
  27. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  28. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +16 -2
  29. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  30. package/packages/client-agent-team/lib/types/client/TeamComposer.js +22 -1
  31. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
  32. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
  33. package/packages/client-agent-team/lib/types/client/TeamConversation.js +3 -3
  34. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +7 -2
  35. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  36. package/packages/client-agent-team/lib/types/client/TeamMessage.js +123 -11
  37. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +4 -0
  38. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  39. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +78 -8
  40. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  41. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  42. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -2
  43. package/packages/client-agent-team/lib/types/client/index.js +1 -1
  44. package/packages/client-agent-team/lib/types/client/locales.d.ts +8 -2
  45. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  46. package/packages/client-agent-team/lib/types/client/locales.js +8 -2
  47. package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts +35 -0
  48. package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts.map +1 -0
  49. package/packages/client-agent-team/lib/types/client/sidebar-drag.js +70 -0
  50. package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts +27 -0
  51. package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts.map +1 -0
  52. package/packages/client-agent-team/lib/types/client/sidebar-order.js +170 -0
  53. package/packages/client-agent-team/lib/types/client/slots.d.ts +2 -2
  54. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  55. package/packages/client-agent-team/lib/types/client/task-refs.d.ts +20 -0
  56. package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -0
  57. package/packages/client-agent-team/lib/types/client/task-refs.js +58 -0
  58. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +13 -0
  59. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  60. package/packages/client-agent-team/lib/types/client/team-formatters.js +26 -2
  61. package/packages/tool-agent-team/lib/index.js +4 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wowyuarm/dsh-agent-team",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Help humans organize tasks and let agents collaborate - a Team plugin for DeepSeek Harness",
5
5
  "repository": {
6
6
  "type": "git",
@@ -111,20 +111,22 @@
111
111
  "./package.json": "./package.json"
112
112
  },
113
113
  "peerDependencies": {
114
+ "@deepseek-ai/cordis": "^4.0.1",
114
115
  "@deepseek-ai/dsh-agent": ">=0.1.1-rc.2 <0.2.0",
115
116
  "@deepseek-ai/dsh-agent-default-model": ">=0.1.1-rc.2 <0.2.0",
116
- "@deepseek-ai/dsh-agent-tool-presentation": ">=0.1.1-rc.2 <0.2.0",
117
117
  "@deepseek-ai/dsh-agent-presets": ">=0.1.1-rc.2 <0.2.0",
118
- "@deepseek-ai/dsh-tool-bash": ">=0.1.1-rc.2 <0.2.0",
119
- "@deepseek-ai/dsh-tool-pwsh": ">=0.1.1-rc.2 <0.2.0",
120
- "@deepseek-ai/dsh-tool-fs": ">=0.1.1-rc.2 <0.2.0",
121
- "@deepseek-ai/dsh-tool-fs-search": ">=0.1.1-rc.2 <0.2.0",
122
- "@deepseek-ai/dsh-tool-jobs": ">=0.1.1-rc.2 <0.2.0",
123
- "@deepseek-ai/dsh-skill-filesystem": ">=0.1.1-rc.2 <0.2.0",
124
- "@deepseek-ai/dsh-tool-skill": ">=0.1.1-rc.2 <0.2.0",
125
- "@deepseek-ai/dsh-tool-todo": ">=0.1.1-rc.2 <0.2.0",
126
- "@deepseek-ai/dsh-tool-web": ">=0.1.1-rc.2 <0.2.0",
118
+ "@deepseek-ai/dsh-agent-tool-presentation": ">=0.1.1-rc.2 <0.2.0",
119
+ "@deepseek-ai/dsh-api-remotes": ">=0.1.1-rc.2 <0.2.0",
127
120
  "@deepseek-ai/dsh-brand": ">=0.1.1-rc.2 <0.2.0",
121
+ "@deepseek-ai/dsh-client-locale": ">=0.1.1-rc.2 <0.2.0",
122
+ "@deepseek-ai/dsh-client-runtime": ">=0.1.1-rc.2 <0.2.0",
123
+ "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.1-rc.2 <0.2.0",
124
+ "@deepseek-ai/dsh-client-ui-layout": ">=0.1.1-rc.2 <0.2.0",
125
+ "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.1-rc.2 <0.2.0",
126
+ "@deepseek-ai/dsh-client-ui-settings-general": ">=0.1.1-rc.2 <0.2.0",
127
+ "@deepseek-ai/dsh-client-ui-sidebar": ">=0.1.1-rc.2 <0.2.0",
128
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.1.1-rc.2 <0.2.0",
129
+ "@deepseek-ai/dsh-client-ui-workspace": ">=0.1.1-rc.2 <0.2.0",
128
130
  "@deepseek-ai/dsh-home-paths": ">=0.1.1-rc.2 <0.2.0",
129
131
  "@deepseek-ai/dsh-invariants": ">=0.1.1-rc.2 <0.2.0",
130
132
  "@deepseek-ai/dsh-llm": ">=0.1.1-rc.2 <0.2.0",
@@ -132,21 +134,27 @@
132
134
  "@deepseek-ai/dsh-scope": ">=0.1.1-rc.2 <0.2.0",
133
135
  "@deepseek-ai/dsh-session": ">=0.1.1-rc.2 <0.2.0",
134
136
  "@deepseek-ai/dsh-session-persistence": ">=0.1.1-rc.2 <0.2.0",
137
+ "@deepseek-ai/dsh-skill-filesystem": ">=0.1.1-rc.2 <0.2.0",
135
138
  "@deepseek-ai/dsh-storage-domain": ">=0.1.1-rc.2 <0.2.0",
139
+ "@deepseek-ai/dsh-tool-bash": ">=0.1.1-rc.2 <0.2.0",
140
+ "@deepseek-ai/dsh-tool-fs": ">=0.1.1-rc.2 <0.2.0",
141
+ "@deepseek-ai/dsh-tool-fs-search": ">=0.1.1-rc.2 <0.2.0",
142
+ "@deepseek-ai/dsh-tool-jobs": ">=0.1.1-rc.2 <0.2.0",
143
+ "@deepseek-ai/dsh-tool-pwsh": ">=0.1.1-rc.2 <0.2.0",
144
+ "@deepseek-ai/dsh-tool-skill": ">=0.1.1-rc.2 <0.2.0",
145
+ "@deepseek-ai/dsh-tool-todo": ">=0.1.1-rc.2 <0.2.0",
146
+ "@deepseek-ai/dsh-tool-web": ">=0.1.1-rc.2 <0.2.0",
136
147
  "@deepseek-ai/dsh-tools": ">=0.1.1-rc.2 <0.2.0",
137
148
  "@deepseek-ai/dsh-typert-protocol": ">=0.1.1-rc.2 <0.2.0",
138
149
  "@deepseek-ai/dsh-workspace": ">=0.1.1-rc.2 <0.2.0",
139
- "@deepseek-ai/cordis": "^4.0.1",
140
- "@deepseek-ai/dsh-api-remotes": ">=0.1.1-rc.2 <0.2.0",
141
- "@deepseek-ai/dsh-client-locale": ">=0.1.1-rc.2 <0.2.0",
142
- "@deepseek-ai/dsh-client-runtime": ">=0.1.1-rc.2 <0.2.0",
143
- "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.1-rc.2 <0.2.0",
144
- "@deepseek-ai/dsh-client-ui-workspace": ">=0.1.1-rc.2 <0.2.0",
145
- "@deepseek-ai/dsh-client-ui-layout": ">=0.1.1-rc.2 <0.2.0",
146
- "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.1-rc.2 <0.2.0",
147
- "@deepseek-ai/dsh-client-ui-settings-general": ">=0.1.1-rc.2 <0.2.0",
148
- "@deepseek-ai/dsh-client-ui-sidebar": ">=0.1.1-rc.2 <0.2.0",
149
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.1-rc.2 <0.2.0",
150
150
  "react": "^18.2.0"
151
- }
151
+ },
152
+ "keywords": [
153
+ "deepseek-harness",
154
+ "dsh",
155
+ "dsh-plugin",
156
+ "agent-team",
157
+ "multi-agent",
158
+ "ai-agents"
159
+ ]
152
160
  }
@@ -44,6 +44,7 @@ import { mkdir, rm, writeFile } from 'node:fs/promises';
44
44
  import { join } from 'node:path';
45
45
  import { isDeepStrictEqual } from 'node:util';
46
46
  import { Service } from '@deepseek-ai/cordis';
47
+ import { installModelSelection } from '@deepseek-ai/dsh-agent';
47
48
  import { createUserMessage } from '@deepseek-ai/dsh-llm';
48
49
  import { dshHomePath } from '@deepseek-ai/dsh-home-paths';
49
50
  import { scopeOf } from '@deepseek-ai/dsh-scope';
@@ -83,13 +84,13 @@ export const AGENT_TEAM_TOOL_NAMES = Object.freeze([
83
84
  let AgentTeam = (() => {
84
85
  let _classSuper = TypertRemoteService;
85
86
  let _instanceExtraInitializers = [];
87
+ let _resolveTaskRefs_decorators;
86
88
  let _membersForClient_decorators;
87
89
  let _changes_decorators;
88
90
  let _createChannel_decorators;
89
91
  let _updateChannel_decorators;
90
92
  let _addMember_decorators;
91
93
  let _recoverMember_decorators;
92
- let _restartMember_decorators;
93
94
  let _updateMember_decorators;
94
95
  let _changeTask_decorators;
95
96
  let _joinChannel_decorators;
@@ -107,13 +108,13 @@ let AgentTeam = (() => {
107
108
  return class AgentTeam extends _classSuper {
108
109
  static {
109
110
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
111
+ _resolveTaskRefs_decorators = [Remote('resolveTaskRefs')];
110
112
  _membersForClient_decorators = [Remote('members')];
111
113
  _changes_decorators = [Remote('changes')];
112
114
  _createChannel_decorators = [Remote('createChannel')];
113
115
  _updateChannel_decorators = [Remote('updateChannel')];
114
116
  _addMember_decorators = [Remote('addMember')];
115
117
  _recoverMember_decorators = [Remote('recoverMember')];
116
- _restartMember_decorators = [Remote('restartMember')];
117
118
  _updateMember_decorators = [Remote('updateMember')];
118
119
  _changeTask_decorators = [Remote('changeTask')];
119
120
  _joinChannel_decorators = [Remote('joinChannel')];
@@ -128,13 +129,13 @@ let AgentTeam = (() => {
128
129
  _threadObservations_decorators = [Remote('threadObservations')];
129
130
  _threadHistory_decorators = [Remote('threadHistory')];
130
131
  _view_decorators = [Remote('view')];
132
+ __esDecorate(this, null, _resolveTaskRefs_decorators, { kind: "method", name: "resolveTaskRefs", static: false, private: false, access: { has: obj => "resolveTaskRefs" in obj, get: obj => obj.resolveTaskRefs }, metadata: _metadata }, null, _instanceExtraInitializers);
131
133
  __esDecorate(this, null, _membersForClient_decorators, { kind: "method", name: "membersForClient", static: false, private: false, access: { has: obj => "membersForClient" in obj, get: obj => obj.membersForClient }, metadata: _metadata }, null, _instanceExtraInitializers);
132
134
  __esDecorate(this, null, _changes_decorators, { kind: "method", name: "changes", static: false, private: false, access: { has: obj => "changes" in obj, get: obj => obj.changes }, metadata: _metadata }, null, _instanceExtraInitializers);
133
135
  __esDecorate(this, null, _createChannel_decorators, { kind: "method", name: "createChannel", static: false, private: false, access: { has: obj => "createChannel" in obj, get: obj => obj.createChannel }, metadata: _metadata }, null, _instanceExtraInitializers);
134
136
  __esDecorate(this, null, _updateChannel_decorators, { kind: "method", name: "updateChannel", static: false, private: false, access: { has: obj => "updateChannel" in obj, get: obj => obj.updateChannel }, metadata: _metadata }, null, _instanceExtraInitializers);
135
137
  __esDecorate(this, null, _addMember_decorators, { kind: "method", name: "addMember", static: false, private: false, access: { has: obj => "addMember" in obj, get: obj => obj.addMember }, metadata: _metadata }, null, _instanceExtraInitializers);
136
138
  __esDecorate(this, null, _recoverMember_decorators, { kind: "method", name: "recoverMember", static: false, private: false, access: { has: obj => "recoverMember" in obj, get: obj => obj.recoverMember }, metadata: _metadata }, null, _instanceExtraInitializers);
137
- __esDecorate(this, null, _restartMember_decorators, { kind: "method", name: "restartMember", static: false, private: false, access: { has: obj => "restartMember" in obj, get: obj => obj.restartMember }, metadata: _metadata }, null, _instanceExtraInitializers);
138
139
  __esDecorate(this, null, _updateMember_decorators, { kind: "method", name: "updateMember", static: false, private: false, access: { has: obj => "updateMember" in obj, get: obj => obj.updateMember }, metadata: _metadata }, null, _instanceExtraInitializers);
139
140
  __esDecorate(this, null, _changeTask_decorators, { kind: "method", name: "changeTask", static: false, private: false, access: { has: obj => "changeTask" in obj, get: obj => obj.changeTask }, metadata: _metadata }, null, _instanceExtraInitializers);
140
141
  __esDecorate(this, null, _joinChannel_decorators, { kind: "method", name: "joinChannel", static: false, private: false, access: { has: obj => "joinChannel" in obj, get: obj => obj.joinChannel }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -163,6 +164,8 @@ let AgentTeam = (() => {
163
164
  domain = __runInitializers(this, _instanceExtraInitializers);
164
165
  ledger;
165
166
  handles = new Map();
167
+ /** Live selection refs let model edits take effect without disposing the Session. */
168
+ modelSelections = new Map();
166
169
  diagnostics = new Map();
167
170
  runtimeErrors = new Map();
168
171
  /** Agent ids with a turn in flight; restarts must wait for the boundary. */
@@ -228,6 +231,7 @@ let AgentTeam = (() => {
228
231
  await this.lifecycleTail;
229
232
  await Promise.all([...this.handles.values()].map(handle => handle.dispose()));
230
233
  this.handles.clear();
234
+ this.modelSelections.clear();
231
235
  this.runningAgents.clear();
232
236
  await domain.close();
233
237
  }, 'agentTeam.dispose');
@@ -261,6 +265,18 @@ let AgentTeam = (() => {
261
265
  return this.requireLedger().listMembers().map(member => this.memberStatus(member));
262
266
  }
263
267
  /** Return only this Workspace's current Member projection to the Client. */
268
+ /** Read-only navigation lookup for branded Task refs found in message bodies. */
269
+ resolveTaskRefs(request) {
270
+ this.requireWorkspace(request.workspaceId);
271
+ const seen = new Set();
272
+ const taskRefs = request.taskRefs.filter(taskRef => {
273
+ if (seen.has(taskRef))
274
+ return false;
275
+ seen.add(taskRef);
276
+ return true;
277
+ });
278
+ return Object.freeze({ resolved: Object.freeze(this.requireLedger().resolveTaskRefs(request.workspaceId, taskRefs)) });
279
+ }
264
280
  membersForClient(request) {
265
281
  this.requireWorkspace(request.workspaceId);
266
282
  return this.members()
@@ -371,6 +387,7 @@ let AgentTeam = (() => {
371
387
  await handle.dispose();
372
388
  this.handles.delete(request.memberId);
373
389
  }
390
+ this.modelSelections.delete(request.memberId);
374
391
  this.diagnostics.delete(request.memberId);
375
392
  this.clearMemberNotificationState(request.memberId);
376
393
  return Object.freeze({ receipt: result.value.receipt, status: this.memberStatus(result.value.member) });
@@ -404,47 +421,6 @@ let AgentTeam = (() => {
404
421
  this.steerResume(member, this.resumeText(kind ?? 'unspecified failure', { operatorRequested: true }));
405
422
  return Object.freeze({ status: this.memberStatus(member) });
406
423
  }
407
- /**
408
- * Restart one enabled Member's live session in place: dispose the handle and
409
- * reactivate on the same session id, so the transcript, private memory, and
410
- * every ledger fact survive while tools and prompt templates are re-minted
411
- * from the running plugin. The audit operation changes no projection state.
412
- */
413
- async restartMember(request) {
414
- return this.enqueueLifecycle(async () => {
415
- this.requireAccepting();
416
- this.requireWorkspace(request.workspaceId);
417
- const stored = this.requireLedger().getMember(request.memberId);
418
- if (stored === undefined || stored.workspaceId !== request.workspaceId)
419
- throw new Error(`unknown Member '${request.memberId}' in workspace '${request.workspaceId}'`);
420
- if (stored.state !== 'enabled')
421
- throw new Error(`Agent Member '${stored.handle}' is ${stored.state}; only enabled Members can be restarted`);
422
- const active = this.handles.get(request.memberId);
423
- if (active === undefined)
424
- throw new Error(`Agent Member '${stored.handle}' has no active session to restart`);
425
- if (this.runningAgents.has(active.agent.id))
426
- throw new Error(`Agent Member '${stored.handle}' is still running; wait for the current turn to end before restarting`);
427
- const result = await this.requireLedger().restartMember({ ...request, actor: agentTeamHumanActor() });
428
- if (result.committed)
429
- this.emitCommitted(result.value.receipt);
430
- // Drop the old handle's transient state: pending recovery episodes and
431
- // error markers belong to the disposed agent, not to the Member.
432
- this.recovery.stopTracking(request.memberId);
433
- this.runtimeErrors.delete(active.agent.id);
434
- this.notifiedInbox.delete(request.memberId);
435
- await active.dispose();
436
- this.handles.delete(request.memberId);
437
- this.diagnostics.delete(request.memberId);
438
- await this.activateMember(stored);
439
- const reactivated = this.handles.get(request.memberId);
440
- if (reactivated === undefined) {
441
- // Reactivation failed; the activation diagnostic carries the reason and
442
- // the audit record stays honest about the attempt.
443
- throw new Error(`Agent Member '${stored.handle}' failed to reactivate: ${this.diagnostics.get(request.memberId) ?? 'unknown error'}`);
444
- }
445
- return Object.freeze({ receipt: result.value.receipt, status: this.memberStatus(stored) });
446
- });
447
- }
448
424
  /** Ledger handle for log lines; falls back to the raw id when unknown. */
449
425
  memberLabel(memberId) {
450
426
  return this.ledger?.getMember(memberId)?.handle ?? memberId;
@@ -494,7 +470,7 @@ let AgentTeam = (() => {
494
470
  if (handle === undefined)
495
471
  throw new Error(`member '${member.handle}' has no active session`);
496
472
  const notifications = this.requireLedger().notificationFacts(member.memberId, { workspaceId: member.workspaceId });
497
- const body = notifications.length === 0 ? text : `${text}\n\n${this.notificationText(notifications)}`;
473
+ const body = notifications.length === 0 ? text : `${text}\n\n${this.notificationText(notifications, member.memberId)}`;
498
474
  const hint = createUserMessage({
499
475
  content: [{ type: 'text', text: body }],
500
476
  source: { kind: 'plugin', plugin: '@wowyuarm/dsh-agent-team', form: 'notice', summary: 'Recovery: continue your interrupted work.' },
@@ -511,10 +487,10 @@ let AgentTeam = (() => {
511
487
  this.steerResume(member, this.resumeText(kind, { attempt }));
512
488
  }
513
489
  /**
514
- * Human edit of one Member's mutable facts. A model change restarts an
515
- * active Member: single-Session Members read their selection only at
516
- * activation, so the quiescing dispose plus reactivation applies the new
517
- * provider/model immediately instead of at the next Host restart.
490
+ * Human edit of one Member's mutable facts. A live model selection is
491
+ * updated in place: disposing an Agent emits session/disposed, which makes
492
+ * the Web Client permanently mark the same Session id unavailable even when
493
+ * Team immediately recreates it.
518
494
  */
519
495
  async updateMember(request) {
520
496
  return this.enqueueLifecycle(async () => {
@@ -526,9 +502,10 @@ let AgentTeam = (() => {
526
502
  const stored = result.value.member;
527
503
  const active = this.handles.get(request.memberId);
528
504
  if (active !== undefined && !isDeepStrictEqual(previous?.model ?? undefined, stored.model ?? undefined)) {
529
- await active.dispose();
530
- this.handles.delete(request.memberId);
531
- await this.activateMember(stored);
505
+ const selection = this.modelSelections.get(request.memberId);
506
+ if (selection === undefined)
507
+ throw new Error(`Agent Member '${stored.handle}' has no live model selection`);
508
+ selection.current = stored.model ?? this.ctx.agentDefaultModel.currentSelection();
532
509
  }
533
510
  return Object.freeze({ receipt: result.value.receipt, status: this.memberStatus(stored) });
534
511
  });
@@ -544,6 +521,7 @@ let AgentTeam = (() => {
544
521
  await handle.dispose();
545
522
  this.handles.delete(request.memberId);
546
523
  }
524
+ this.modelSelections.delete(request.memberId);
547
525
  this.diagnostics.delete(request.memberId);
548
526
  this.clearMemberNotificationState(request.memberId);
549
527
  await this.cleanupRemovedMember(result.value.member);
@@ -844,9 +822,16 @@ let AgentTeam = (() => {
844
822
  await this.initializePrivateMemory(member.privateMemoryPath);
845
823
  const persisted = knownSessions !== undefined ? knownSessions.has(member.sessionId)
846
824
  : (await this.ctx.sessionPersistence.list()).some(header => header.id === member.sessionId);
825
+ // AgentOptions declares only provider/model. Install the full selection
826
+ // through the public Agent model-selection seam so reasoning effort is
827
+ // applied to the next request and not lost during activation.
828
+ const selection = member.model ?? this.ctx.agentDefaultModel.currentSelection();
829
+ const agentOptions = { provider: selection.provider, model: selection.model };
830
+ const selected = { current: selection, assembled: undefined };
847
831
  const setup = async (agentCtx) => {
848
832
  await this.ctx.agentPresets.mount(agentCtx, member.presetId);
849
833
  this.validateMemberPreset(agentCtx);
834
+ installModelSelection(agentCtx, selected);
850
835
  return {
851
836
  commit: () => {
852
837
  const agent = agentCtx.agent;
@@ -857,10 +842,6 @@ let AgentTeam = (() => {
857
842
  },
858
843
  };
859
844
  };
860
- // A Member-pinned selection wins; otherwise the Host default applies at
861
- // every activation, so an override survives restarts and a cleared one
862
- // re-reads the live default.
863
- const agentOptions = member.model ?? this.ctx.agentDefaultModel.currentSelection();
864
845
  created = persisted
865
846
  ? await this.ctx.agents.resume({ resumeSessionId: member.sessionId, agentOptions, setup })
866
847
  : await this.ctx.agents.create({
@@ -871,12 +852,14 @@ let AgentTeam = (() => {
871
852
  });
872
853
  await workspace.attachSession(member.sessionId);
873
854
  this.handles.set(member.memberId, created);
855
+ this.modelSelections.set(member.memberId, selected);
874
856
  this.diagnostics.delete(member.memberId);
875
857
  this.nameMemberSession(member, created.agent);
876
858
  this.notifyMember(created.agent);
877
859
  }
878
860
  catch (error) {
879
861
  await created?.dispose();
862
+ this.modelSelections.delete(member.memberId);
880
863
  this.diagnostics.set(member.memberId, error instanceof Error ? error.message : String(error));
881
864
  }
882
865
  finally {
@@ -998,7 +981,7 @@ let AgentTeam = (() => {
998
981
  if (existingHint !== undefined)
999
982
  agent.inbox.remove(existingHint.id);
1000
983
  const hint = createUserMessage({
1001
- content: [{ type: 'text', text: this.notificationText(notifications) }],
984
+ content: [{ type: 'text', text: this.notificationText(notifications, member.memberId) }],
1002
985
  source: { kind: 'plugin', plugin: '@wowyuarm/dsh-agent-team', form: 'notice', summary: 'Team Inbox has unread work.' },
1003
986
  });
1004
987
  this.notifiedInbox.set(member.memberId, signature);
@@ -1010,7 +993,7 @@ let AgentTeam = (() => {
1010
993
  throw error;
1011
994
  }
1012
995
  }
1013
- notificationText(notifications) {
996
+ notificationText(notifications, readerId) {
1014
997
  const maxCharacters = 32 * 1024;
1015
998
  const sections = ['Team Inbox has unread work.'];
1016
999
  let characterCount = sections[0].length;
@@ -1036,19 +1019,19 @@ let AgentTeam = (() => {
1036
1019
  ? 'human' : this.requireLedger().getMember(fact.message.sender)?.handle ?? fact.message.sender;
1037
1020
  const detail = ['Direct Team mention', `From: ${sender}`, `Channel: ${item.channelRef}`,
1038
1021
  `Task: ${item.task.taskRef}`, `Thread: ${item.thread.threadRef}`, `Message ref: ${fact.message.messageRef}`,
1039
- `Revision: ${item.thread.revision}`, `Message: ${this.boundedNotificationBody(fact.message.body)}`].join('\n');
1022
+ `Message: ${this.boundedNotificationBody(fact.message.body)}`].join('\n');
1040
1023
  if (append(detail))
1041
1024
  detailedFactCount += 1;
1042
1025
  }
1043
1026
  else if (fact.kind === 'activity') {
1044
- const detail = `${this.activityNotification(fact.activity)}\nThread: ${item.thread.threadRef}\nRevision: ${item.thread.revision}`;
1027
+ const detail = `${this.activityNotification(fact.activity, readerId)}\nThread: ${item.thread.threadRef}`;
1045
1028
  if (append(detail))
1046
1029
  detailedFactCount += 1;
1047
1030
  }
1048
1031
  }
1049
1032
  const ordinaryCount = facts.filter(entry => entry.fact.kind === 'message' && !entry.direct).length;
1050
1033
  if (ordinaryCount > 0)
1051
- append(`Task ${item.task.taskRef}: ${ordinaryCount} unread update${ordinaryCount === 1 ? '' : 's'}; revision ${item.thread.revision}.`);
1034
+ append(`Task ${item.task.taskRef}: ${ordinaryCount} unread update${ordinaryCount === 1 ? '' : 's'}.`);
1052
1035
  }
1053
1036
  if (omitted)
1054
1037
  sections.push('More unread work remains in team_inbox; the automatic context is bounded.');
@@ -1059,9 +1042,15 @@ let AgentTeam = (() => {
1059
1042
  const limit = 8 * 1024;
1060
1043
  return body.length <= limit ? body : `${body.slice(0, limit)}\n[Message body truncated; use team_thread read for the full Message.]`;
1061
1044
  }
1062
- activityNotification(activity) {
1045
+ activityNotification(activity, readerId) {
1063
1046
  const actor = activity.actor === AGENT_TEAM_HUMAN_MEMBER_ID
1064
1047
  ? 'human' : this.requireLedger().getMember(activity.actor)?.handle ?? activity.actor;
1048
+ // Early acceptance completes the reader's own open Claim inside the accept
1049
+ // operation: say so plainly, or the owner keeps working on a done Task.
1050
+ if (activity.kind === 'accept' && activity.actor === AGENT_TEAM_HUMAN_MEMBER_ID && readerId !== undefined
1051
+ && activity.completedClaimRefs?.some(claimRef => this.requireLedger().getClaim(claimRef)?.owner === readerId)) {
1052
+ return `Team Task update\n${actor} accepted Task ${activity.taskRef} and your open Claim was completed with it. No further work is needed.`;
1053
+ }
1065
1054
  if (activity.kind === 'claim' || activity.kind === 'done' || activity.kind === 'release') {
1066
1055
  return `Team Task update\n${actor} ${activity.kind} Claim ${activity.claimRef} on Task ${activity.taskRef}.`;
1067
1056
  }
@@ -191,31 +191,6 @@ export class AgentTeamLedger {
191
191
  resumeMember(request) {
192
192
  return this.setMemberState(request, 'enabled');
193
193
  }
194
- /**
195
- * Audit record of one in-place Member session restart. The operation changes
196
- * no projection state — identity, transcript, and memory are untouched — so
197
- * replay treats it as a marker while the Host re-mints the live handle.
198
- */
199
- restartMember(request) {
200
- return this.enqueue(async () => {
201
- const existing = this.state.byRequest.get(request.requestId);
202
- if (existing !== undefined) {
203
- this.assertSameMemberRestart(existing, request);
204
- return this.resolved(this.memberResult(existing));
205
- }
206
- this.assertHumanActor(request.actor);
207
- const member = this.requireMember(request.memberId);
208
- if (member.state !== 'enabled')
209
- throw new Error(`Agent Member '${member.handle}' is ${member.state}; only enabled Members can be restarted`);
210
- const operation = Object.freeze({
211
- ...this.operationBase(request, this.nextSequence()), kind: 'team/member-session-restarted',
212
- data: Object.freeze({ member }),
213
- });
214
- await this.table.put(operation.operationId, operation);
215
- this.apply(operation);
216
- return this.committed(this.memberResult(operation));
217
- });
218
- }
219
194
  getMember(memberId) {
220
195
  return this.state.members.get(memberId);
221
196
  }
@@ -477,13 +452,24 @@ export class AgentTeamLedger {
477
452
  throw new Error(`Task '${task.taskRef}' is already ${task.resolution}`);
478
453
  if (request.action === 'close' && task.resolution === 'closed')
479
454
  throw new Error(`Task '${task.taskRef}' is already closed`);
480
- if (request.action === 'accept' && task.status !== 'in_review')
481
- throw new Error(`Task '${task.taskRef}' must be in_review before acceptance`);
455
+ // Acceptance normally waits for every Claim to finish (in_review);
456
+ // a Human may also accept early while work is in progress, which then
457
+ // completes the still-active Claims inside the same atomic operation.
458
+ if (request.action === 'accept' && task.status !== 'in_review' && task.status !== 'in_progress') {
459
+ throw new Error(`Task '${task.taskRef}' must be in_review or in_progress before acceptance`);
460
+ }
461
+ const priorActiveClaims = [...this.state.claims.values()].filter(claim => claim.taskRef === task.taskRef && claim.state === 'active');
462
+ if (request.action === 'accept' && task.status === 'in_progress' && priorActiveClaims.length === 0) {
463
+ throw new Error(`Task '${task.taskRef}' has no active Claims to complete for early acceptance`);
464
+ }
482
465
  if (request.action === 'reopen' && task.resolution === 'open')
483
466
  throw new Error(`Task '${task.taskRef}' is already open`);
484
467
  const sequence = this.nextSequence();
485
- const claims = [...this.state.claims.values()].filter(claim => claim.taskRef === task.taskRef).map(claim => request.action === 'close' && claim.state === 'active' ? Object.freeze({ ...claim, state: 'released' }) : claim);
468
+ const claims = [...this.state.claims.values()].filter(claim => claim.taskRef === task.taskRef).map(claim => request.action === 'close' && claim.state === 'active' ? Object.freeze({ ...claim, state: 'released' })
469
+ : request.action === 'accept' && claim.state === 'active' ? Object.freeze({ ...claim, state: 'done' })
470
+ : claim);
486
471
  const releasedClaims = claims.filter(claim => claim.state === 'released' && this.state.claims.get(claim.claimRef)?.state === 'active');
472
+ const completedClaims = claims.filter(claim => claim.state === 'done' && this.state.claims.get(claim.claimRef)?.state === 'active');
487
473
  const resolution = request.action === 'reopen' ? 'open' : request.action === 'accept' ? 'accepted' : 'closed';
488
474
  const status = resolution === 'accepted' ? 'done' : resolution === 'closed' ? 'closed'
489
475
  : this.deriveTaskStatus(task.taskRef, claims);
@@ -491,12 +477,13 @@ export class AgentTeamLedger {
491
477
  const nextThread = Object.freeze({ ...thread, revision: sequence });
492
478
  const activity = Object.freeze({ activityRef: this.ref('activity'), kind: request.action,
493
479
  taskRef: task.taskRef, threadRef: task.threadRef, actor: request.actor.memberId, sequence,
494
- ...(releasedClaims.length === 0 ? {} : { releasedClaimRefs: Object.freeze(releasedClaims.map(claim => claim.claimRef)) }) });
480
+ ...(releasedClaims.length === 0 ? {} : { releasedClaimRefs: Object.freeze(releasedClaims.map(claim => claim.claimRef)) }),
481
+ ...(completedClaims.length === 0 ? {} : { completedClaimRefs: Object.freeze(completedClaims.map(claim => claim.claimRef)) }) });
495
482
  const inbox = request.action === 'close'
496
483
  ? this.closeThreadInbox(activity, thread.threadRef)
497
484
  : request.action === 'reopen'
498
485
  ? this.reopenThreadInbox(activity, thread.threadRef)
499
- : this.inboxDelta();
486
+ : this.acceptThreadInbox(activity, thread.threadRef, completedClaims.map(claim => claim.owner));
500
487
  const operation = Object.freeze({
501
488
  ...this.operationBase(request, sequence), kind: 'team/task-changed',
502
489
  data: Object.freeze({ workspaceId: request.workspaceId, baseRevision: request.baseRevision,
@@ -745,6 +732,21 @@ export class AgentTeamLedger {
745
732
  getTask(taskRef) {
746
733
  return this.state.tasks.get(taskRef);
747
734
  }
735
+ getClaim(claimRef) {
736
+ return this.state.claims.get(claimRef);
737
+ }
738
+ /** Navigation facts for message-body Task refs; unknown refs are omitted. */
739
+ resolveTaskRefs(workspaceId, taskRefs) {
740
+ const numbers = this.taskNumbers(workspaceId);
741
+ const resolved = [];
742
+ for (const taskRef of taskRefs) {
743
+ const task = this.state.tasks.get(taskRef);
744
+ if (task === undefined || this.state.channels.get(task.channelRef)?.workspaceId !== workspaceId)
745
+ continue;
746
+ resolved.push(Object.freeze({ taskRef: task.taskRef, channelRef: task.channelRef, threadRef: task.threadRef, taskNumber: numbers.get(task.taskRef) ?? 0 }));
747
+ }
748
+ return resolved;
749
+ }
748
750
  view(request, memberId) {
749
751
  const limit = request.limit ?? 20;
750
752
  const direction = request.direction ?? 'after';
@@ -1001,7 +1003,7 @@ export class AgentTeamLedger {
1001
1003
  if (operation.kind === 'team/member-added') {
1002
1004
  assertHuman();
1003
1005
  const { member, channelRefs } = operation.data;
1004
- if (projection.members.has(member.memberId) || channelRefs.length === 0 || new Set(channelRefs).size !== channelRefs.length)
1006
+ if (projection.members.has(member.memberId) || new Set(channelRefs).size !== channelRefs.length)
1005
1007
  throw new Error('invalid Member creation');
1006
1008
  this.addRef(refs, member.memberId);
1007
1009
  for (const channelRef of channelRefs) {
@@ -1202,12 +1204,18 @@ export class AgentTeamLedger {
1202
1204
  const expectedResolution = activity.kind === 'accept' ? 'accepted' : activity.kind === 'close' ? 'closed' : 'open';
1203
1205
  const expectedStatus = expectedResolution === 'accepted' ? 'done' : expectedResolution === 'closed' ? 'closed'
1204
1206
  : this.deriveTaskStatus(task.taskRef, operation.data.claims);
1207
+ const expectedCompletedClaimRefs = operation.data.claims
1208
+ .filter(claim => projection.claims.get(claim.claimRef)?.state === 'active' && claim.state === 'done')
1209
+ .map(claim => claim.claimRef);
1205
1210
  if (task.resolution !== expectedResolution || task.status !== expectedStatus
1206
1211
  || (activity.kind !== 'close' && activity.releasedClaimRefs !== undefined)
1212
+ || (activity.kind !== 'accept' && activity.completedClaimRefs !== undefined)
1207
1213
  || (activity.kind === 'close' && !this.sameList(activity.releasedClaimRefs ?? [], operation.data.claims
1208
1214
  .filter(claim => projection.claims.get(claim.claimRef)?.state === 'active' && claim.state === 'released')
1209
- .map(claim => claim.claimRef))))
1215
+ .map(claim => claim.claimRef)))
1216
+ || !this.sameList(activity.completedClaimRefs ?? [], expectedCompletedClaimRefs)) {
1210
1217
  throw new Error('invalid Task state transition');
1218
+ }
1211
1219
  const priorClaims = [...projection.claims.values()].filter(claim => claim.taskRef === task.taskRef).sort((left, right) => left.claimRef.localeCompare(right.claimRef));
1212
1220
  const operationClaims = [...operation.data.claims].sort((left, right) => left.claimRef.localeCompare(right.claimRef));
1213
1221
  if (priorClaims.length !== operationClaims.length || priorClaims.some((claim, index) => claim.claimRef !== operationClaims[index]?.claimRef))
@@ -1218,7 +1226,9 @@ export class AgentTeamLedger {
1218
1226
  throw new Error('invalid Task Claim identity');
1219
1227
  const legal = activity.kind === 'close'
1220
1228
  ? (previousClaim.state === 'active' ? claim.state === 'released' : claim.state === previousClaim.state)
1221
- : claim.state === previousClaim.state;
1229
+ : activity.kind === 'accept'
1230
+ ? (previousClaim.state === 'active' ? claim.state === 'done' : claim.state === previousClaim.state)
1231
+ : claim.state === previousClaim.state;
1222
1232
  if (!legal)
1223
1233
  throw new Error('invalid Task Claim transition');
1224
1234
  }
@@ -1227,7 +1237,7 @@ export class AgentTeamLedger {
1227
1237
  ? this.closeThreadInboxFrom(projection, activity, thread.threadRef)
1228
1238
  : activity.kind === 'reopen'
1229
1239
  ? this.reopenThreadInboxFrom(projection, activity, thread.threadRef)
1230
- : this.inboxDelta();
1240
+ : this.acceptThreadInboxFrom(projection, activity, thread.threadRef, expectedCompletedClaimRefs);
1231
1241
  if (!isDeepStrictEqual(operation.data.inbox, expectedInbox))
1232
1242
  throw new Error('invalid Task inbox projection');
1233
1243
  this.validateInboxDelta(operation.data.inbox, projection, refs, [], [], [activity]);
@@ -1648,6 +1658,25 @@ export class AgentTeamLedger {
1648
1658
  activityRef: activity.activityRef, sequence: activity.sequence }));
1649
1659
  return this.inboxDelta([], removed, [], [...projection.directMarkers.values()].filter(marker => marker.threadRef === threadRef), activityMarkers, [...projection.activityMarkers.values()].filter(marker => marker.threadRef === threadRef));
1650
1660
  }
1661
+ /**
1662
+ * Early acceptance completes open Claims inside the accept operation; their
1663
+ * owners learn about it through activity markers regardless of whether they
1664
+ * still follow the Thread. Plain accepts (no completed Claims) stay silent.
1665
+ */
1666
+ acceptThreadInbox(activity, threadRef, completedOwners) {
1667
+ if (completedOwners.length === 0)
1668
+ return this.inboxDelta();
1669
+ const recipients = [...new Set(completedOwners)].filter(memberId => memberId !== activity.actor);
1670
+ const markers = recipients.map(memberId => Object.freeze({ memberId, threadRef,
1671
+ activityRef: activity.activityRef, sequence: activity.sequence }));
1672
+ return this.inboxDelta([], [], [], [], markers);
1673
+ }
1674
+ acceptThreadInboxFrom(projection, activity, threadRef, completedClaimRefs) {
1675
+ if (completedClaimRefs.length === 0)
1676
+ return this.inboxDelta();
1677
+ const owners = completedClaimRefs.map(claimRef => projection.claims.get(claimRef)?.owner).filter(owner => owner !== undefined);
1678
+ return this.acceptThreadInbox(activity, threadRef, owners);
1679
+ }
1651
1680
  reopenThreadInbox(activity, threadRef) {
1652
1681
  return this.reopenThreadInboxFrom(this.state, activity, threadRef);
1653
1682
  }
@@ -1987,11 +2016,6 @@ export class AgentTeamLedger {
1987
2016
  || !this.sameList(operation.data.channelRefs, this.normalizeUnique(request.channelRefs, 'initial Member Channels')))
1988
2017
  this.throwRequestCollision(request.requestId);
1989
2018
  }
1990
- assertSameMemberRestart(operation, request) {
1991
- if (operation.kind !== 'team/member-session-restarted' || !this.sameActor(operation.actor, request.actor)
1992
- || operation.data.member.memberId !== request.memberId)
1993
- this.throwRequestCollision(request.requestId);
1994
- }
1995
2019
  assertSameMemberState(operation, request, state) {
1996
2020
  const kind = state === 'suspended' ? 'team/member-suspended' : 'team/member-resumed';
1997
2021
  if (operation.kind !== kind || !this.sameActor(operation.actor, request.actor) || operation.data.member.memberId !== request.memberId)
@@ -26,13 +26,18 @@ const operationBase = {
26
26
  const modelSelectionSchema = z.object({
27
27
  provider: z.string().min(1),
28
28
  model: z.string().min(1),
29
- }).strict();
29
+ reasoningEffort: z.string().min(1).transform(value => value).optional(),
30
+ }).strict().transform(({ provider, model, reasoningEffort }) => ({
31
+ provider,
32
+ model,
33
+ ...(reasoningEffort === undefined ? {} : { reasoningEffort }),
34
+ }));
30
35
  const memberSchema = z.object({
31
36
  memberId: memberIdSchema,
32
37
  sessionId: sessionIdSchema,
33
38
  workspaceId: workspaceIdSchema,
34
39
  handle: z.string().min(1),
35
- description: z.string().min(1),
40
+ description: z.string(),
36
41
  presetId: z.string().min(1),
37
42
  // Ledgers written before per-Member model selection existed omit the field.
38
43
  model: modelSelectionSchema.optional(),
@@ -43,7 +48,7 @@ const channelSchema = z.object({
43
48
  channelRef: channelRefSchema,
44
49
  workspaceId: workspaceIdSchema,
45
50
  name: z.string().min(1),
46
- description: z.string().min(1),
51
+ description: z.string(),
47
52
  createdAtSequence: z.number().int().positive(),
48
53
  }).strict();
49
54
  // Ledgers written before message occurredAt or attachments existed store bare
@@ -155,6 +160,7 @@ const taskActivitySchema = z.object({
155
160
  ...activityBase,
156
161
  kind: z.union([z.literal('accept'), z.literal('close'), z.literal('reopen')]),
157
162
  releasedClaimRefs: z.array(claimRefSchema).min(1).optional(),
163
+ completedClaimRefs: z.array(claimRefSchema).min(1).optional(),
158
164
  }).strict();
159
165
  const claimsReleasedActivitySchema = z.object({
160
166
  ...activityBase,
@@ -208,7 +214,7 @@ const storedAgentTeamOperationSchema = z.discriminatedUnion('kind', [
208
214
  ...operationBase,
209
215
  previousOperationId: operationIdSchema.nullable(),
210
216
  kind: z.literal('team/member-added'),
211
- data: z.object({ member: memberSchema, channelRefs: z.array(channelRefSchema).min(1) }).strict(),
217
+ data: z.object({ member: memberSchema, channelRefs: z.array(channelRefSchema) }).strict(),
212
218
  }).strict(),
213
219
  z.object({
214
220
  ...operationBase,