@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.
- package/README.md +23 -3
- package/README.zh.md +23 -3
- package/package.json +50 -43
- package/packages/agent-team/README.md +8 -0
- package/packages/agent-team/README.zh.md +8 -0
- package/packages/agent-team/core-skills/member-skill-manager/SKILL.md +41 -0
- package/packages/agent-team/core-skills/member-skill-manager/references/auth-and-config.md +30 -0
- package/packages/agent-team/core-skills/member-skill-manager/references/writing-great-skills.md +17 -0
- package/packages/agent-team/lib/index.js +168 -37
- package/packages/agent-team/lib/ledger.js +440 -18
- package/packages/agent-team/lib/member-context.js +7 -3
- package/packages/agent-team/lib/member-runtime.js +229 -0
- package/packages/agent-team/lib/member-skills.js +94 -0
- package/packages/agent-team/lib/preset-roster.js +1 -0
- package/packages/agent-team/lib/spec.js +56 -1
- package/packages/agent-team/lib/typert.host.js +359 -77
- package/packages/agent-team/lib/typert.remote-client.d.ts +5 -1
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +248 -43
- package/packages/agent-team/lib/types/entities.js +1 -0
- package/packages/agent-team/lib/types/index.d.ts +51 -7
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +62 -1
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-runtime.d.ts +108 -0
- package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -0
- package/packages/agent-team/lib/types/member-skills.d.ts +45 -0
- package/packages/agent-team/lib/types/member-skills.d.ts.map +1 -0
- package/packages/agent-team/lib/types/operations.js +1 -0
- package/packages/agent-team/lib/types/preset-roster.d.ts.map +1 -1
- package/packages/agent-team/lib/types/requests-results.js +1 -0
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/entities.d.ts +339 -0
- package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -0
- package/packages/agent-team/lib/types/types/operations.d.ts +303 -0
- package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -0
- package/packages/agent-team/lib/types/types/requests-results.d.ts +527 -0
- package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -0
- package/packages/agent-team/lib/types/types.d.ts +10 -985
- package/packages/agent-team/lib/types/types.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +11 -6
- package/packages/client-agent-team/README.md +4 -2
- package/packages/client-agent-team/README.zh.md +4 -2
- package/packages/client-agent-team/lib/client.js +636 -572
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +3 -5
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +37 -26
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts +2 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +31 -5
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +2 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts +12 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberDock.js +13 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +4 -8
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +7 -40
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +10 -3
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +61 -20
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +25 -28
- package/packages/client-agent-team/lib/types/client/locales.d.ts +32 -16
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +32 -16
- package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +1 -3
- package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/member-session-input.js +18 -6
- package/packages/client-agent-team/lib/types/client/navigation.d.ts +2 -1
- package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/slots.d.ts +3 -1
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-membership.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/team-membership.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts +5 -7
- package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/timeline-scroll.js +9 -22
- package/packages/tool-agent-team/lib/index.js +25 -5
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts +0 -17
- package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +0 -1
- 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 {
|
|
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
|
-
/**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|