@wowyuarm/dsh-agent-team 0.1.12 → 0.1.13
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 +2 -0
- package/README.zh.md +2 -0
- package/cordis.patch.yml +7 -3
- package/package.json +14 -2
- package/packages/agent-team/README.md +2 -2
- package/packages/agent-team/README.zh.md +2 -2
- package/packages/agent-team/lib/index.js +151 -69
- package/packages/agent-team/lib/ledger.js +249 -31
- package/packages/agent-team/lib/member-context.js +18 -1
- package/packages/agent-team/lib/member-runtime.js +5 -4
- package/packages/agent-team/lib/mentions.js +48 -24
- package/packages/agent-team/lib/spec.js +19 -0
- package/packages/agent-team/lib/typert.host.js +187 -41
- package/packages/agent-team/lib/typert.remote-client.d.ts +7 -3
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +132 -27
- package/packages/agent-team/lib/types/index.d.ts +32 -7
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +46 -2
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-context.d.ts +6 -0
- package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/mentions.d.ts +20 -7
- package/packages/agent-team/lib/types/mentions.d.ts.map +1 -1
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/entities.d.ts +3 -2
- package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/operations.d.ts +37 -1
- package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/requests-results.d.ts +50 -10
- package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
- package/packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts +87 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts.map +1 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/schema.d.ts +59 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/schema.d.ts.map +1 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/unit.d.ts +57 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/unit.d.ts.map +1 -0
- package/packages/agent-team/lib/vendor/storage-sqlite/index.js +139 -0
- package/packages/agent-team/lib/vendor/storage-sqlite/schema.js +120 -0
- package/packages/agent-team/lib/vendor/storage-sqlite/unit.js +132 -0
- package/packages/client-agent-team/README.md +1 -1
- package/packages/client-agent-team/README.zh.md +1 -1
- package/packages/client-agent-team/lib/client.js +603 -343
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentImport.d.ts +12 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentImport.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentImport.js +69 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +3 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +29 -17
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +19 -44
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +2 -2
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +15 -14
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +15 -29
- 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/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +8 -1
- package/packages/client-agent-team/lib/types/client/locales.d.ts +18 -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 +18 -0
- 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-changes.d.ts +15 -31
- package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-changes.js +82 -111
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +13 -11
- 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 +29 -35
- 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/host-access.js +19 -0
- package/packages/tool-agent-team/lib/index.js +42 -33
- package/packages/tool-agent-team/lib/types/host-access.d.ts +10 -0
- package/packages/tool-agent-team/lib/types/host-access.d.ts.map +1 -1
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts +0 -41
- package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/scope-coverage.js +0 -64
package/README.md
CHANGED
|
@@ -59,6 +59,8 @@ dsh plugin --profile web add @wowyuarm/dsh-agent-team
|
|
|
59
59
|
dsh web
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
Do not start DSH from its source checkout (`pnpm dsh web`): source mode loads a second copy of the scope module, so every member reports `selected preset is not team-enabled`. Always start the compiled CLI.
|
|
63
|
+
|
|
62
64
|
Agent Team is opt-in. Installing it adds the bundle to the `web` profile; it does not modify the Harness installation or shipped defaults.
|
|
63
65
|
|
|
64
66
|
### 3. Verify and try it
|
package/README.zh.md
CHANGED
|
@@ -59,6 +59,8 @@ dsh plugin --profile web add @wowyuarm/dsh-agent-team
|
|
|
59
59
|
dsh web
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
不要从 DSH 源码 checkout 启动(`pnpm dsh web`):源码模式会加载第二份 scope 模块,所有成员都会报 `selected preset is not team-enabled`。请始终使用编译产物启动。
|
|
63
|
+
|
|
62
64
|
Agent Team 是显式 opt-in 的。安装只会把 bundle 加入 `web` profile,不会修改 Harness 安装,也不会修改 shipped defaults。
|
|
63
65
|
|
|
64
66
|
### 3. 验证并开始使用
|
package/cordis.patch.yml
CHANGED
|
@@ -15,10 +15,14 @@
|
|
|
15
15
|
name: '@deepseek-ai/dsh-invariants'
|
|
16
16
|
- id: wowyuarm-agent-team-invariant
|
|
17
17
|
name: '@wowyuarm/dsh-agent-team/invariant'
|
|
18
|
-
# Team ledger medium backend.
|
|
19
|
-
#
|
|
18
|
+
# Team ledger medium backend. Vendored under our own package name (see
|
|
19
|
+
# packages/agent-team/src/vendor/storage-sqlite/): DSH Desktop generation
|
|
20
|
+
# installers strip `@deepseek-ai/*` copies and resolve them from the host
|
|
21
|
+
# closure, which ships no sqlite backend — a loader row naming that
|
|
22
|
+
# package blocks boot (GitHub issue #28). Mounted here as an ordinary
|
|
23
|
+
# storage provider row beside the shipped JSON backend.
|
|
20
24
|
- id: storage-sqlite
|
|
21
|
-
name: '@
|
|
25
|
+
name: '@wowyuarm/dsh-agent-team/sqlite-backend'
|
|
22
26
|
config:
|
|
23
27
|
path: !!js dshHomePath('storages', 'agent_team.sqlite')
|
|
24
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wowyuarm/dsh-agent-team",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
|
+
"license": "MIT",
|
|
4
5
|
"description": "A persistent agent team for long-running collaboration in DeepSeek Harness",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
@@ -41,6 +42,9 @@
|
|
|
41
42
|
},
|
|
42
43
|
"client": {
|
|
43
44
|
"platform": "web",
|
|
45
|
+
"external": [
|
|
46
|
+
"@deepseek-ai/dsh-api-gateway/client"
|
|
47
|
+
],
|
|
44
48
|
"inject": [
|
|
45
49
|
"@deepseek-ai/dsh-api-remotes",
|
|
46
50
|
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
@@ -54,10 +58,10 @@
|
|
|
54
58
|
}
|
|
55
59
|
},
|
|
56
60
|
"dependencies": {
|
|
57
|
-
"@deepseek-ai/dsh-storage-sqlite": ">=0.1.5-rc.1 <0.2.0",
|
|
58
61
|
"zod": "^4.4.3"
|
|
59
62
|
},
|
|
60
63
|
"devDependencies": {
|
|
64
|
+
"@deepseek-ai/dsh-storage-sqlite": "0.1.5-rc.2",
|
|
61
65
|
"@testing-library/dom": "^10.4.1",
|
|
62
66
|
"@testing-library/react": "^16.3.2",
|
|
63
67
|
"@types/node": "^24.0.0",
|
|
@@ -105,6 +109,10 @@
|
|
|
105
109
|
"types": "./packages/agent-team/lib/types/preset-roster.d.ts",
|
|
106
110
|
"default": "./packages/agent-team/lib/preset-roster.js"
|
|
107
111
|
},
|
|
112
|
+
"./sqlite-backend": {
|
|
113
|
+
"types": "./packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts",
|
|
114
|
+
"default": "./packages/agent-team/lib/vendor/storage-sqlite/index.js"
|
|
115
|
+
},
|
|
108
116
|
"./member-context": {
|
|
109
117
|
"types": "./packages/agent-team/lib/types/member-context.d.ts",
|
|
110
118
|
"default": "./packages/agent-team/lib/member-context.js"
|
|
@@ -117,6 +125,10 @@
|
|
|
117
125
|
"types": "./packages/agent-team/lib/types/time-format.d.ts",
|
|
118
126
|
"default": "./packages/agent-team/lib/time-format.js"
|
|
119
127
|
},
|
|
128
|
+
"./mentions": {
|
|
129
|
+
"types": "./packages/agent-team/lib/types/mentions.d.ts",
|
|
130
|
+
"default": "./packages/agent-team/lib/mentions.js"
|
|
131
|
+
},
|
|
120
132
|
"./member-skills": {
|
|
121
133
|
"types": "./packages/agent-team/lib/types/member-skills.d.ts",
|
|
122
134
|
"default": "./packages/agent-team/lib/member-skills.js"
|
|
@@ -18,7 +18,7 @@ One durable record stores progress instead of the projection it reported. A `tea
|
|
|
18
18
|
|
|
19
19
|
`storage-domain` validates every record at the durable read boundary and rejects a backend unit stamped with another version. The Team updates its projection only after `KvTable.put()` resolves. Its Fiber owns the Domain handle; disposal rejects new Service calls through Cordis removal, drains accepted Domain writes, and closes the backend unit before the name can reopen.
|
|
20
20
|
|
|
21
|
-
Member creation commits one stable Member/session/Workspace/preset/private-memory identity before unpublished Agent setup. Setup mounts the selected preset and validates its marked `team_message` plus all eight Team tools before publication. Failure leaves only that Member unavailable. Suspend waits for the owned `AgentHandle` to stop; resume and Host remount restore the exact persisted session.
|
|
21
|
+
Member creation commits one stable Member/session/Workspace/preset/private-memory identity before unpublished Agent setup. The creation Workspace is recorded as the Member's creation site; further Workspaces are joined and left through the participation operations (`team/member-workspace-joined`/`team/member-workspace-left`), which grant collaboration immediately without moving the Session. Setup mounts the selected preset and validates its marked `team_message` plus all eight Team tools before publication. Failure leaves only that Member unavailable. Suspend waits for the owned `AgentHandle` to stop; resume and Host remount restore the exact persisted session.
|
|
22
22
|
|
|
23
23
|
Archival is the reversible third state between suspend and remove, for Members and Channels alike. `archiveMember` commits `team/member-archived`, disposes the live session while keeping private memory and the Session log on disk, archives the Session from grouping surfaces, and releases the Member's active Claims with public `claims_released` Activities plus Attention/marker cleanup. `archiveChannel` commits `team/channel-archived` with the same release shape across every owner on the Channel's Threads. Memberships survive both forms — hidden state, not departure. Archived entities are gone from every Team API surface: projections, ref resolution (their Task refs stop resolving, so message bodies render plain text), and ref-addressed reads (`readThread`/`threadHistory`/`threadObservations`/`listClaims` reject with an explicit archived error) — while the facts stay complete in the ledger for replay and a future restore; that boundary is the archival-vs-remove divide. Removal from archived stays available as the data hygiene path; there is no restore entry point yet (mirroring archived dsh sessions).
|
|
24
24
|
|
|
@@ -34,7 +34,7 @@ Adding a Member to a Channel grants future read/send/claim authority but injects
|
|
|
34
34
|
|
|
35
35
|
Member replies require the exact current Thread revision and atomically update Message and Thread facts. `threadRef` is the primary collaboration identity; released task-only Clients may send a Host-resolved `taskRef` alias for taskful Threads, while Task/Claim operations remain Task-ref based. Unread work must be read before a mutation; stale revisions are rejected after that unread gate. A closed Task rejects replies and new Attention; reopening restores the Task without restoring prior Attention. Taskless Threads still support replies, follow, mentions, Inbox, read, and history, but have no Claims or Task-resolution path. Top-level Messages may mention Agents directly: mentioned Members start following the new Thread. In an existing Thread, a Human reply mentioning a Member that is not currently following requires a process-local, one-use confirmation token before the operation commits, while such a Member reply commits and delivers only to Members the Thread has ever carried, reporting the rest as undelivered. Mentions are authored in the Message body as `@Handle` and resolved by the Host against the Channel's addressable names; Message facts carry the resolved refs, and the Client renders mention chips only for those Members. Claim/done/release and Task changes are ordered host-authored Activities. Active Claims exclude only the same normalized Direction; different Directions can run concurrently. Task state is derived from Claims unless Human acceptance or closure overrides it. Close releases active Claims and clears Thread Attention atomically. Member removal marks the member inactive, releases owned active Claims, clears its Attention and direct markers, and archives the session after the durable commit. Message and Activity facts share one bounded sequence cursor.
|
|
36
36
|
|
|
37
|
-
`changes(
|
|
37
|
+
`changes({ scope? }, signal?)` returns an `AsyncIterable<{ version: number }>` through a streaming Remote. It emits the current baseline, then matching workspace/channel/thread/presence invalidations; omitting scope observes shared projections but not presence. A paused consumer retains only the latest pending version; cancellation or Host disposal closes the stream. The Client must reread projections on every baseline, even when the version is unchanged or lower after a restart. See [architecture](../../docs/architecture.md#host-authority) for scope/version and recovery semantics. Thread reads change only private read state: they wake no scope, and a read with no unread or markers to consume appends no operation. After a commit, Inbox hints are recomputed only for affected Members.
|
|
38
38
|
|
|
39
39
|
M1 supports one Host writer. The ledger is permanent and has no snapshot or compaction path.
|
|
40
40
|
|
|
@@ -18,7 +18,7 @@ Service 使用 `ctx.storageDomain`、`ctx.workspaceRegistry`、`ctx.agents`、`c
|
|
|
18
18
|
|
|
19
19
|
`storage-domain` 在持久读取处校验每条 record,并拒绝被其他版本标记的 backend unit。Team 只在 `KvTable.put()` 完成后更新 projection。其 Fiber 持有 Domain handle;dispose 通过 Cordis 移除拒绝新的 Service 调用,排空已接受的 Domain write,并在名称可重新打开前关闭 backend unit。
|
|
20
20
|
|
|
21
|
-
创建 Member 时,先提交稳定的 Member/session/Workspace/preset/private-memory 身份,再执行 unpublished Agent setup。Setup 挂载指定 preset,并在发布前检查带 marker 的 `team_message` 和全部八个 Team tools。失败只把该 Member 标为 unavailable。Suspend 等待所属 `AgentHandle` 完全停止;resume 和 Host remount 恢复同一个持久 session。
|
|
21
|
+
创建 Member 时,先提交稳定的 Member/session/Workspace/preset/private-memory 身份,再执行 unpublished Agent setup。创建 Workspace 记录为 Member 的创建地;更多 Workspace 通过参与操作(`team/member-workspace-joined`/`team/member-workspace-left`)加入与退出——加入即获得协作能力,不移动 Session。Setup 挂载指定 preset,并在发布前检查带 marker 的 `team_message` 和全部八个 Team tools。失败只把该 Member 标为 unavailable。Suspend 等待所属 `AgentHandle` 完全停止;resume 和 Host remount 恢复同一个持久 session。
|
|
22
22
|
|
|
23
23
|
归档(archival)是介于 suspend 与 remove 之间的可逆第三态,Member 与 Channel 通用。`archiveMember` 提交 `team/member-archived`,dispose 活跃 session(私有记忆与 Session log 留在磁盘),把 Session 从分组面归档,并释放该 Member 的活跃 Claim(公开 `claims_released` Activity + Attention/marker 清理)。`archiveChannel` 提交 `team/channel-archived`,对该频道全部 Threads 上所有 owner 做同样的释放。两种归档都保留 Memberships——是隐藏而非离开。归档实体在所有 Team API 面上"默认不存在":投影、ref 解析(其 Task ref 不再解析,消息正文渲染为纯文本)、按 ref 的读取(`readThread`/`threadHistory`/`threadObservations`/`listClaims` 以明确的 archived 错误拒绝)——而事实在 ledger 中完整保留,供重放与未来恢复;这条边界正是归档与 remove 的分界。从归档态 remove 仍可用(数据卫生路径);本轮有意不提供恢复入口(对齐 dsh session 归档现状)。
|
|
24
24
|
|
|
@@ -34,7 +34,7 @@ Skills 是 Member 私有的:preset 不带共享 skill-filesystem row,Host
|
|
|
34
34
|
|
|
35
35
|
Member reply 必须携带准确的当前 Thread revision,并在一个 operation 内更新 Message 和 Thread facts。`threadRef` 是协作主身份;released task-only Client 可为 taskful Thread 传入 Host-resolved `taskRef` alias,而 Task/Claim 操作仍以 Task ref 为准。未读工作必须先 read;revision 过期时拒绝写入。Closed Task 拒绝 reply 和新的 Attention;reopen 恢复 Task,但不恢复之前的 Attention。taskless Thread 仍支持 reply、follow、mention、Inbox、read 和 history,但没有 Claim 或 Task resolution path。顶层消息可以直接 mention Agent:被 mention 的 Member 会开始关注新 Thread。在既有 Thread 中,Human 的 reply 提到当前未 follow 的 Member 时必须先取得 process-local one-use confirmation token 才提交 operation;Member 的此类 reply 会提交,但只送达该 Thread 曾经承载过的 Member,其余在结果里报告为未送达。mention 在 Message 正文里以 `@Handle` 撰写,由 Host 按 Channel 的可寻址名字解析;Message fact 携带解析后的 refs,Client 只为这些 Member 渲染 mention chip。claim/done/release 和 Task change 是有序的 host-authored Activity。Active Claim 只排斥相同的 normalized Direction;不同 Direction 可以并行。Task status 从 Claims 派生,Human accept/close 是覆盖事实。Close 原子释放 active Claims 并清除 Thread Attention。Member remove 原子标记 inactive、释放 owned active Claims、清除该成员的 Attention 和 direct markers,再归档 session。Message 与 Activity facts 共用一个有界 sequence cursor。
|
|
36
36
|
|
|
37
|
-
`changes()`
|
|
37
|
+
`changes({ scope? }, signal?)` 通过流式 Remote 返回 `AsyncIterable<{ version: number }>`。先发送当前基线,之后发送匹配的 workspace/channel/thread/presence 通知;省略 scope 时观察共享投影,不包含 presence。消费者暂停期间只保留最新待发送版本;取消或 Host 释放时关闭流。Client 必须在每次基线到达时重读投影,即使重启后的版本相同或更低。scope、版本和恢复语义见[架构文档](../../docs/architecture.zh.md#host-authority)。Thread read 只改变私有已读状态,不唤醒任何 scope;没有未读或 marker 可消费时不追加 operation。提交后只为受影响的 Members 重算 Inbox hints。
|
|
38
38
|
|
|
39
39
|
M1 支持单个 Host writer。Ledger 永久保留,不提供 snapshot 或 compaction。
|
|
40
40
|
|
|
@@ -178,6 +178,8 @@ let AgentTeam = (() => {
|
|
|
178
178
|
let _promoteThread_decorators;
|
|
179
179
|
let _joinChannel_decorators;
|
|
180
180
|
let _removeChannelMember_decorators;
|
|
181
|
+
let _joinWorkspace_decorators;
|
|
182
|
+
let _leaveWorkspace_decorators;
|
|
181
183
|
let _sendMessage_decorators;
|
|
182
184
|
let _putAttachment_decorators;
|
|
183
185
|
let _getAttachment_decorators;
|
|
@@ -193,7 +195,7 @@ let AgentTeam = (() => {
|
|
|
193
195
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
194
196
|
_resolveTaskRefs_decorators = [Remote('resolveTaskRefs')];
|
|
195
197
|
_membersForClient_decorators = [Remote('members')];
|
|
196
|
-
_changes_decorators = [Remote('
|
|
198
|
+
_changes_decorators = [Remote({ mode: 'stream' })];
|
|
197
199
|
_createChannel_decorators = [Remote('createChannel')];
|
|
198
200
|
_updateChannel_decorators = [Remote('updateChannel')];
|
|
199
201
|
_archiveChannel_decorators = [Remote('archiveChannel')];
|
|
@@ -206,6 +208,8 @@ let AgentTeam = (() => {
|
|
|
206
208
|
_promoteThread_decorators = [Remote('promoteThread')];
|
|
207
209
|
_joinChannel_decorators = [Remote('joinChannel')];
|
|
208
210
|
_removeChannelMember_decorators = [Remote('removeChannelMember')];
|
|
211
|
+
_joinWorkspace_decorators = [Remote('joinWorkspace')];
|
|
212
|
+
_leaveWorkspace_decorators = [Remote('leaveWorkspace')];
|
|
209
213
|
_sendMessage_decorators = [Remote('sendMessage')];
|
|
210
214
|
_putAttachment_decorators = [Remote('putAttachment')];
|
|
211
215
|
_getAttachment_decorators = [Remote('getAttachment')];
|
|
@@ -231,6 +235,8 @@ let AgentTeam = (() => {
|
|
|
231
235
|
__esDecorate(this, null, _promoteThread_decorators, { kind: "method", name: "promoteThread", static: false, private: false, access: { has: obj => "promoteThread" in obj, get: obj => obj.promoteThread }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
232
236
|
__esDecorate(this, null, _joinChannel_decorators, { kind: "method", name: "joinChannel", static: false, private: false, access: { has: obj => "joinChannel" in obj, get: obj => obj.joinChannel }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
233
237
|
__esDecorate(this, null, _removeChannelMember_decorators, { kind: "method", name: "removeChannelMember", static: false, private: false, access: { has: obj => "removeChannelMember" in obj, get: obj => obj.removeChannelMember }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
238
|
+
__esDecorate(this, null, _joinWorkspace_decorators, { kind: "method", name: "joinWorkspace", static: false, private: false, access: { has: obj => "joinWorkspace" in obj, get: obj => obj.joinWorkspace }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
239
|
+
__esDecorate(this, null, _leaveWorkspace_decorators, { kind: "method", name: "leaveWorkspace", static: false, private: false, access: { has: obj => "leaveWorkspace" in obj, get: obj => obj.leaveWorkspace }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
234
240
|
__esDecorate(this, null, _sendMessage_decorators, { kind: "method", name: "sendMessage", static: false, private: false, access: { has: obj => "sendMessage" in obj, get: obj => obj.sendMessage }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
235
241
|
__esDecorate(this, null, _putAttachment_decorators, { kind: "method", name: "putAttachment", static: false, private: false, access: { has: obj => "putAttachment" in obj, get: obj => obj.putAttachment }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
236
242
|
__esDecorate(this, null, _getAttachment_decorators, { kind: "method", name: "getAttachment", static: false, private: false, access: { has: obj => "getAttachment" in obj, get: obj => obj.getAttachment }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -382,7 +388,7 @@ let AgentTeam = (() => {
|
|
|
382
388
|
if (kind !== undefined)
|
|
383
389
|
this.ctx.logger.warn(`agent-team: member '${member.handle}' hit a recoverable ${kind} error; recording a consecutive error occurrence`);
|
|
384
390
|
this.recovery.onError(member.memberId, message);
|
|
385
|
-
this.
|
|
391
|
+
this.emitMemberPresenceChanged(member);
|
|
386
392
|
});
|
|
387
393
|
this.ctx.on('agent/status', ({ agent, status }) => {
|
|
388
394
|
const member = this.memberForAgent(agent);
|
|
@@ -394,7 +400,7 @@ let AgentTeam = (() => {
|
|
|
394
400
|
const recovered = this.clearMemberFailure(member.memberId, 'runtime');
|
|
395
401
|
if (recovered)
|
|
396
402
|
this.notifiedInbox.delete(member.memberId);
|
|
397
|
-
this.
|
|
403
|
+
this.emitMemberPresenceChanged(member);
|
|
398
404
|
// A rollover/recovery in flight delivers its own sequenced
|
|
399
405
|
// rederived Inbox after the handoff and carried input; a status-driven
|
|
400
406
|
// steer here would claim the handoff turn's next step and leapfrog
|
|
@@ -411,7 +417,7 @@ let AgentTeam = (() => {
|
|
|
411
417
|
if (this.memberFailures.get(member.memberId)?.runtime === undefined) {
|
|
412
418
|
this.recovery.onCleanTurnEnd(member.memberId);
|
|
413
419
|
}
|
|
414
|
-
this.
|
|
420
|
+
this.emitMemberPresenceChanged(member);
|
|
415
421
|
}
|
|
416
422
|
});
|
|
417
423
|
// The store's dispatch carrier is untagged, so a scope-tagged listener
|
|
@@ -477,6 +483,7 @@ let AgentTeam = (() => {
|
|
|
477
483
|
// One metadata listing serves every Member restore; per-member list calls
|
|
478
484
|
// would repeat the same I/O linearly during startup.
|
|
479
485
|
const persistedSessions = new Set((await this.persistedSessionHeaders()).map(snapshot => snapshot.header.id));
|
|
486
|
+
await this.sweepWorkspaceParticipations(ledger);
|
|
480
487
|
for (const member of ledger.listMembers()) {
|
|
481
488
|
if (member.state === 'enabled')
|
|
482
489
|
await this.activateMember(member, undefined, persistedSessions);
|
|
@@ -484,6 +491,31 @@ let AgentTeam = (() => {
|
|
|
484
491
|
await this.memberRuntime.cleanupRemovedMember(member);
|
|
485
492
|
}
|
|
486
493
|
}
|
|
494
|
+
/**
|
|
495
|
+
* Lazy Workspace-deletion handling: a Workspace that no longer exists can
|
|
496
|
+
* only be detected through the registry — there is no deletion event. A
|
|
497
|
+
* dead non-default participation is withdrawn here (its Claims and
|
|
498
|
+
* Attention release through the normal leave path); a dead default
|
|
499
|
+
* Workspace is left alone — activation fails on its missing cwd and the
|
|
500
|
+
* Member surfaces as unavailable for Human attention.
|
|
501
|
+
*/
|
|
502
|
+
async sweepWorkspaceParticipations(ledger) {
|
|
503
|
+
for (const member of ledger.listMembers()) {
|
|
504
|
+
if (member.state === 'inactive' || member.state === 'archived')
|
|
505
|
+
continue;
|
|
506
|
+
for (const workspaceId of ledger.workspacesOf(member.memberId)) {
|
|
507
|
+
if (workspaceId === member.workspaceId || this.ctx.workspaceRegistry.get(workspaceId) !== undefined)
|
|
508
|
+
continue;
|
|
509
|
+
this.ctx.logger.warn(`agent-team: workspace '${workspaceId}' is gone; withdrawing member '${member.handle}' from it`);
|
|
510
|
+
try {
|
|
511
|
+
await ledger.leaveWorkspace({ requestId: randomUUID(), workspaceId, memberId: member.memberId, actor: agentTeamHumanActor() });
|
|
512
|
+
}
|
|
513
|
+
catch (error) {
|
|
514
|
+
this.ctx.logger.warn(`agent-team: failed to withdraw member '${member.handle}' from deleted workspace '${workspaceId}': ${error instanceof Error ? error.message : String(error)}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
487
519
|
/**
|
|
488
520
|
* Whether one Member Session has durable persisted content, decided through
|
|
489
521
|
* {@link SessionPersistence.stat} rather than a bare metadata listing: the
|
|
@@ -519,56 +551,52 @@ let AgentTeam = (() => {
|
|
|
519
551
|
});
|
|
520
552
|
return Object.freeze({ resolved: Object.freeze(this.requireLedger().resolveTaskRefs(request.workspaceId, taskRefs)) });
|
|
521
553
|
}
|
|
522
|
-
/**
|
|
554
|
+
/** Browser-safe Human roster, optionally filtered to one participation. */
|
|
523
555
|
membersForClient(request) {
|
|
524
|
-
|
|
556
|
+
if (request.workspaceId !== undefined)
|
|
557
|
+
this.requireWorkspace(request.workspaceId);
|
|
558
|
+
const ledger = this.requireLedger();
|
|
525
559
|
return this.members()
|
|
526
|
-
.filter(status => status.member.
|
|
527
|
-
.map(({ member: { privateMemoryPath: _privateMemoryPath, ...member }, ...status }) => Object.freeze({ ...status, member: Object.freeze(member) }));
|
|
560
|
+
.filter(status => request.workspaceId === undefined || ledger.participatesIn(status.member.memberId, request.workspaceId))
|
|
561
|
+
.map(({ member: { privateMemoryPath: _privateMemoryPath, ...member }, ...status }) => Object.freeze({ ...status, member: Object.freeze(member), workspaceIds: ledger.workspacesOf(member.memberId) }));
|
|
528
562
|
}
|
|
529
|
-
/**
|
|
530
|
-
async changes(request, signal) {
|
|
531
|
-
if (!Number.isInteger(request.afterVersion) || request.afterVersion < 0)
|
|
532
|
-
throw new Error('afterVersion must be a non-negative integer');
|
|
563
|
+
/** Emit a current baseline, then coalesced invalidations until canceled. */
|
|
564
|
+
async *changes(request, signal) {
|
|
533
565
|
const scope = this.validateChangeScope(request.scope);
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
}, 25_000);
|
|
557
|
-
const onAbort = () => {
|
|
558
|
-
if (settled)
|
|
566
|
+
let pending;
|
|
567
|
+
let resume;
|
|
568
|
+
const waiter = {
|
|
569
|
+
scope,
|
|
570
|
+
wake: version => {
|
|
571
|
+
pending = version;
|
|
572
|
+
resume?.();
|
|
573
|
+
},
|
|
574
|
+
};
|
|
575
|
+
const onAbort = () => { resume?.(); };
|
|
576
|
+
if (signal?.aborted || !this.accepting)
|
|
577
|
+
return;
|
|
578
|
+
this.changeWaiters.add(waiter);
|
|
579
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
580
|
+
try {
|
|
581
|
+
// Listen before yielding so a commit during the consumer's read is kept.
|
|
582
|
+
yield { version: this.changeVersionOf(scope) };
|
|
583
|
+
while (!signal?.aborted && this.accepting) {
|
|
584
|
+
if (pending === undefined)
|
|
585
|
+
await new Promise(resolve => { resume = resolve; });
|
|
586
|
+
resume = undefined;
|
|
587
|
+
if (signal?.aborted || !this.accepting)
|
|
559
588
|
return;
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
566
|
-
if (signal?.aborted === true) {
|
|
567
|
-
onAbort();
|
|
568
|
-
return;
|
|
589
|
+
if (pending !== undefined) {
|
|
590
|
+
const version = pending;
|
|
591
|
+
pending = undefined;
|
|
592
|
+
yield { version };
|
|
593
|
+
}
|
|
569
594
|
}
|
|
570
|
-
|
|
571
|
-
|
|
595
|
+
}
|
|
596
|
+
finally {
|
|
597
|
+
this.changeWaiters.delete(waiter);
|
|
598
|
+
signal?.removeEventListener('abort', onAbort);
|
|
599
|
+
}
|
|
572
600
|
}
|
|
573
601
|
/** Return durable Team status without issuing a model request or a storage write. */
|
|
574
602
|
status() {
|
|
@@ -672,7 +700,7 @@ let AgentTeam = (() => {
|
|
|
672
700
|
async recoverMember(request) {
|
|
673
701
|
this.requireAccepting();
|
|
674
702
|
const member = this.requireLedger().getMember(request.memberId);
|
|
675
|
-
if (member === undefined ||
|
|
703
|
+
if (member === undefined || !this.requireLedger().participatesIn(request.memberId, request.workspaceId))
|
|
676
704
|
throw new Error(`unknown Member '${request.memberId}' in workspace '${request.workspaceId}'`);
|
|
677
705
|
this.recovery.stopTracking(request.memberId);
|
|
678
706
|
// An orphaned composition cannot be steered: its tools are gone, so a
|
|
@@ -730,7 +758,7 @@ let AgentTeam = (() => {
|
|
|
730
758
|
this.requireAccepting();
|
|
731
759
|
this.requireWorkspace(request.workspaceId);
|
|
732
760
|
const stored = this.requireLedger().getMember(request.memberId);
|
|
733
|
-
if (stored === undefined ||
|
|
761
|
+
if (stored === undefined || !this.requireLedger().participatesIn(request.memberId, request.workspaceId))
|
|
734
762
|
throw new Error(`unknown Member '${request.memberId}' in workspace '${request.workspaceId}'`);
|
|
735
763
|
if (stored.state !== 'enabled')
|
|
736
764
|
throw new Error(`Agent Member '${stored.handle}' is ${stored.state}; only enabled Members can start from a new context`);
|
|
@@ -876,7 +904,7 @@ let AgentTeam = (() => {
|
|
|
876
904
|
reactivated.agent.steer(this.contextManagement.handoffMessageFor(plan));
|
|
877
905
|
for (const message of carriedInput)
|
|
878
906
|
reactivated.agent.followup(message);
|
|
879
|
-
const notifications = this.requireLedger().notificationFacts(
|
|
907
|
+
const notifications = this.requireLedger().notificationFacts(rolled.member.memberId);
|
|
880
908
|
if (notifications.length > 0)
|
|
881
909
|
this.notifyMember(reactivated.agent, carriedInput.length > 0);
|
|
882
910
|
});
|
|
@@ -914,7 +942,7 @@ let AgentTeam = (() => {
|
|
|
914
942
|
const handle = this.handles.get(member.memberId);
|
|
915
943
|
if (handle === undefined)
|
|
916
944
|
throw new Error(`member '${member.handle}' has no active session`);
|
|
917
|
-
const notifications = this.requireLedger().notificationFacts(member.memberId
|
|
945
|
+
const notifications = this.requireLedger().notificationFacts(member.memberId);
|
|
918
946
|
const body = notifications.length === 0 ? text : `${text}\n\n${this.notificationText(notifications, member.memberId)}`;
|
|
919
947
|
const hint = createUserMessage({
|
|
920
948
|
content: [{ type: 'text', text: body }],
|
|
@@ -1045,6 +1073,22 @@ let AgentTeam = (() => {
|
|
|
1045
1073
|
this.emitCommitted(result.value.receipt);
|
|
1046
1074
|
return result.value;
|
|
1047
1075
|
}
|
|
1076
|
+
/** Human-only Workspace participation grant; a pure relation — no Session moves. */
|
|
1077
|
+
async joinWorkspace(request) {
|
|
1078
|
+
const actor = this.humanCall(request.workspaceId);
|
|
1079
|
+
const result = await this.requireLedger().joinWorkspace({ ...request, actor });
|
|
1080
|
+
if (result.committed)
|
|
1081
|
+
this.emitCommitted(result.value.receipt);
|
|
1082
|
+
return result.value;
|
|
1083
|
+
}
|
|
1084
|
+
/** Human-only Workspace participation withdrawal and Workspace-scoped cleanup. */
|
|
1085
|
+
async leaveWorkspace(request) {
|
|
1086
|
+
const actor = this.humanCall(request.workspaceId);
|
|
1087
|
+
const result = await this.requireLedger().leaveWorkspace({ ...request, actor });
|
|
1088
|
+
if (result.committed)
|
|
1089
|
+
this.emitCommitted(result.value.receipt);
|
|
1090
|
+
return result.value;
|
|
1091
|
+
}
|
|
1048
1092
|
/** Human top-level Thread start; asTask attaches an optional Task overlay. */
|
|
1049
1093
|
async sendMessage(request) {
|
|
1050
1094
|
return this.sendMessageAs(this.humanCall(request.workspaceId), request);
|
|
@@ -1185,8 +1229,10 @@ let AgentTeam = (() => {
|
|
|
1185
1229
|
}
|
|
1186
1230
|
inboxForAgent(agent, request) {
|
|
1187
1231
|
const actor = this.memberActor(agent);
|
|
1232
|
+
if (request.workspaceId === undefined)
|
|
1233
|
+
return this.requireLedger().memberInbox(actor, request);
|
|
1188
1234
|
this.requireAgentWorkspace(actor, request.workspaceId);
|
|
1189
|
-
return this.requireLedger().inbox(actor, request);
|
|
1235
|
+
return this.requireLedger().inbox(actor, { ...request, workspaceId: request.workspaceId });
|
|
1190
1236
|
}
|
|
1191
1237
|
async readThreadForAgent(agent, request) {
|
|
1192
1238
|
const actor = this.memberCall(agent, request.workspaceId);
|
|
@@ -1270,7 +1316,7 @@ let AgentTeam = (() => {
|
|
|
1270
1316
|
}
|
|
1271
1317
|
try {
|
|
1272
1318
|
const message = createUserMessage({
|
|
1273
|
-
content: [{ type: 'text', text: this.dmRelayText(agent, recipient, request.body.trim(), result.value.receipt.occurredAt, result.value.receipt.operationId) }],
|
|
1319
|
+
content: [{ type: 'text', text: this.dmRelayText(agent, recipient, request.body.trim(), result.value.receipt.occurredAt, result.value.receipt.operationId, request.workspaceId) }],
|
|
1274
1320
|
source: { kind: 'plugin', plugin: AGENT_TEAM_PLUGIN_ID, form: 'relay' },
|
|
1275
1321
|
});
|
|
1276
1322
|
// An idle recipient gets one ordinary turn; a busy one is steered into
|
|
@@ -1286,26 +1332,38 @@ let AgentTeam = (() => {
|
|
|
1286
1332
|
return result.value;
|
|
1287
1333
|
}
|
|
1288
1334
|
/** Relay body: the DM itself plus one bounded line of adjacent context. */
|
|
1289
|
-
dmRelayText(senderAgent, recipient, body, occurredAt, excluding) {
|
|
1335
|
+
dmRelayText(senderAgent, recipient, body, occurredAt, excluding, workspaceId) {
|
|
1290
1336
|
const sender = this.memberForAgent(senderAgent);
|
|
1291
1337
|
const prior = this.requireLedger().dmHistoryBetween(senderAgent.id, recipient.memberId, excluding);
|
|
1292
1338
|
const header = `Direct message from @${sender?.handle ?? 'a Team Member'} at ${formatTeamTimestamp(occurredAt)}:`;
|
|
1293
1339
|
const context = prior === undefined ? '' : `\n\n[most recent prior DM between you: ${prior}]`;
|
|
1294
|
-
return `${header}\n\n${body}${context}`;
|
|
1340
|
+
return `${header}\nWorkspace: ${workspaceId}\n\n${body}${context}`;
|
|
1295
1341
|
}
|
|
1296
1342
|
threadHistoryForAgent(agent, request) {
|
|
1297
1343
|
const actor = this.memberActor(agent);
|
|
1298
1344
|
this.requireAgentWorkspace(actor, request.workspaceId);
|
|
1299
1345
|
return this.requireLedger().threadHistory(actor, request);
|
|
1300
1346
|
}
|
|
1301
|
-
/**
|
|
1347
|
+
/** Live participation addresses; paths remain owned by the Harness registry. */
|
|
1348
|
+
workspacesForAgent(agent) {
|
|
1349
|
+
const actor = this.memberActor(agent);
|
|
1350
|
+
const member = this.requireLedger().getMember(actor.memberId);
|
|
1351
|
+
return Object.freeze(this.requireLedger().workspacesOf(actor.memberId).map(workspaceId => Object.freeze({
|
|
1352
|
+
workspaceId, path: this.ctx.workspaceRegistry.get(workspaceId)?.path, default: workspaceId === member.workspaceId,
|
|
1353
|
+
})));
|
|
1354
|
+
}
|
|
1355
|
+
/** Agent-only bounded discovery projection; participation list carries registry titles when known. */
|
|
1302
1356
|
viewForAgent(agent, request) {
|
|
1303
1357
|
const member = this.memberForAgent(agent);
|
|
1304
1358
|
if (member === undefined)
|
|
1305
1359
|
throw new Error('Agent is not an active Team Member');
|
|
1306
|
-
if (member.
|
|
1360
|
+
if (!this.requireLedger().participatesIn(member.memberId, request.workspaceId))
|
|
1307
1361
|
throw new Error('Member cannot view another Workspace');
|
|
1308
|
-
|
|
1362
|
+
const view = this.requireLedger().view(request, member.memberId);
|
|
1363
|
+
return Object.freeze({ ...view, workspaces: Object.freeze(view.workspaces.map(participation => {
|
|
1364
|
+
const title = this.ctx.workspaceRegistry.get(participation.workspaceId)?.title;
|
|
1365
|
+
return title === undefined ? participation : Object.freeze({ ...participation, title });
|
|
1366
|
+
})) });
|
|
1309
1367
|
}
|
|
1310
1368
|
/** Validate the durable ledger against an independently replayed projection. */
|
|
1311
1369
|
validateLedger() {
|
|
@@ -2040,9 +2098,10 @@ let AgentTeam = (() => {
|
|
|
2040
2098
|
this.requireWorkspace(workspaceId);
|
|
2041
2099
|
return agentTeamHumanActor();
|
|
2042
2100
|
}
|
|
2043
|
-
/** Fence one Member call: accepting Host, live Member binding, matching Workspace. */
|
|
2101
|
+
/** Fence one Member call: accepting Host, live Member binding, matching Workspace participation. */
|
|
2044
2102
|
memberCall(agent, workspaceId) {
|
|
2045
2103
|
this.requireAccepting();
|
|
2104
|
+
this.requireWorkspace(workspaceId);
|
|
2046
2105
|
const actor = this.memberActor(agent);
|
|
2047
2106
|
this.requireAgentWorkspace(actor, workspaceId);
|
|
2048
2107
|
return actor;
|
|
@@ -2054,7 +2113,7 @@ let AgentTeam = (() => {
|
|
|
2054
2113
|
return Object.freeze({ kind: 'member', memberId: member.memberId, handle: member.handle });
|
|
2055
2114
|
}
|
|
2056
2115
|
requireAgentWorkspace(actor, workspaceId) {
|
|
2057
|
-
if (this.requireLedger().
|
|
2116
|
+
if (!this.requireLedger().participatesIn(actor.memberId, workspaceId))
|
|
2058
2117
|
throw new Error('Member cannot mutate another Workspace');
|
|
2059
2118
|
}
|
|
2060
2119
|
/**
|
|
@@ -2313,7 +2372,7 @@ let AgentTeam = (() => {
|
|
|
2313
2372
|
}
|
|
2314
2373
|
finally {
|
|
2315
2374
|
// Activation only changes this Workspace's presence projection.
|
|
2316
|
-
this.
|
|
2375
|
+
this.emitMemberPresenceChanged(member);
|
|
2317
2376
|
}
|
|
2318
2377
|
}
|
|
2319
2378
|
/**
|
|
@@ -2486,6 +2545,25 @@ let AgentTeam = (() => {
|
|
|
2486
2545
|
}
|
|
2487
2546
|
const ledger = this.requireLedger();
|
|
2488
2547
|
this.emitChanged(ledger.changeScopesOf(operation));
|
|
2548
|
+
if (operation.kind === 'team/member-workspace-joined' || operation.kind === 'team/member-workspace-left') {
|
|
2549
|
+
const agent = this.handles.get(operation.data.memberId)?.agent;
|
|
2550
|
+
if (agent !== undefined) {
|
|
2551
|
+
const path = this.ctx.workspaceRegistry.get(operation.data.workspaceId)?.path;
|
|
2552
|
+
const text = `Team participation changed: you have ${operation.kind === 'team/member-workspace-joined' ? 'joined' : 'left'} Workspace ${operation.data.workspaceId}${path === undefined ? ' (path unavailable)' : ` (${JSON.stringify(path)})`}.\nCurrent Workspace ids: ${ledger.workspacesOf(operation.data.memberId).join(', ')}. This replaces earlier participation information. Your Session and cwd have not moved.`;
|
|
2553
|
+
const notice = createUserMessage({ content: [{ type: 'text', text }],
|
|
2554
|
+
source: { kind: 'plugin', plugin: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: 'Team Workspace participation changed' } });
|
|
2555
|
+
try {
|
|
2556
|
+
if (agent.status === 'idle' || agent.inbox.nextTurn.some(message => message.source.kind === 'user'))
|
|
2557
|
+
agent.followup(notice);
|
|
2558
|
+
else
|
|
2559
|
+
agent.steer(notice);
|
|
2560
|
+
}
|
|
2561
|
+
catch (error) {
|
|
2562
|
+
// Notification delivery cannot roll back a committed participation.
|
|
2563
|
+
this.ctx.logger.warn(`agent-team: participation notice not delivered: ${error instanceof Error ? error.message : String(error)}`);
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2489
2567
|
for (const memberId of ledger.affectedMembersOf(operation)) {
|
|
2490
2568
|
const handle = this.handles.get(memberId);
|
|
2491
2569
|
if (handle !== undefined)
|
|
@@ -2521,10 +2599,8 @@ let AgentTeam = (() => {
|
|
|
2521
2599
|
.map((job) => `${job.name ?? job.id}`);
|
|
2522
2600
|
}
|
|
2523
2601
|
emitAutoCompactionChanged(memberId) {
|
|
2524
|
-
const workspaceId
|
|
2525
|
-
|
|
2526
|
-
return;
|
|
2527
|
-
this.emitPresenceChanged(workspaceId);
|
|
2602
|
+
for (const workspaceId of this.ledger?.workspacesOf(memberId) ?? [])
|
|
2603
|
+
this.emitPresenceChanged(workspaceId);
|
|
2528
2604
|
}
|
|
2529
2605
|
/**
|
|
2530
2606
|
* Presence-only invalidation: Agent running/idle/activation/failure changes
|
|
@@ -2534,12 +2610,17 @@ let AgentTeam = (() => {
|
|
|
2534
2610
|
emitPresenceChanged(workspaceId) {
|
|
2535
2611
|
this.emitChanged([{ kind: 'presence', workspaceId }]);
|
|
2536
2612
|
}
|
|
2613
|
+
/** Presence invalidation in every Workspace the Member participates in — each panel listing it must refresh. */
|
|
2614
|
+
emitMemberPresenceChanged(member) {
|
|
2615
|
+
for (const workspaceId of this.requireLedger().workspacesOf(member.memberId))
|
|
2616
|
+
this.emitPresenceChanged(workspaceId);
|
|
2617
|
+
}
|
|
2537
2618
|
/** Wake from durable unread state with bounded facts for direct and state-changing work. */
|
|
2538
2619
|
notifyMember(agent, sequenced = false) {
|
|
2539
2620
|
const member = this.memberForAgent(agent);
|
|
2540
2621
|
if (member === undefined || member.state !== 'enabled')
|
|
2541
2622
|
return;
|
|
2542
|
-
const notifications = this.requireLedger().notificationFacts(member.memberId
|
|
2623
|
+
const notifications = this.requireLedger().notificationFacts(member.memberId);
|
|
2543
2624
|
if (notifications.length === 0) {
|
|
2544
2625
|
this.notifiedInbox.delete(member.memberId);
|
|
2545
2626
|
return;
|
|
@@ -2612,6 +2693,8 @@ let AgentTeam = (() => {
|
|
|
2612
2693
|
return true;
|
|
2613
2694
|
};
|
|
2614
2695
|
for (const { item, facts } of notifications.slice(0, 8)) {
|
|
2696
|
+
if (!append(`Workspace: ${item.workspaceId}\nChannel: ${item.channelRef}`))
|
|
2697
|
+
break;
|
|
2615
2698
|
for (const { fact, direct } of facts) {
|
|
2616
2699
|
if (detailedFactCount >= 20) {
|
|
2617
2700
|
omitted = true;
|
|
@@ -2641,7 +2724,7 @@ let AgentTeam = (() => {
|
|
|
2641
2724
|
}
|
|
2642
2725
|
if (omitted)
|
|
2643
2726
|
sections.push('More unread work remains in team_inbox; the automatic context is bounded.');
|
|
2644
|
-
sections.push('Use team_thread read with the relevant threadRef before acting or replying. Use team_inbox only when you need to triage the remaining Threads.');
|
|
2727
|
+
sections.push('Use team_thread read with the relevant workspace and threadRef before acting or replying. Use team_inbox only when you need to triage the remaining Threads.');
|
|
2645
2728
|
return sections.join('\n\n');
|
|
2646
2729
|
}
|
|
2647
2730
|
boundedNotificationBody(body) {
|
|
@@ -2731,7 +2814,6 @@ let AgentTeam = (() => {
|
|
|
2731
2814
|
const waiterScope = waiter.scope;
|
|
2732
2815
|
if (scopes !== undefined && (waiterScope === undefined ? !touchesProjection : !scopes.some(scope => sameChangeScope(scope, waiterScope))))
|
|
2733
2816
|
continue;
|
|
2734
|
-
this.changeWaiters.delete(waiter);
|
|
2735
2817
|
waiter.wake(this.changeVersionOf(waiterScope));
|
|
2736
2818
|
}
|
|
2737
2819
|
}
|