@wowyuarm/dsh-agent-team 0.1.6 → 0.1.8

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 (108) 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/core-skills/member-skill-manager/SKILL.md +2 -0
  7. package/packages/agent-team/lib/index.js +140 -204
  8. package/packages/agent-team/lib/ledger.js +436 -25
  9. package/packages/agent-team/lib/member-context.js +6 -2
  10. package/packages/agent-team/lib/member-runtime.js +229 -0
  11. package/packages/agent-team/lib/preset-roster.js +1 -0
  12. package/packages/agent-team/lib/progress-nudge.js +320 -0
  13. package/packages/agent-team/lib/spec.js +30 -1
  14. package/packages/agent-team/lib/typert.host.js +250 -70
  15. package/packages/agent-team/lib/typert.remote-client.d.ts +5 -1
  16. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  17. package/packages/agent-team/lib/typert.remote-client.js +168 -38
  18. package/packages/agent-team/lib/types/entities.js +1 -0
  19. package/packages/agent-team/lib/types/index.d.ts +28 -44
  20. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  21. package/packages/agent-team/lib/types/ledger.d.ts +60 -2
  22. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  23. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  24. package/packages/agent-team/lib/types/member-runtime.d.ts +108 -0
  25. package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -0
  26. package/packages/agent-team/lib/types/operations.js +1 -0
  27. package/packages/agent-team/lib/types/preset-roster.d.ts.map +1 -1
  28. package/packages/agent-team/lib/types/progress-nudge.d.ts +136 -0
  29. package/packages/agent-team/lib/types/progress-nudge.d.ts.map +1 -0
  30. package/packages/agent-team/lib/types/requests-results.js +1 -0
  31. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  32. package/packages/agent-team/lib/types/types/entities.d.ts +357 -0
  33. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -0
  34. package/packages/agent-team/lib/types/types/operations.d.ts +303 -0
  35. package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -0
  36. package/packages/agent-team/lib/types/types/requests-results.d.ts +529 -0
  37. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -0
  38. package/packages/agent-team/lib/types/types.d.ts +10 -1070
  39. package/packages/agent-team/lib/types/types.d.ts.map +1 -1
  40. package/packages/agent-team/preset/team-member/agent.cordis.yml +6 -4
  41. package/packages/client-agent-team/README.md +3 -1
  42. package/packages/client-agent-team/README.zh.md +3 -1
  43. package/packages/client-agent-team/lib/client.js +773 -733
  44. package/packages/client-agent-team/lib/client.js.map +1 -1
  45. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +3 -2
  46. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  47. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +30 -7
  48. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +1 -1
  49. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  50. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +5 -9
  51. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts +2 -1
  52. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  53. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +31 -5
  54. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +3 -1
  55. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  56. package/packages/client-agent-team/lib/types/client/TeamComposer.js +39 -28
  57. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
  58. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
  59. package/packages/client-agent-team/lib/types/client/TeamConversation.js +2 -2
  60. package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.d.ts.map +1 -1
  61. package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.js +3 -2
  62. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  63. package/packages/client-agent-team/lib/types/client/TeamMessage.js +15 -3
  64. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +2 -2
  65. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
  66. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +4 -6
  67. package/packages/client-agent-team/lib/types/client/TeamStateDot.d.ts +14 -0
  68. package/packages/client-agent-team/lib/types/client/TeamStateDot.d.ts.map +1 -0
  69. package/packages/client-agent-team/lib/types/client/TeamStateDot.js +15 -0
  70. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +2 -1
  71. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  72. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +41 -16
  73. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  74. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  75. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -2
  76. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts +1 -1
  77. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts.map +1 -1
  78. package/packages/client-agent-team/lib/types/client/index.d.ts +1 -1
  79. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  80. package/packages/client-agent-team/lib/types/client/index.js +9 -77
  81. package/packages/client-agent-team/lib/types/client/locales.d.ts +26 -0
  82. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  83. package/packages/client-agent-team/lib/types/client/locales.js +26 -0
  84. package/packages/client-agent-team/lib/types/client/navigation.d.ts +2 -1
  85. package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
  86. package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts +1 -2
  87. package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts.map +1 -1
  88. package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts +1 -1
  89. package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts.map +1 -1
  90. package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts +1 -1
  91. package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts.map +1 -1
  92. package/packages/client-agent-team/lib/types/client/slots.d.ts +5 -1
  93. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  94. package/packages/client-agent-team/lib/types/client/task-refs.d.ts +7 -1
  95. package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -1
  96. package/packages/client-agent-team/lib/types/client/task-refs.js +25 -3
  97. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +11 -10
  98. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  99. package/packages/client-agent-team/lib/types/client/team-formatters.js +14 -11
  100. package/packages/client-agent-team/lib/types/client/team-membership.d.ts +1 -1
  101. package/packages/client-agent-team/lib/types/client/team-membership.d.ts.map +1 -1
  102. package/packages/tool-agent-team/lib/index.js +9 -9
  103. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts +0 -17
  104. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +0 -1
  105. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.js +0 -135
  106. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +0 -22
  107. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +0 -1
  108. package/packages/client-agent-team/lib/types/client/member-session-input.js +0 -98
@@ -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,21 @@ 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";
56
+ import { ProgressNudgeCoordinator } from "./progress-nudge.js";
59
57
  import { classifyRecoverableError, RecoveryCoordinator, RECOVERY_MAX_CONSECUTIVE_ERRORS } from "./recovery.js";
60
58
  import { agentTeamDomainSpec } from "./spec.js";
61
59
  export { agentTeamDomainSpec, agentTeamOperationSchema } from "./spec.js";
62
60
  export { AGENT_TEAM_HUMAN_MEMBER_ID, AGENT_TEAM_INITIALIZE_REQUEST_ID } from "./ledger.js";
61
+ export { AGENT_TEAM_TOOL_NAMES } from "./member-runtime.js";
63
62
  /** Process-stable marker carried by the final Team message tool definition. */
64
63
  export const AGENT_TEAM_PRESET_MARKER = Symbol.for('@wowyuarm/dsh-agent-team.preset');
65
64
  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
65
  const INBOX_NOTICE_SUMMARY = 'Team Inbox has unread work.';
74
66
  const RECOVERY_NOTICE_SUMMARY = 'Recovery: continue your interrupted work.';
75
67
  const ORPHANED_MEMBER_DIAGNOSTIC = 'Member preset composition was lost after a reload; its tools are unavailable. Resume rebuilds the member in place.';
@@ -82,27 +74,11 @@ function sameChangeScope(left, right) {
82
74
  return left.threadRef === right.threadRef;
83
75
  return false;
84
76
  }
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
77
  /** Mark the preset's `team_message` definition as an Agent Team consumer. */
94
78
  export function markAgentTeamPreset(definition) {
95
79
  Object.defineProperty(definition, AGENT_TEAM_PRESET_MARKER, { value: true });
96
80
  return definition;
97
81
  }
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
82
  /**
107
83
  * A DM was durably recorded but its session injection could not run (no live
108
84
  * handle, or the wake itself failed). The recorded DM stays durable; the
@@ -127,10 +103,12 @@ let AgentTeam = (() => {
127
103
  let _changes_decorators;
128
104
  let _createChannel_decorators;
129
105
  let _updateChannel_decorators;
106
+ let _archiveChannel_decorators;
130
107
  let _addMember_decorators;
131
108
  let _recoverMember_decorators;
132
109
  let _clearMemberContext_decorators;
133
110
  let _updateMember_decorators;
111
+ let _archiveMember_decorators;
134
112
  let _changeTask_decorators;
135
113
  let _promoteThread_decorators;
136
114
  let _joinChannel_decorators;
@@ -153,10 +131,12 @@ let AgentTeam = (() => {
153
131
  _changes_decorators = [Remote('changes')];
154
132
  _createChannel_decorators = [Remote('createChannel')];
155
133
  _updateChannel_decorators = [Remote('updateChannel')];
134
+ _archiveChannel_decorators = [Remote('archiveChannel')];
156
135
  _addMember_decorators = [Remote('addMember')];
157
136
  _recoverMember_decorators = [Remote('recoverMember')];
158
137
  _clearMemberContext_decorators = [Remote('clearMemberContext')];
159
138
  _updateMember_decorators = [Remote('updateMember')];
139
+ _archiveMember_decorators = [Remote('archiveMember')];
160
140
  _changeTask_decorators = [Remote('changeTask')];
161
141
  _promoteThread_decorators = [Remote('promoteThread')];
162
142
  _joinChannel_decorators = [Remote('joinChannel')];
@@ -176,10 +156,12 @@ let AgentTeam = (() => {
176
156
  __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
157
  __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
158
  __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);
159
+ __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
160
  __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
161
  __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
162
  __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
163
  __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);
164
+ __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
165
  __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
166
  __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
167
  __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);
@@ -208,12 +190,25 @@ let AgentTeam = (() => {
208
190
  domain = __runInitializers(this, _instanceExtraInitializers);
209
191
  ledger;
210
192
  handles = new Map();
193
+ /** Live Member per session id; drives the root session/event listener. */
194
+ memberBySessionId = new Map();
211
195
  /** Live selection refs let model edits take effect without disposing the Session. */
212
196
  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();
197
+ /** Agent ids with a turn in flight; restarts must wait for the boundary. */
198
+ runningAgents = new Set();
199
+ /** Per-Member runtime state: tool restrictions, skill mounts, warnings, private memory. */
200
+ memberRuntime = new MemberRuntime({
201
+ // Deferred field access: the constructor runs before `handles` is
202
+ // populated, so the resolver must dereference at call time.
203
+ ctx: this.ctx,
204
+ liveMemberContext: memberId => {
205
+ const handle = this.handles.get(memberId);
206
+ if (handle === undefined)
207
+ throw new Error(`Agent Member '${this.memberLabel(memberId)}' has no live session for a tool-policy update`);
208
+ return handle.agent.ctx;
209
+ },
210
+ runningAgents: this.runningAgents,
211
+ });
217
212
  /**
218
213
  * Why one Member shows error presence, per failure source. Reads prefer
219
214
  * activation, then runtime, then compaction; slots clear independently, so
@@ -222,23 +217,7 @@ let AgentTeam = (() => {
222
217
  * stale keys.
223
218
  */
224
219
  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
220
  autoCompaction;
240
- /** Agent ids with a turn in flight; restarts must wait for the boundary. */
241
- runningAgents = new Set();
242
221
  notifiedInbox = new Map();
243
222
  attachmentGcTimer;
244
223
  recovery = new RecoveryCoordinator({
@@ -250,6 +229,22 @@ let AgentTeam = (() => {
250
229
  this.ctx.logger.warn(`agent-team: member '${this.memberLabel(memberId)}' reached ${consecutiveFailures}/${RECOVERY_MAX_CONSECUTIVE_ERRORS} consecutive recoverable failures; leaving it in error for the operator`);
251
230
  },
252
231
  });
232
+ /**
233
+ * Advisory progress-visibility nudges. Purely in-process: the ledger owns
234
+ * eligibility, this coordinator owns counting and notices, and neither ever
235
+ * writes Team facts. See docs/team-collaboration.md for the regimes.
236
+ */
237
+ progressNudge = new ProgressNudgeCoordinator({
238
+ agentForMember: memberId => this.handles.get(memberId)?.agent,
239
+ targetsForMember: memberId => this.requireLedger().progressNudgeTargets(memberId),
240
+ sessionLogForMember: (memberId, sessionId) => {
241
+ const handle = this.handles.get(memberId);
242
+ if (handle === undefined || handle.agent.session.id !== sessionId)
243
+ return undefined;
244
+ return { events: handle.agent.session.ownEvents() };
245
+ },
246
+ log: message => { this.ctx.logger.warn(message); },
247
+ });
253
248
  lifecycleTail = Promise.resolve();
254
249
  accepting = true;
255
250
  changeVersion = 0;
@@ -276,6 +271,10 @@ let AgentTeam = (() => {
276
271
  content: [{ type: 'text', text: preCompactionNoticeText() }],
277
272
  source: { kind: 'plugin', plugin: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: PRE_COMPACTION_NOTICE_SUMMARY },
278
273
  });
274
+ // Persist-your-conclusions outranks a queued progress nudge.
275
+ const member = this.memberForAgent(agent);
276
+ if (member !== undefined)
277
+ this.progressNudge.revokePendingNotice(member.memberId);
279
278
  agent.steer(hint);
280
279
  },
281
280
  });
@@ -319,10 +318,28 @@ let AgentTeam = (() => {
319
318
  this.emitChanged([{ kind: 'workspace', workspaceId: member.workspaceId }]);
320
319
  }
321
320
  });
321
+ // Progress nudges count every `tool/call` of each Member Session. The
322
+ // store's dispatch carrier is untagged, so a scope-tagged listener inside
323
+ // the Agent setup would receive nothing; one root listener that maps the
324
+ // session id back to its Member is the seam that works (same shape as the
325
+ // `agent/status` listener above). The first turn of a freshly published
326
+ // Member cannot race this: `handles.set()` precedes the same activation
327
+ // continuation that publishes the Agent, so by the time any tool call
328
+ // streams, the map lookup succeeds.
329
+ this.ctx.on('session/event', (session, event) => {
330
+ const memberId = this.memberBySessionId.get(session.id);
331
+ if (memberId === undefined)
332
+ return;
333
+ const handle = this.handles.get(memberId);
334
+ if (handle === undefined || handle.agent.session.id !== session.id)
335
+ return;
336
+ this.progressNudge.onSessionEvent(memberId, session.id, handle.agent, event);
337
+ });
322
338
  const domain = await this.ctx.storageDomain.open(agentTeamDomainSpec);
323
339
  this.ctx.effect(() => async () => {
324
340
  this.accepting = false;
325
341
  this.recovery.dispose();
342
+ this.progressNudge.dispose();
326
343
  await this.autoCompaction.dispose();
327
344
  if (this.attachmentGcTimer !== undefined)
328
345
  clearInterval(this.attachmentGcTimer);
@@ -332,14 +349,7 @@ let AgentTeam = (() => {
332
349
  await Promise.all([...this.handles.values()].map(handle => handle.dispose()));
333
350
  this.handles.clear();
334
351
  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();
352
+ this.memberRuntime.disposeAll();
343
353
  this.runningAgents.clear();
344
354
  await domain.close();
345
355
  }, 'agentTeam.dispose');
@@ -357,7 +367,7 @@ let AgentTeam = (() => {
357
367
  if (member.state === 'enabled')
358
368
  await this.activateMember(member, undefined, persistedSessions);
359
369
  else if (member.state === 'inactive')
360
- await this.cleanupRemovedMember(member);
370
+ await this.memberRuntime.cleanupRemovedMember(member);
361
371
  }
362
372
  }
363
373
  /** Resolve one exact live Agent to its durable Team Member; forks do not inherit identity. */
@@ -458,6 +468,20 @@ let AgentTeam = (() => {
458
468
  this.emitCommitted(result.value.receipt);
459
469
  return result.value;
460
470
  }
471
+ /**
472
+ * Archive one Channel: hidden from every surface with all facts kept. Pure
473
+ * ledger projection change — Member sessions stay live (they may work in
474
+ * other Channels), every active Claim on the Channel's Threads releases,
475
+ * and affected Members' Attention clears.
476
+ */
477
+ async archiveChannel(request) {
478
+ this.requireAccepting();
479
+ this.requireWorkspace(request.workspaceId);
480
+ const result = await this.requireLedger().archiveChannel({ ...request, actor: agentTeamHumanActor() });
481
+ if (result.committed)
482
+ this.emitCommitted(result.value.receipt);
483
+ return result.value;
484
+ }
461
485
  /** Create a durable Member and atomically grant its declared initial Channels. */
462
486
  async addMember(request) {
463
487
  return this.enqueueLifecycle(async () => {
@@ -583,12 +607,14 @@ let AgentTeam = (() => {
583
607
  // Drop the old handle's transient state: pending recovery episodes and
584
608
  // error markers belong to the disposed agent, not to the Member.
585
609
  this.recovery.stopTracking(request.memberId);
610
+ // The fresh Session may re-earn one Claim suggestion per Thread; the
611
+ // old Session's one-shot records must not leak into it.
612
+ this.progressNudge.stopTracking(request.memberId);
613
+ this.memberBySessionId.delete(previousSessionId);
586
614
  await active.dispose();
587
615
  this.handles.delete(request.memberId);
588
616
  this.modelSelections.delete(request.memberId);
589
- this.skillSelections.delete(request.memberId);
590
- this.releaseMemberToolPolicy(request.memberId);
591
- this.releaseMemberSkillProvider(request.memberId);
617
+ this.memberRuntime.forgetMember(request.memberId);
592
618
  this.clearMemberFailure(request.memberId, 'activation');
593
619
  this.clearMemberNotificationState(request.memberId);
594
620
  // The previous log survives on disk; archiving hides it from every
@@ -647,6 +673,9 @@ let AgentTeam = (() => {
647
673
  if (this.isInboxNotice(pending))
648
674
  handle.agent.inbox.remove(pending.id);
649
675
  }
676
+ // Recovery carries the concrete interrupted work; a queued progress nudge
677
+ // is stale next to it and must not survive as a second reminder.
678
+ this.progressNudge.revokePendingNotice(member.memberId);
650
679
  handle.agent.steer(hint);
651
680
  }
652
681
  /** Automatic-recovery wakeup; throwing tells the coordinator the target is gone. */
@@ -696,37 +725,15 @@ let AgentTeam = (() => {
696
725
  */
697
726
  async applyCapabilityEdit(active, stored) {
698
727
  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;
728
+ const waited = await this.memberRuntime.awaitTurnBoundary(active);
729
+ if (waited && this.handles.get(memberId) !== active) {
730
+ // The wait resolved because the old generation was disposed, not
731
+ // because the turn ended; the ledger intent applies at the next
732
+ // activation instead.
733
+ return;
725
734
  }
726
- this.reapplyMemberToolPolicy(stored);
727
- const skillSelection = this.skillSelections.get(memberId);
728
- if (skillSelection !== undefined)
729
- skillSelection.swap(stored.capabilities?.skills?.allow);
735
+ this.memberRuntime.reapplyMemberToolPolicy(stored);
736
+ this.memberRuntime.swapSkillSelection(memberId, stored.capabilities?.skills?.allow);
730
737
  }
731
738
  /** Irreversibly remove one Member, archive its Session, and delete its private namespace. */
732
739
  async removeMember(request) {
@@ -735,7 +742,23 @@ let AgentTeam = (() => {
735
742
  if (result.committed)
736
743
  this.emitCommitted(result.value.receipt);
737
744
  await this.disposeMemberSession(request.memberId, result.value.member);
738
- await this.cleanupRemovedMember(result.value.member);
745
+ await this.memberRuntime.cleanupRemovedMember(result.value.member);
746
+ return result.value;
747
+ });
748
+ }
749
+ /**
750
+ * Archive one Member: commit the archival, stop its live session (disposal
751
+ * only — private memory and the Session log stay on disk for a future
752
+ * restore), and archive the Session from every grouping surface. Like
753
+ * removal, all active Claims release and the Member's Attention clears.
754
+ */
755
+ async archiveMember(request) {
756
+ return this.enqueueLifecycle(async () => {
757
+ const result = await this.requireLedger().archiveMember({ ...request, actor: agentTeamHumanActor() });
758
+ if (result.committed)
759
+ this.emitCommitted(result.value.receipt);
760
+ await this.disposeMemberSession(request.memberId, result.value.member);
761
+ await this.ctx.workspaceRegistry.archiveSession(result.value.member.sessionId);
739
762
  return result.value;
740
763
  });
741
764
  }
@@ -1068,7 +1091,7 @@ let AgentTeam = (() => {
1068
1091
  try {
1069
1092
  const workspace = this.requireWorkspace(member.workspaceId);
1070
1093
  const workspacePath = knownWorkspacePath ?? workspace.path;
1071
- await this.initializePrivateMemory(member.privateMemoryPath);
1094
+ await this.memberRuntime.initializePrivateMemory(member.privateMemoryPath);
1072
1095
  const persisted = knownSessions !== undefined ? knownSessions.has(member.sessionId)
1073
1096
  : (await this.ctx.sessionPersistence.list()).some(header => header.id === member.sessionId);
1074
1097
  // AgentOptions declares only provider/model. Install the full selection
@@ -1083,7 +1106,7 @@ let AgentTeam = (() => {
1083
1106
  const skillSelection = { current: member.capabilities?.skills?.allow, swap: () => { } };
1084
1107
  const setup = async (agentCtx) => {
1085
1108
  await this.ctx.agentPresets.mount(agentCtx, member.presetId);
1086
- this.applyMemberToolPolicy(agentCtx, member);
1109
+ this.memberRuntime.applyMemberToolPolicy(agentCtx, member);
1087
1110
  this.validateMemberPreset(agentCtx);
1088
1111
  installModelSelection(agentCtx, selected);
1089
1112
  return {
@@ -1091,7 +1114,13 @@ let AgentTeam = (() => {
1091
1114
  const agent = agentCtx.agent;
1092
1115
  if (agent === undefined)
1093
1116
  throw new Error('agent-team setup has no unpublished Agent');
1094
- if (effectiveSandboxMode(agent.session.events) !== 'danger-full-access')
1117
+ // The member scope composes no sandbox-policy service (the preset
1118
+ // owns no sandbox row), so read the last logged mode straight from
1119
+ // the session log; `sandbox/mode` is log-only and never joins the
1120
+ // model-visible surface. Re-appending only when the effective mode
1121
+ // differs keeps resumed members from growing redundant events.
1122
+ const logged = agent.session.ownEvents().toReversed().find(event => event.type === 'sandbox/mode');
1123
+ if (logged?.data.mode !== 'danger-full-access')
1095
1124
  setSandboxMode(agent.session, 'danger-full-access');
1096
1125
  },
1097
1126
  };
@@ -1106,19 +1135,11 @@ let AgentTeam = (() => {
1106
1135
  agentOptions,
1107
1136
  setup,
1108
1137
  });
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
- }));
1138
+ this.memberRuntime.mountMemberSkillProvider(member, created.agent.ctx, skillSelection);
1118
1139
  await workspace.attachSession(member.sessionId);
1119
1140
  this.handles.set(member.memberId, created);
1141
+ this.memberBySessionId.set(member.sessionId, member.memberId);
1120
1142
  this.modelSelections.set(member.memberId, selected);
1121
- this.skillSelections.set(member.memberId, skillSelection);
1122
1143
  this.clearMemberFailure(member.memberId, 'activation');
1123
1144
  this.nameMemberSession(member, created.agent);
1124
1145
  this.notifyMember(created.agent);
@@ -1127,9 +1148,7 @@ let AgentTeam = (() => {
1127
1148
  catch (error) {
1128
1149
  await created?.dispose();
1129
1150
  this.modelSelections.delete(member.memberId);
1130
- this.skillSelections.delete(member.memberId);
1131
- this.releaseMemberToolPolicy(member.memberId);
1132
- this.releaseMemberSkillProvider(member.memberId);
1151
+ this.memberRuntime.forgetMember(member.memberId);
1133
1152
  this.setMemberFailure(member.memberId, 'activation', error instanceof Error ? error.message : String(error));
1134
1153
  }
1135
1154
  finally {
@@ -1156,9 +1175,7 @@ let AgentTeam = (() => {
1156
1175
  if (stale !== undefined) {
1157
1176
  this.handles.delete(memberId);
1158
1177
  this.modelSelections.delete(memberId);
1159
- this.skillSelections.delete(memberId);
1160
- this.releaseMemberToolPolicy(memberId);
1161
- this.releaseMemberSkillProvider(memberId);
1178
+ this.memberRuntime.forgetMember(memberId);
1162
1179
  // The composition-loss diagnostic this heal answers is stale once the
1163
1180
  // rebuild starts; a later activation must not resurface it.
1164
1181
  this.clearMemberFailure(memberId, 'compaction');
@@ -1199,99 +1216,11 @@ let AgentTeam = (() => {
1199
1216
  if (missing.length > 0)
1200
1217
  throw new Error(`team-enabled preset is missing tools: ${missing.join(', ')}`);
1201
1218
  }
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
1219
  memberStatus(member) {
1293
1220
  if (member.state === 'inactive')
1294
1221
  return Object.freeze({ member, availability: 'inactive', presence: 'unavailable' });
1222
+ if (member.state === 'archived')
1223
+ return Object.freeze({ member, availability: 'archived', presence: 'unavailable' });
1295
1224
  if (member.state === 'suspended')
1296
1225
  return Object.freeze({ member, availability: 'suspended', presence: 'unavailable' });
1297
1226
  const failures = this.memberFailures.get(member.memberId);
@@ -1308,7 +1237,7 @@ let AgentTeam = (() => {
1308
1237
  return Object.freeze({ member, availability: 'active', presence: 'error', diagnostic: runtimeError });
1309
1238
  // Capability warnings are runtime-derived at activation (handles-scoped,
1310
1239
  // like failures): absent while capabilities resolve cleanly.
1311
- const capabilityWarnings = this.capabilityWarnings.get(member.memberId);
1240
+ const capabilityWarnings = this.memberRuntime.capabilityWarningsFor(member.memberId);
1312
1241
  return Object.freeze({
1313
1242
  member, availability: 'active', presence: handle.agent.status === 'running' ? 'working' : 'available',
1314
1243
  ...(capabilityWarnings === undefined ? {} : { capabilityWarnings }),
@@ -1353,6 +1282,10 @@ let AgentTeam = (() => {
1353
1282
  }
1354
1283
  const ledger = this.requireLedger();
1355
1284
  this.emitChanged(ledger.changeScopesOf(operation));
1285
+ // Nudge reconciliation precedes notifyMember: a committed reply both
1286
+ // resets the author's silence and cancels any of its queued nudge before
1287
+ // the Inbox notice (higher priority) is considered.
1288
+ this.progressNudge.onCommitted(operation);
1356
1289
  for (const memberId of ledger.affectedMembersOf(operation)) {
1357
1290
  const handle = this.handles.get(memberId);
1358
1291
  if (handle !== undefined)
@@ -1391,6 +1324,9 @@ let AgentTeam = (() => {
1391
1324
  const existingInboxHint = pending.find(message => this.isInboxNotice(message));
1392
1325
  if (existingInboxHint !== undefined)
1393
1326
  agent.inbox.remove(existingInboxHint.id);
1327
+ // A durable Inbox notice outranks a queued progress nudge; revoke it so
1328
+ // the model reads concrete unread work instead of a generic reminder.
1329
+ this.progressNudge.revokePendingNotice(member.memberId);
1394
1330
  const hint = createUserMessage({
1395
1331
  content: [{ type: 'text', text: this.notificationText(notifications, member.memberId) }],
1396
1332
  source: { kind: 'plugin', plugin: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: INBOX_NOTICE_SUMMARY },
@@ -1496,14 +1432,14 @@ let AgentTeam = (() => {
1496
1432
  await handle.dispose();
1497
1433
  this.handles.delete(memberId);
1498
1434
  }
1435
+ this.memberBySessionId.delete(member.sessionId);
1499
1436
  this.modelSelections.delete(memberId);
1500
- this.skillSelections.delete(memberId);
1501
- this.releaseMemberToolPolicy(memberId);
1502
- this.releaseMemberSkillProvider(memberId);
1437
+ this.memberRuntime.forgetMember(memberId);
1503
1438
  this.clearMemberFailure(memberId, 'activation');
1504
1439
  }
1505
1440
  clearMemberRecoveryState(member) {
1506
1441
  this.recovery.stopTracking(member.memberId);
1442
+ this.progressNudge.stopTracking(member.memberId);
1507
1443
  this.clearMemberFailure(member.memberId, 'runtime');
1508
1444
  this.clearMemberNotificationState(member.memberId);
1509
1445
  }