@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.
- package/README.md +11 -5
- package/README.zh.md +11 -5
- package/package.json +44 -42
- package/packages/agent-team/README.md +2 -0
- package/packages/agent-team/README.zh.md +2 -0
- package/packages/agent-team/lib/index.js +82 -204
- package/packages/agent-team/lib/ledger.js +301 -13
- package/packages/agent-team/lib/member-context.js +5 -1
- package/packages/agent-team/lib/member-runtime.js +229 -0
- package/packages/agent-team/lib/preset-roster.js +1 -0
- package/packages/agent-team/lib/spec.js +30 -1
- package/packages/agent-team/lib/typert.host.js +253 -74
- 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 +172 -43
- package/packages/agent-team/lib/types/entities.js +1 -0
- package/packages/agent-team/lib/types/index.d.ts +20 -44
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +34 -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/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 -1070
- package/packages/agent-team/lib/types/types.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +4 -2
- package/packages/client-agent-team/README.md +3 -1
- package/packages/client-agent-team/README.zh.md +3 -1
- package/packages/client-agent-team/lib/client.js +444 -298
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +3 -2
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +30 -7
- 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/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/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 +28 -0
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +28 -0
- 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/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
|
@@ -97,7 +97,7 @@ export class AgentTeamLedger {
|
|
|
97
97
|
this.assertJoinableMember(request.workspaceId, memberId);
|
|
98
98
|
const sequence = this.nextSequence();
|
|
99
99
|
const channel = Object.freeze({
|
|
100
|
-
channelRef: this.ref('channel'), workspaceId: request.workspaceId, name, description, createdAtSequence: sequence,
|
|
100
|
+
channelRef: this.ref('channel'), workspaceId: request.workspaceId, name, description, createdAtSequence: sequence, state: 'active',
|
|
101
101
|
});
|
|
102
102
|
const operation = Object.freeze({
|
|
103
103
|
...this.operationBase(request, sequence), kind: 'team/channel-created',
|
|
@@ -121,7 +121,7 @@ export class AgentTeamLedger {
|
|
|
121
121
|
const description = request.description.trim();
|
|
122
122
|
if (name === '')
|
|
123
123
|
throw new Error('channel name must not be empty');
|
|
124
|
-
const channel = Object.freeze({ ...this.
|
|
124
|
+
const channel = Object.freeze({ ...this.requireActiveChannel(request.workspaceId, request.channelRef), name, description });
|
|
125
125
|
const operation = Object.freeze({
|
|
126
126
|
...this.operationBase(request, this.nextSequence()), kind: 'team/channel-updated',
|
|
127
127
|
data: Object.freeze({ workspaceId: request.workspaceId, channel }),
|
|
@@ -150,7 +150,7 @@ export class AgentTeamLedger {
|
|
|
150
150
|
// still drivable through its DM view, and joins Channels later.
|
|
151
151
|
const channelRefs = this.normalizeUnique(request.channelRefs, 'initial Member Channels');
|
|
152
152
|
for (const channelRef of channelRefs)
|
|
153
|
-
this.
|
|
153
|
+
this.requireActiveChannel(request.workspaceId, channelRef);
|
|
154
154
|
this.assertHandleAvailable(request.workspaceId, handle);
|
|
155
155
|
this.assertModelSelection(request.member.model);
|
|
156
156
|
this.assertCapabilities(request.member.capabilities);
|
|
@@ -180,6 +180,8 @@ export class AgentTeamLedger {
|
|
|
180
180
|
const prior = this.requireMember(request.memberId);
|
|
181
181
|
if (prior.state === 'inactive')
|
|
182
182
|
throw new Error(`Agent Member '${prior.memberId}' is inactive and can no longer be edited`);
|
|
183
|
+
if (prior.state === 'archived')
|
|
184
|
+
throw new Error(`Agent Member '${prior.memberId}' is archived and can no longer be edited`);
|
|
183
185
|
const handle = request.handle.trim();
|
|
184
186
|
const description = request.description.trim();
|
|
185
187
|
if (handle === '')
|
|
@@ -255,7 +257,7 @@ export class AgentTeamLedger {
|
|
|
255
257
|
return this.resolved(this.joinResult(existing));
|
|
256
258
|
}
|
|
257
259
|
this.assertHumanActor(request.actor);
|
|
258
|
-
const channel = this.
|
|
260
|
+
const channel = this.requireActiveChannel(request.workspaceId, request.channelRef);
|
|
259
261
|
const member = this.requireMember(request.memberId);
|
|
260
262
|
if (member.workspaceId !== request.workspaceId)
|
|
261
263
|
throw new Error('Member and Channel must belong to one Workspace');
|
|
@@ -307,6 +309,48 @@ export class AgentTeamLedger {
|
|
|
307
309
|
return this.committed(this.channelMemberRemovalResult(operation));
|
|
308
310
|
});
|
|
309
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* Archive one Channel: hidden from every surface, facts kept recoverable.
|
|
314
|
+
* Every active Claim on the Channel's Threads releases with one public
|
|
315
|
+
* Activity per (owner, Thread), and every Member's Attention and markers
|
|
316
|
+
* for those Threads clear — a hidden Channel must not leave Tasks stuck in
|
|
317
|
+
* progress behind it or phantom unread counts.
|
|
318
|
+
*/
|
|
319
|
+
archiveChannel(request) {
|
|
320
|
+
return this.enqueue(async () => {
|
|
321
|
+
const existing = this.state.byRequest.get(request.requestId);
|
|
322
|
+
if (existing !== undefined) {
|
|
323
|
+
this.assertSameChannelArchival(existing, request);
|
|
324
|
+
return this.resolved(this.channelArchivalResult(existing));
|
|
325
|
+
}
|
|
326
|
+
this.assertHumanActor(request.actor);
|
|
327
|
+
const channel = this.requireChannel(request.workspaceId, request.channelRef);
|
|
328
|
+
if (channel.state === 'archived')
|
|
329
|
+
throw new Error(`Channel '${channel.channelRef}' is already archived`);
|
|
330
|
+
const nextChannel = Object.freeze({ ...channel, state: 'archived' });
|
|
331
|
+
const threadRefs = this.channelThreadRefs(channel.channelRef);
|
|
332
|
+
const releasedClaims = [...this.state.claims.values()]
|
|
333
|
+
.filter(claim => claim.state === 'active' && threadRefs.has(claim.threadRef))
|
|
334
|
+
.map(claim => Object.freeze({ ...claim, state: 'released' }));
|
|
335
|
+
const nextClaims = new Map(this.state.claims);
|
|
336
|
+
for (const claim of releasedClaims)
|
|
337
|
+
nextClaims.set(claim.claimRef, claim);
|
|
338
|
+
const sequence = this.nextSequence();
|
|
339
|
+
const owners = [...new Set(releasedClaims.map(claim => claim.owner))].sort();
|
|
340
|
+
const activities = Object.freeze(owners.flatMap(owner => this.releaseSummaries(releasedClaims.filter(claim => claim.owner === owner), owner, sequence)));
|
|
341
|
+
const threads = this.threadsForActivities(activities);
|
|
342
|
+
const tasks = this.tasksForClaims(releasedClaims, nextClaims);
|
|
343
|
+
const inbox = this.channelArchivalInbox(threadRefs);
|
|
344
|
+
const operation = Object.freeze({
|
|
345
|
+
...this.operationBase(request, sequence), kind: 'team/channel-archived',
|
|
346
|
+
data: Object.freeze({ workspaceId: request.workspaceId, channel: nextChannel,
|
|
347
|
+
claims: Object.freeze(releasedClaims), activities, tasks, threads, inbox }),
|
|
348
|
+
});
|
|
349
|
+
await this.table.put(operation.operationId, operation);
|
|
350
|
+
this.apply(operation);
|
|
351
|
+
return this.committed(this.channelArchivalResult(operation));
|
|
352
|
+
});
|
|
353
|
+
}
|
|
310
354
|
sendMessage(request) {
|
|
311
355
|
return this.enqueue(async () => {
|
|
312
356
|
const recipients = this.normalizeRecipients(request.actor, request.recipients);
|
|
@@ -316,7 +360,7 @@ export class AgentTeamLedger {
|
|
|
316
360
|
return this.resolved(this.messageResult(existing));
|
|
317
361
|
}
|
|
318
362
|
const actor = this.assertActorForWorkspace(request.actor, request.workspaceId);
|
|
319
|
-
const channel = this.
|
|
363
|
+
const channel = this.requireActiveChannel(request.workspaceId, request.channelRef);
|
|
320
364
|
if (actor.kind === 'member')
|
|
321
365
|
this.requireMemberChannel(this.requireMember(actor.memberId), channel.channelRef);
|
|
322
366
|
const body = request.body.trim();
|
|
@@ -589,7 +633,9 @@ export class AgentTeamLedger {
|
|
|
589
633
|
const activities = this.releaseSummaries(releasedClaims, member.memberId, sequence);
|
|
590
634
|
const threads = this.threadsForActivities(activities);
|
|
591
635
|
const tasks = this.tasksForClaims(releasedClaims, projectedClaims);
|
|
592
|
-
|
|
636
|
+
// The Member leaves entirely, so Attention and markers clear on every
|
|
637
|
+
// Thread — taskless ones too — matching the replay validator's scope.
|
|
638
|
+
const threadRefs = new Set(this.state.threads.keys());
|
|
593
639
|
const inbox = this.removeMemberThreadInbox(member.memberId, threadRefs);
|
|
594
640
|
const operation = Object.freeze({
|
|
595
641
|
...this.operationBase(request, sequence), kind: 'team/member-removed',
|
|
@@ -601,6 +647,49 @@ export class AgentTeamLedger {
|
|
|
601
647
|
return this.committed(this.removalResult(operation));
|
|
602
648
|
});
|
|
603
649
|
}
|
|
650
|
+
/**
|
|
651
|
+
* Archive one Member: hidden from every surface, data kept recoverable.
|
|
652
|
+
* The release shape matches removal — active Claims release with public
|
|
653
|
+
* Activities and the Member's Attention/markers clear — because a hidden
|
|
654
|
+
* Member must not leave Tasks stuck in progress or phantom unread counts.
|
|
655
|
+
*/
|
|
656
|
+
archiveMember(request) {
|
|
657
|
+
return this.enqueue(async () => {
|
|
658
|
+
const existing = this.state.byRequest.get(request.requestId);
|
|
659
|
+
if (existing !== undefined) {
|
|
660
|
+
this.assertSameArchival(existing, request);
|
|
661
|
+
return this.resolved(this.archivalResult(existing));
|
|
662
|
+
}
|
|
663
|
+
this.assertHumanActor(request.actor);
|
|
664
|
+
const member = this.requireMember(request.memberId);
|
|
665
|
+
if (member.state === 'inactive')
|
|
666
|
+
throw new Error(`Agent Member '${member.memberId}' is already inactive`);
|
|
667
|
+
if (member.state === 'archived')
|
|
668
|
+
throw new Error(`Agent Member '${member.memberId}' is already archived`);
|
|
669
|
+
const nextMember = Object.freeze({ ...member, state: 'archived' });
|
|
670
|
+
const releasedClaims = [...this.state.claims.values()].filter(claim => claim.owner === member.memberId && claim.state === 'active')
|
|
671
|
+
.map(claim => Object.freeze({ ...claim, state: 'released' }));
|
|
672
|
+
const projectedClaims = new Map(this.state.claims);
|
|
673
|
+
for (const claim of releasedClaims)
|
|
674
|
+
projectedClaims.set(claim.claimRef, claim);
|
|
675
|
+
const sequence = this.nextSequence();
|
|
676
|
+
const activities = this.releaseSummaries(releasedClaims, member.memberId, sequence);
|
|
677
|
+
const threads = this.threadsForActivities(activities);
|
|
678
|
+
const tasks = this.tasksForClaims(releasedClaims, projectedClaims);
|
|
679
|
+
// The Member leaves entirely, so Attention and markers clear on every
|
|
680
|
+
// Thread — taskless ones too — matching the replay validator's scope.
|
|
681
|
+
const threadRefs = new Set(this.state.threads.keys());
|
|
682
|
+
const inbox = this.removeMemberThreadInbox(member.memberId, threadRefs);
|
|
683
|
+
const operation = Object.freeze({
|
|
684
|
+
...this.operationBase(request, sequence), kind: 'team/member-archived',
|
|
685
|
+
data: Object.freeze({ member: nextMember, claims: Object.freeze(releasedClaims), activities, tasks, threads, inbox }),
|
|
686
|
+
});
|
|
687
|
+
await this.table.put(operation.operationId, operation);
|
|
688
|
+
this.apply(operation);
|
|
689
|
+
this.confirmations.clear();
|
|
690
|
+
return this.committed(this.archivalResult(operation));
|
|
691
|
+
});
|
|
692
|
+
}
|
|
604
693
|
changeAttention(request) {
|
|
605
694
|
return this.enqueue(async () => {
|
|
606
695
|
const existing = this.state.byRequest.get(request.requestId);
|
|
@@ -858,7 +947,11 @@ export class AgentTeamLedger {
|
|
|
858
947
|
const resolved = [];
|
|
859
948
|
for (const taskRef of taskRefs) {
|
|
860
949
|
const task = this.state.tasks.get(taskRef);
|
|
861
|
-
|
|
950
|
+
const channel = task === undefined ? undefined : this.state.channels.get(task.channelRef);
|
|
951
|
+
// Archived Channels do not exist on Team API surfaces: their Tasks stop
|
|
952
|
+
// resolving, so live message bodies render the refs as plain
|
|
953
|
+
// non-navigable text through the existing unknown-ref path.
|
|
954
|
+
if (task === undefined || channel?.workspaceId !== workspaceId || channel?.state === 'archived')
|
|
862
955
|
continue;
|
|
863
956
|
resolved.push(Object.freeze({ taskRef: task.taskRef, channelRef: task.channelRef, threadRef: task.threadRef, taskNumber: numbers.get(task.taskRef) ?? 0 }));
|
|
864
957
|
}
|
|
@@ -880,7 +973,7 @@ export class AgentTeamLedger {
|
|
|
880
973
|
throw new Error('Member cannot view another Workspace');
|
|
881
974
|
}
|
|
882
975
|
if (request.channelRef !== undefined) {
|
|
883
|
-
this.
|
|
976
|
+
this.requireActiveChannel(request.workspaceId, request.channelRef);
|
|
884
977
|
if (memberId !== undefined && !this.isChannelMember(request.channelRef, memberId))
|
|
885
978
|
throw new Error(`Agent Member '${memberId}' is not authorized for Channel '${request.channelRef}'`);
|
|
886
979
|
}
|
|
@@ -891,12 +984,14 @@ export class AgentTeamLedger {
|
|
|
891
984
|
throw new Error(`unknown Thread ref '${request.threadRef}'`);
|
|
892
985
|
if (this.state.channels.get(channelRef)?.workspaceId !== request.workspaceId)
|
|
893
986
|
throw new Error(`Thread '${request.threadRef}' does not belong to Workspace '${request.workspaceId}'`);
|
|
987
|
+
this.assertThreadChannelActive(channelRef);
|
|
894
988
|
if (request.channelRef !== undefined && channelRef !== request.channelRef)
|
|
895
989
|
throw new Error(`Thread '${request.threadRef}' does not belong to Channel '${request.channelRef}'`);
|
|
896
990
|
if (memberId !== undefined && !this.isChannelMember(channelRef, memberId))
|
|
897
991
|
throw new Error(`Agent Member '${memberId}' is not authorized for Channel '${channelRef}'`);
|
|
898
992
|
}
|
|
899
993
|
const channels = [...this.state.channels.values()].filter(channel => channel.workspaceId === request.workspaceId
|
|
994
|
+
&& channel.state !== 'archived'
|
|
900
995
|
&& (memberId === undefined || this.isChannelMember(channel.channelRef, memberId)));
|
|
901
996
|
const channelRefs = new Set(channels.map(channel => channel.channelRef));
|
|
902
997
|
const allFacts = this.state.orderedFacts.filter(fact => {
|
|
@@ -931,7 +1026,10 @@ export class AgentTeamLedger {
|
|
|
931
1026
|
return Object.freeze({
|
|
932
1027
|
humanMemberId: initialization.data.humanMemberId,
|
|
933
1028
|
channels: Object.freeze(channels),
|
|
934
|
-
members: Object.freeze([...this.state.memberships.entries()].filter(([channelRef]) => channelRefs.has(channelRef)).flatMap(([channelRef, ids]) => [...ids].filter(id =>
|
|
1029
|
+
members: Object.freeze([...this.state.memberships.entries()].filter(([channelRef]) => channelRefs.has(channelRef)).flatMap(([channelRef, ids]) => [...ids].filter(id => {
|
|
1030
|
+
const state = this.state.members.get(id)?.state;
|
|
1031
|
+
return state !== 'inactive' && state !== 'archived';
|
|
1032
|
+
}).sort().map(memberId => Object.freeze({ channelRef, memberId })))),
|
|
935
1033
|
tasks: Object.freeze(visibleTasks),
|
|
936
1034
|
threads: Object.freeze([...this.state.threads.values()].filter(thread => {
|
|
937
1035
|
const channelRef = this.channelRefForThread(thread.threadRef);
|
|
@@ -981,6 +1079,20 @@ export class AgentTeamLedger {
|
|
|
981
1079
|
case 'team/member-updated':
|
|
982
1080
|
case 'team/member-removed':
|
|
983
1081
|
return [{ kind: 'workspace', workspaceId: operation.data.member.workspaceId }];
|
|
1082
|
+
case 'team/member-archived': {
|
|
1083
|
+
// Claim releases are thread-visible facts: open Channel and Thread
|
|
1084
|
+
// pages refetch alongside the workspace-wide roster change.
|
|
1085
|
+
const channelByTask = new Map(operation.data.tasks.map(task => [task.taskRef, task.channelRef]));
|
|
1086
|
+
const scopes = [{ kind: 'workspace', workspaceId: operation.data.member.workspaceId }];
|
|
1087
|
+
for (const activity of operation.data.activities) {
|
|
1088
|
+
scopes.push({ kind: 'thread', threadRef: activity.threadRef });
|
|
1089
|
+
const channelRef = channelByTask.get(activity.taskRef);
|
|
1090
|
+
if (channelRef !== undefined && !scopes.some(scope => scope.kind === 'channel' && scope.channelRef === channelRef)) {
|
|
1091
|
+
scopes.push({ kind: 'channel', channelRef });
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
return scopes;
|
|
1095
|
+
}
|
|
984
1096
|
case 'team/channel-member-added':
|
|
985
1097
|
return [{ kind: 'workspace', workspaceId: operation.data.workspaceId }, { kind: 'channel', channelRef: operation.data.channelRef }];
|
|
986
1098
|
case 'team/channel-member-removed': {
|
|
@@ -998,6 +1110,15 @@ export class AgentTeamLedger {
|
|
|
998
1110
|
}
|
|
999
1111
|
return scopes;
|
|
1000
1112
|
}
|
|
1113
|
+
case 'team/channel-archived': {
|
|
1114
|
+
const scopes = [
|
|
1115
|
+
{ kind: 'workspace', workspaceId: operation.data.workspaceId },
|
|
1116
|
+
{ kind: 'channel', channelRef: operation.data.channel.channelRef },
|
|
1117
|
+
];
|
|
1118
|
+
for (const activity of operation.data.activities)
|
|
1119
|
+
scopes.push({ kind: 'thread', threadRef: activity.threadRef });
|
|
1120
|
+
return scopes;
|
|
1121
|
+
}
|
|
1001
1122
|
case 'team/message-sent':
|
|
1002
1123
|
case 'team/thread-replied':
|
|
1003
1124
|
return [{ kind: 'channel', channelRef: operation.data.message.channelRef }, { kind: 'thread', threadRef: operation.data.message.threadRef }];
|
|
@@ -1062,7 +1183,9 @@ export class AgentTeamLedger {
|
|
|
1062
1183
|
case 'team/thread-attention-changed':
|
|
1063
1184
|
return [operation.data.thread.threadRef];
|
|
1064
1185
|
case 'team/channel-member-removed':
|
|
1186
|
+
case 'team/channel-archived':
|
|
1065
1187
|
case 'team/member-removed':
|
|
1188
|
+
case 'team/member-archived':
|
|
1066
1189
|
return operation.data.activities.map(activity => activity.threadRef);
|
|
1067
1190
|
case 'team/initialized':
|
|
1068
1191
|
case 'team/channel-created':
|
|
@@ -1209,7 +1332,8 @@ export class AgentTeamLedger {
|
|
|
1209
1332
|
assertHuman();
|
|
1210
1333
|
const prior = projection.channels.get(operation.data.channel.channelRef);
|
|
1211
1334
|
if (prior === undefined || prior.workspaceId !== operation.data.workspaceId
|
|
1212
|
-
|| operation.data.channel.workspaceId !== prior.workspaceId || operation.data.channel.createdAtSequence !== prior.createdAtSequence
|
|
1335
|
+
|| operation.data.channel.workspaceId !== prior.workspaceId || operation.data.channel.createdAtSequence !== prior.createdAtSequence
|
|
1336
|
+
|| operation.data.channel.state !== prior.state) {
|
|
1213
1337
|
throw new Error('invalid Channel update');
|
|
1214
1338
|
}
|
|
1215
1339
|
return;
|
|
@@ -1217,7 +1341,7 @@ export class AgentTeamLedger {
|
|
|
1217
1341
|
if (operation.kind === 'team/member-updated') {
|
|
1218
1342
|
assertHuman();
|
|
1219
1343
|
const prior = projection.members.get(operation.data.member.memberId);
|
|
1220
|
-
if (prior === undefined || prior.state === 'inactive' || operation.data.member.state !== prior.state
|
|
1344
|
+
if (prior === undefined || prior.state === 'inactive' || prior.state === 'archived' || operation.data.member.state !== prior.state
|
|
1221
1345
|
|| operation.data.member.sessionId !== prior.sessionId || operation.data.member.workspaceId !== prior.workspaceId
|
|
1222
1346
|
|| operation.data.member.presetId !== prior.presetId
|
|
1223
1347
|
|| operation.data.member.privateMemoryPath !== prior.privateMemoryPath)
|
|
@@ -1250,6 +1374,18 @@ export class AgentTeamLedger {
|
|
|
1250
1374
|
this.validateReleaseCleanup(operation.data, projection, member.memberId, threadRefs, operation.sequence, refs);
|
|
1251
1375
|
return;
|
|
1252
1376
|
}
|
|
1377
|
+
if (operation.kind === 'team/channel-archived') {
|
|
1378
|
+
assertHuman();
|
|
1379
|
+
const prior = projection.channels.get(operation.data.channel.channelRef);
|
|
1380
|
+
if (prior === undefined || prior.state !== 'active' || operation.data.channel.state !== 'archived'
|
|
1381
|
+
|| operation.data.channel.workspaceId !== prior.workspaceId || operation.data.workspaceId !== prior.workspaceId
|
|
1382
|
+
|| operation.data.channel.name !== prior.name || operation.data.channel.description !== prior.description
|
|
1383
|
+
|| operation.data.channel.createdAtSequence !== prior.createdAtSequence)
|
|
1384
|
+
throw new Error('invalid Channel archival');
|
|
1385
|
+
const threadRefs = new Set([...projection.threads.keys()].filter(threadRef => this.channelRefForThreadFrom(projection, threadRef) === prior.channelRef));
|
|
1386
|
+
this.validateChannelArchivalCleanup(operation.data, projection, threadRefs, operation.sequence, refs);
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1253
1389
|
if (operation.kind === 'team/member-removed') {
|
|
1254
1390
|
assertHuman();
|
|
1255
1391
|
const prior = projection.members.get(operation.data.member.memberId);
|
|
@@ -1259,6 +1395,15 @@ export class AgentTeamLedger {
|
|
|
1259
1395
|
this.validateReleaseCleanup(operation.data, projection, prior.memberId, threadRefs, operation.sequence, refs);
|
|
1260
1396
|
return;
|
|
1261
1397
|
}
|
|
1398
|
+
if (operation.kind === 'team/member-archived') {
|
|
1399
|
+
assertHuman();
|
|
1400
|
+
const prior = projection.members.get(operation.data.member.memberId);
|
|
1401
|
+
if (prior === undefined || (prior.state !== 'enabled' && prior.state !== 'suspended') || operation.data.member.state !== 'archived' || !this.sameMemberIdentity(prior, operation.data.member))
|
|
1402
|
+
throw new Error('invalid Member archival');
|
|
1403
|
+
const threadRefs = new Set(projection.threads.keys());
|
|
1404
|
+
this.validateReleaseCleanup(operation.data, projection, prior.memberId, threadRefs, operation.sequence, refs);
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1262
1407
|
if (operation.kind === 'team/thread-attention-changed') {
|
|
1263
1408
|
const actor = operation.actor.kind === 'member' ? assertMember() : (assertHuman(), undefined);
|
|
1264
1409
|
if (operation.data.memberId !== operation.actor.memberId)
|
|
@@ -1581,6 +1726,68 @@ export class AgentTeamLedger {
|
|
|
1581
1726
|
throw new Error('invalid Member inbox cleanup');
|
|
1582
1727
|
this.validateInboxDelta(data.inbox, projection, refs);
|
|
1583
1728
|
}
|
|
1729
|
+
/** Replay validation of one Channel archival's release snapshot across every owner. */
|
|
1730
|
+
validateChannelArchivalCleanup(data, projection, threadRefs, sequence, refs) {
|
|
1731
|
+
const releasedClaims = [...projection.claims.values()]
|
|
1732
|
+
.filter(claim => claim.state === 'active' && threadRefs.has(claim.threadRef))
|
|
1733
|
+
.map(claim => Object.freeze({ ...claim, state: 'released' }));
|
|
1734
|
+
if (data.claims.length !== releasedClaims.length || data.claims.some((claim, index) => {
|
|
1735
|
+
const expected = releasedClaims[index];
|
|
1736
|
+
return expected === undefined || !this.sameClaim(expected, claim);
|
|
1737
|
+
}))
|
|
1738
|
+
throw new Error('invalid released Claim projection');
|
|
1739
|
+
// One activity per (owner, Thread), owners sorted like the commit path.
|
|
1740
|
+
const byOwner = new Map();
|
|
1741
|
+
for (const claim of releasedClaims) {
|
|
1742
|
+
const byThread = byOwner.get(claim.owner) ?? new Map();
|
|
1743
|
+
byOwner.set(claim.owner, byThread);
|
|
1744
|
+
byThread.set(claim.threadRef, [...(byThread.get(claim.threadRef) ?? []), claim]);
|
|
1745
|
+
}
|
|
1746
|
+
const expectedActivities = [...byOwner.keys()].sort().flatMap(owner => [...byOwner.get(owner).entries()].map(([threadRef, claims]) => ({
|
|
1747
|
+
kind: 'claims_released', taskRef: claims[0].taskRef, threadRef, actor: owner, sequence,
|
|
1748
|
+
claimRefs: claims.map(claim => claim.claimRef).sort(),
|
|
1749
|
+
})));
|
|
1750
|
+
if (data.activities.length !== expectedActivities.length || data.activities.some((activity, index) => {
|
|
1751
|
+
const expected = expectedActivities[index];
|
|
1752
|
+
return expected === undefined || activity.kind !== expected.kind || activity.taskRef !== expected.taskRef
|
|
1753
|
+
|| activity.threadRef !== expected.threadRef || activity.actor !== expected.actor || activity.sequence !== expected.sequence
|
|
1754
|
+
|| !this.sameList(activity.claimRefs, expected.claimRefs);
|
|
1755
|
+
}))
|
|
1756
|
+
throw new Error('invalid released Claim activities');
|
|
1757
|
+
for (const claim of data.claims) {
|
|
1758
|
+
const prior = projection.claims.get(claim.claimRef);
|
|
1759
|
+
if (prior === undefined)
|
|
1760
|
+
throw new Error('invalid released Claim reference');
|
|
1761
|
+
}
|
|
1762
|
+
for (const activity of data.activities)
|
|
1763
|
+
this.addRef(refs, activity.activityRef);
|
|
1764
|
+
const projectedClaims = new Map(projection.claims);
|
|
1765
|
+
for (const claim of releasedClaims)
|
|
1766
|
+
projectedClaims.set(claim.claimRef, claim);
|
|
1767
|
+
const expectedTasks = [...new Set(releasedClaims.map(claim => claim.taskRef))].map(taskRef => {
|
|
1768
|
+
const task = projection.tasks.get(taskRef);
|
|
1769
|
+
return Object.freeze({ ...task, status: this.deriveResolvedTaskStatus(task, projectedClaims.values()) });
|
|
1770
|
+
});
|
|
1771
|
+
const expectedThreads = expectedActivities.map(activity => {
|
|
1772
|
+
const thread = projection.threads.get(activity.threadRef);
|
|
1773
|
+
return Object.freeze({ ...thread, revision: sequence });
|
|
1774
|
+
});
|
|
1775
|
+
if (!isDeepStrictEqual(data.tasks, expectedTasks) || !isDeepStrictEqual(data.threads, expectedThreads)) {
|
|
1776
|
+
throw new Error('invalid released Claim Task or Thread projection');
|
|
1777
|
+
}
|
|
1778
|
+
// Attention and marker cleanup covers EVERY Member on the archived Threads.
|
|
1779
|
+
const expectedAttention = [...projection.attention.values()]
|
|
1780
|
+
.filter(attention => threadRefs.has(attention.threadRef))
|
|
1781
|
+
.map(attention => ({ memberId: attention.memberId, threadRef: attention.threadRef }));
|
|
1782
|
+
const expectedMarkers = [...projection.directMarkers.values()]
|
|
1783
|
+
.filter(marker => threadRefs.has(marker.threadRef));
|
|
1784
|
+
const expectedActivityMarkers = [...projection.activityMarkers.values()]
|
|
1785
|
+
.filter(marker => threadRefs.has(marker.threadRef));
|
|
1786
|
+
const expectedInbox = this.inboxDelta([], expectedAttention, [], expectedMarkers, [], expectedActivityMarkers);
|
|
1787
|
+
if (!isDeepStrictEqual(data.inbox, expectedInbox))
|
|
1788
|
+
throw new Error('invalid Channel archival inbox cleanup');
|
|
1789
|
+
this.validateInboxDelta(data.inbox, projection, refs);
|
|
1790
|
+
}
|
|
1584
1791
|
validateMessageInbox(operation, projection) {
|
|
1585
1792
|
const { message, mentions } = operation.data;
|
|
1586
1793
|
const mentionedAgents = mentions.filter(memberId => projection.members.has(memberId));
|
|
@@ -1612,7 +1819,7 @@ export class AgentTeamLedger {
|
|
|
1612
1819
|
if (memberId === AGENT_TEAM_HUMAN_MEMBER_ID)
|
|
1613
1820
|
return true;
|
|
1614
1821
|
const member = projection.members.get(memberId);
|
|
1615
|
-
return member !== undefined && member.state !== 'inactive'
|
|
1822
|
+
return member !== undefined && member.state !== 'inactive' && member.state !== 'archived'
|
|
1616
1823
|
&& projection.channels.get(channelRef)?.workspaceId === member.workspaceId
|
|
1617
1824
|
&& this.isChannelMemberFrom(projection, channelRef, memberId);
|
|
1618
1825
|
}
|
|
@@ -1679,6 +1886,22 @@ export class AgentTeamLedger {
|
|
|
1679
1886
|
this.applyInboxDelta(target, operation.data.inbox);
|
|
1680
1887
|
return;
|
|
1681
1888
|
}
|
|
1889
|
+
if (operation.kind === 'team/channel-archived') {
|
|
1890
|
+
// Archival keeps Memberships: hidden state, not departure — a future
|
|
1891
|
+
// restore returns every Member to the Channel. Visibility filters by
|
|
1892
|
+
// channel state instead.
|
|
1893
|
+
target.channels.set(operation.data.channel.channelRef, operation.data.channel);
|
|
1894
|
+
for (const claim of operation.data.claims)
|
|
1895
|
+
target.claims.set(claim.claimRef, claim);
|
|
1896
|
+
for (const activity of operation.data.activities)
|
|
1897
|
+
this.appendActivityFact(target, activity);
|
|
1898
|
+
for (const task of operation.data.tasks)
|
|
1899
|
+
target.tasks.set(task.taskRef, task);
|
|
1900
|
+
for (const thread of operation.data.threads)
|
|
1901
|
+
target.threads.set(thread.threadRef, thread);
|
|
1902
|
+
this.applyInboxDelta(target, operation.data.inbox);
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1682
1905
|
if (operation.kind === 'team/member-removed') {
|
|
1683
1906
|
target.members.set(operation.data.member.memberId, operation.data.member);
|
|
1684
1907
|
for (const membership of target.memberships.values())
|
|
@@ -1694,6 +1917,22 @@ export class AgentTeamLedger {
|
|
|
1694
1917
|
this.applyInboxDelta(target, operation.data.inbox);
|
|
1695
1918
|
return;
|
|
1696
1919
|
}
|
|
1920
|
+
if (operation.kind === 'team/member-archived') {
|
|
1921
|
+
// Archival keeps Memberships: hidden state, not departure — a future
|
|
1922
|
+
// restore returns the Member to its Channels. Visibility filters by
|
|
1923
|
+
// member state instead.
|
|
1924
|
+
target.members.set(operation.data.member.memberId, operation.data.member);
|
|
1925
|
+
for (const claim of operation.data.claims)
|
|
1926
|
+
target.claims.set(claim.claimRef, claim);
|
|
1927
|
+
for (const activity of operation.data.activities)
|
|
1928
|
+
this.appendActivityFact(target, activity);
|
|
1929
|
+
for (const task of operation.data.tasks)
|
|
1930
|
+
target.tasks.set(task.taskRef, task);
|
|
1931
|
+
for (const thread of operation.data.threads)
|
|
1932
|
+
target.threads.set(thread.threadRef, thread);
|
|
1933
|
+
this.applyInboxDelta(target, operation.data.inbox);
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1697
1936
|
if (operation.kind === 'team/thread-promoted') {
|
|
1698
1937
|
this.appendActivityFact(target, operation.data.activity);
|
|
1699
1938
|
target.tasks.set(operation.data.task.taskRef, operation.data.task);
|
|
@@ -1944,6 +2183,14 @@ export class AgentTeamLedger {
|
|
|
1944
2183
|
const activityMarkers = [...this.state.activityMarkers.values()].filter(marker => marker.memberId === memberId && threadRefs.has(marker.threadRef));
|
|
1945
2184
|
return this.inboxDelta([], removed, [], markers, [], activityMarkers);
|
|
1946
2185
|
}
|
|
2186
|
+
/** Attention and marker cleanup for EVERY Member on the given Threads. */
|
|
2187
|
+
channelArchivalInbox(threadRefs) {
|
|
2188
|
+
const removed = [...this.state.attention.values()].filter(attention => threadRefs.has(attention.threadRef))
|
|
2189
|
+
.map(attention => Object.freeze({ memberId: attention.memberId, threadRef: attention.threadRef }));
|
|
2190
|
+
const markers = [...this.state.directMarkers.values()].filter(marker => threadRefs.has(marker.threadRef));
|
|
2191
|
+
const activityMarkers = [...this.state.activityMarkers.values()].filter(marker => threadRefs.has(marker.threadRef));
|
|
2192
|
+
return this.inboxDelta([], removed, [], markers, [], activityMarkers);
|
|
2193
|
+
}
|
|
1947
2194
|
inboxDelta(set = [], removed = [], addedMarkers = [], removedMarkers = [], addedActivityMarkers = [], removedActivityMarkers = []) {
|
|
1948
2195
|
return Object.freeze({
|
|
1949
2196
|
attention: Object.freeze({ set: Object.freeze(set), removed: Object.freeze(removed) }),
|
|
@@ -2019,7 +2266,7 @@ export class AgentTeamLedger {
|
|
|
2019
2266
|
if (memberId === AGENT_TEAM_HUMAN_MEMBER_ID)
|
|
2020
2267
|
continue;
|
|
2021
2268
|
const member = this.requireMember(memberId);
|
|
2022
|
-
if (member.state === 'inactive' || member.workspaceId !== channel.workspaceId || !this.isChannelMember(channel.channelRef, memberId)) {
|
|
2269
|
+
if (member.state === 'inactive' || member.state === 'archived' || member.workspaceId !== channel.workspaceId || !this.isChannelMember(channel.channelRef, memberId)) {
|
|
2023
2270
|
throw new Error(`Agent Member '${memberId}' is not authorized for Channel '${channel.channelRef}'`);
|
|
2024
2271
|
}
|
|
2025
2272
|
}
|
|
@@ -2070,6 +2317,9 @@ export class AgentTeamLedger {
|
|
|
2070
2317
|
threadForActor(actor, workspaceId, taskRef) {
|
|
2071
2318
|
const task = this.requireTask(workspaceId, taskRef);
|
|
2072
2319
|
const thread = this.requireThread(task.threadRef);
|
|
2320
|
+
// Archived Channels do not exist on Team API surfaces: their Tasks and
|
|
2321
|
+
// Claims are unreachable for reads (listClaims) as for writes.
|
|
2322
|
+
this.assertThreadChannelActive(task.channelRef);
|
|
2073
2323
|
if (actor.kind === 'member')
|
|
2074
2324
|
this.requireMemberChannel(this.requireMember(actor.memberId), task.channelRef);
|
|
2075
2325
|
return { task, thread };
|
|
@@ -2245,6 +2495,13 @@ export class AgentTeamLedger {
|
|
|
2245
2495
|
throw new Error(`Thread '${thread.threadRef}' has no Channel`);
|
|
2246
2496
|
if (projection.channels.get(channelRef)?.workspaceId !== workspaceId)
|
|
2247
2497
|
throw new Error(`Thread '${thread.threadRef}' does not belong to Workspace '${workspaceId}'`);
|
|
2498
|
+
// Archived Channels do not exist on Team API surfaces: their Threads are
|
|
2499
|
+
// unreachable by ref, for reads as for writes. Replay stays honest —
|
|
2500
|
+
// operations predating the archival resolve against the then-active
|
|
2501
|
+
// Channel because replay applies in sequence.
|
|
2502
|
+
if (projection.channels.get(channelRef)?.state === 'archived') {
|
|
2503
|
+
throw new Error(`Channel '${channelRef}' is archived and no longer accepts Team work`);
|
|
2504
|
+
}
|
|
2248
2505
|
if (task !== undefined && task.channelRef !== channelRef)
|
|
2249
2506
|
throw new Error(`Task '${task.taskRef}' does not belong to Thread '${thread.threadRef}'`);
|
|
2250
2507
|
return { ...(task === undefined ? {} : { task }), thread, channelRef };
|
|
@@ -2257,6 +2514,19 @@ export class AgentTeamLedger {
|
|
|
2257
2514
|
throw new Error(`Channel '${channelRef}' does not belong to Workspace '${workspaceId}'`);
|
|
2258
2515
|
return channel;
|
|
2259
2516
|
}
|
|
2517
|
+
/** Guard for every mutating or Channel-scoped surface flow: archived Channels reject. */
|
|
2518
|
+
requireActiveChannel(workspaceId, channelRef) {
|
|
2519
|
+
const channel = this.requireChannel(workspaceId, channelRef);
|
|
2520
|
+
if (channel.state === 'archived')
|
|
2521
|
+
throw new Error(`Channel '${channelRef}' is archived and no longer accepts Team work`);
|
|
2522
|
+
return channel;
|
|
2523
|
+
}
|
|
2524
|
+
/** Thread-mutation guard: the Channel owning the Thread must still be active. */
|
|
2525
|
+
assertThreadChannelActive(channelRef) {
|
|
2526
|
+
if (this.state.channels.get(channelRef)?.state === 'archived') {
|
|
2527
|
+
throw new Error(`Channel '${channelRef}' is archived and no longer accepts Team work`);
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2260
2530
|
requireMember(memberId) {
|
|
2261
2531
|
const member = this.state.members.get(memberId);
|
|
2262
2532
|
if (member === undefined)
|
|
@@ -2393,6 +2663,11 @@ export class AgentTeamLedger {
|
|
|
2393
2663
|
|| operation.data.workspaceId !== request.workspaceId || operation.data.channelRef !== request.channelRef || operation.data.memberId !== request.memberId)
|
|
2394
2664
|
this.throwRequestCollision(request.requestId);
|
|
2395
2665
|
}
|
|
2666
|
+
assertSameChannelArchival(operation, request) {
|
|
2667
|
+
if (operation.kind !== 'team/channel-archived' || !this.sameActor(operation.actor, request.actor)
|
|
2668
|
+
|| operation.data.workspaceId !== request.workspaceId || operation.data.channel.channelRef !== request.channelRef)
|
|
2669
|
+
this.throwRequestCollision(request.requestId);
|
|
2670
|
+
}
|
|
2396
2671
|
assertSameDm(operation, request) {
|
|
2397
2672
|
if (operation.kind !== 'team/dm-sent' || !this.sameActor(operation.actor, request.actor)
|
|
2398
2673
|
|| operation.data.workspaceId !== request.workspaceId
|
|
@@ -2448,6 +2723,11 @@ export class AgentTeamLedger {
|
|
|
2448
2723
|
|| operation.data.member.memberId !== request.memberId)
|
|
2449
2724
|
this.throwRequestCollision(request.requestId);
|
|
2450
2725
|
}
|
|
2726
|
+
assertSameArchival(operation, request) {
|
|
2727
|
+
if (operation.kind !== 'team/member-archived' || !this.sameActor(operation.actor, request.actor)
|
|
2728
|
+
|| operation.data.member.memberId !== request.memberId)
|
|
2729
|
+
this.throwRequestCollision(request.requestId);
|
|
2730
|
+
}
|
|
2451
2731
|
assertSameAttention(operation, request) {
|
|
2452
2732
|
if (operation.kind !== 'team/thread-attention-changed' || !this.sameActor(operation.actor, request.actor)
|
|
2453
2733
|
|| operation.data.workspaceId !== request.workspaceId
|
|
@@ -2538,6 +2818,10 @@ export class AgentTeamLedger {
|
|
|
2538
2818
|
return Object.freeze({ receipt: this.receipt(operation), channelRef: operation.data.channelRef, memberId: operation.data.memberId,
|
|
2539
2819
|
releasedClaims: operation.data.claims, removedAttention: operation.data.inbox.attention.removed });
|
|
2540
2820
|
}
|
|
2821
|
+
channelArchivalResult(operation) {
|
|
2822
|
+
return Object.freeze({ receipt: this.receipt(operation), channel: operation.data.channel,
|
|
2823
|
+
releasedClaims: operation.data.claims });
|
|
2824
|
+
}
|
|
2541
2825
|
messageResult(operation) {
|
|
2542
2826
|
return Object.freeze(this.committedMessageResult(operation));
|
|
2543
2827
|
}
|
|
@@ -2578,6 +2862,10 @@ export class AgentTeamLedger {
|
|
|
2578
2862
|
return Object.freeze({ receipt: this.receipt(operation), member: operation.data.member,
|
|
2579
2863
|
releasedClaims: operation.data.claims, removedAttention: operation.data.inbox.attention.removed });
|
|
2580
2864
|
}
|
|
2865
|
+
archivalResult(operation) {
|
|
2866
|
+
return Object.freeze({ receipt: this.receipt(operation), member: operation.data.member,
|
|
2867
|
+
releasedClaims: operation.data.claims, removedAttention: operation.data.inbox.attention.removed });
|
|
2868
|
+
}
|
|
2581
2869
|
operationBase(request, sequence) {
|
|
2582
2870
|
return { sequence, operationId: this.createOperationId(), requestId: request.requestId, occurredAt: this.createOccurredAt(),
|
|
2583
2871
|
actor: Object.freeze({ ...request.actor }), previousOperationId: this.state.ordered.at(-1)?.operationId ?? null };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
3
|
+
import { SessionLogOffset } from '@deepseek-ai/dsh-session';
|
|
3
4
|
export const name = 'wowyuarm-agent-team-member-context';
|
|
4
5
|
const MAX_MEMORY_BYTES = 8 * 1024;
|
|
5
6
|
const BEGIN = '<team-member-private-memory>';
|
|
@@ -30,7 +31,10 @@ export function apply(ctx) {
|
|
|
30
31
|
}
|
|
31
32
|
const text = `${renderMemberIdentity(member)}\n\n${memory}`;
|
|
32
33
|
const latestText = agent.session.surface.nodes.toReversed().flatMap(sequence => {
|
|
33
|
-
|
|
34
|
+
// `nodes` are event identities (SessionSeq); snapshotEvents takes log
|
|
35
|
+
// offsets. Re-entering through the validating constructor keeps the two
|
|
36
|
+
// number domains explicit (the seq = log.length contiguity contract).
|
|
37
|
+
const event = agent.session.snapshotEvents(SessionLogOffset(sequence), SessionLogOffset(sequence + 1))[0];
|
|
34
38
|
return event?.type === 'user/message'
|
|
35
39
|
&& event.data.source.kind === 'plugin'
|
|
36
40
|
&& event.data.source.plugin === name
|