@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.
Files changed (127) hide show
  1. package/README.md +5 -3
  2. package/README.zh.md +5 -3
  3. package/cordis.patch.yml +2 -185
  4. package/package.json +52 -46
  5. package/packages/agent-team/README.md +1 -1
  6. package/packages/agent-team/README.zh.md +1 -1
  7. package/packages/agent-team/core-skills/member-memory-manager/SKILL.md +30 -0
  8. package/packages/agent-team/lib/context-continuity-host.js +91 -0
  9. package/packages/agent-team/lib/context-projection.js +266 -489
  10. package/packages/agent-team/lib/context-source.js +17 -106
  11. package/packages/agent-team/lib/human-avatar.js +61 -0
  12. package/packages/agent-team/lib/human-profile.js +46 -0
  13. package/packages/agent-team/lib/human-update-check.js +119 -0
  14. package/packages/agent-team/lib/index.js +408 -264
  15. package/packages/agent-team/lib/ledger.js +120 -15
  16. package/packages/agent-team/lib/member-context.js +9 -8
  17. package/packages/agent-team/lib/member-runtime.js +1 -1
  18. package/packages/agent-team/lib/member-time-context.js +2 -5
  19. package/packages/agent-team/lib/preset-roster.js +15 -0
  20. package/packages/agent-team/lib/pressure-policy.js +9 -6
  21. package/packages/agent-team/lib/session-remediation.js +493 -0
  22. package/packages/agent-team/lib/stored-session-reader.js +2 -1
  23. package/packages/agent-team/lib/typert.host.js +605 -291
  24. package/packages/agent-team/lib/typert.remote-client.d.ts +15 -6
  25. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  26. package/packages/agent-team/lib/typert.remote-client.js +367 -244
  27. package/packages/agent-team/lib/types/context-continuity-host.d.ts +84 -0
  28. package/packages/agent-team/lib/types/context-continuity-host.d.ts.map +1 -0
  29. package/packages/agent-team/lib/types/context-projection.d.ts +130 -185
  30. package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -1
  31. package/packages/agent-team/lib/types/context-source.d.ts +17 -81
  32. package/packages/agent-team/lib/types/context-source.d.ts.map +1 -1
  33. package/packages/agent-team/lib/types/human-avatar.d.ts +29 -0
  34. package/packages/agent-team/lib/types/human-avatar.d.ts.map +1 -0
  35. package/packages/agent-team/lib/types/human-profile.d.ts +44 -0
  36. package/packages/agent-team/lib/types/human-profile.d.ts.map +1 -0
  37. package/packages/agent-team/lib/types/human-update-check.d.ts +73 -0
  38. package/packages/agent-team/lib/types/human-update-check.d.ts.map +1 -0
  39. package/packages/agent-team/lib/types/index.d.ts +112 -38
  40. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  41. package/packages/agent-team/lib/types/ledger.d.ts +31 -2
  42. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  43. package/packages/agent-team/lib/types/member-context.d.ts +0 -14
  44. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  45. package/packages/agent-team/lib/types/member-time-context.d.ts +0 -15
  46. package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -1
  47. package/packages/agent-team/lib/types/preset-roster.d.ts +5 -0
  48. package/packages/agent-team/lib/types/preset-roster.d.ts.map +1 -0
  49. package/packages/agent-team/lib/types/pressure-policy.d.ts +6 -0
  50. package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
  51. package/packages/agent-team/lib/types/session-remediation.d.ts +172 -0
  52. package/packages/agent-team/lib/types/session-remediation.d.ts.map +1 -0
  53. package/packages/agent-team/lib/types/stored-session-reader.d.ts +2 -1
  54. package/packages/agent-team/lib/types/stored-session-reader.d.ts.map +1 -1
  55. package/packages/agent-team/lib/types/types/entities.d.ts +20 -3
  56. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  57. package/packages/agent-team/lib/types/types/requests-results.d.ts +63 -0
  58. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  59. package/packages/agent-team/preset/team-member/agent.cordis.yml +166 -0
  60. package/packages/client-agent-team/lib/client.js +1667 -516
  61. package/packages/client-agent-team/lib/client.js.map +1 -1
  62. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts +15 -0
  63. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts.map +1 -0
  64. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.js +115 -0
  65. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  66. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +12 -8
  67. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts +22 -1
  68. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts.map +1 -1
  69. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.js +25 -2
  70. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +6 -1
  71. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  72. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +69 -31
  73. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +4 -4
  74. package/packages/client-agent-team/lib/types/client/TeamComposer.js +3 -3
  75. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
  76. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
  77. package/packages/client-agent-team/lib/types/client/TeamConversation.js +12 -5
  78. package/packages/client-agent-team/lib/types/client/TeamFooterAction.js +2 -2
  79. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +4 -1
  80. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -1
  81. package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +22 -7
  82. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +11 -1
  83. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
  84. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +69 -8
  85. package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +2 -2
  86. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +18 -3
  87. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  88. package/packages/client-agent-team/lib/types/client/TeamMessage.js +213 -67
  89. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts.map +1 -1
  90. package/packages/client-agent-team/lib/types/client/TeamRowMenu.js +2 -2
  91. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.js +2 -2
  92. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +5 -0
  93. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  94. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +55 -29
  95. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +3 -3
  96. package/packages/client-agent-team/lib/types/client/TeamWorkspaceSelector.js +2 -2
  97. package/packages/client-agent-team/lib/types/client/avatar-image.d.ts +21 -0
  98. package/packages/client-agent-team/lib/types/client/avatar-image.d.ts.map +1 -0
  99. package/packages/client-agent-team/lib/types/client/avatar-image.js +9 -0
  100. package/packages/client-agent-team/lib/types/client/human-identity.d.ts +94 -0
  101. package/packages/client-agent-team/lib/types/client/human-identity.d.ts.map +1 -0
  102. package/packages/client-agent-team/lib/types/client/human-identity.js +117 -0
  103. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  104. package/packages/client-agent-team/lib/types/client/index.js +112 -41
  105. package/packages/client-agent-team/lib/types/client/locales.d.ts +72 -2
  106. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  107. package/packages/client-agent-team/lib/types/client/locales.js +78 -2
  108. package/packages/client-agent-team/lib/types/client/multi-menu-field.js +2 -2
  109. package/packages/client-agent-team/lib/types/client/refs.d.ts +71 -0
  110. package/packages/client-agent-team/lib/types/client/refs.d.ts.map +1 -0
  111. package/packages/client-agent-team/lib/types/client/refs.js +181 -0
  112. package/packages/client-agent-team/lib/types/client/slots.d.ts +8 -2
  113. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  114. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +57 -9
  115. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  116. package/packages/client-agent-team/lib/types/client/team-formatters.js +85 -17
  117. package/packages/tool-agent-team/README.md +1 -1
  118. package/packages/tool-agent-team/README.zh.md +1 -1
  119. package/packages/tool-agent-team/lib/context-tools.js +20 -6
  120. package/packages/tool-agent-team/lib/index.js +2 -2
  121. package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
  122. package/packages/agent-team/lib/context-management.js +0 -371
  123. package/packages/agent-team/lib/types/context-management.d.ts +0 -154
  124. package/packages/agent-team/lib/types/context-management.d.ts.map +0 -1
  125. package/packages/client-agent-team/lib/types/client/task-refs.d.ts +0 -31
  126. package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +0 -1
  127. 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({ items: Object.freeze(selected), recent,
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
- /** One Member as a row draws them: the id carries the identity hue, the handle the initial. */
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
- if (request.channelRef !== undefined) {
1416
- this.requireActiveChannel(request.workspaceId, request.channelRef);
1417
- if (memberId !== undefined && !this.isChannelMember(request.channelRef, memberId))
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 (request.channelRef !== undefined && channelRef !== request.channelRef)
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 (request.channelRef !== undefined && channelRef !== request.channelRef)
1513
+ if (channelRefFilter !== undefined && channelRef !== channelRefFilter)
1444
1514
  return false;
1445
- if (request.threadRef !== undefined && threadRef !== request.threadRef)
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
- && (request.channelRef === undefined || task.channelRef === request.channelRef)
1487
- && (request.threadRef === undefined || task.threadRef === request.threadRef));
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
- && (request.channelRef === undefined || channelRef === request.channelRef)
1515
- && (request.threadRef === undefined || thread.threadRef === request.threadRef);
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
- if (operation.actor.kind !== 'human' || operation.actor.memberId !== humanMemberId || operation.actor.handle !== HUMAN_ACTOR.handle) {
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
- if (actor.kind !== 'human' || actor.memberId !== initialization.data.humanMemberId || actor.handle !== HUMAN_ACTOR.handle) {
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: AGENT_TEAM_HUMAN_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 = 8 * 1024;
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
- // Both identities are this producer's own: the kind written now and
47
- // the read-time conversion's rename of this producer's V3 history.
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
- ? '\n\n[Maintenance warning: memory.md exceeds the 8 KiB context budget. Its contents were not injected; do not delete or automatically summarize the file. Maintain a smaller index explicitly.]'
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.\n\n${escape(body)}${warning}\n${END}`;
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## 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' });
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
- // Both identities are this producer's own: the kind written now and the
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 { CONTEXT_PRESSURE_NOTICE_SUMMARY } from "./context-management.js";
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 isAgentTeamSourceKind(source?.kind)
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);