@wowyuarm/dsh-agent-team 0.1.14-alpha.2 → 0.1.14
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 +5 -3
- package/README.zh.md +5 -3
- package/cordis.patch.yml +2 -185
- package/package.json +52 -46
- package/packages/agent-team/README.md +1 -1
- package/packages/agent-team/README.zh.md +1 -1
- package/packages/agent-team/core-skills/member-memory-manager/SKILL.md +30 -0
- package/packages/agent-team/lib/context-continuity-host.js +91 -0
- package/packages/agent-team/lib/context-projection.js +266 -489
- package/packages/agent-team/lib/context-source.js +17 -106
- package/packages/agent-team/lib/human-avatar.js +61 -0
- package/packages/agent-team/lib/human-profile.js +46 -0
- package/packages/agent-team/lib/human-update-check.js +119 -0
- package/packages/agent-team/lib/index.js +408 -264
- package/packages/agent-team/lib/ledger.js +120 -15
- package/packages/agent-team/lib/member-context.js +9 -8
- package/packages/agent-team/lib/member-runtime.js +1 -1
- package/packages/agent-team/lib/member-time-context.js +2 -5
- package/packages/agent-team/lib/preset-roster.js +15 -0
- package/packages/agent-team/lib/pressure-policy.js +9 -6
- package/packages/agent-team/lib/session-remediation.js +493 -0
- package/packages/agent-team/lib/stored-session-reader.js +2 -1
- package/packages/agent-team/lib/typert.host.js +605 -291
- package/packages/agent-team/lib/typert.remote-client.d.ts +15 -6
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +367 -244
- package/packages/agent-team/lib/types/context-continuity-host.d.ts +84 -0
- package/packages/agent-team/lib/types/context-continuity-host.d.ts.map +1 -0
- package/packages/agent-team/lib/types/context-projection.d.ts +130 -185
- package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -1
- package/packages/agent-team/lib/types/context-source.d.ts +17 -81
- package/packages/agent-team/lib/types/context-source.d.ts.map +1 -1
- package/packages/agent-team/lib/types/human-avatar.d.ts +29 -0
- package/packages/agent-team/lib/types/human-avatar.d.ts.map +1 -0
- package/packages/agent-team/lib/types/human-profile.d.ts +44 -0
- package/packages/agent-team/lib/types/human-profile.d.ts.map +1 -0
- package/packages/agent-team/lib/types/human-update-check.d.ts +73 -0
- package/packages/agent-team/lib/types/human-update-check.d.ts.map +1 -0
- package/packages/agent-team/lib/types/index.d.ts +112 -38
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +31 -2
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-context.d.ts +0 -14
- package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-time-context.d.ts +0 -15
- package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/preset-roster.d.ts +5 -0
- package/packages/agent-team/lib/types/preset-roster.d.ts.map +1 -0
- package/packages/agent-team/lib/types/pressure-policy.d.ts +6 -0
- package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
- package/packages/agent-team/lib/types/session-remediation.d.ts +172 -0
- package/packages/agent-team/lib/types/session-remediation.d.ts.map +1 -0
- package/packages/agent-team/lib/types/stored-session-reader.d.ts +2 -1
- package/packages/agent-team/lib/types/stored-session-reader.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/entities.d.ts +20 -3
- package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/requests-results.d.ts +63 -0
- package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +166 -0
- package/packages/client-agent-team/lib/client.js +1667 -516
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts +15 -0
- package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/HumanSettingsSection.js +115 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +12 -8
- package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts +22 -1
- package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAvatarStack.js +25 -2
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +6 -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 +69 -31
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +4 -4
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +3 -3
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.js +12 -5
- package/packages/client-agent-team/lib/types/client/TeamFooterAction.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +4 -1
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +22 -7
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +11 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +69 -8
- package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +18 -3
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +213 -67
- package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamRowMenu.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamSidebarSection.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +5 -0
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +55 -29
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +3 -3
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceSelector.js +2 -2
- package/packages/client-agent-team/lib/types/client/avatar-image.d.ts +21 -0
- package/packages/client-agent-team/lib/types/client/avatar-image.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/avatar-image.js +9 -0
- package/packages/client-agent-team/lib/types/client/human-identity.d.ts +94 -0
- package/packages/client-agent-team/lib/types/client/human-identity.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/human-identity.js +117 -0
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +112 -41
- package/packages/client-agent-team/lib/types/client/locales.d.ts +72 -2
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +78 -2
- package/packages/client-agent-team/lib/types/client/multi-menu-field.js +2 -2
- package/packages/client-agent-team/lib/types/client/refs.d.ts +71 -0
- package/packages/client-agent-team/lib/types/client/refs.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/refs.js +181 -0
- package/packages/client-agent-team/lib/types/client/slots.d.ts +8 -2
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +57 -9
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.js +85 -17
- package/packages/tool-agent-team/README.md +1 -1
- package/packages/tool-agent-team/README.zh.md +1 -1
- package/packages/tool-agent-team/lib/context-tools.js +20 -6
- package/packages/tool-agent-team/lib/index.js +2 -2
- package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
- package/packages/agent-team/lib/context-management.js +0 -371
- package/packages/agent-team/lib/types/context-management.d.ts +0 -154
- package/packages/agent-team/lib/types/context-management.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts +0 -31
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/task-refs.js +0 -99
|
@@ -45,6 +45,15 @@ function boundedInboxPreview(body) {
|
|
|
45
45
|
const firstLine = body.split('\n', 1)[0]?.trim() ?? '';
|
|
46
46
|
return firstLine.length > 120 ? `${firstLine.slice(0, 119)}…` : firstLine;
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Chip title for a resolved Thread ref: the same opening-line gist as the
|
|
50
|
+
* Inbox preview, but capped at 40 characters — a chip shares its line with
|
|
51
|
+
* prose, so the 120-character row bound would crowd the message out.
|
|
52
|
+
*/
|
|
53
|
+
function boundedThreadTitle(body) {
|
|
54
|
+
const firstLine = body.split('\n', 1)[0]?.trim() ?? '';
|
|
55
|
+
return firstLine.length > 40 ? `${firstLine.slice(0, 39)}…` : firstLine;
|
|
56
|
+
}
|
|
48
57
|
/**
|
|
49
58
|
* How many Threads one Workspace's 「最近活跃」 slice may carry. It bounds what
|
|
50
59
|
* the Host hands over, not what the reader sees: the Client merges every
|
|
@@ -100,6 +109,13 @@ export class AgentTeamLedger {
|
|
|
100
109
|
createOccurredAt;
|
|
101
110
|
createRef;
|
|
102
111
|
operationTail = Promise.resolve();
|
|
112
|
+
/**
|
|
113
|
+
* Runtime human display name. Durable identity stays `member:human`; only
|
|
114
|
+
* the handle read by @ matching and handle-uniqueness follows this value.
|
|
115
|
+
* The Host syncs it from the `agent-team-human` settings namespace; the
|
|
116
|
+
* ledger never persists it, so a rename affects only later resolutions.
|
|
117
|
+
*/
|
|
118
|
+
humanHandle = AGENT_TEAM_HUMAN_HANDLE;
|
|
103
119
|
/**
|
|
104
120
|
* Head of the durable records the constructor's record-level replay
|
|
105
121
|
* validated, adoptable once by the invariant mount. See `validateAtMount`.
|
|
@@ -130,6 +146,19 @@ export class AgentTeamLedger {
|
|
|
130
146
|
lastOperationId: head?.operationId ?? null,
|
|
131
147
|
});
|
|
132
148
|
}
|
|
149
|
+
/** Current human display name for @ matching and uniqueness checks. */
|
|
150
|
+
humanDisplayHandle() {
|
|
151
|
+
return this.humanHandle;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Sync the runtime human display name from Host settings. Never persisted:
|
|
155
|
+
* a rename affects only later @ resolutions and handle checks.
|
|
156
|
+
*/
|
|
157
|
+
setHumanDisplayHandle(handle) {
|
|
158
|
+
const name = handle.normalize('NFKC').trim();
|
|
159
|
+
if (name !== '')
|
|
160
|
+
this.humanHandle = name;
|
|
161
|
+
}
|
|
133
162
|
initialize(request = {
|
|
134
163
|
requestId: AGENT_TEAM_INITIALIZE_REQUEST_ID,
|
|
135
164
|
actor: HUMAN_ACTOR,
|
|
@@ -1180,7 +1209,8 @@ export class AgentTeamLedger {
|
|
|
1180
1209
|
const recent = authorized.kind === 'human'
|
|
1181
1210
|
? this.recentInboxItems(authorized.memberId, workspaceIds[0], unreadThreads, taskNumbers)
|
|
1182
1211
|
: Object.freeze([]);
|
|
1183
|
-
return Object.freeze({
|
|
1212
|
+
return Object.freeze({ humanMemberId: this.initialization().data.humanMemberId,
|
|
1213
|
+
items: Object.freeze(selected), recent,
|
|
1184
1214
|
totalUnreadCount: items.reduce((sum, item) => sum + item.unreadCount, 0),
|
|
1185
1215
|
totalDirectCount: items.reduce((sum, item) => sum + item.directCount, 0) });
|
|
1186
1216
|
}
|
|
@@ -1194,8 +1224,16 @@ export class AgentTeamLedger {
|
|
|
1194
1224
|
inboxActorFor(fact) {
|
|
1195
1225
|
return this.memberActor(fact.kind === 'message' ? fact.message.sender : fact.activity.actor);
|
|
1196
1226
|
}
|
|
1197
|
-
/**
|
|
1227
|
+
/**
|
|
1228
|
+
* One Member as a row draws them: the id carries the identity hue, the handle
|
|
1229
|
+
* the initial. The Human is the one Member the Agent roster never holds, so
|
|
1230
|
+
* their row reads the runtime display name rather than falling back to the
|
|
1231
|
+
* durable `member:human` id — the same name every other seat and @ matching
|
|
1232
|
+
* already use.
|
|
1233
|
+
*/
|
|
1198
1234
|
memberActor(memberId) {
|
|
1235
|
+
if (memberId === this.initialization().data.humanMemberId)
|
|
1236
|
+
return Object.freeze({ memberId, name: this.humanHandle });
|
|
1199
1237
|
return Object.freeze({ memberId, name: this.state.members.get(memberId)?.handle ?? memberId });
|
|
1200
1238
|
}
|
|
1201
1239
|
/**
|
|
@@ -1380,6 +1418,30 @@ export class AgentTeamLedger {
|
|
|
1380
1418
|
getClaim(claimRef) {
|
|
1381
1419
|
return this.state.claims.get(claimRef);
|
|
1382
1420
|
}
|
|
1421
|
+
/** Navigation facts for message-body Thread refs; unknown refs are omitted. */
|
|
1422
|
+
resolveThreadRefs(workspaceId, threadRefs) {
|
|
1423
|
+
const numbers = this.taskNumbers(workspaceId);
|
|
1424
|
+
const resolved = [];
|
|
1425
|
+
for (const threadRef of threadRefs) {
|
|
1426
|
+
const key = this.uniqueRefKey(this.state.threads, threadRef, 'thread');
|
|
1427
|
+
const thread = key === undefined ? undefined : this.state.threads.get(key);
|
|
1428
|
+
const channelRef = thread === undefined ? undefined : this.channelRefForThread(thread.threadRef);
|
|
1429
|
+
const channel = channelRef === undefined ? undefined : this.state.channels.get(channelRef);
|
|
1430
|
+
// Same archival rule as tasks: archived Channels do not exist on Team
|
|
1431
|
+
// API surfaces, so their Threads stop resolving and message bodies
|
|
1432
|
+
// render the refs as plain non-navigable text.
|
|
1433
|
+
if (thread === undefined || channelRef === undefined || channel?.workspaceId !== workspaceId || channel?.state === 'archived')
|
|
1434
|
+
continue;
|
|
1435
|
+
const task = thread.taskRef === undefined ? undefined : this.state.tasks.get(thread.taskRef);
|
|
1436
|
+
resolved.push(Object.freeze({
|
|
1437
|
+
threadRef: thread.threadRef,
|
|
1438
|
+
channelRef,
|
|
1439
|
+
...(task === undefined ? {} : { taskRef: task.taskRef, taskNumber: numbers.get(task.taskRef) ?? 0 }),
|
|
1440
|
+
title: boundedThreadTitle(this.threadAnchor(thread.threadRef).body),
|
|
1441
|
+
}));
|
|
1442
|
+
}
|
|
1443
|
+
return resolved;
|
|
1444
|
+
}
|
|
1383
1445
|
/** Navigation facts for message-body Task refs; unknown refs are omitted. */
|
|
1384
1446
|
resolveTaskRefs(workspaceId, taskRefs) {
|
|
1385
1447
|
const numbers = this.taskNumbers(workspaceId);
|
|
@@ -1412,20 +1474,28 @@ export class AgentTeamLedger {
|
|
|
1412
1474
|
if (!this.participatesIn(member.memberId, request.workspaceId))
|
|
1413
1475
|
throw new Error('Member cannot view another Workspace');
|
|
1414
1476
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1477
|
+
// Validation above accepts unique UUID abbreviations, so resolve both
|
|
1478
|
+
// filters to full keys once: comparing the authored spelling in the fact
|
|
1479
|
+
// filters below matches nothing and returns an empty view that every
|
|
1480
|
+
// caller reads as "no such thread" (task #17).
|
|
1481
|
+
const channelRefFilter = request.channelRef === undefined
|
|
1482
|
+
? undefined
|
|
1483
|
+
: this.requireActiveChannel(request.workspaceId, request.channelRef).channelRef;
|
|
1484
|
+
if (channelRefFilter !== undefined) {
|
|
1485
|
+
if (memberId !== undefined && !this.isChannelMember(channelRefFilter, memberId))
|
|
1418
1486
|
throw new Error(`Agent Member '${memberId}' is not authorized for Channel '${request.channelRef}'`);
|
|
1419
1487
|
}
|
|
1488
|
+
let threadRefFilter;
|
|
1420
1489
|
if (request.threadRef !== undefined) {
|
|
1421
1490
|
const thread = this.requireThread(request.threadRef);
|
|
1491
|
+
threadRefFilter = thread.threadRef;
|
|
1422
1492
|
const channelRef = this.channelRefForThread(thread.threadRef);
|
|
1423
1493
|
if (channelRef === undefined)
|
|
1424
1494
|
throw new Error(`unknown Thread ref '${request.threadRef}'`);
|
|
1425
1495
|
if (this.state.channels.get(channelRef)?.workspaceId !== request.workspaceId)
|
|
1426
1496
|
throw new Error(`Thread '${request.threadRef}' does not belong to Workspace '${request.workspaceId}'`);
|
|
1427
1497
|
this.assertThreadChannelActive(channelRef);
|
|
1428
|
-
if (
|
|
1498
|
+
if (channelRefFilter !== undefined && channelRef !== channelRefFilter)
|
|
1429
1499
|
throw new Error(`Thread '${request.threadRef}' does not belong to Channel '${request.channelRef}'`);
|
|
1430
1500
|
if (memberId !== undefined && !this.isChannelMember(channelRef, memberId))
|
|
1431
1501
|
throw new Error(`Agent Member '${memberId}' is not authorized for Channel '${channelRef}'`);
|
|
@@ -1440,9 +1510,9 @@ export class AgentTeamLedger {
|
|
|
1440
1510
|
const channelRef = this.channelRefForThread(threadRef);
|
|
1441
1511
|
if (channelRef === undefined || !channelRefs.has(channelRef))
|
|
1442
1512
|
return false;
|
|
1443
|
-
if (
|
|
1513
|
+
if (channelRefFilter !== undefined && channelRef !== channelRefFilter)
|
|
1444
1514
|
return false;
|
|
1445
|
-
if (
|
|
1515
|
+
if (threadRefFilter !== undefined && threadRef !== threadRefFilter)
|
|
1446
1516
|
return false;
|
|
1447
1517
|
if (request.topLevelOnly && (fact.kind !== 'message' || !fact.message.topLevel))
|
|
1448
1518
|
return false;
|
|
@@ -1483,8 +1553,8 @@ export class AgentTeamLedger {
|
|
|
1483
1553
|
}
|
|
1484
1554
|
}
|
|
1485
1555
|
const visibleTasks = [...this.state.tasks.values()].filter(task => channelRefs.has(task.channelRef)
|
|
1486
|
-
&& (
|
|
1487
|
-
&& (
|
|
1556
|
+
&& (channelRefFilter === undefined || task.channelRef === channelRefFilter)
|
|
1557
|
+
&& (threadRefFilter === undefined || task.threadRef === threadRefFilter));
|
|
1488
1558
|
const taskNumbers = this.taskNumbers(request.workspaceId);
|
|
1489
1559
|
const items = selected.filter((fact) => fact.kind === 'message').map(fact => {
|
|
1490
1560
|
const message = fact.message;
|
|
@@ -1511,8 +1581,8 @@ export class AgentTeamLedger {
|
|
|
1511
1581
|
threads: Object.freeze([...this.state.threads.values()].filter(thread => {
|
|
1512
1582
|
const channelRef = this.channelRefForThread(thread.threadRef);
|
|
1513
1583
|
return channelRef !== undefined && channelRefs.has(channelRef)
|
|
1514
|
-
&& (
|
|
1515
|
-
&& (
|
|
1584
|
+
&& (channelRefFilter === undefined || channelRef === channelRefFilter)
|
|
1585
|
+
&& (threadRefFilter === undefined || thread.threadRef === threadRefFilter);
|
|
1516
1586
|
})),
|
|
1517
1587
|
taskNumbers: Object.freeze(visibleTasks.map(task => Object.freeze({ taskRef: task.taskRef, taskNumber: taskNumbers.get(task.taskRef) ?? 0 }))),
|
|
1518
1588
|
items: Object.freeze(items),
|
|
@@ -1766,7 +1836,10 @@ export class AgentTeamLedger {
|
|
|
1766
1836
|
throw new Error('agent-team ledger has no Human Member');
|
|
1767
1837
|
const humanMemberId = human.data.humanMemberId;
|
|
1768
1838
|
const assertHuman = () => {
|
|
1769
|
-
|
|
1839
|
+
// memberId is the durable Human authority; the handle is display-only
|
|
1840
|
+
// and may carry a configured name (old records carry the historic
|
|
1841
|
+
// literal), so replay never judges it here.
|
|
1842
|
+
if (operation.actor.kind !== 'human' || operation.actor.memberId !== humanMemberId) {
|
|
1770
1843
|
throw new Error(`agent-team operation ${operation.sequence} has invalid Human authority`);
|
|
1771
1844
|
}
|
|
1772
1845
|
};
|
|
@@ -1897,6 +1970,12 @@ export class AgentTeamLedger {
|
|
|
1897
1970
|
&& other.handle.normalize('NFKC').trim().toLowerCase() === normalized)
|
|
1898
1971
|
throw new Error('invalid Member update handle');
|
|
1899
1972
|
}
|
|
1973
|
+
// Replay-time human collision uses the Host-synced runtime name; the
|
|
1974
|
+
// default keeps old ledgers (written before configurable names) valid.
|
|
1975
|
+
// A stored rename that now collides fails replay loudly rather than
|
|
1976
|
+
// silently forking the @ namespace, so the operator renames first.
|
|
1977
|
+
if (normalized === this.humanHandle.normalize('NFKC').trim().toLowerCase())
|
|
1978
|
+
throw new Error('invalid Member update handle');
|
|
1900
1979
|
return;
|
|
1901
1980
|
}
|
|
1902
1981
|
if (operation.kind === 'team/channel-member-added') {
|
|
@@ -3207,7 +3286,8 @@ export class AgentTeamLedger {
|
|
|
3207
3286
|
}
|
|
3208
3287
|
assertHumanActor(actor) {
|
|
3209
3288
|
const initialization = this.initialization();
|
|
3210
|
-
|
|
3289
|
+
// memberId is the durable Human authority; the handle is display-only.
|
|
3290
|
+
if (actor.kind !== 'human' || actor.memberId !== initialization.data.humanMemberId) {
|
|
3211
3291
|
throw new Error('agent-team operation lacks Human authority');
|
|
3212
3292
|
}
|
|
3213
3293
|
}
|
|
@@ -3442,9 +3522,16 @@ export class AgentTeamLedger {
|
|
|
3442
3522
|
* Channel plus the Human. A name outside this set stays prose, which is what
|
|
3443
3523
|
* keeps an incidental name-drop from reaching someone the Channel cannot
|
|
3444
3524
|
* deliver to.
|
|
3525
|
+
*
|
|
3526
|
+
* The Human is reachable under two names: the current display handle and the
|
|
3527
|
+
* permanent `human` alias, so a rename never silently orphans `@human`
|
|
3528
|
+
* (matching ignores case, and chips render the display name either way).
|
|
3529
|
+
* The alias yields when another candidate already answers to it, so one
|
|
3530
|
+
* written name never notifies two different Members; enrollment reserves the
|
|
3531
|
+
* literal, so that yield only ever covers data predating the reservation.
|
|
3445
3532
|
*/
|
|
3446
3533
|
mentionCandidatesFor(channelRef) {
|
|
3447
|
-
const candidates = [{ memberId: AGENT_TEAM_HUMAN_MEMBER_ID, handle:
|
|
3534
|
+
const candidates = [{ memberId: AGENT_TEAM_HUMAN_MEMBER_ID, handle: this.humanHandle }];
|
|
3448
3535
|
for (const member of this.state.members.values()) {
|
|
3449
3536
|
if (member.state === 'inactive' || member.state === 'archived')
|
|
3450
3537
|
continue;
|
|
@@ -3452,6 +3539,10 @@ export class AgentTeamLedger {
|
|
|
3452
3539
|
continue;
|
|
3453
3540
|
candidates.push({ memberId: member.memberId, handle: member.handle });
|
|
3454
3541
|
}
|
|
3542
|
+
if (this.humanHandle.normalize('NFKC').trim().toLowerCase() !== AGENT_TEAM_HUMAN_HANDLE
|
|
3543
|
+
&& !candidates.some(candidate => candidate.handle.normalize('NFKC').trim().toLowerCase() === AGENT_TEAM_HUMAN_HANDLE)) {
|
|
3544
|
+
candidates.push({ memberId: AGENT_TEAM_HUMAN_MEMBER_ID, handle: AGENT_TEAM_HUMAN_HANDLE });
|
|
3545
|
+
}
|
|
3455
3546
|
return Object.freeze(candidates);
|
|
3456
3547
|
}
|
|
3457
3548
|
/**
|
|
@@ -3512,6 +3603,20 @@ export class AgentTeamLedger {
|
|
|
3512
3603
|
&& member.handle.normalize('NFKC').trim().toLowerCase() === normalized)) {
|
|
3513
3604
|
throw new Error(`Agent Member handle '${handle}' is already active in Workspace '${workspaceId}'`);
|
|
3514
3605
|
}
|
|
3606
|
+
// The human display name shares the @ namespace: an agent handle colliding
|
|
3607
|
+
// with it would make body mentions ambiguous, so it is reserved globally.
|
|
3608
|
+
// The ledger's runtime humanHandle is the single source; replay uses the
|
|
3609
|
+
// Host-synced value, and the default equals the historic literal.
|
|
3610
|
+
// The historic literal itself is reserved permanently alongside it: `@human`
|
|
3611
|
+
// is the documented permanent alias for the Human, so it must never belong
|
|
3612
|
+
// to an agent even after a rename moves the display name elsewhere.
|
|
3613
|
+
const humanNormalized = this.humanHandle.normalize('NFKC').trim().toLowerCase();
|
|
3614
|
+
if (humanNormalized !== '' && normalized === humanNormalized) {
|
|
3615
|
+
throw new Error(`Agent Member handle '${handle}' collides with the human display name`);
|
|
3616
|
+
}
|
|
3617
|
+
if (normalized === AGENT_TEAM_HUMAN_HANDLE) {
|
|
3618
|
+
throw new Error(`Agent Member handle '${handle}' collides with the reserved human alias`);
|
|
3619
|
+
}
|
|
3515
3620
|
}
|
|
3516
3621
|
/** Provider route and model id are exact identifiers; only whitespace-only values are rejected. */
|
|
3517
3622
|
assertModelSelection(model) {
|
|
@@ -2,10 +2,9 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
4
4
|
import { SessionLogOffset } from '@deepseek-ai/dsh-session';
|
|
5
|
-
import { v3RenamedSourceKind } from "./context-source.js";
|
|
6
5
|
import { memberMemoryDirectoryPath } from "./member-runtime.js";
|
|
7
6
|
export const name = 'wowyuarm-agent-team-member-context';
|
|
8
|
-
const MAX_MEMORY_BYTES =
|
|
7
|
+
const MAX_MEMORY_BYTES = 16 * 1024;
|
|
9
8
|
const BEGIN = '<team-member-private-memory>';
|
|
10
9
|
const END = '</team-member-private-memory>';
|
|
11
10
|
export function apply(ctx) {
|
|
@@ -43,9 +42,8 @@ export function apply(ctx) {
|
|
|
43
42
|
// number domains explicit (the seq = log.length contiguity contract).
|
|
44
43
|
const event = agent.session.snapshotEvents(SessionLogOffset(sequence), SessionLogOffset(sequence + 1))[0];
|
|
45
44
|
return event?.type === 'user/message'
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
&& (event.data.source.kind === name || event.data.source.kind === v3RenamedSourceKind(name))
|
|
45
|
+
&& event.data.source.kind === 'plugin'
|
|
46
|
+
&& event.data.source.plugin === name
|
|
49
47
|
&& event.data.content[0]?.type === 'text'
|
|
50
48
|
? [event.data.content[0].text]
|
|
51
49
|
: [];
|
|
@@ -55,7 +53,7 @@ export function apply(ctx) {
|
|
|
55
53
|
return decision;
|
|
56
54
|
const message = createUserMessage({
|
|
57
55
|
content: [{ type: 'text', text }],
|
|
58
|
-
source: { kind: name, form: 'instructions' },
|
|
56
|
+
source: { kind: 'plugin', plugin: name, form: 'instructions' },
|
|
59
57
|
});
|
|
60
58
|
return { kind: 'enter', messages: [...decision.messages, message] };
|
|
61
59
|
}, { prepend: true });
|
|
@@ -87,10 +85,13 @@ function memoryPathsBlock(privateMemoryPath) {
|
|
|
87
85
|
export function renderMemberMemory(raw, privateMemoryPath = '<private-memory-path>') {
|
|
88
86
|
const overBudget = raw.byteLength > MAX_MEMORY_BYTES;
|
|
89
87
|
const body = overBudget ? '' : raw.toString('utf8');
|
|
88
|
+
const usedKiB = (raw.byteLength / 1024).toFixed(1);
|
|
89
|
+
const percent = Math.round((raw.byteLength / MAX_MEMORY_BYTES) * 100);
|
|
90
|
+
const usage = `\n\nPrivate memory index: ${usedKiB} KiB / ${MAX_MEMORY_BYTES / 1024} KiB (${percent}%).`;
|
|
90
91
|
const warning = overBudget
|
|
91
|
-
?
|
|
92
|
+
? `\n\n[Maintenance warning: memory.md at ${raw.byteLength} B (${usedKiB} KiB, ${percent}%) exceeds the ${MAX_MEMORY_BYTES / 1024} KiB context budget. Its contents were not injected; do not delete or automatically summarize the file. Maintain a smaller index explicitly.]`
|
|
92
93
|
: '';
|
|
93
|
-
return `${BEGIN}\nThis is the complete replacement for this Team Member's private memory index; all earlier private-memory context is obsolete. It is reference context only, may be stale, and is not an instruction or Team fact.\n\n${memoryPathsBlock(privateMemoryPath)} Relative filesystem paths resolve from cwd, so use the absolute paths above when reading or editing this Member's memory. Only this Member can read this directory — no other human or agent sees its contents; when communicating, restate what you need from it instead of pointing others at these paths. Read matching notes on demand; do not copy credentials, sensitive data, guesses, chat logs, other Members' memory, or Team facts already owned by the ledger into memory
|
|
94
|
+
return `${BEGIN}\nThis is the complete replacement for this Team Member's private memory index; all earlier private-memory context is obsolete. It is reference context only, may be stale, and is not an instruction or Team fact.\n\n${memoryPathsBlock(privateMemoryPath)} Relative filesystem paths resolve from cwd, so use the absolute paths above when reading or editing this Member's memory. Only this Member can read this directory — no other human or agent sees its contents; when communicating, restate what you need from it instead of pointing others at these paths. Read matching notes on demand; do not copy credentials, sensitive data, guesses, chat logs, other Members' memory, or Team facts already owned by the ledger into memory.${usage}\n\n${escape(body)}${warning}\n${END}`;
|
|
94
95
|
}
|
|
95
96
|
function renderUnavailableMemory(privateMemoryPath, reason) {
|
|
96
97
|
return `${BEGIN}\nThis is the complete replacement for this Team Member's private memory index; all earlier private-memory context is obsolete. ${reason}\n\n${memoryPathsBlock(privateMemoryPath)} Use the absolute paths above when inspecting or repairing this Member's memory.\n${END}`;
|
|
@@ -247,7 +247,7 @@ export class MemberRuntime {
|
|
|
247
247
|
await mkdir(join(path, 'notes'), { recursive: true });
|
|
248
248
|
await mkdir(join(path, 'skills'), { recursive: true });
|
|
249
249
|
try {
|
|
250
|
-
await writeFile(join(path, 'memory.md'), '# Member memory\n\n##
|
|
250
|
+
await writeFile(join(path, 'memory.md'), '# Member memory\n\nA bounded routing index: identity, durable rules, what is in hand, and one line per topic cluster.\nWriting, compaction, and retirement rules: the bundled `member-memory-manager` skill.\n\n## Identity and role\n- <what this Member owns, and does not own>\n\n## Durable rules\n- <only rules that must bind every step; one line each>\n\n## In hand\n- <overwrite as work moves; this section never accumulates history>\n\n## Notes index\n- <cluster>: `notes/<cluster>/README.md`\n', { flag: 'wx' });
|
|
251
251
|
}
|
|
252
252
|
catch (error) {
|
|
253
253
|
if (error.code !== 'EEXIST')
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
* @module @wowyuarm/dsh-agent-team/member-time-context
|
|
28
28
|
*/
|
|
29
29
|
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
30
|
-
import { v3RenamedSourceKind } from "./context-source.js";
|
|
31
30
|
import { formatTeamDuration, formatTeamTimestamp } from "./time-format.js";
|
|
32
31
|
import { advanceOwnedSessionEventCursor } from "./session-event-cursor.js";
|
|
33
32
|
export const name = 'wowyuarm-agent-team-member-time-context';
|
|
@@ -50,9 +49,7 @@ export function applyClockEvent(state, event) {
|
|
|
50
49
|
return state.openTurn === -1 ? state : { ...state, lastTurnInjectionTime: null, openTurn: -1 };
|
|
51
50
|
case 'user/message': {
|
|
52
51
|
const source = event.data.source;
|
|
53
|
-
|
|
54
|
-
// read-time conversion's rename of this producer's V3 history.
|
|
55
|
-
const injected = source.kind === name || source.kind === v3RenamedSourceKind(name);
|
|
52
|
+
const injected = source.kind === 'plugin' && source.plugin === name;
|
|
56
53
|
const withMessage = state.lastMessageTime === event.time ? state : { ...state, lastMessageTime: event.time };
|
|
57
54
|
if (!injected)
|
|
58
55
|
return withMessage;
|
|
@@ -137,7 +134,7 @@ export function apply(ctx, config = {}) {
|
|
|
137
134
|
const text = renderClockSnapshot({ now, turn, step, previous });
|
|
138
135
|
const message = createUserMessage({
|
|
139
136
|
content: [{ type: 'text', text }],
|
|
140
|
-
source: { kind: name, form: 'snapshot', sections: [{ name, text }] },
|
|
137
|
+
source: { kind: 'plugin', plugin: name, form: 'snapshot', sections: [{ name, text }] },
|
|
141
138
|
});
|
|
142
139
|
return { kind: 'enter', messages: [...decision.messages, message] };
|
|
143
140
|
}, { prepend: true });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import AgentPresets from '@deepseek-ai/dsh-agent-presets';
|
|
4
|
+
export const inject = ['loader'];
|
|
5
|
+
/** Bundle-private Agent preset roster. The composition must isolate `agentPresets` around this provider and AgentTeam. */
|
|
6
|
+
export function apply(ctx) {
|
|
7
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const root = resolve(here, '../preset');
|
|
9
|
+
ctx.plugin(AgentPresets, {
|
|
10
|
+
default: 'team-member',
|
|
11
|
+
roots: [{ path: root, trust: 'system' }],
|
|
12
|
+
includeShippedRoot: false,
|
|
13
|
+
includeUserRoot: false,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -16,9 +16,14 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { CONTEXT_WINDOW_EXCEEDED_CODE } from '@deepseek-ai/dsh-llm';
|
|
18
18
|
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
19
|
-
import {
|
|
20
|
-
import { AGENT_TEAM_PLUGIN_ID, isAgentTeamSourceKind } from "./context-source.js";
|
|
19
|
+
import { AGENT_TEAM_PLUGIN_ID } from "./context-source.js";
|
|
21
20
|
import { advanceOwnedSessionEventCursor } from "./session-event-cursor.js";
|
|
21
|
+
/**
|
|
22
|
+
* Stable summary of the one-shot rollover pressure notice. It is a durable
|
|
23
|
+
* marker: the notice's own presence in a Session's log is what latches the
|
|
24
|
+
* policy, so the wording is read back and must not drift.
|
|
25
|
+
*/
|
|
26
|
+
export const CONTEXT_PRESSURE_NOTICE_SUMMARY = 'Context pressure: prepare a handoff';
|
|
22
27
|
/**
|
|
23
28
|
* Whether this Session's own span already carries the pressure notice: either
|
|
24
29
|
* surfaced as a `user/message` or still queued in a durable
|
|
@@ -50,10 +55,8 @@ export function contextPressureNoticeText(input) {
|
|
|
50
55
|
}
|
|
51
56
|
/** Whether one user message is this policy's one-shot pressure notice. */
|
|
52
57
|
function isPressureNotice(message) {
|
|
53
|
-
// The source arrives untyped from the inbox; identity is matched by exact
|
|
54
|
-
// kind — the shape written now and the read-time conversion's V3 rename.
|
|
55
58
|
const source = message.source;
|
|
56
|
-
return
|
|
59
|
+
return source?.plugin === AGENT_TEAM_PLUGIN_ID
|
|
57
60
|
&& source?.summary === CONTEXT_PRESSURE_NOTICE_SUMMARY;
|
|
58
61
|
}
|
|
59
62
|
export class PressurePolicyCoordinator {
|
|
@@ -131,7 +134,7 @@ export class PressurePolicyCoordinator {
|
|
|
131
134
|
activeClaims: this.options.activeClaimLabels(member.memberId),
|
|
132
135
|
runningJobs: this.options.runningJobLabels(member.memberId),
|
|
133
136
|
}) }],
|
|
134
|
-
source: { kind: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: CONTEXT_PRESSURE_NOTICE_SUMMARY },
|
|
137
|
+
source: { kind: 'plugin', plugin: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: CONTEXT_PRESSURE_NOTICE_SUMMARY },
|
|
135
138
|
});
|
|
136
139
|
try {
|
|
137
140
|
agent.steer(notice);
|