@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
|
@@ -42,23 +42,21 @@ export interface MentionSegment {
|
|
|
42
42
|
readonly name?: string;
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
|
-
* Locate one Message's
|
|
46
|
-
*
|
|
47
|
-
* boundaries,
|
|
48
|
-
*
|
|
49
|
-
*
|
|
45
|
+
* Locate one Message's delivered mention names inside its literal body. Matching
|
|
46
|
+
* is the shared Host delivery scan — an authored `@`, case-insensitive on Unicode
|
|
47
|
+
* word boundaries, longest handle first, code quoted rather than called — so a chip
|
|
48
|
+
* never lands where delivery would not reach. Mention segments render the canonical
|
|
49
|
+
* `@Handle`; names absent from the body come back unmatched so the consumer can
|
|
50
|
+
* append them as a fallback chip row.
|
|
50
51
|
*/
|
|
51
52
|
export declare function splitMentionNames(text: string, names: readonly string[]): {
|
|
52
53
|
segments: MentionSegment[];
|
|
53
54
|
unmatched: readonly string[];
|
|
54
55
|
};
|
|
55
56
|
/**
|
|
56
|
-
* Whether one draft spells a handle as an authored `@mention`:
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* Client's single answer to "did the author call this name" — the composer
|
|
60
|
-
* prunes picked recipients with it and previews the draft's notifications with
|
|
61
|
-
* it.
|
|
57
|
+
* Whether one draft spells a handle as an authored `@mention`: the shared
|
|
58
|
+
* delivery scan, so the composer's recipient prune and will-notify preview agree
|
|
59
|
+
* with the Host on what the draft actually calls.
|
|
62
60
|
*/
|
|
63
61
|
export declare function containsMention(body: string, handle: string): boolean;
|
|
64
62
|
/** Whether one draft carries the `@all` marker the mention menu expands. */
|
|
@@ -84,6 +82,10 @@ export declare function allMentionMembers(members: readonly AgentTeamClientMembe
|
|
|
84
82
|
export declare function mentionedMemberIds(body: string, members: readonly AgentTeamClientMemberStatus[]): readonly AgentTeamMemberId[];
|
|
85
83
|
/** Canonical chip handles for one Message's structured mention refs. */
|
|
86
84
|
export declare function mentionNamesOf(mentions: readonly AgentTeamMemberId[], handles: ReadonlyMap<AgentTeamMemberId, string>): string[];
|
|
85
|
+
/** Accessible label for one "who is on this work" stack: its owners' handles, comma-separated. */
|
|
86
|
+
export declare function claimersLabel(owners: ReadonlyArray<{
|
|
87
|
+
readonly name: string;
|
|
88
|
+
}>, t: TeamConversationProps['t']): string;
|
|
87
89
|
/**
|
|
88
90
|
* Whether an Agent body survives literal rendering unchanged: no fences,
|
|
89
91
|
* inline code, emphasis markers, links, images, tables, or block constructs.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-formatters.d.ts","sourceRoot":"","sources":["../../../src/client/team-formatters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"team-formatters.d.ts","sourceRoot":"","sources":["../../../src/client/team-formatters.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hasAllMarker, resolveBodyMentions, scanBodyHandles } from '@wowyuarm/dsh-agent-team/mentions';
|
|
1
2
|
export function formatTaskStatus(status, t) {
|
|
2
3
|
return t({
|
|
3
4
|
todo: 'taskStatusTodo',
|
|
@@ -81,56 +82,42 @@ export function isSingleBrandedRef(text) {
|
|
|
81
82
|
const matches = [...trimmed.matchAll(BRANDED_REF_PATTERN)];
|
|
82
83
|
return matches.length === 1 && matches[0][0] === trimmed;
|
|
83
84
|
}
|
|
84
|
-
function escapeRegExp(value) {
|
|
85
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
86
|
-
}
|
|
87
85
|
/**
|
|
88
|
-
* Locate one Message's
|
|
89
|
-
*
|
|
90
|
-
* boundaries,
|
|
91
|
-
*
|
|
92
|
-
*
|
|
86
|
+
* Locate one Message's delivered mention names inside its literal body. Matching
|
|
87
|
+
* is the shared Host delivery scan — an authored `@`, case-insensitive on Unicode
|
|
88
|
+
* word boundaries, longest handle first, code quoted rather than called — so a chip
|
|
89
|
+
* never lands where delivery would not reach. Mention segments render the canonical
|
|
90
|
+
* `@Handle`; names absent from the body come back unmatched so the consumer can
|
|
91
|
+
* append them as a fallback chip row.
|
|
93
92
|
*/
|
|
94
93
|
export function splitMentionNames(text, names) {
|
|
95
94
|
if (names.length === 0)
|
|
96
95
|
return { segments: [{ text, mention: false }], unmatched: [] };
|
|
97
|
-
const ordered = [...names].sort((left, right) => right.length - left.length);
|
|
98
|
-
// The lookbehind keeps the optional '@' from consuming the separator of an
|
|
99
|
-
// email-like occurrence: the character before the name (or before its '@')
|
|
100
|
-
// must not be a letter, digit, underscore, or another '@'.
|
|
101
|
-
const pattern = new RegExp(`(?<![\\p{L}\\p{N}_@])@?(?:${ordered.map(name => `(${escapeRegExp(name)})`).join('|')})(?=$|[^\\p{L}\\p{N}_])`, 'giu');
|
|
102
96
|
const segments = [];
|
|
103
97
|
const matched = new Set();
|
|
104
98
|
let cursor = 0;
|
|
105
|
-
for (const match of text
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const name = ordered[groupIndex - 1];
|
|
112
|
-
segments.push({ text: `@${name}`, mention: true, name });
|
|
113
|
-
matched.add(name.toLowerCase());
|
|
114
|
-
cursor = match.index + match[0].length;
|
|
99
|
+
for (const match of scanBodyHandles(text, names)) {
|
|
100
|
+
if (match.start > cursor)
|
|
101
|
+
segments.push({ text: text.slice(cursor, match.start), mention: false });
|
|
102
|
+
segments.push({ text: `@${match.handle}`, mention: true, name: match.handle });
|
|
103
|
+
matched.add(match.handle.toLowerCase());
|
|
104
|
+
cursor = match.end;
|
|
115
105
|
}
|
|
116
106
|
if (cursor < text.length)
|
|
117
107
|
segments.push({ text: text.slice(cursor), mention: false });
|
|
118
108
|
return { segments, unmatched: names.filter(name => !matched.has(name.toLowerCase())) };
|
|
119
109
|
}
|
|
120
110
|
/**
|
|
121
|
-
* Whether one draft spells a handle as an authored `@mention`:
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* Client's single answer to "did the author call this name" — the composer
|
|
125
|
-
* prunes picked recipients with it and previews the draft's notifications with
|
|
126
|
-
* it.
|
|
111
|
+
* Whether one draft spells a handle as an authored `@mention`: the shared
|
|
112
|
+
* delivery scan, so the composer's recipient prune and will-notify preview agree
|
|
113
|
+
* with the Host on what the draft actually calls.
|
|
127
114
|
*/
|
|
128
115
|
export function containsMention(body, handle) {
|
|
129
|
-
return
|
|
116
|
+
return scanBodyHandles(body, [handle]).length > 0;
|
|
130
117
|
}
|
|
131
118
|
/** Whether one draft carries the `@all` marker the mention menu expands. */
|
|
132
119
|
export function containsAllMention(body) {
|
|
133
|
-
return
|
|
120
|
+
return hasAllMarker(body);
|
|
134
121
|
}
|
|
135
122
|
/**
|
|
136
123
|
* Every Member the mention menu's `@all` row stands for: the roster this
|
|
@@ -153,12 +140,15 @@ export function allMentionMembers(members) {
|
|
|
153
140
|
* rather than the prose the Host reads.
|
|
154
141
|
*/
|
|
155
142
|
export function mentionedMemberIds(body, members) {
|
|
156
|
-
if (
|
|
143
|
+
if (hasAllMarker(body))
|
|
157
144
|
return allMentionMembers(members).map(status => status.member.memberId);
|
|
158
|
-
|
|
145
|
+
const candidates = members
|
|
159
146
|
.filter(status => status.member.state !== 'inactive' && status.member.state !== 'archived')
|
|
160
|
-
.
|
|
161
|
-
|
|
147
|
+
.map(status => ({ memberId: status.member.memberId, handle: status.member.handle }));
|
|
148
|
+
// The draft's author is the Human reader, and a Message never mentions its
|
|
149
|
+
// own author: resolving with that sender drops a self-call exactly where
|
|
150
|
+
// delivery would.
|
|
151
|
+
return resolveBodyMentions(body, candidates, 'member:human').memberIds;
|
|
162
152
|
}
|
|
163
153
|
/** Canonical chip handles for one Message's structured mention refs. */
|
|
164
154
|
export function mentionNamesOf(mentions, handles) {
|
|
@@ -168,6 +158,10 @@ export function mentionNamesOf(mentions, handles) {
|
|
|
168
158
|
.map(memberId => memberId === 'member:human' ? 'human' : handles.get(memberId))
|
|
169
159
|
.filter((name) => name !== undefined);
|
|
170
160
|
}
|
|
161
|
+
/** Accessible label for one "who is on this work" stack: its owners' handles, comma-separated. */
|
|
162
|
+
export function claimersLabel(owners, t) {
|
|
163
|
+
return t('claimers', { names: owners.map(owner => `@${owner.name}`).join(', ') });
|
|
164
|
+
}
|
|
171
165
|
const MARKDOWN_BLOCK_CONSTRUCT = /(^|\n)[ \t]{0,3}(?:#{1,6}[ \t]|>[ \t]|[-*+][ \t]|\d+[.)][ \t])|^[ \t]*\|.+\|/m;
|
|
172
166
|
const MARKDOWN_INLINE_CONSTRUCT = /[`*_[\]!]|~~~|```/;
|
|
173
167
|
/**
|
|
@@ -19,7 +19,7 @@ Mentions are authored in the Message body, as `@Handle` (the `@` is required, ma
|
|
|
19
19
|
|
|
20
20
|
Every agent-facing render carries absolute event instants in the fixed Team coordination zone UTC+8 with an explicit offset (`2026-09-08T17:00:00+08:00`): `team_thread` fact lines and anchors stamp each fact with its committing operation's instant, `team_inbox` rows carry `newestOccurredAt`, `team_view` Thread rows carry `lastActivityAt`, notifications state `Occurred at:`, and committed mutations render `Committed at:` from the receipt. The same stored instant renders byte-identically on every reread path; only absolute timestamps are rendered, never relative text, and sequence and revision — not wall-clock time — remain the ordering authority.
|
|
21
21
|
|
|
22
|
-
Canonical results expose stable refs, optional Task status, Thread revision, Claim history, Attention, and unread facts as structured fields. The model-visible renders follow the action decision surface: the write token appears only on a fully drained `team_thread read` and a committed public mutation; browsing results (`team_view`, `team_inbox`, status/follow/unfollow, `history`, `team_claim list`) and all typed rejections render no revision and no token. Typed `unread_required` and `stale_revision` results keep the structured fields needed to reread and retry deliberately. Tool execution resolves the exact live `exec.agent`; arguments cannot select or impersonate the actor
|
|
22
|
+
Canonical results expose stable refs, optional Task status, Thread revision, Claim history, Attention, and unread facts as structured fields. The model-visible renders follow the action decision surface: the write token appears only on a fully drained `team_thread read` and a committed public mutation; browsing results (`team_view`, `team_inbox`, status/follow/unfollow, `history`, `team_claim list`) and all typed rejections render no revision and no token. Typed `unread_required` and `stale_revision` results keep the structured fields needed to reread and retry deliberately. Tool execution resolves the exact live `exec.agent`; arguments cannot select or impersonate the actor, and the optional `workspace` selector can only pick among that actor's participated Workspaces. Write request identity derives from sessionId plus tool callId. `context_rollover` and `context_checkpoint` conclude the Agent turn (a rollover must not be followed by old-generation work, and a checkpoint resolves at its containing turn's end); every other Team tool returns to the model loop.
|
|
23
23
|
|
|
24
24
|
The complete implemented protocol is documented in [`../../docs/team-collaboration.md`](../../docs/team-collaboration.md).
|
|
25
25
|
|
|
@@ -19,7 +19,7 @@ mention 在 Message 正文里撰写,形如 `@Handle`(`@` 必需、大小写
|
|
|
19
19
|
|
|
20
20
|
所有 agent-facing 渲染都在固定的 Team 协调时区 UTC+8 下携带带显式偏移的绝对事件时刻(`2026-09-08T17:00:00+08:00`):`team_thread` 的 fact 行与 anchor 为每条 fact 标注其提交 operation 的时刻,`team_inbox` 行携带 `newestOccurredAt`,`team_view` 的 Thread 行携带 `lastActivityAt`,通知给出 `Occurred at:`,已提交变更从 receipt 渲染 `Committed at:`。同一存储时刻在任何重读路径中渲染完全一致;只渲染绝对 timestamp,绝不出现相对时间文案,且 sequence 与 revision——而非 wall-clock 时间——仍是顺序 authority。
|
|
21
21
|
|
|
22
|
-
Canonical result 以结构化字段暴露稳定 refs、可选 Task status、Thread revision、Claim history、Attention 和未读 facts。模型可见的渲染遵循 action decision surface:写令牌只出现在未读清零的 `team_thread read` 与一次已提交的 public mutation 上;浏览类结果(`team_view`、`team_inbox`、status/follow/unfollow、`history`、`team_claim list`)与所有类型化拒绝都不渲染 revision 与令牌。类型化的 `unread_required` 与 `stale_revision` 结果保留重新读取和审慎重试所需的结构化字段。工具执行通过准确的 live `exec.agent` 解析 actor;参数不能选择或冒充 actor
|
|
22
|
+
Canonical result 以结构化字段暴露稳定 refs、可选 Task status、Thread revision、Claim history、Attention 和未读 facts。模型可见的渲染遵循 action decision surface:写令牌只出现在未读清零的 `team_thread read` 与一次已提交的 public mutation 上;浏览类结果(`team_view`、`team_inbox`、status/follow/unfollow、`history`、`team_claim list`)与所有类型化拒绝都不渲染 revision 与令牌。类型化的 `unread_required` 与 `stale_revision` 结果保留重新读取和审慎重试所需的结构化字段。工具执行通过准确的 live `exec.agent` 解析 actor;参数不能选择或冒充 actor,可选的 `workspace` 选择器只能在该 actor 已参与的 Workspace 中选择。写操作的 request identity 由 sessionId 与 tool callId 派生。`context_rollover` 与 `context_checkpoint` 结束 Agent turn;其余 Team tools 将结果返回模型循环,不主动结束 turn。
|
|
23
23
|
|
|
24
24
|
完整的已实现协议见 [`../../docs/team-collaboration.md`](../../docs/team-collaboration.md)。
|
|
25
25
|
|
|
@@ -18,3 +18,22 @@ export function member(agent) {
|
|
|
18
18
|
throw new Error('team tool requires an active Team Member');
|
|
19
19
|
return current;
|
|
20
20
|
}
|
|
21
|
+
/** A collaboration address, never a cwd or Session switch. */
|
|
22
|
+
export const workspaceParam = {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Target Workspace id exactly as listed in your participation context or team_view. Required when you participate in more than one Workspace; only a sole participation can be inferred. This does not change your Session cwd.',
|
|
25
|
+
};
|
|
26
|
+
/** Reject ambiguous routing before a tool reads facts or attempts a mutation. */
|
|
27
|
+
export function workspaceOf(args, agent) {
|
|
28
|
+
const workspaces = service(agent).workspacesForAgent(agent);
|
|
29
|
+
const choices = workspaces.map(workspace => workspace.workspaceId).join(', ');
|
|
30
|
+
if (args.workspace === undefined) {
|
|
31
|
+
if (workspaces.length !== 1)
|
|
32
|
+
throw new Error(`workspace is required when participating in multiple Workspaces. Choose: ${choices}`);
|
|
33
|
+
return workspaces[0].workspaceId;
|
|
34
|
+
}
|
|
35
|
+
const selected = workspaces.find(workspace => workspace.workspaceId === args.workspace);
|
|
36
|
+
if (selected === undefined)
|
|
37
|
+
throw new Error(`Not participating in Workspace '${args.workspace}'. Choose: ${choices}`);
|
|
38
|
+
return selected.workspaceId;
|
|
39
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AGENT_TEAM_HUMAN_HANDLE, AgentTeamDmDeliveryError, markAgentTeamPreset } from '@wowyuarm/dsh-agent-team/host';
|
|
2
2
|
import { formatTeamTimestamp } from '@wowyuarm/dsh-agent-team/time-format';
|
|
3
3
|
import { registerContextTools } from "./context-tools.js";
|
|
4
|
-
import {
|
|
4
|
+
import { service, workspaceOf, workspaceParam } from "./host-access.js";
|
|
5
5
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
6
6
|
export const name = 'wowyuarm-agent-team-tools';
|
|
7
7
|
export const inject = ['tools'];
|
|
@@ -114,13 +114,13 @@ function rejectionLines(noun, value) {
|
|
|
114
114
|
}
|
|
115
115
|
const teamInbox = defineTool({
|
|
116
116
|
name: 'team_inbox',
|
|
117
|
-
description: 'List your bounded Team Inbox for triage: unread Thread summaries with counts, without message bodies and without marking anything read. Read a selected Thread with team_thread read; the inbox itself authorizes no mutation.',
|
|
118
|
-
parameters: { limit: { type: 'number' } },
|
|
117
|
+
description: 'List your bounded Team Inbox across all joined Workspaces for triage: unread Thread summaries with counts, without message bodies and without marking anything read. Read a selected Thread with team_thread read; the inbox itself authorizes no mutation.',
|
|
118
|
+
parameters: { limit: { type: 'number' }, workspace: { type: 'string', description: 'Optional Workspace id filter. Omit to triage all your participations together.' } },
|
|
119
119
|
output: {
|
|
120
120
|
schema: { type: 'object', additionalProperties: false, properties: {
|
|
121
121
|
totalUnreadCount: { type: 'number', required: true }, totalDirectCount: { type: 'number', required: true },
|
|
122
122
|
items: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: {
|
|
123
|
-
threadRef: { type: 'string', required: true }, channelRef: { type: 'string', required: true },
|
|
123
|
+
workspaceId: { type: 'string', required: true }, threadRef: { type: 'string', required: true }, channelRef: { type: 'string', required: true },
|
|
124
124
|
taskRef: { type: 'string' }, status: { type: 'string' }, revision: { type: 'number', required: true }, unreadCount: { type: 'number', required: true }, directCount: { type: 'number', required: true },
|
|
125
125
|
taskNumber: { type: 'number' }, newestOccurredAt: { type: 'string' },
|
|
126
126
|
} } },
|
|
@@ -139,7 +139,7 @@ const teamInbox = defineTool({
|
|
|
139
139
|
const shown = value.items.reduce((sum, item) => sum + item.unreadCount, 0);
|
|
140
140
|
return [{ type: 'text', text: [
|
|
141
141
|
`Inbox — ${value.totalUnreadCount} unread update(s) total, ${value.totalDirectCount} direct, across ${value.items.length} Thread(s) shown${value.totalUnreadCount > shown ? `; ${value.totalUnreadCount - shown} more on Threads beyond this bounded list — call again with a larger limit.` : '.'}`,
|
|
142
|
-
...value.items.map(item => `${item.threadRef}${item.channelRef === undefined ? '' : ` · ${item.channelRef}`}${item.taskRef === undefined ? '' : ` · ${taskStanding(item)}`} · ${item.unreadCount} unread, ${item.directCount} direct${item.newestOccurredAt === undefined ? '' : ` · newest ${formatTeamTimestamp(item.newestOccurredAt)}`}`),
|
|
142
|
+
...value.items.map(item => `${item.workspaceId} · ${item.threadRef}${item.channelRef === undefined ? '' : ` · ${item.channelRef}`}${item.taskRef === undefined ? '' : ` · ${taskStanding(item)}`} · ${item.unreadCount} unread, ${item.directCount} direct${item.newestOccurredAt === undefined ? '' : ` · newest ${formatTeamTimestamp(item.newestOccurredAt)}`}`),
|
|
143
143
|
'Read a selected Thread with team_thread read. Listing changes no read state and supplies no write token.',
|
|
144
144
|
].join('\n') }];
|
|
145
145
|
},
|
|
@@ -148,16 +148,13 @@ const teamInbox = defineTool({
|
|
|
148
148
|
const agent = exec.agent;
|
|
149
149
|
if (agent === undefined)
|
|
150
150
|
throw new Error('team_inbox requires an Agent session');
|
|
151
|
-
const current = member(agent);
|
|
152
151
|
const host = service(agent);
|
|
153
|
-
const inbox = host.inboxForAgent(agent, { workspaceId:
|
|
154
|
-
const taskNumbers = new Map(host.viewForAgent(agent, { workspaceId: current.workspaceId, topLevelOnly: true, includeActivities: false, direction: 'before' })
|
|
155
|
-
.taskNumbers.map(entry => [entry.taskRef, entry.taskNumber]));
|
|
152
|
+
const inbox = host.inboxForAgent(agent, { ...(args.workspace === undefined ? {} : { workspaceId: workspaceOf(args, agent) }), ...(args.limit === undefined ? {} : { limit: args.limit }) });
|
|
156
153
|
return {
|
|
157
154
|
totalUnreadCount: inbox.totalUnreadCount, totalDirectCount: inbox.totalDirectCount,
|
|
158
155
|
items: inbox.items.map(item => {
|
|
159
|
-
const taskNumber = item.
|
|
160
|
-
return { threadRef: item.thread.threadRef, channelRef: item.channelRef,
|
|
156
|
+
const taskNumber = item.taskNumber;
|
|
157
|
+
return { workspaceId: item.workspaceId, threadRef: item.thread.threadRef, channelRef: item.channelRef,
|
|
161
158
|
...(item.task === undefined ? {} : { taskRef: item.task.taskRef, status: item.task.status }),
|
|
162
159
|
revision: item.thread.revision, unreadCount: item.unreadCount, directCount: item.directCount, newestOccurredAt: item.newestOccurredAt,
|
|
163
160
|
...(taskNumber === undefined ? {} : { taskNumber }) };
|
|
@@ -172,10 +169,11 @@ const teamThread = defineTool({
|
|
|
172
169
|
action: { type: 'string', required: true, enum: ['status', 'follow', 'unfollow', 'read', 'history'] },
|
|
173
170
|
threadRef: { type: 'string', description: "Full branded Thread ref exactly as returned by Team tools, including the 'thread:' prefix. An unambiguous abbreviation of the first 6+ UUID hex characters also resolves." },
|
|
174
171
|
taskRef: { type: 'string', description: "Optional Task ref alias for released clients. Prefer threadRef; if both are given they must identify the same Thread." },
|
|
175
|
-
beforeSequence: { type: 'number' }, limit: { type: 'number' },
|
|
172
|
+
beforeSequence: { type: 'number' }, limit: { type: 'number' }, workspace: workspaceParam,
|
|
176
173
|
},
|
|
177
174
|
output: {
|
|
178
175
|
schema: { type: 'object', additionalProperties: false, properties: {
|
|
176
|
+
workspaceId: { type: 'string', required: true }, channelRef: { type: 'string', required: true },
|
|
179
177
|
kind: { type: 'string', required: true }, threadRef: { type: 'string', required: true }, taskRef: { type: 'string' },
|
|
180
178
|
revision: { type: 'number', required: true }, status: { type: 'string' }, resolution: { type: 'string' }, taskNumber: { type: 'number' },
|
|
181
179
|
following: { type: 'boolean', required: true }, readThroughSequence: { type: 'number' }, remainingUnreadCount: { type: 'number' }, earlierFactCount: { type: 'number' }, cursor: { type: 'number' }, hasMore: { type: 'boolean' },
|
|
@@ -201,15 +199,16 @@ const teamThread = defineTool({
|
|
|
201
199
|
// current collision surface or any write token.
|
|
202
200
|
render: (args, value) => {
|
|
203
201
|
const standing = taskStanding(value);
|
|
202
|
+
const source = `Workspace: ${value.workspaceId} · Channel: ${value.channelRef}`;
|
|
204
203
|
if (value.kind === 'status') {
|
|
205
|
-
return [{ type: 'text', text:
|
|
204
|
+
return [{ type: 'text', text: `${source}\nAttention status — ${value.following ? 'following' : 'not following'} ${value.threadRef}${standing === '' ? '' : ` · ${standing}`}.` }];
|
|
206
205
|
}
|
|
207
206
|
if (value.kind === 'follow' || value.kind === 'unfollow') {
|
|
208
|
-
return [{ type: 'text', text:
|
|
207
|
+
return [{ type: 'text', text: `${source}\nAttention changed — ${value.following ? 'now following' : 'no longer following'} ${value.threadRef}${standing === '' ? '' : ` · ${standing}`}.` }];
|
|
209
208
|
}
|
|
210
209
|
if (value.kind === 'history') {
|
|
211
210
|
const facts = value.facts;
|
|
212
|
-
const lines = [`History for ${value.threadRef}${standing === '' ? '' : ` · ${standing}`}`];
|
|
211
|
+
const lines = [source, `History for ${value.threadRef}${standing === '' ? '' : ` · ${standing}`}`];
|
|
213
212
|
// First page (no beforeSequence supplied) orients on the full
|
|
214
213
|
// anchor; a continuation orients on the shared bounded subject. An
|
|
215
214
|
// anchor already selected as a fact never repeats.
|
|
@@ -235,6 +234,7 @@ const teamThread = defineTool({
|
|
|
235
234
|
// A read with nothing unread writes no durable operation, so the opening
|
|
236
235
|
// line claims a commit only when unread facts were actually acknowledged.
|
|
237
236
|
const lines = [
|
|
237
|
+
source,
|
|
238
238
|
acknowledged === 0
|
|
239
239
|
? `Read — no unread updates on ${value.threadRef}; nothing remains.`
|
|
240
240
|
: `Read committed — acknowledged ${acknowledged} unread update(s) on ${value.threadRef}; ${remaining} remain.`,
|
|
@@ -281,15 +281,14 @@ const teamThread = defineTool({
|
|
|
281
281
|
const agent = exec.agent;
|
|
282
282
|
if (agent === undefined)
|
|
283
283
|
throw new Error('team_thread requires an Agent session');
|
|
284
|
-
const current = member(agent);
|
|
285
284
|
const host = service(agent);
|
|
286
285
|
if (args.threadRef === undefined && args.taskRef === undefined)
|
|
287
286
|
throw new Error('team_thread requires threadRef');
|
|
288
|
-
const base = { workspaceId:
|
|
287
|
+
const base = { workspaceId: workspaceOf(args, agent), ...(args.threadRef === undefined ? {} : { threadRef: args.threadRef }), ...(args.taskRef === undefined ? {} : { taskRef: args.taskRef }) };
|
|
289
288
|
const taskNumberOf = (task) => {
|
|
290
289
|
if (task === undefined)
|
|
291
290
|
return {};
|
|
292
|
-
const resolved = host.resolveTaskRefs({ workspaceId:
|
|
291
|
+
const resolved = host.resolveTaskRefs({ workspaceId: base.workspaceId, taskRefs: [task.taskRef] }).resolved[0];
|
|
293
292
|
return resolved === undefined ? {} : { taskNumber: resolved.taskNumber };
|
|
294
293
|
};
|
|
295
294
|
if (args.action === 'status') {
|
|
@@ -297,26 +296,26 @@ const teamThread = defineTool({
|
|
|
297
296
|
throw new Error('status does not accept history arguments');
|
|
298
297
|
const status = host.attentionStatusForAgent(agent, base);
|
|
299
298
|
const snapshot = host.threadHistoryForAgent(agent, { ...base, beforeSequence: 1, limit: 1 });
|
|
300
|
-
return threadResult('status', snapshot, status.attention, [], taskNumberOf(snapshot.task));
|
|
299
|
+
return threadResult('status', base.workspaceId, snapshot, status.attention, [], taskNumberOf(snapshot.task));
|
|
301
300
|
}
|
|
302
301
|
if (args.action === 'follow' || args.action === 'unfollow') {
|
|
303
302
|
if (args.beforeSequence !== undefined || args.limit !== undefined)
|
|
304
303
|
throw new Error(`${args.action} does not accept history arguments`);
|
|
305
304
|
const result = await host.changeAttentionForAgent(agent, { requestId: requestId(agent.id, exec.callId), ...base, action: args.action });
|
|
306
305
|
const snapshot = host.threadHistoryForAgent(agent, { ...base, beforeSequence: 1, limit: 1 });
|
|
307
|
-
return threadResult(args.action, snapshot, result.attention, [], taskNumberOf(snapshot.task));
|
|
306
|
+
return threadResult(args.action, base.workspaceId, snapshot, result.attention, [], taskNumberOf(snapshot.task));
|
|
308
307
|
}
|
|
309
308
|
if (args.action === 'history') {
|
|
310
309
|
const history = host.threadHistoryForAgent(agent, { ...base, ...(args.beforeSequence === undefined ? {} : { beforeSequence: args.beforeSequence }), ...(args.limit === undefined ? {} : { limit: args.limit }) });
|
|
311
310
|
const status = host.attentionStatusForAgent(agent, base);
|
|
312
|
-
return threadResult('history', history, status.attention, history.facts.map(fact => fact.kind === 'message'
|
|
311
|
+
return threadResult('history', base.workspaceId, history, status.attention, history.facts.map(fact => fact.kind === 'message'
|
|
313
312
|
? { sequence: fact.sequence, kind: 'message', body: fact.message.body, sender: fact.message.sender, mentions: [...fact.mentions], occurredAt: fact.occurredAt }
|
|
314
313
|
: activityFactView(fact.sequence, fact.activity, undefined, fact.occurredAt)), { cursor: history.cursor, hasMore: history.hasMore, ...taskNumberOf(history.task) });
|
|
315
314
|
}
|
|
316
315
|
if (args.beforeSequence !== undefined || args.limit !== undefined)
|
|
317
316
|
throw new Error('read does not accept history arguments');
|
|
318
317
|
const read = await host.readThreadForAgent(agent, { requestId: requestId(agent.id, exec.callId), ...base });
|
|
319
|
-
return threadResult('read', read, read.attention, read.facts.map(entry => entry.fact.kind === 'message'
|
|
318
|
+
return threadResult('read', base.workspaceId, read, read.attention, read.facts.map(entry => entry.fact.kind === 'message'
|
|
320
319
|
? { sequence: entry.fact.sequence, kind: 'message', body: entry.fact.message.body, sender: entry.fact.message.sender, mentions: [...entry.fact.mentions], unread: entry.unread, direct: entry.direct, occurredAt: entry.fact.occurredAt }
|
|
321
320
|
: activityFactView(entry.fact.sequence, entry.fact.activity, { unread: entry.unread, direct: entry.direct }, entry.fact.occurredAt)), { readThroughSequence: read.readThroughSequence, remainingUnreadCount: read.remainingUnreadCount, ...(read.earlierFactCount === undefined ? {} : { earlierFactCount: read.earlierFactCount }), ...(read.contextAdvice === undefined ? {} : { contextAdvice: adviceView(read.contextAdvice) }), ...taskNumberOf(read.task) });
|
|
322
321
|
},
|
|
@@ -332,8 +331,9 @@ function factLine(fact) {
|
|
|
332
331
|
function claimLine(claim) {
|
|
333
332
|
return `${claim.claimRef} — ${claim.owner}: ${claim.direction}`;
|
|
334
333
|
}
|
|
335
|
-
function threadResult(kind, snapshot, attention, facts, extra = {}) {
|
|
334
|
+
function threadResult(kind, workspaceId, snapshot, attention, facts, extra = {}) {
|
|
336
335
|
return {
|
|
336
|
+
workspaceId, channelRef: snapshot.anchor.channelRef,
|
|
337
337
|
kind, threadRef: snapshot.thread.threadRef, revision: snapshot.thread.revision,
|
|
338
338
|
...(snapshot.task === undefined ? {} : { taskRef: snapshot.task.taskRef, status: snapshot.task.status, resolution: snapshot.task.resolution }),
|
|
339
339
|
...(extra.taskNumber === undefined ? {} : { taskNumber: extra.taskNumber }),
|
|
@@ -365,6 +365,7 @@ const teamMessage = markAgentTeamPreset(defineTool({
|
|
|
365
365
|
asTask: { type: 'boolean', description: 'When true, start creates a Task with the Thread. Default false creates a taskless Thread.' },
|
|
366
366
|
body: { type: 'string', required: true, description: "Markdown body. Lead with the conclusion or state. Mention the Human only when they must know or decide; when a decision is owed, say plainly what needs deciding and what happens by default if nobody answers — no fixed template. Keep it the shortest useful message for the recipients; mechanical detail follows below. Cite Team refs exactly as returned, as bare text with one colon (e.g. task:0f0a…) — never a double colon, never inside backticks or quotes. Unambiguous UUID abbreviations (first 6+ hex chars) also resolve. Mention a Member by writing `@Handle` (`@` required, case-insensitive) in the prose: that is what delivers the Message to them and renders the mention chip, and `@all` reaches the whole Channel." }, baseRevision: { type: 'number', description: "The next-write token from your latest fully drained team_thread read (or your own last committed mutation) on this Thread. Copy the explicitly rendered value verbatim; never increment, derive, compare, or cite it — it is an opaque concurrency token, not a fact about the Thread." },
|
|
367
367
|
attachments: { type: 'array', items: { type: 'string' }, description: 'Absolute file paths to share, e.g. screenshots or generated artifacts; images render as thumbnails for recipients. The Host validates each path and copies the file into the attachment cache, and members also receive one cached path per attachment; if any path fails validation the whole send is rejected.' },
|
|
368
|
+
workspace: workspaceParam,
|
|
368
369
|
},
|
|
369
370
|
output: {
|
|
370
371
|
schema: { type: 'object', additionalProperties: false, properties: {
|
|
@@ -408,7 +409,6 @@ const teamMessage = markAgentTeamPreset(defineTool({
|
|
|
408
409
|
const agent = exec.agent;
|
|
409
410
|
if (agent === undefined)
|
|
410
411
|
throw new Error('team_message requires an Agent session');
|
|
411
|
-
const current = member(agent);
|
|
412
412
|
const host = service(agent);
|
|
413
413
|
const rawPaths = args.attachments;
|
|
414
414
|
const attachmentPaths = Array.isArray(rawPaths) ? rawPaths.filter((entry) => typeof entry === 'string' && entry.trim() !== '') : undefined;
|
|
@@ -416,7 +416,7 @@ const teamMessage = markAgentTeamPreset(defineTool({
|
|
|
416
416
|
if (args.action === 'start') {
|
|
417
417
|
if (args.channelRef === undefined || args.taskRef !== undefined || args.threadRef !== undefined || args.baseRevision !== undefined)
|
|
418
418
|
throw new Error('start requires channelRef and does not accept threadRef, taskRef, or baseRevision');
|
|
419
|
-
const result = await host.sendMessageForAgent(agent, { requestId: requestId(agent.id, exec.callId), workspaceId:
|
|
419
|
+
const result = await host.sendMessageForAgent(agent, { requestId: requestId(agent.id, exec.callId), workspaceId: workspaceOf(args, agent),
|
|
420
420
|
channelRef: args.channelRef, body: args.body, asTask: args.asTask === true, ...paths });
|
|
421
421
|
return messageOutcome(result, 'start');
|
|
422
422
|
}
|
|
@@ -426,7 +426,7 @@ const teamMessage = markAgentTeamPreset(defineTool({
|
|
|
426
426
|
throw new Error('dm requires memberRef and body only; it does not accept channelRef, threadRef, taskRef, baseRevision, asTask, or attachments');
|
|
427
427
|
}
|
|
428
428
|
try {
|
|
429
|
-
const result = await host.dmForAgent(agent, { requestId: requestId(agent.id, exec.callId), workspaceId:
|
|
429
|
+
const result = await host.dmForAgent(agent, { requestId: requestId(agent.id, exec.callId), workspaceId: workspaceOf(args, agent),
|
|
430
430
|
recipientMemberId: args.memberRef, body: args.body });
|
|
431
431
|
return { kind: 'dm-sent', recipientMemberId: result.recipient.memberId, recipientHandle: result.recipient.handle, delivered: true, occurredAt: result.receipt.occurredAt };
|
|
432
432
|
}
|
|
@@ -441,7 +441,7 @@ const teamMessage = markAgentTeamPreset(defineTool({
|
|
|
441
441
|
if ((args.threadRef === undefined && args.taskRef === undefined) || args.channelRef !== undefined || args.asTask !== undefined || typeof baseRevision !== 'number' || !Number.isSafeInteger(baseRevision) || baseRevision < 1) {
|
|
442
442
|
throw new Error('reply requires threadRef and a positive baseRevision; drain the Thread with team_thread read and copy the token it renders, or reuse the one your own last committed mutation rendered');
|
|
443
443
|
}
|
|
444
|
-
const result = await host.replyForAgent(agent, { requestId: requestId(agent.id, exec.callId), workspaceId:
|
|
444
|
+
const result = await host.replyForAgent(agent, { requestId: requestId(agent.id, exec.callId), workspaceId: workspaceOf(args, agent),
|
|
445
445
|
...(args.threadRef === undefined ? {} : { threadRef: args.threadRef }),
|
|
446
446
|
...(args.taskRef === undefined ? {} : { taskRef: args.taskRef }),
|
|
447
447
|
body: args.body, baseRevision, ...paths });
|
|
@@ -477,6 +477,7 @@ const teamClaim = defineTool({
|
|
|
477
477
|
taskRef: { type: 'string', required: true, description: "Full branded Task ref exactly as returned by Team tools, including the 'task:' prefix. An unambiguous abbreviation of the first 6+ UUID hex characters also resolves." },
|
|
478
478
|
baseRevision: { type: 'number', description: "The next-write token from your latest fully drained team_thread read (or your own last committed mutation) on this Task's Thread. Copy the explicitly rendered value verbatim; never increment, derive, compare, or cite it — it is an opaque concurrency token, not a fact about the Task." }, direction: { type: 'string' },
|
|
479
479
|
claimRef: { type: 'string', description: "Full branded Claim ref exactly as returned by team_claim, including the 'claim:' prefix. An unambiguous abbreviation of the first 6+ UUID hex characters also resolves." },
|
|
480
|
+
workspace: workspaceParam,
|
|
480
481
|
},
|
|
481
482
|
output: {
|
|
482
483
|
schema: { type: 'object', additionalProperties: false, properties: {
|
|
@@ -518,9 +519,8 @@ const teamClaim = defineTool({
|
|
|
518
519
|
const agent = exec.agent;
|
|
519
520
|
if (agent === undefined)
|
|
520
521
|
throw new Error('team_claim requires an Agent session');
|
|
521
|
-
const current = member(agent);
|
|
522
522
|
const host = service(agent);
|
|
523
|
-
const base = { workspaceId:
|
|
523
|
+
const base = { workspaceId: workspaceOf(args, agent), taskRef: args.taskRef };
|
|
524
524
|
if (args.action === 'list') {
|
|
525
525
|
if (args.baseRevision !== undefined || args.direction !== undefined || args.claimRef !== undefined)
|
|
526
526
|
throw new Error('list accepts only taskRef');
|
|
@@ -557,11 +557,13 @@ const teamView = defineTool({
|
|
|
557
557
|
description: 'Discover your authorized Team addresses: current Channels, a newest-first page of top-level Threads (each with its bounded anchor subject; Task standing inline on taskful rows), and current Members. This is an address book, not a work queue — unread work lives in team_inbox, and a Thread is read with team_thread read. The cursor pages Thread rows only.',
|
|
558
558
|
parameters: {
|
|
559
559
|
channelRef: { type: 'string', description: "Full branded Channel ref exactly as returned by Team tools, including the 'channel:' prefix. An unambiguous abbreviation of the first 6+ UUID hex characters also resolves." },
|
|
560
|
-
limit: { type: 'number' }, cursor: { type: 'number' },
|
|
560
|
+
limit: { type: 'number' }, cursor: { type: 'number' }, workspace: workspaceParam,
|
|
561
561
|
},
|
|
562
562
|
output: {
|
|
563
563
|
schema: { type: 'object', additionalProperties: false, properties: {
|
|
564
564
|
channels: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: { channelRef: { type: 'string', required: true }, name: { type: 'string', required: true } } } },
|
|
565
|
+
workspaceId: { type: 'string', required: true },
|
|
566
|
+
workspaces: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: { workspaceId: { type: 'string', required: true }, title: { type: 'string' }, default: { type: 'boolean', required: true } } } },
|
|
565
567
|
members: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: {
|
|
566
568
|
memberId: { type: 'string', required: true }, kind: { type: 'string', required: true }, handle: { type: 'string', required: true }, description: { type: 'string', required: true }, presence: { type: 'string', required: true },
|
|
567
569
|
} } },
|
|
@@ -581,7 +583,7 @@ const teamView = defineTool({
|
|
|
581
583
|
// which is reading, never writing from a directory snapshot.
|
|
582
584
|
render: (_args, value) => {
|
|
583
585
|
const continuation = value.page === 'threads';
|
|
584
|
-
const lines = [];
|
|
586
|
+
const lines = [`Workspace: ${value.workspaceId}`];
|
|
585
587
|
if (!continuation) {
|
|
586
588
|
lines.push('Team directory', '', 'Channels — current');
|
|
587
589
|
if (value.channels.length === 0)
|
|
@@ -596,6 +598,10 @@ const teamView = defineTool({
|
|
|
596
598
|
else
|
|
597
599
|
lines.push(...value.threads.map(thread => `${thread.threadRef} · ${thread.channelRef}${thread.taskRef === undefined ? ' · taskless' : ` · ${taskStanding(thread)}`} — ${thread.subject}${thread.lastActivityAt === undefined ? '' : ` · last activity ${formatTeamTimestamp(thread.lastActivityAt)}`}`));
|
|
598
600
|
lines.push(`Thread cursor ${value.cursor}; hasMore=${value.hasMore ? 'true' : 'false'}${value.hasMore ? ' — older Thread anchors exist; page again with this cursor.' : ' — no older Threads remain.'}`);
|
|
601
|
+
if (!continuation && value.workspaces.length > 1) {
|
|
602
|
+
lines.push('', 'Workspaces — you participate in');
|
|
603
|
+
lines.push(...value.workspaces.map(workspace => `${workspace.workspaceId}${workspace.title === undefined ? '' : ` · ${workspace.title}`}${workspace.default ? ' · default — your Session and cwd live here' : ' — address it with the workspace argument; file work needs absolute paths'}`));
|
|
604
|
+
}
|
|
599
605
|
if (!continuation) {
|
|
600
606
|
lines.push('', 'Members — current');
|
|
601
607
|
if (value.members.length === 0)
|
|
@@ -610,11 +616,14 @@ const teamView = defineTool({
|
|
|
610
616
|
const agent = exec.agent;
|
|
611
617
|
if (agent === undefined)
|
|
612
618
|
throw new Error('team_view requires an Agent session');
|
|
613
|
-
const current = member(agent);
|
|
614
619
|
const host = service(agent);
|
|
615
|
-
const
|
|
620
|
+
const workspaceId = workspaceOf(args, agent);
|
|
621
|
+
const view = host.viewForAgent(agent, { workspaceId, ...(args.channelRef === undefined ? {} : { channelRef: args.channelRef }), ...(args.limit === undefined ? {} : { limit: args.limit }), ...(args.cursor === undefined ? {} : { cursor: args.cursor }), topLevelOnly: true, includeActivities: false, direction: 'before' });
|
|
616
622
|
const visibleMemberIds = new Set(view.members.map(membership => membership.memberId));
|
|
617
623
|
return {
|
|
624
|
+
workspaceId,
|
|
625
|
+
workspaces: view.workspaces.map(participation => ({ workspaceId: participation.workspaceId,
|
|
626
|
+
...(participation.title === undefined ? {} : { title: participation.title }), default: participation.default })),
|
|
618
627
|
channels: view.channels.map(channel => ({ channelRef: channel.channelRef, name: channel.name })),
|
|
619
628
|
members: [
|
|
620
629
|
{ memberId: view.humanMemberId, kind: 'human', handle: AGENT_TEAM_HUMAN_HANDLE, description: 'Human Team Member', presence: 'available' },
|
|
@@ -5,10 +5,20 @@
|
|
|
5
5
|
* rejection rather than a silent no-op. Both messages are user-facing text.
|
|
6
6
|
*/
|
|
7
7
|
import AgentTeam from '@wowyuarm/dsh-agent-team/host';
|
|
8
|
+
import type { WorkspaceId } from '@deepseek-ai/dsh-api-workspace-controller/client';
|
|
8
9
|
/** The Agent shape every Team tool receives. */
|
|
9
10
|
export type TeamToolAgent = NonNullable<Parameters<AgentTeam['memberForAgent']>[0]>;
|
|
10
11
|
/** Resolve the Team Host service, or reject the tool call. */
|
|
11
12
|
export declare function service(agent: TeamToolAgent): AgentTeam;
|
|
12
13
|
/** Resolve the calling Team Member, or reject the tool call. */
|
|
13
14
|
export declare function member(agent: TeamToolAgent): import("@wowyuarm/dsh-agent-team/types").AgentTeamAgentMember;
|
|
15
|
+
/** A collaboration address, never a cwd or Session switch. */
|
|
16
|
+
export declare const workspaceParam: {
|
|
17
|
+
type: "string";
|
|
18
|
+
description: string;
|
|
19
|
+
};
|
|
20
|
+
/** Reject ambiguous routing before a tool reads facts or attempts a mutation. */
|
|
21
|
+
export declare function workspaceOf(args: {
|
|
22
|
+
workspace?: string | undefined;
|
|
23
|
+
}, agent: TeamToolAgent): WorkspaceId;
|
|
14
24
|
//# sourceMappingURL=host-access.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-access.d.ts","sourceRoot":"","sources":["../../src/host-access.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,SAAS,MAAM,+BAA+B,CAAA;
|
|
1
|
+
{"version":3,"file":"host-access.d.ts","sourceRoot":"","sources":["../../src/host-access.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,SAAS,MAAM,+BAA+B,CAAA;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,UAAU,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAEnF,8DAA8D;AAC9D,wBAAgB,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAIvD;AAED,gEAAgE;AAChE,wBAAgB,MAAM,CAAC,KAAK,EAAE,aAAa,iEAI1C;AAED,8DAA8D;AAC9D,eAAO,MAAM,cAAc;;;CAG1B,CAAA;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,EAAE,KAAK,EAAE,aAAa,GAAG,WAAW,CAUvG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAelD,eAAO,MAAM,IAAI,8BAA8B,CAAA;AAC/C,eAAO,MAAM,MAAM,UAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAelD,eAAO,MAAM,IAAI,8BAA8B,CAAA;AAC/C,eAAO,MAAM,MAAM,UAAY,CAAA;AA8qB/B,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAOxC"}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/** The change scopes whose wakes one surface answers with a shared refresh round. */
|
|
2
|
-
export type SupplementalScope = 'workspace' | 'presence';
|
|
3
|
-
/** One change wake, carrying a version from the scope that issued it. */
|
|
4
|
-
export interface ScopeWake {
|
|
5
|
-
readonly scope: SupplementalScope;
|
|
6
|
-
readonly version: number;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Per-scope coverage of the change versions one surface has already answered.
|
|
10
|
-
*
|
|
11
|
-
* A version is only comparable inside the scope that issued it: the Host
|
|
12
|
-
* answers a workspace scope from the durable ledger position and a presence
|
|
13
|
-
* scope from its process-local wake epoch, which starts low and climbs on every
|
|
14
|
-
* Agent transition. One high-water mark shared by both would therefore let a
|
|
15
|
-
* presence epoch swallow a later, smaller ledger sequence and park the surface
|
|
16
|
-
* on stale data, so coverage and pending versions are kept per scope.
|
|
17
|
-
*
|
|
18
|
-
* One round answers one version per scope. The Host delivers one event to every
|
|
19
|
-
* matching scope, so the first version a round sees in a scope it has not
|
|
20
|
-
* answered yet is that event reaching a second scope, and joins the round
|
|
21
|
-
* already fetching instead of earning a second identical read. A newer version
|
|
22
|
-
* in a scope the round has answered arrived after the round issued its fetch,
|
|
23
|
-
* so it owes one trailing round.
|
|
24
|
-
*/
|
|
25
|
-
export declare class ScopeCoverage {
|
|
26
|
-
private readonly covered;
|
|
27
|
-
private readonly pending;
|
|
28
|
-
/** The scopes the open round has an answer for. */
|
|
29
|
-
private readonly answered;
|
|
30
|
-
private fetching;
|
|
31
|
-
/** Folds one wake in and answers whether it opens a round. */
|
|
32
|
-
wake(scope: SupplementalScope, version: number): boolean;
|
|
33
|
-
/** Opens a round's fetch window; versions observed from here on are pending. */
|
|
34
|
-
beginRound(): void;
|
|
35
|
-
/** Closes a round's fetch window and answers whether a trailing round is owed. */
|
|
36
|
-
finishRound(): boolean;
|
|
37
|
-
/** Drops every observation: a new owner starts with nothing covered. */
|
|
38
|
-
reset(): void;
|
|
39
|
-
private coveredOf;
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=scope-coverage.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scope-coverage.d.ts","sourceRoot":"","sources":["../../../src/client/scope-coverage.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,UAAU,CAAA;AAExD,yEAAyE;AACzE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAA;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+B;IACxD,OAAO,CAAC,QAAQ,CAAQ;IAExB,8DAA8D;IAC9D,IAAI,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAWxD,gFAAgF;IAChF,UAAU,IAAI,IAAI;IAKlB,kFAAkF;IAClF,WAAW,IAAI,OAAO;IAYtB,wEAAwE;IACxE,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,SAAS;CAGlB"}
|