@wowyuarm/dsh-agent-team 0.1.5 → 0.1.7

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 (100) hide show
  1. package/README.md +23 -3
  2. package/README.zh.md +23 -3
  3. package/package.json +50 -43
  4. package/packages/agent-team/README.md +8 -0
  5. package/packages/agent-team/README.zh.md +8 -0
  6. package/packages/agent-team/core-skills/member-skill-manager/SKILL.md +41 -0
  7. package/packages/agent-team/core-skills/member-skill-manager/references/auth-and-config.md +30 -0
  8. package/packages/agent-team/core-skills/member-skill-manager/references/writing-great-skills.md +17 -0
  9. package/packages/agent-team/lib/index.js +168 -37
  10. package/packages/agent-team/lib/ledger.js +440 -18
  11. package/packages/agent-team/lib/member-context.js +7 -3
  12. package/packages/agent-team/lib/member-runtime.js +229 -0
  13. package/packages/agent-team/lib/member-skills.js +94 -0
  14. package/packages/agent-team/lib/preset-roster.js +1 -0
  15. package/packages/agent-team/lib/spec.js +56 -1
  16. package/packages/agent-team/lib/typert.host.js +359 -77
  17. package/packages/agent-team/lib/typert.remote-client.d.ts +5 -1
  18. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  19. package/packages/agent-team/lib/typert.remote-client.js +248 -43
  20. package/packages/agent-team/lib/types/entities.js +1 -0
  21. package/packages/agent-team/lib/types/index.d.ts +51 -7
  22. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  23. package/packages/agent-team/lib/types/ledger.d.ts +62 -1
  24. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  25. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  26. package/packages/agent-team/lib/types/member-runtime.d.ts +108 -0
  27. package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -0
  28. package/packages/agent-team/lib/types/member-skills.d.ts +45 -0
  29. package/packages/agent-team/lib/types/member-skills.d.ts.map +1 -0
  30. package/packages/agent-team/lib/types/operations.js +1 -0
  31. package/packages/agent-team/lib/types/preset-roster.d.ts.map +1 -1
  32. package/packages/agent-team/lib/types/requests-results.js +1 -0
  33. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  34. package/packages/agent-team/lib/types/types/entities.d.ts +339 -0
  35. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -0
  36. package/packages/agent-team/lib/types/types/operations.d.ts +303 -0
  37. package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -0
  38. package/packages/agent-team/lib/types/types/requests-results.d.ts +527 -0
  39. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -0
  40. package/packages/agent-team/lib/types/types.d.ts +10 -985
  41. package/packages/agent-team/lib/types/types.d.ts.map +1 -1
  42. package/packages/agent-team/preset/team-member/agent.cordis.yml +11 -6
  43. package/packages/client-agent-team/README.md +4 -2
  44. package/packages/client-agent-team/README.zh.md +4 -2
  45. package/packages/client-agent-team/lib/client.js +636 -572
  46. package/packages/client-agent-team/lib/client.js.map +1 -1
  47. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +3 -5
  48. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  49. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +37 -26
  50. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +1 -1
  51. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  52. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +2 -2
  53. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts +2 -1
  54. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  55. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +31 -5
  56. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  57. package/packages/client-agent-team/lib/types/client/TeamComposer.js +2 -1
  58. package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts +12 -0
  59. package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts.map +1 -0
  60. package/packages/client-agent-team/lib/types/client/TeamMemberDock.js +13 -0
  61. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +4 -8
  62. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
  63. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +7 -40
  64. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  65. package/packages/client-agent-team/lib/types/client/TeamMessage.js +10 -3
  66. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +1 -1
  67. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  68. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +61 -20
  69. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  70. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  71. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -2
  72. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts +1 -1
  73. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts.map +1 -1
  74. package/packages/client-agent-team/lib/types/client/index.d.ts +1 -1
  75. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  76. package/packages/client-agent-team/lib/types/client/index.js +25 -28
  77. package/packages/client-agent-team/lib/types/client/locales.d.ts +32 -16
  78. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  79. package/packages/client-agent-team/lib/types/client/locales.js +32 -16
  80. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +1 -3
  81. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +1 -1
  82. package/packages/client-agent-team/lib/types/client/member-session-input.js +18 -6
  83. package/packages/client-agent-team/lib/types/client/navigation.d.ts +2 -1
  84. package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
  85. package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts +1 -1
  86. package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts.map +1 -1
  87. package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts +1 -1
  88. package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts.map +1 -1
  89. package/packages/client-agent-team/lib/types/client/slots.d.ts +3 -1
  90. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  91. package/packages/client-agent-team/lib/types/client/team-membership.d.ts +1 -1
  92. package/packages/client-agent-team/lib/types/client/team-membership.d.ts.map +1 -1
  93. package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts +5 -7
  94. package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts.map +1 -1
  95. package/packages/client-agent-team/lib/types/client/timeline-scroll.js +9 -22
  96. package/packages/tool-agent-team/lib/index.js +25 -5
  97. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
  98. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts +0 -17
  99. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +0 -1
  100. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.js +0 -135
@@ -40,8 +40,6 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
40
40
  done = true;
41
41
  };
42
42
  import { randomUUID } from 'node:crypto';
43
- import { mkdir, rm, writeFile } from 'node:fs/promises';
44
- import { join } from 'node:path';
45
43
  import { isDeepStrictEqual } from 'node:util';
46
44
  import { Service } from '@deepseek-ai/cordis';
47
45
  import { installModelSelection } from '@deepseek-ai/dsh-agent';
@@ -49,15 +47,17 @@ import { createUserMessage } from '@deepseek-ai/dsh-llm';
49
47
  import { dshHomePath } from '@deepseek-ai/dsh-home-paths';
50
48
  import { scopeOf } from '@deepseek-ai/dsh-scope';
51
49
  import { SessionId } from '@deepseek-ai/dsh-session';
52
- import { effectiveSandboxMode, setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy';
50
+ import { setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy';
53
51
  import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
54
52
  import { ATTACHMENT_MAX_BYTES, attachmentPayloadPath, attachmentsRoot, copyPathAttachment, newAttachmentId, readAttachment, sanitizeMediaType, sweepAttachmentCache, validatePathAttachment, writeAttachment } from "./attachments.js";
55
53
  import { acceptedTaskCompactionMembers, AutoCompactionCoordinator, PRE_COMPACTION_NOTICE_SUMMARY, preCompactionNoticeText } from "./auto-compaction.js";
56
54
  import { AGENT_TEAM_HUMAN_MEMBER_ID, AgentTeamLedger, agentTeamHumanActor } from "./ledger.js";
55
+ import { AGENT_TEAM_TOOL_NAMES, deepCopyCapabilities, MemberRuntime } from "./member-runtime.js";
57
56
  import { classifyRecoverableError, RecoveryCoordinator, RECOVERY_MAX_CONSECUTIVE_ERRORS } from "./recovery.js";
58
57
  import { agentTeamDomainSpec } from "./spec.js";
59
58
  export { agentTeamDomainSpec, agentTeamOperationSchema } from "./spec.js";
60
59
  export { AGENT_TEAM_HUMAN_MEMBER_ID, AGENT_TEAM_INITIALIZE_REQUEST_ID } from "./ledger.js";
60
+ export { AGENT_TEAM_TOOL_NAMES } from "./member-runtime.js";
61
61
  /** Process-stable marker carried by the final Team message tool definition. */
62
62
  export const AGENT_TEAM_PRESET_MARKER = Symbol.for('@wowyuarm/dsh-agent-team.preset');
63
63
  const AGENT_TEAM_PLUGIN_ID = '@wowyuarm/dsh-agent-team';
@@ -78,14 +78,22 @@ export function markAgentTeamPreset(definition) {
78
78
  Object.defineProperty(definition, AGENT_TEAM_PRESET_MARKER, { value: true });
79
79
  return definition;
80
80
  }
81
- /** Model-facing capabilities every Team-enabled preset must publish. */
82
- export const AGENT_TEAM_TOOL_NAMES = Object.freeze([
83
- 'team_inbox',
84
- 'team_thread',
85
- 'team_message',
86
- 'team_claim',
87
- 'team_view',
88
- ]);
81
+ /**
82
+ * A DM was durably recorded but its session injection could not run (no live
83
+ * handle, or the wake itself failed). The recorded DM stays durable; the
84
+ * sender should not blindly retry — the recipient recovers it through its DM
85
+ * history once its session is live again.
86
+ */
87
+ export class AgentTeamDmDeliveryError extends Error {
88
+ recipientMemberId;
89
+ recipientHandle;
90
+ constructor(recipientMemberId, recipientHandle, message) {
91
+ super(message);
92
+ this.recipientMemberId = recipientMemberId;
93
+ this.recipientHandle = recipientHandle;
94
+ this.name = 'AgentTeamDmDeliveryError';
95
+ }
96
+ }
89
97
  let AgentTeam = (() => {
90
98
  let _classSuper = TypertRemoteService;
91
99
  let _instanceExtraInitializers = [];
@@ -94,10 +102,12 @@ let AgentTeam = (() => {
94
102
  let _changes_decorators;
95
103
  let _createChannel_decorators;
96
104
  let _updateChannel_decorators;
105
+ let _archiveChannel_decorators;
97
106
  let _addMember_decorators;
98
107
  let _recoverMember_decorators;
99
108
  let _clearMemberContext_decorators;
100
109
  let _updateMember_decorators;
110
+ let _archiveMember_decorators;
101
111
  let _changeTask_decorators;
102
112
  let _promoteThread_decorators;
103
113
  let _joinChannel_decorators;
@@ -120,10 +130,12 @@ let AgentTeam = (() => {
120
130
  _changes_decorators = [Remote('changes')];
121
131
  _createChannel_decorators = [Remote('createChannel')];
122
132
  _updateChannel_decorators = [Remote('updateChannel')];
133
+ _archiveChannel_decorators = [Remote('archiveChannel')];
123
134
  _addMember_decorators = [Remote('addMember')];
124
135
  _recoverMember_decorators = [Remote('recoverMember')];
125
136
  _clearMemberContext_decorators = [Remote('clearMemberContext')];
126
137
  _updateMember_decorators = [Remote('updateMember')];
138
+ _archiveMember_decorators = [Remote('archiveMember')];
127
139
  _changeTask_decorators = [Remote('changeTask')];
128
140
  _promoteThread_decorators = [Remote('promoteThread')];
129
141
  _joinChannel_decorators = [Remote('joinChannel')];
@@ -143,10 +155,12 @@ let AgentTeam = (() => {
143
155
  __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);
144
156
  __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);
145
157
  __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);
158
+ __esDecorate(this, null, _archiveChannel_decorators, { kind: "method", name: "archiveChannel", static: false, private: false, access: { has: obj => "archiveChannel" in obj, get: obj => obj.archiveChannel }, metadata: _metadata }, null, _instanceExtraInitializers);
146
159
  __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);
147
160
  __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);
148
161
  __esDecorate(this, null, _clearMemberContext_decorators, { kind: "method", name: "clearMemberContext", static: false, private: false, access: { has: obj => "clearMemberContext" in obj, get: obj => obj.clearMemberContext }, metadata: _metadata }, null, _instanceExtraInitializers);
149
162
  __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);
163
+ __esDecorate(this, null, _archiveMember_decorators, { kind: "method", name: "archiveMember", static: false, private: false, access: { has: obj => "archiveMember" in obj, get: obj => obj.archiveMember }, metadata: _metadata }, null, _instanceExtraInitializers);
150
164
  __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);
151
165
  __esDecorate(this, null, _promoteThread_decorators, { kind: "method", name: "promoteThread", static: false, private: false, access: { has: obj => "promoteThread" in obj, get: obj => obj.promoteThread }, metadata: _metadata }, null, _instanceExtraInitializers);
152
166
  __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);
@@ -177,6 +191,21 @@ let AgentTeam = (() => {
177
191
  handles = new Map();
178
192
  /** Live selection refs let model edits take effect without disposing the Session. */
179
193
  modelSelections = new Map();
194
+ /** Agent ids with a turn in flight; restarts must wait for the boundary. */
195
+ runningAgents = new Set();
196
+ /** Per-Member runtime state: tool restrictions, skill mounts, warnings, private memory. */
197
+ memberRuntime = new MemberRuntime({
198
+ // Deferred field access: the constructor runs before `handles` is
199
+ // populated, so the resolver must dereference at call time.
200
+ ctx: this.ctx,
201
+ liveMemberContext: memberId => {
202
+ const handle = this.handles.get(memberId);
203
+ if (handle === undefined)
204
+ throw new Error(`Agent Member '${this.memberLabel(memberId)}' has no live session for a tool-policy update`);
205
+ return handle.agent.ctx;
206
+ },
207
+ runningAgents: this.runningAgents,
208
+ });
180
209
  /**
181
210
  * Why one Member shows error presence, per failure source. Reads prefer
182
211
  * activation, then runtime, then compaction; slots clear independently, so
@@ -186,8 +215,6 @@ let AgentTeam = (() => {
186
215
  */
187
216
  memberFailures = new Map();
188
217
  autoCompaction;
189
- /** Agent ids with a turn in flight; restarts must wait for the boundary. */
190
- runningAgents = new Set();
191
218
  notifiedInbox = new Map();
192
219
  attachmentGcTimer;
193
220
  recovery = new RecoveryCoordinator({
@@ -281,6 +308,7 @@ let AgentTeam = (() => {
281
308
  await Promise.all([...this.handles.values()].map(handle => handle.dispose()));
282
309
  this.handles.clear();
283
310
  this.modelSelections.clear();
311
+ this.memberRuntime.disposeAll();
284
312
  this.runningAgents.clear();
285
313
  await domain.close();
286
314
  }, 'agentTeam.dispose');
@@ -298,7 +326,7 @@ let AgentTeam = (() => {
298
326
  if (member.state === 'enabled')
299
327
  await this.activateMember(member, undefined, persistedSessions);
300
328
  else if (member.state === 'inactive')
301
- await this.cleanupRemovedMember(member);
329
+ await this.memberRuntime.cleanupRemovedMember(member);
302
330
  }
303
331
  }
304
332
  /** Resolve one exact live Agent to its durable Team Member; forks do not inherit identity. */
@@ -399,6 +427,20 @@ let AgentTeam = (() => {
399
427
  this.emitCommitted(result.value.receipt);
400
428
  return result.value;
401
429
  }
430
+ /**
431
+ * Archive one Channel: hidden from every surface with all facts kept. Pure
432
+ * ledger projection change — Member sessions stay live (they may work in
433
+ * other Channels), every active Claim on the Channel's Threads releases,
434
+ * and affected Members' Attention clears.
435
+ */
436
+ async archiveChannel(request) {
437
+ this.requireAccepting();
438
+ this.requireWorkspace(request.workspaceId);
439
+ const result = await this.requireLedger().archiveChannel({ ...request, actor: agentTeamHumanActor() });
440
+ if (result.committed)
441
+ this.emitCommitted(result.value.receipt);
442
+ return result.value;
443
+ }
402
444
  /** Create a durable Member and atomically grant its declared initial Channels. */
403
445
  async addMember(request) {
404
446
  return this.enqueueLifecycle(async () => {
@@ -413,6 +455,7 @@ let AgentTeam = (() => {
413
455
  description: request.description,
414
456
  presetId: request.presetId,
415
457
  ...(request.model === undefined ? {} : { model: Object.freeze({ ...request.model }) }),
458
+ ...(request.capabilities === undefined ? {} : { capabilities: Object.freeze(deepCopyCapabilities(request.capabilities)) }),
416
459
  privateMemoryPath: dshHomePath('agent-team', 'members', memberId),
417
460
  state: 'enabled',
418
461
  });
@@ -526,6 +569,7 @@ let AgentTeam = (() => {
526
569
  await active.dispose();
527
570
  this.handles.delete(request.memberId);
528
571
  this.modelSelections.delete(request.memberId);
572
+ this.memberRuntime.forgetMember(request.memberId);
529
573
  this.clearMemberFailure(request.memberId, 'activation');
530
574
  this.clearMemberNotificationState(request.memberId);
531
575
  // The previous log survives on disk; archiving hides it from every
@@ -616,9 +660,33 @@ let AgentTeam = (() => {
616
660
  throw new Error(`Agent Member '${stored.handle}' has no live model selection`);
617
661
  selection.current = stored.model ?? this.ctx.agentDefaultModel.currentSelection();
618
662
  }
663
+ if (active !== undefined && !isDeepStrictEqual(previous?.capabilities ?? undefined, stored.capabilities ?? undefined)) {
664
+ await this.applyCapabilityEdit(active, stored);
665
+ }
619
666
  return Object.freeze({ receipt: result.value.receipt, status: this.memberStatus(stored) });
620
667
  });
621
668
  }
669
+ /**
670
+ * Live-apply a capability edit at a turn boundary: while the Agent runs, the
671
+ * current turn keeps its schemas and catalog; the swap happens once idle,
672
+ * so the next step recomputes schemas from the new restriction and the
673
+ * durable replacement skill catalog from the new selection, with the same
674
+ * Session and history surviving. Suspend/remove during the wait cancels
675
+ * the swap — the disposed handle released the old restriction already and
676
+ * no disposer leaks.
677
+ */
678
+ async applyCapabilityEdit(active, stored) {
679
+ const memberId = stored.memberId;
680
+ const waited = await this.memberRuntime.awaitTurnBoundary(active);
681
+ if (waited && this.handles.get(memberId) !== active) {
682
+ // The wait resolved because the old generation was disposed, not
683
+ // because the turn ended; the ledger intent applies at the next
684
+ // activation instead.
685
+ return;
686
+ }
687
+ this.memberRuntime.reapplyMemberToolPolicy(stored);
688
+ this.memberRuntime.swapSkillSelection(memberId, stored.capabilities?.skills?.allow);
689
+ }
622
690
  /** Irreversibly remove one Member, archive its Session, and delete its private namespace. */
623
691
  async removeMember(request) {
624
692
  return this.enqueueLifecycle(async () => {
@@ -626,7 +694,23 @@ let AgentTeam = (() => {
626
694
  if (result.committed)
627
695
  this.emitCommitted(result.value.receipt);
628
696
  await this.disposeMemberSession(request.memberId, result.value.member);
629
- await this.cleanupRemovedMember(result.value.member);
697
+ await this.memberRuntime.cleanupRemovedMember(result.value.member);
698
+ return result.value;
699
+ });
700
+ }
701
+ /**
702
+ * Archive one Member: commit the archival, stop its live session (disposal
703
+ * only — private memory and the Session log stay on disk for a future
704
+ * restore), and archive the Session from every grouping surface. Like
705
+ * removal, all active Claims release and the Member's Attention clears.
706
+ */
707
+ async archiveMember(request) {
708
+ return this.enqueueLifecycle(async () => {
709
+ const result = await this.requireLedger().archiveMember({ ...request, actor: agentTeamHumanActor() });
710
+ if (result.committed)
711
+ this.emitCommitted(result.value.receipt);
712
+ await this.disposeMemberSession(request.memberId, result.value.member);
713
+ await this.ctx.workspaceRegistry.archiveSession(result.value.member.sessionId);
630
714
  return result.value;
631
715
  });
632
716
  }
@@ -813,6 +897,49 @@ let AgentTeam = (() => {
813
897
  this.emitCommitted(result.value.receipt);
814
898
  return result.value;
815
899
  }
900
+ /**
901
+ * Agent-only direct message: append the audit-only dm-sent operation, then
902
+ * inject the body into the recipient's live session. The ledger commit is
903
+ * the durable fact; the injection is a transient runtime effect, so a
904
+ * missing handle or a failed wake returns a structured delivery error while
905
+ * the recorded DM stays durable for the recipient's recovery path.
906
+ */
907
+ async dmForAgent(agent, request) {
908
+ const actor = this.memberCall(agent, request.workspaceId);
909
+ const result = await this.requireLedger().sendDm({ ...request, actor });
910
+ if (!result.committed)
911
+ return result.value;
912
+ this.emitCommitted(result.value.receipt);
913
+ const recipient = result.value.recipient;
914
+ const handle = this.handles.get(recipient.memberId);
915
+ if (handle === undefined) {
916
+ throw new AgentTeamDmDeliveryError(recipient.memberId, recipient.handle, `DM recorded but not delivered: Agent Member '${recipient.handle}' has no live session; it will find the message in its DM history after recovery`);
917
+ }
918
+ try {
919
+ const message = createUserMessage({
920
+ content: [{ type: 'text', text: this.dmRelayText(agent, recipient, request.body.trim(), result.value.receipt.operationId) }],
921
+ source: { kind: 'plugin', plugin: AGENT_TEAM_PLUGIN_ID, form: 'relay' },
922
+ });
923
+ // An idle recipient gets one ordinary turn; a busy one is steered into
924
+ // its current turn — the same wake split subagent continuations use.
925
+ if (handle.agent.status === 'idle')
926
+ handle.agent.followup(message);
927
+ else
928
+ handle.agent.steer(message);
929
+ }
930
+ catch (error) {
931
+ throw new AgentTeamDmDeliveryError(recipient.memberId, recipient.handle, `DM recorded but not delivered: ${error instanceof Error ? error.message : String(error)}`);
932
+ }
933
+ return result.value;
934
+ }
935
+ /** Relay body: the DM itself plus one bounded line of adjacent context. */
936
+ dmRelayText(senderAgent, recipient, body, excluding) {
937
+ const sender = this.memberForAgent(senderAgent);
938
+ const prior = this.requireLedger().dmHistoryBetween(senderAgent.id, recipient.memberId, excluding);
939
+ const header = `Direct message from @${sender?.handle ?? 'a Team Member'}:`;
940
+ const context = prior === undefined ? '' : `\n\n[most recent prior DM between you: ${prior}]`;
941
+ return `${header}\n\n${body}${context}`;
942
+ }
816
943
  threadHistoryForAgent(agent, request) {
817
944
  const actor = this.memberActor(agent);
818
945
  this.requireAgentWorkspace(actor, request.workspaceId);
@@ -916,7 +1043,7 @@ let AgentTeam = (() => {
916
1043
  try {
917
1044
  const workspace = this.requireWorkspace(member.workspaceId);
918
1045
  const workspacePath = knownWorkspacePath ?? workspace.path;
919
- await this.initializePrivateMemory(member.privateMemoryPath);
1046
+ await this.memberRuntime.initializePrivateMemory(member.privateMemoryPath);
920
1047
  const persisted = knownSessions !== undefined ? knownSessions.has(member.sessionId)
921
1048
  : (await this.ctx.sessionPersistence.list()).some(header => header.id === member.sessionId);
922
1049
  // AgentOptions declares only provider/model. Install the full selection
@@ -925,8 +1052,13 @@ let AgentTeam = (() => {
925
1052
  const selection = member.model ?? this.ctx.agentDefaultModel.currentSelection();
926
1053
  const agentOptions = { provider: selection.provider, model: selection.model };
927
1054
  const selected = { current: selection, assembled: undefined };
1055
+ // Absent skills.allow loads every discovered private skill; a present
1056
+ // allow-list filters the catalog by name through the live ref below.
1057
+ // `swap` is bound by the provider at activation (no-op until then).
1058
+ const skillSelection = { current: member.capabilities?.skills?.allow, swap: () => { } };
928
1059
  const setup = async (agentCtx) => {
929
1060
  await this.ctx.agentPresets.mount(agentCtx, member.presetId);
1061
+ this.memberRuntime.applyMemberToolPolicy(agentCtx, member);
930
1062
  this.validateMemberPreset(agentCtx);
931
1063
  installModelSelection(agentCtx, selected);
932
1064
  return {
@@ -934,7 +1066,13 @@ let AgentTeam = (() => {
934
1066
  const agent = agentCtx.agent;
935
1067
  if (agent === undefined)
936
1068
  throw new Error('agent-team setup has no unpublished Agent');
937
- if (effectiveSandboxMode(agent.session.events) !== 'danger-full-access')
1069
+ // The member scope composes no sandbox-policy service (the preset
1070
+ // owns no sandbox row), so read the last logged mode straight from
1071
+ // the session log; `sandbox/mode` is log-only and never joins the
1072
+ // model-visible surface. Re-appending only when the effective mode
1073
+ // differs keeps resumed members from growing redundant events.
1074
+ const logged = agent.session.ownEvents().toReversed().find(event => event.type === 'sandbox/mode');
1075
+ if (logged?.data.mode !== 'danger-full-access')
938
1076
  setSandboxMode(agent.session, 'danger-full-access');
939
1077
  },
940
1078
  };
@@ -949,6 +1087,7 @@ let AgentTeam = (() => {
949
1087
  agentOptions,
950
1088
  setup,
951
1089
  });
1090
+ this.memberRuntime.mountMemberSkillProvider(member, created.agent.ctx, skillSelection);
952
1091
  await workspace.attachSession(member.sessionId);
953
1092
  this.handles.set(member.memberId, created);
954
1093
  this.modelSelections.set(member.memberId, selected);
@@ -960,6 +1099,7 @@ let AgentTeam = (() => {
960
1099
  catch (error) {
961
1100
  await created?.dispose();
962
1101
  this.modelSelections.delete(member.memberId);
1102
+ this.memberRuntime.forgetMember(member.memberId);
963
1103
  this.setMemberFailure(member.memberId, 'activation', error instanceof Error ? error.message : String(error));
964
1104
  }
965
1105
  finally {
@@ -986,6 +1126,7 @@ let AgentTeam = (() => {
986
1126
  if (stale !== undefined) {
987
1127
  this.handles.delete(memberId);
988
1128
  this.modelSelections.delete(memberId);
1129
+ this.memberRuntime.forgetMember(memberId);
989
1130
  // The composition-loss diagnostic this heal answers is stale once the
990
1131
  // rebuild starts; a later activation must not resurface it.
991
1132
  this.clearMemberFailure(memberId, 'compaction');
@@ -1026,28 +1167,11 @@ let AgentTeam = (() => {
1026
1167
  if (missing.length > 0)
1027
1168
  throw new Error(`team-enabled preset is missing tools: ${missing.join(', ')}`);
1028
1169
  }
1029
- async initializePrivateMemory(path) {
1030
- await mkdir(join(path, 'notes'), { recursive: true });
1031
- try {
1032
- await writeFile(join(path, 'memory.md'), '# Member memory\n\n## Stable facts\n- Add only verified, durable facts that help future work.\n\n## Notes index\n- Add focused `notes/*.md` entries here when a reusable detail needs on-demand reading.\n', { flag: 'wx' });
1033
- }
1034
- catch (error) {
1035
- if (error.code !== 'EEXIST')
1036
- throw error;
1037
- }
1038
- }
1039
- async cleanupRemovedMember(member) {
1040
- const results = await Promise.allSettled([
1041
- this.ctx.workspaceRegistry.archiveSession(member.sessionId),
1042
- rm(member.privateMemoryPath, { recursive: true, force: true }),
1043
- ]);
1044
- const failures = results.flatMap(result => result.status === 'rejected' ? [result.reason] : []);
1045
- if (failures.length > 0)
1046
- throw new AggregateError(failures, `failed to clean up removed Member '${member.memberId}'`);
1047
- }
1048
1170
  memberStatus(member) {
1049
1171
  if (member.state === 'inactive')
1050
1172
  return Object.freeze({ member, availability: 'inactive', presence: 'unavailable' });
1173
+ if (member.state === 'archived')
1174
+ return Object.freeze({ member, availability: 'archived', presence: 'unavailable' });
1051
1175
  if (member.state === 'suspended')
1052
1176
  return Object.freeze({ member, availability: 'suspended', presence: 'unavailable' });
1053
1177
  const failures = this.memberFailures.get(member.memberId);
@@ -1062,7 +1186,13 @@ let AgentTeam = (() => {
1062
1186
  const runtimeError = failures?.runtime ?? failures?.compaction;
1063
1187
  if (runtimeError !== undefined)
1064
1188
  return Object.freeze({ member, availability: 'active', presence: 'error', diagnostic: runtimeError });
1065
- return Object.freeze({ member, availability: 'active', presence: handle.agent.status === 'running' ? 'working' : 'available' });
1189
+ // Capability warnings are runtime-derived at activation (handles-scoped,
1190
+ // like failures): absent while capabilities resolve cleanly.
1191
+ const capabilityWarnings = this.memberRuntime.capabilityWarningsFor(member.memberId);
1192
+ return Object.freeze({
1193
+ member, availability: 'active', presence: handle.agent.status === 'running' ? 'working' : 'available',
1194
+ ...(capabilityWarnings === undefined ? {} : { capabilityWarnings }),
1195
+ });
1066
1196
  }
1067
1197
  setMemberFailure(memberId, slot, message) {
1068
1198
  const failures = this.memberFailures.get(memberId) ?? {};
@@ -1247,6 +1377,7 @@ let AgentTeam = (() => {
1247
1377
  this.handles.delete(memberId);
1248
1378
  }
1249
1379
  this.modelSelections.delete(memberId);
1380
+ this.memberRuntime.forgetMember(memberId);
1250
1381
  this.clearMemberFailure(memberId, 'activation');
1251
1382
  }
1252
1383
  clearMemberRecoveryState(member) {