@wowyuarm/dsh-agent-team 0.1.6 → 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 (85) hide show
  1. package/README.md +11 -5
  2. package/README.zh.md +11 -5
  3. package/package.json +44 -42
  4. package/packages/agent-team/README.md +2 -0
  5. package/packages/agent-team/README.zh.md +2 -0
  6. package/packages/agent-team/lib/index.js +82 -204
  7. package/packages/agent-team/lib/ledger.js +301 -13
  8. package/packages/agent-team/lib/member-context.js +5 -1
  9. package/packages/agent-team/lib/member-runtime.js +229 -0
  10. package/packages/agent-team/lib/preset-roster.js +1 -0
  11. package/packages/agent-team/lib/spec.js +30 -1
  12. package/packages/agent-team/lib/typert.host.js +253 -74
  13. package/packages/agent-team/lib/typert.remote-client.d.ts +5 -1
  14. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  15. package/packages/agent-team/lib/typert.remote-client.js +172 -43
  16. package/packages/agent-team/lib/types/entities.js +1 -0
  17. package/packages/agent-team/lib/types/index.d.ts +20 -44
  18. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  19. package/packages/agent-team/lib/types/ledger.d.ts +34 -1
  20. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  21. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  22. package/packages/agent-team/lib/types/member-runtime.d.ts +108 -0
  23. package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -0
  24. package/packages/agent-team/lib/types/operations.js +1 -0
  25. package/packages/agent-team/lib/types/preset-roster.d.ts.map +1 -1
  26. package/packages/agent-team/lib/types/requests-results.js +1 -0
  27. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  28. package/packages/agent-team/lib/types/types/entities.d.ts +339 -0
  29. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -0
  30. package/packages/agent-team/lib/types/types/operations.d.ts +303 -0
  31. package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -0
  32. package/packages/agent-team/lib/types/types/requests-results.d.ts +527 -0
  33. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -0
  34. package/packages/agent-team/lib/types/types.d.ts +10 -1070
  35. package/packages/agent-team/lib/types/types.d.ts.map +1 -1
  36. package/packages/agent-team/preset/team-member/agent.cordis.yml +4 -2
  37. package/packages/client-agent-team/README.md +3 -1
  38. package/packages/client-agent-team/README.zh.md +3 -1
  39. package/packages/client-agent-team/lib/client.js +444 -298
  40. package/packages/client-agent-team/lib/client.js.map +1 -1
  41. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +3 -2
  42. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  43. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +30 -7
  44. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +1 -1
  45. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  46. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +2 -2
  47. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts +2 -1
  48. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  49. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +31 -5
  50. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  51. package/packages/client-agent-team/lib/types/client/TeamComposer.js +2 -1
  52. package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts +12 -0
  53. package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts.map +1 -0
  54. package/packages/client-agent-team/lib/types/client/TeamMemberDock.js +13 -0
  55. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  56. package/packages/client-agent-team/lib/types/client/TeamMessage.js +10 -3
  57. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +1 -1
  58. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  59. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  60. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  61. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -2
  62. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts +1 -1
  63. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts.map +1 -1
  64. package/packages/client-agent-team/lib/types/client/index.d.ts +1 -1
  65. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  66. package/packages/client-agent-team/lib/types/client/index.js +25 -28
  67. package/packages/client-agent-team/lib/types/client/locales.d.ts +28 -0
  68. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  69. package/packages/client-agent-team/lib/types/client/locales.js +28 -0
  70. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +1 -3
  71. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +1 -1
  72. package/packages/client-agent-team/lib/types/client/member-session-input.js +18 -6
  73. package/packages/client-agent-team/lib/types/client/navigation.d.ts +2 -1
  74. package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
  75. package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts +1 -1
  76. package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts.map +1 -1
  77. package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts +1 -1
  78. package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts.map +1 -1
  79. package/packages/client-agent-team/lib/types/client/slots.d.ts +3 -1
  80. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  81. package/packages/client-agent-team/lib/types/client/team-membership.d.ts +1 -1
  82. package/packages/client-agent-team/lib/types/client/team-membership.d.ts.map +1 -1
  83. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts +0 -17
  84. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +0 -1
  85. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.js +0 -135
@@ -40,9 +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 { dirname, join, resolve } from 'node:path';
45
- import { fileURLToPath } from 'node:url';
46
43
  import { isDeepStrictEqual } from 'node:util';
47
44
  import { Service } from '@deepseek-ai/cordis';
48
45
  import { installModelSelection } from '@deepseek-ai/dsh-agent';
@@ -50,26 +47,20 @@ import { createUserMessage } from '@deepseek-ai/dsh-llm';
50
47
  import { dshHomePath } from '@deepseek-ai/dsh-home-paths';
51
48
  import { scopeOf } from '@deepseek-ai/dsh-scope';
52
49
  import { SessionId } from '@deepseek-ai/dsh-session';
53
- import { effectiveSandboxMode, setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy';
50
+ import { setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy';
54
51
  import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
55
52
  import { ATTACHMENT_MAX_BYTES, attachmentPayloadPath, attachmentsRoot, copyPathAttachment, newAttachmentId, readAttachment, sanitizeMediaType, sweepAttachmentCache, validatePathAttachment, writeAttachment } from "./attachments.js";
56
53
  import { acceptedTaskCompactionMembers, AutoCompactionCoordinator, PRE_COMPACTION_NOTICE_SUMMARY, preCompactionNoticeText } from "./auto-compaction.js";
57
54
  import { AGENT_TEAM_HUMAN_MEMBER_ID, AgentTeamLedger, agentTeamHumanActor } from "./ledger.js";
58
- import * as memberSkills from "./member-skills.js";
55
+ import { AGENT_TEAM_TOOL_NAMES, deepCopyCapabilities, MemberRuntime } from "./member-runtime.js";
59
56
  import { classifyRecoverableError, RecoveryCoordinator, RECOVERY_MAX_CONSECUTIVE_ERRORS } from "./recovery.js";
60
57
  import { agentTeamDomainSpec } from "./spec.js";
61
58
  export { agentTeamDomainSpec, agentTeamOperationSchema } from "./spec.js";
62
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";
63
61
  /** Process-stable marker carried by the final Team message tool definition. */
64
62
  export const AGENT_TEAM_PRESET_MARKER = Symbol.for('@wowyuarm/dsh-agent-team.preset');
65
63
  const AGENT_TEAM_PLUGIN_ID = '@wowyuarm/dsh-agent-team';
66
- /**
67
- * Read-only core skills shipped beside the preset (the meta skill first:
68
- * its description routes any skill-management work to itself). Resolved
69
- * from this module's emitted location so it follows the installed plugin,
70
- * like the preset roster's own root.
71
- */
72
- const BUNDLED_SKILLS_DIRECTORY = resolve(dirname(fileURLToPath(import.meta.url)), '../core-skills');
73
64
  const INBOX_NOTICE_SUMMARY = 'Team Inbox has unread work.';
74
65
  const RECOVERY_NOTICE_SUMMARY = 'Recovery: continue your interrupted work.';
75
66
  const ORPHANED_MEMBER_DIAGNOSTIC = 'Member preset composition was lost after a reload; its tools are unavailable. Resume rebuilds the member in place.';
@@ -82,27 +73,11 @@ function sameChangeScope(left, right) {
82
73
  return left.threadRef === right.threadRef;
83
74
  return false;
84
75
  }
85
- /** Copy a Remote-supplied capability overlay into owned frozen storage. */
86
- function deepCopyCapabilities(capabilities) {
87
- const copyAllow = (allow) => allow === undefined ? {} : { allow: [...allow] };
88
- return {
89
- ...(capabilities.tools === undefined ? {} : { tools: copyAllow(capabilities.tools.allow) }),
90
- ...(capabilities.skills === undefined ? {} : { skills: copyAllow(capabilities.skills.allow) }),
91
- };
92
- }
93
76
  /** Mark the preset's `team_message` definition as an Agent Team consumer. */
94
77
  export function markAgentTeamPreset(definition) {
95
78
  Object.defineProperty(definition, AGENT_TEAM_PRESET_MARKER, { value: true });
96
79
  return definition;
97
80
  }
98
- /** Model-facing capabilities every Team-enabled preset must publish. */
99
- export const AGENT_TEAM_TOOL_NAMES = Object.freeze([
100
- 'team_inbox',
101
- 'team_thread',
102
- 'team_message',
103
- 'team_claim',
104
- 'team_view',
105
- ]);
106
81
  /**
107
82
  * A DM was durably recorded but its session injection could not run (no live
108
83
  * handle, or the wake itself failed). The recorded DM stays durable; the
@@ -127,10 +102,12 @@ let AgentTeam = (() => {
127
102
  let _changes_decorators;
128
103
  let _createChannel_decorators;
129
104
  let _updateChannel_decorators;
105
+ let _archiveChannel_decorators;
130
106
  let _addMember_decorators;
131
107
  let _recoverMember_decorators;
132
108
  let _clearMemberContext_decorators;
133
109
  let _updateMember_decorators;
110
+ let _archiveMember_decorators;
134
111
  let _changeTask_decorators;
135
112
  let _promoteThread_decorators;
136
113
  let _joinChannel_decorators;
@@ -153,10 +130,12 @@ let AgentTeam = (() => {
153
130
  _changes_decorators = [Remote('changes')];
154
131
  _createChannel_decorators = [Remote('createChannel')];
155
132
  _updateChannel_decorators = [Remote('updateChannel')];
133
+ _archiveChannel_decorators = [Remote('archiveChannel')];
156
134
  _addMember_decorators = [Remote('addMember')];
157
135
  _recoverMember_decorators = [Remote('recoverMember')];
158
136
  _clearMemberContext_decorators = [Remote('clearMemberContext')];
159
137
  _updateMember_decorators = [Remote('updateMember')];
138
+ _archiveMember_decorators = [Remote('archiveMember')];
160
139
  _changeTask_decorators = [Remote('changeTask')];
161
140
  _promoteThread_decorators = [Remote('promoteThread')];
162
141
  _joinChannel_decorators = [Remote('joinChannel')];
@@ -176,10 +155,12 @@ let AgentTeam = (() => {
176
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);
177
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);
178
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);
179
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);
180
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);
181
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);
182
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);
183
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);
184
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);
185
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);
@@ -210,10 +191,21 @@ let AgentTeam = (() => {
210
191
  handles = new Map();
211
192
  /** Live selection refs let model edits take effect without disposing the Session. */
212
193
  modelSelections = new Map();
213
- /** Live skill selection refs let capability edits re-filter the catalog without disposing the Session. */
214
- skillSelections = new Map();
215
- /** Per-Member private skill provider disposers; released with the Member's agent scope. */
216
- skillProviderDisposals = 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
+ });
217
209
  /**
218
210
  * Why one Member shows error presence, per failure source. Reads prefer
219
211
  * activation, then runtime, then compaction; slots clear independently, so
@@ -222,23 +214,7 @@ let AgentTeam = (() => {
222
214
  * stale keys.
223
215
  */
224
216
  memberFailures = new Map();
225
- /**
226
- * Live per-Member tool restriction disposers, mirroring modelSelections:
227
- * activation registers, disposal paths release, edits swap at a turn
228
- * boundary. Deliberate interface reservation: the restriction seam is the
229
- * primitive future Runtime Revision manifests orchestrate — do not remove
230
- * during cleanup.
231
- */
232
- memberRestrictions = new Map();
233
- /**
234
- * Runtime-derived capability warnings, recomputed at every activation (like
235
- * memberFailures, keyed by Member and never persisted): persisted warnings
236
- * would lie after a Host restart or a Harness upgrade renames tools.
237
- */
238
- capabilityWarnings = new Map();
239
217
  autoCompaction;
240
- /** Agent ids with a turn in flight; restarts must wait for the boundary. */
241
- runningAgents = new Set();
242
218
  notifiedInbox = new Map();
243
219
  attachmentGcTimer;
244
220
  recovery = new RecoveryCoordinator({
@@ -332,14 +308,7 @@ let AgentTeam = (() => {
332
308
  await Promise.all([...this.handles.values()].map(handle => handle.dispose()));
333
309
  this.handles.clear();
334
310
  this.modelSelections.clear();
335
- this.skillSelections.clear();
336
- for (const dispose of this.skillProviderDisposals.values())
337
- dispose();
338
- this.skillProviderDisposals.clear();
339
- for (const dispose of this.memberRestrictions.values())
340
- dispose();
341
- this.memberRestrictions.clear();
342
- this.capabilityWarnings.clear();
311
+ this.memberRuntime.disposeAll();
343
312
  this.runningAgents.clear();
344
313
  await domain.close();
345
314
  }, 'agentTeam.dispose');
@@ -357,7 +326,7 @@ let AgentTeam = (() => {
357
326
  if (member.state === 'enabled')
358
327
  await this.activateMember(member, undefined, persistedSessions);
359
328
  else if (member.state === 'inactive')
360
- await this.cleanupRemovedMember(member);
329
+ await this.memberRuntime.cleanupRemovedMember(member);
361
330
  }
362
331
  }
363
332
  /** Resolve one exact live Agent to its durable Team Member; forks do not inherit identity. */
@@ -458,6 +427,20 @@ let AgentTeam = (() => {
458
427
  this.emitCommitted(result.value.receipt);
459
428
  return result.value;
460
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
+ }
461
444
  /** Create a durable Member and atomically grant its declared initial Channels. */
462
445
  async addMember(request) {
463
446
  return this.enqueueLifecycle(async () => {
@@ -586,9 +569,7 @@ let AgentTeam = (() => {
586
569
  await active.dispose();
587
570
  this.handles.delete(request.memberId);
588
571
  this.modelSelections.delete(request.memberId);
589
- this.skillSelections.delete(request.memberId);
590
- this.releaseMemberToolPolicy(request.memberId);
591
- this.releaseMemberSkillProvider(request.memberId);
572
+ this.memberRuntime.forgetMember(request.memberId);
592
573
  this.clearMemberFailure(request.memberId, 'activation');
593
574
  this.clearMemberNotificationState(request.memberId);
594
575
  // The previous log survives on disk; archiving hides it from every
@@ -696,37 +677,15 @@ let AgentTeam = (() => {
696
677
  */
697
678
  async applyCapabilityEdit(active, stored) {
698
679
  const memberId = stored.memberId;
699
- if (this.runningAgents.has(active.agent.id)) {
700
- await new Promise(resolve => {
701
- const disposers = [];
702
- const settle = () => {
703
- for (const dispose of disposers.splice(0))
704
- dispose();
705
- resolve();
706
- };
707
- disposers.push(this.ctx.on('agent/status', (payload) => {
708
- if (payload.agent !== active.agent)
709
- return;
710
- // A turn that ends — clean idle or error — is the boundary; the
711
- // next step recomputes schemas from the new restriction.
712
- if (payload.status !== 'running')
713
- settle();
714
- }),
715
- // Disposal (suspend/remove/reactivation) resolves the wait: the old
716
- // restriction went with the disposed scope, so only the ledger
717
- // intent remains to apply at the next activation.
718
- this.ctx.on('session/disposed', (session) => {
719
- if (session.id === active.agent.session.id)
720
- settle();
721
- }));
722
- });
723
- if (this.handles.get(memberId) !== active)
724
- return;
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;
725
686
  }
726
- this.reapplyMemberToolPolicy(stored);
727
- const skillSelection = this.skillSelections.get(memberId);
728
- if (skillSelection !== undefined)
729
- skillSelection.swap(stored.capabilities?.skills?.allow);
687
+ this.memberRuntime.reapplyMemberToolPolicy(stored);
688
+ this.memberRuntime.swapSkillSelection(memberId, stored.capabilities?.skills?.allow);
730
689
  }
731
690
  /** Irreversibly remove one Member, archive its Session, and delete its private namespace. */
732
691
  async removeMember(request) {
@@ -735,7 +694,23 @@ let AgentTeam = (() => {
735
694
  if (result.committed)
736
695
  this.emitCommitted(result.value.receipt);
737
696
  await this.disposeMemberSession(request.memberId, result.value.member);
738
- 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);
739
714
  return result.value;
740
715
  });
741
716
  }
@@ -1068,7 +1043,7 @@ let AgentTeam = (() => {
1068
1043
  try {
1069
1044
  const workspace = this.requireWorkspace(member.workspaceId);
1070
1045
  const workspacePath = knownWorkspacePath ?? workspace.path;
1071
- await this.initializePrivateMemory(member.privateMemoryPath);
1046
+ await this.memberRuntime.initializePrivateMemory(member.privateMemoryPath);
1072
1047
  const persisted = knownSessions !== undefined ? knownSessions.has(member.sessionId)
1073
1048
  : (await this.ctx.sessionPersistence.list()).some(header => header.id === member.sessionId);
1074
1049
  // AgentOptions declares only provider/model. Install the full selection
@@ -1083,7 +1058,7 @@ let AgentTeam = (() => {
1083
1058
  const skillSelection = { current: member.capabilities?.skills?.allow, swap: () => { } };
1084
1059
  const setup = async (agentCtx) => {
1085
1060
  await this.ctx.agentPresets.mount(agentCtx, member.presetId);
1086
- this.applyMemberToolPolicy(agentCtx, member);
1061
+ this.memberRuntime.applyMemberToolPolicy(agentCtx, member);
1087
1062
  this.validateMemberPreset(agentCtx);
1088
1063
  installModelSelection(agentCtx, selected);
1089
1064
  return {
@@ -1091,7 +1066,13 @@ let AgentTeam = (() => {
1091
1066
  const agent = agentCtx.agent;
1092
1067
  if (agent === undefined)
1093
1068
  throw new Error('agent-team setup has no unpublished Agent');
1094
- 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')
1095
1076
  setSandboxMode(agent.session, 'danger-full-access');
1096
1077
  },
1097
1078
  };
@@ -1106,19 +1087,10 @@ let AgentTeam = (() => {
1106
1087
  agentOptions,
1107
1088
  setup,
1108
1089
  });
1109
- // The Member-private skill provider registers on the created agent's
1110
- // exact scope layer (the traceable-service seam, like the tool
1111
- // restriction): bundled read-only core skills plus this Member's own
1112
- // private directory.
1113
- this.skillProviderDisposals.set(member.memberId, memberSkills.mountMemberSkillProvider(created.agent.ctx, {
1114
- skillsDirectory: join(member.privateMemoryPath, 'skills'),
1115
- bundledSkillsDirectory: BUNDLED_SKILLS_DIRECTORY,
1116
- selection: skillSelection,
1117
- }));
1090
+ this.memberRuntime.mountMemberSkillProvider(member, created.agent.ctx, skillSelection);
1118
1091
  await workspace.attachSession(member.sessionId);
1119
1092
  this.handles.set(member.memberId, created);
1120
1093
  this.modelSelections.set(member.memberId, selected);
1121
- this.skillSelections.set(member.memberId, skillSelection);
1122
1094
  this.clearMemberFailure(member.memberId, 'activation');
1123
1095
  this.nameMemberSession(member, created.agent);
1124
1096
  this.notifyMember(created.agent);
@@ -1127,9 +1099,7 @@ let AgentTeam = (() => {
1127
1099
  catch (error) {
1128
1100
  await created?.dispose();
1129
1101
  this.modelSelections.delete(member.memberId);
1130
- this.skillSelections.delete(member.memberId);
1131
- this.releaseMemberToolPolicy(member.memberId);
1132
- this.releaseMemberSkillProvider(member.memberId);
1102
+ this.memberRuntime.forgetMember(member.memberId);
1133
1103
  this.setMemberFailure(member.memberId, 'activation', error instanceof Error ? error.message : String(error));
1134
1104
  }
1135
1105
  finally {
@@ -1156,9 +1126,7 @@ let AgentTeam = (() => {
1156
1126
  if (stale !== undefined) {
1157
1127
  this.handles.delete(memberId);
1158
1128
  this.modelSelections.delete(memberId);
1159
- this.skillSelections.delete(memberId);
1160
- this.releaseMemberToolPolicy(memberId);
1161
- this.releaseMemberSkillProvider(memberId);
1129
+ this.memberRuntime.forgetMember(memberId);
1162
1130
  // The composition-loss diagnostic this heal answers is stale once the
1163
1131
  // rebuild starts; a later activation must not resurface it.
1164
1132
  this.clearMemberFailure(memberId, 'compaction');
@@ -1199,99 +1167,11 @@ let AgentTeam = (() => {
1199
1167
  if (missing.length > 0)
1200
1168
  throw new Error(`team-enabled preset is missing tools: ${missing.join(', ')}`);
1201
1169
  }
1202
- /**
1203
- * Apply one Member's persisted tool allow-list as a scoped restriction on
1204
- * the freshly composed preset surface, and derive activation-time warnings
1205
- * for entries the current tool surface no longer knows. Runs inside setup
1206
- * BEFORE validateMemberPreset so the validation observes the restricted
1207
- * view (the Host always unions the five Team tools over the configured
1208
- * list). Deliberate interface reservation: this restriction seam is the
1209
- * primitive future Runtime Revision manifests orchestrate — do not remove
1210
- * during cleanup.
1211
- */
1212
- applyMemberToolPolicy(agentCtx, member) {
1213
- const scope = scopeOf(agentCtx);
1214
- const configured = member.capabilities?.tools?.allow;
1215
- if (configured === undefined) {
1216
- this.capabilityWarnings.delete(member.memberId);
1217
- return;
1218
- }
1219
- // Drop names the current surface does not know rather than failing the
1220
- // activation: a Harness upgrade renaming a tool must not make the Member
1221
- // unavailable. The warning carries the known-name digest so a distant
1222
- // future reader can diagnose the drift.
1223
- const known = this.ctx.tools.schemas(scope).map(tool => tool.name);
1224
- const knownSet = new Set(known);
1225
- const dropped = configured.filter(name => !knownSet.has(name));
1226
- this.setCapabilityWarnings(member.memberId, dropped.map(name => ({ name, knownNames: known })));
1227
- const allow = [...new Set([...configured.filter(name => knownSet.has(name)), ...AGENT_TEAM_TOOL_NAMES])];
1228
- if (allow.length === 0)
1229
- return;
1230
- // tools.restrict() requires a scoped context and rejects names outside
1231
- // the inherited surface; both errors surface as this Member's activation
1232
- // failure without touching any other Member.
1233
- const dispose = agentCtx.tools.restrict({ allow });
1234
- this.memberRestrictions.set(member.memberId, dispose);
1235
- }
1236
- /** Swap a live Member's tool policy at a turn boundary: dispose the old restriction, apply the new. */
1237
- reapplyMemberToolPolicy(member) {
1238
- this.releaseMemberToolPolicy(member.memberId);
1239
- this.applyMemberToolPolicy(this.requireLiveMemberContext(member.memberId), member);
1240
- }
1241
- requireLiveMemberContext(memberId) {
1242
- const handle = this.handles.get(memberId);
1243
- if (handle === undefined)
1244
- throw new Error(`Agent Member '${this.memberLabel(memberId)}' has no live session for a tool-policy update`);
1245
- return handle.agent.ctx;
1246
- }
1247
- /** Release one Member's restriction disposer and warning state; safe to call twice. */
1248
- releaseMemberToolPolicy(memberId) {
1249
- const dispose = this.memberRestrictions.get(memberId);
1250
- if (dispose !== undefined) {
1251
- this.memberRestrictions.delete(memberId);
1252
- dispose();
1253
- }
1254
- this.capabilityWarnings.delete(memberId);
1255
- }
1256
- /** Release one Member's private skill provider; safe to call twice. */
1257
- releaseMemberSkillProvider(memberId) {
1258
- const dispose = this.skillProviderDisposals.get(memberId);
1259
- if (dispose === undefined)
1260
- return;
1261
- this.skillProviderDisposals.delete(memberId);
1262
- dispose();
1263
- }
1264
- setCapabilityWarnings(memberId, warnings) {
1265
- if (warnings.length === 0)
1266
- this.capabilityWarnings.delete(memberId);
1267
- else
1268
- this.capabilityWarnings.set(memberId, Object.freeze([...warnings]));
1269
- }
1270
- async initializePrivateMemory(path) {
1271
- await mkdir(join(path, 'notes'), { recursive: true });
1272
- // The Member-private skills directory starts empty; the per-Member
1273
- // provider scans exactly this root (default roots excluded).
1274
- await mkdir(join(path, 'skills'), { recursive: true });
1275
- try {
1276
- 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' });
1277
- }
1278
- catch (error) {
1279
- if (error.code !== 'EEXIST')
1280
- throw error;
1281
- }
1282
- }
1283
- async cleanupRemovedMember(member) {
1284
- const results = await Promise.allSettled([
1285
- this.ctx.workspaceRegistry.archiveSession(member.sessionId),
1286
- rm(member.privateMemoryPath, { recursive: true, force: true }),
1287
- ]);
1288
- const failures = results.flatMap(result => result.status === 'rejected' ? [result.reason] : []);
1289
- if (failures.length > 0)
1290
- throw new AggregateError(failures, `failed to clean up removed Member '${member.memberId}'`);
1291
- }
1292
1170
  memberStatus(member) {
1293
1171
  if (member.state === 'inactive')
1294
1172
  return Object.freeze({ member, availability: 'inactive', presence: 'unavailable' });
1173
+ if (member.state === 'archived')
1174
+ return Object.freeze({ member, availability: 'archived', presence: 'unavailable' });
1295
1175
  if (member.state === 'suspended')
1296
1176
  return Object.freeze({ member, availability: 'suspended', presence: 'unavailable' });
1297
1177
  const failures = this.memberFailures.get(member.memberId);
@@ -1308,7 +1188,7 @@ let AgentTeam = (() => {
1308
1188
  return Object.freeze({ member, availability: 'active', presence: 'error', diagnostic: runtimeError });
1309
1189
  // Capability warnings are runtime-derived at activation (handles-scoped,
1310
1190
  // like failures): absent while capabilities resolve cleanly.
1311
- const capabilityWarnings = this.capabilityWarnings.get(member.memberId);
1191
+ const capabilityWarnings = this.memberRuntime.capabilityWarningsFor(member.memberId);
1312
1192
  return Object.freeze({
1313
1193
  member, availability: 'active', presence: handle.agent.status === 'running' ? 'working' : 'available',
1314
1194
  ...(capabilityWarnings === undefined ? {} : { capabilityWarnings }),
@@ -1497,9 +1377,7 @@ let AgentTeam = (() => {
1497
1377
  this.handles.delete(memberId);
1498
1378
  }
1499
1379
  this.modelSelections.delete(memberId);
1500
- this.skillSelections.delete(memberId);
1501
- this.releaseMemberToolPolicy(memberId);
1502
- this.releaseMemberSkillProvider(memberId);
1380
+ this.memberRuntime.forgetMember(memberId);
1503
1381
  this.clearMemberFailure(memberId, 'activation');
1504
1382
  }
1505
1383
  clearMemberRecoveryState(member) {