@wowyuarm/dsh-agent-team 0.1.7 → 0.1.9
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/package.json +2 -1
- package/packages/agent-team/README.md +5 -3
- package/packages/agent-team/README.zh.md +5 -3
- package/packages/agent-team/core-skills/member-skill-manager/SKILL.md +2 -0
- package/packages/agent-team/lib/attachments.js +14 -4
- package/packages/agent-team/lib/context-management.js +379 -0
- package/packages/agent-team/lib/context-projection.js +574 -0
- package/packages/agent-team/lib/context-source.js +72 -0
- package/packages/agent-team/lib/index.js +1258 -54
- package/packages/agent-team/lib/ledger.js +357 -26
- package/packages/agent-team/lib/member-context.js +7 -3
- package/packages/agent-team/lib/member-runtime.js +159 -4
- package/packages/agent-team/lib/pressure-policy.js +200 -0
- package/packages/agent-team/lib/progress-nudge.js +320 -0
- package/packages/agent-team/lib/spec.js +18 -0
- package/packages/agent-team/lib/typert.host.js +124 -42
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +39 -25
- package/packages/agent-team/lib/types/attachments.d.ts +1 -1
- package/packages/agent-team/lib/types/attachments.d.ts.map +1 -1
- package/packages/agent-team/lib/types/context-management.d.ts +142 -0
- package/packages/agent-team/lib/types/context-management.d.ts.map +1 -0
- package/packages/agent-team/lib/types/context-projection.d.ts +219 -0
- package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -0
- package/packages/agent-team/lib/types/context-source.d.ts +83 -0
- package/packages/agent-team/lib/types/context-source.d.ts.map +1 -0
- package/packages/agent-team/lib/types/index.d.ts +283 -2
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +120 -7
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-runtime.d.ts +30 -2
- package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -1
- package/packages/agent-team/lib/types/pressure-policy.d.ts +106 -0
- package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -0
- package/packages/agent-team/lib/types/progress-nudge.d.ts +136 -0
- package/packages/agent-team/lib/types/progress-nudge.d.ts.map +1 -0
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/entities.d.ts +32 -1
- package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/operations.d.ts +38 -3
- package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/requests-results.d.ts +54 -1
- package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +10 -2
- package/packages/client-agent-team/lib/client.js +283 -398
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +1 -2
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +61 -41
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +3 -7
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +3 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +37 -27
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.js +3 -2
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +5 -0
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +2 -2
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +4 -6
- package/packages/client-agent-team/lib/types/client/TeamStateDot.d.ts +14 -0
- package/packages/client-agent-team/lib/types/client/TeamStateDot.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamStateDot.js +15 -0
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +1 -0
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +41 -16
- 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 +36 -78
- package/packages/client-agent-team/lib/types/client/locales.d.ts +0 -16
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +0 -16
- package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts +1 -2
- package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/slots.d.ts +8 -1
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts +7 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.js +25 -3
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +11 -10
- 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 +14 -11
- package/packages/tool-agent-team/README.md +8 -5
- package/packages/tool-agent-team/README.zh.md +8 -5
- package/packages/tool-agent-team/lib/context-tools.js +179 -0
- package/packages/tool-agent-team/lib/index.js +144 -25
- package/packages/tool-agent-team/lib/types/context-tools.d.ts +16 -0
- package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -0
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/auto-compaction.js +0 -201
- package/packages/agent-team/lib/types/auto-compaction.d.ts +0 -47
- package/packages/agent-team/lib/types/auto-compaction.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts +0 -12
- package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberDock.js +0 -13
- package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +0 -20
- package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/member-session-input.js +0 -110
|
@@ -1,7 +1,44 @@
|
|
|
1
1
|
import { AgentTeamDmDeliveryError, markAgentTeamPreset } from '@wowyuarm/dsh-agent-team/host';
|
|
2
|
+
import { registerContextTools } from "./context-tools.js";
|
|
2
3
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
3
4
|
export const name = 'wowyuarm-agent-team-tools';
|
|
4
5
|
export const inject = ['tools'];
|
|
6
|
+
function activityFactView(sequence, activity, markers) {
|
|
7
|
+
return {
|
|
8
|
+
sequence, kind: 'activity', activity: activity.kind, actor: activity.actor, taskRef: activity.taskRef,
|
|
9
|
+
...(activity.claimRef === undefined ? {} : { claimRef: activity.claimRef }),
|
|
10
|
+
...(activity.claimRefs === undefined || activity.claimRefs.length === 0 ? {} : { claimRefs: [...activity.claimRefs] }),
|
|
11
|
+
...(activity.completedClaimRefs === undefined || activity.completedClaimRefs.length === 0 ? {} : { completedClaimRefs: [...activity.completedClaimRefs] }),
|
|
12
|
+
...(activity.acceptedClaimRefs === undefined || activity.acceptedClaimRefs.length === 0 ? {} : { acceptedClaimRefs: [...activity.acceptedClaimRefs] }),
|
|
13
|
+
...(activity.releasedClaimRefs === undefined || activity.releasedClaimRefs.length === 0 ? {} : { releasedClaimRefs: [...activity.releasedClaimRefs] }),
|
|
14
|
+
...(markers === undefined ? {} : { unread: markers.unread, direct: markers.direct }),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function adviceView(advice) {
|
|
18
|
+
return {
|
|
19
|
+
...(advice.usageTokens === undefined ? {} : { usageTokens: advice.usageTokens }),
|
|
20
|
+
...(advice.taskBoundaryThreshold === undefined ? {} : { taskBoundaryThreshold: advice.taskBoundaryThreshold }),
|
|
21
|
+
...(advice.handoffAt === undefined ? {} : { handoffAt: advice.handoffAt }),
|
|
22
|
+
...(advice.hardLimit === undefined ? {} : { hardLimit: advice.hardLimit }),
|
|
23
|
+
action: advice.action, guidance: advice.guidance,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Render one structured activity fact as a self-describing decision-surface line. */
|
|
27
|
+
function activityLine(fact) {
|
|
28
|
+
const segments = [`${fact.sequence}`, fact.actor, fact.activity];
|
|
29
|
+
segments.push(`Task ${fact.taskRef}`);
|
|
30
|
+
if (fact.claimRef !== undefined)
|
|
31
|
+
segments.push(`Claim ${fact.claimRef}`);
|
|
32
|
+
if (fact.claimRefs !== undefined)
|
|
33
|
+
segments.push(`claims released ${fact.claimRefs.join(', ')}`);
|
|
34
|
+
if (fact.completedClaimRefs !== undefined)
|
|
35
|
+
segments.push(`completed claims ${fact.completedClaimRefs.join(', ')}`);
|
|
36
|
+
if (fact.acceptedClaimRefs !== undefined)
|
|
37
|
+
segments.push(`accepted claims ${fact.acceptedClaimRefs.join(', ')}`);
|
|
38
|
+
if (fact.releasedClaimRefs !== undefined)
|
|
39
|
+
segments.push(`released claims ${fact.releasedClaimRefs.join(', ')}`);
|
|
40
|
+
return segments.join(' ');
|
|
41
|
+
}
|
|
5
42
|
function service(agent) {
|
|
6
43
|
const host = agent.ctx.get('agentTeam');
|
|
7
44
|
if (host === undefined)
|
|
@@ -30,8 +67,9 @@ const teamInbox = defineTool({
|
|
|
30
67
|
taskNumber: { type: 'number' },
|
|
31
68
|
} } },
|
|
32
69
|
} },
|
|
33
|
-
render: (_args, value) => [{ type: 'text', text: value.items.length === 0 ?
|
|
34
|
-
: value.
|
|
70
|
+
render: (_args, value) => [{ type: 'text', text: value.items.length === 0 ? `No unread Team work.${value.totalUnreadCount > 0 ? ` (${value.totalUnreadCount} unread on Threads beyond this bounded list — call again with a larger limit.)` : ''}`
|
|
71
|
+
: [`${value.totalUnreadCount} unread update(s) total, ${value.totalDirectCount} direct, across ${value.items.length} Thread(s) shown${value.totalUnreadCount > value.items.reduce((sum, item) => sum + item.unreadCount, 0) ? ' — more exist beyond this bounded list' : ''}`,
|
|
72
|
+
...value.items.map(item => `${item.threadRef}${item.channelRef === undefined ? '' : ` · ${item.channelRef}`}${item.taskRef === undefined ? '' : ` · ${item.taskRef}`}${item.taskNumber === undefined ? '' : ` (#${item.taskNumber})`}${item.status === undefined ? '' : ` (${item.status})`} · ${item.unreadCount} unread, ${item.directCount} direct, revision ${item.revision}`)].join('\n') }],
|
|
35
73
|
},
|
|
36
74
|
async execute(args, exec) {
|
|
37
75
|
const agent = exec.agent;
|
|
@@ -59,7 +97,7 @@ const teamThread = defineTool({
|
|
|
59
97
|
description: 'Read or manage your Attention on one Thread. read acknowledges one chronological batch; history does not change read state. Prefer threadRef; taskRef is a compatibility alias when the Thread has a Task.',
|
|
60
98
|
parameters: {
|
|
61
99
|
action: { type: 'string', required: true, enum: ['status', 'follow', 'unfollow', 'read', 'history'] },
|
|
62
|
-
threadRef: { type: 'string', description: "Full branded Thread ref exactly as returned by Team tools, including the 'thread:' prefix." },
|
|
100
|
+
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." },
|
|
63
101
|
taskRef: { type: 'string', description: "Optional Task ref alias for released clients. Prefer threadRef; if both are given they must identify the same Thread." },
|
|
64
102
|
beforeSequence: { type: 'number' }, limit: { type: 'number' },
|
|
65
103
|
},
|
|
@@ -75,14 +113,56 @@ const teamThread = defineTool({
|
|
|
75
113
|
claimRef: { type: 'string', required: true }, direction: { type: 'string', required: true }, state: { type: 'string', required: true }, owner: { type: 'string', required: true },
|
|
76
114
|
} } },
|
|
77
115
|
facts: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: {
|
|
78
|
-
sequence: { type: 'number', required: true }, kind: { type: 'string', required: true }, body: { type: 'string' }, sender: { type: 'string' }, mentions: { type: 'array', items: { type: 'string' } }, activity: { type: 'string' }, unread: { type: 'boolean' }, direct: { type: 'boolean' },
|
|
116
|
+
sequence: { type: 'number', required: true }, kind: { type: 'string', required: true }, body: { type: 'string' }, sender: { type: 'string' }, mentions: { type: 'array', items: { type: 'string' } }, activity: { type: 'string' }, actor: { type: 'string' }, taskRef: { type: 'string' }, claimRef: { type: 'string' }, claimRefs: { type: 'array', items: { type: 'string' } }, completedClaimRefs: { type: 'array', items: { type: 'string' } }, acceptedClaimRefs: { type: 'array', items: { type: 'string' } }, releasedClaimRefs: { type: 'array', items: { type: 'string' } }, unread: { type: 'boolean' }, direct: { type: 'boolean' },
|
|
79
117
|
} } },
|
|
118
|
+
contextAdvice: { type: 'object', additionalProperties: false, properties: {
|
|
119
|
+
usageTokens: { type: 'number' }, taskBoundaryThreshold: { type: 'number' }, handoffAt: { type: 'number' }, hardLimit: { type: 'number' },
|
|
120
|
+
action: { type: 'string', required: true }, guidance: { type: 'string', required: true },
|
|
121
|
+
} },
|
|
80
122
|
} },
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
123
|
+
// Renders are the only channel a tool result reaches the model through:
|
|
124
|
+
// the header states the Thread ref and the Task's standing, each activity
|
|
125
|
+
// line names the actor and every Claim the activity concluded, message
|
|
126
|
+
// facts carry their unread/direct markers so a bounded batch can be
|
|
127
|
+
// re-read discriminately, an acceptance the reader just acknowledged
|
|
128
|
+
// carries one context-guidance section, and read/history footers state
|
|
129
|
+
// what remains or whether older facts exist.
|
|
130
|
+
render: (_args, value) => {
|
|
131
|
+
// The header always identifies the Thread first — the ref the model
|
|
132
|
+
// must echo in its next team_message reply — then the Task's standing
|
|
133
|
+
// when the Thread is taskful. An empty-facts status/follow result
|
|
134
|
+
// still carries the same identifying surface.
|
|
135
|
+
const header = [
|
|
136
|
+
value.threadRef,
|
|
137
|
+
value.taskRef === undefined ? '' : `${value.taskRef} · ${value.status}${value.resolution === undefined ? '' : `/${value.resolution}`}`,
|
|
138
|
+
`revision ${value.revision}, following=${value.following}`,
|
|
139
|
+
].filter(part => part !== '').join(' · ');
|
|
140
|
+
const lines = [header];
|
|
141
|
+
// The Claims snapshot is the collision surface: another Member's
|
|
142
|
+
// active Claim on this Task is invisible while facts alone render,
|
|
143
|
+
// yet exactly what the model must see before claiming its own angle.
|
|
144
|
+
for (const claim of value.claims)
|
|
145
|
+
lines.push(`Claim ${claim.claimRef} · ${claim.state} — ${claim.owner}: ${claim.direction}`);
|
|
146
|
+
// The anchor is the Thread's root task statement. Render it whenever
|
|
147
|
+
// it is not already among the facts, so a model reading a Thread for
|
|
148
|
+
// the first time never loses the original ask.
|
|
149
|
+
if (!value.facts.some(fact => fact.sequence === value.anchor.sequence))
|
|
150
|
+
lines.push(`Anchor ${value.anchor.sequence} [${value.anchor.sender}] ${value.anchor.body}`);
|
|
151
|
+
for (const fact of value.facts) {
|
|
152
|
+
lines.push(fact.kind === 'message'
|
|
153
|
+
? `${fact.sequence} [${fact.sender ?? 'unknown sender'}]${fact.unread === undefined ? '' : fact.direct === true ? ' [direct]' : fact.unread === true ? ' [unread]' : ''} ${fact.body}`
|
|
154
|
+
: activityLine(fact));
|
|
155
|
+
if (fact.kind !== 'message' && fact.unread === true)
|
|
156
|
+
lines.push(`${fact.sequence} … (unread activity)`);
|
|
157
|
+
}
|
|
158
|
+
if (value.kind === 'read')
|
|
159
|
+
lines.push(`Read through sequence ${value.readThroughSequence}; ${value.remainingUnreadCount ?? 0} unread update(s) remaining — call team_thread read again${(value.remainingUnreadCount ?? 0) > 0 ? '' : ' when new work arrives'}.`);
|
|
160
|
+
if (value.kind === 'history')
|
|
161
|
+
lines.push(`History cursor ${value.cursor}; hasMore=${value.hasMore ? 'true' : 'false'}${value.hasMore ? ' — older facts exist; page again with beforeSequence set to the cursor.' : ' — no older facts remain.'}`);
|
|
162
|
+
if (value.kind === 'read' && value.contextAdvice !== undefined)
|
|
163
|
+
lines.push(...adviceLines(value.contextAdvice));
|
|
164
|
+
return [{ type: 'text', text: lines.join('\n') }];
|
|
165
|
+
},
|
|
86
166
|
},
|
|
87
167
|
async execute(args, exec) {
|
|
88
168
|
const agent = exec.agent;
|
|
@@ -112,14 +192,14 @@ const teamThread = defineTool({
|
|
|
112
192
|
const status = host.attentionStatusForAgent(agent, base);
|
|
113
193
|
return threadResult('history', history, status.attention, history.facts.map(fact => fact.kind === 'message'
|
|
114
194
|
? { sequence: fact.sequence, kind: 'message', body: fact.message.body, sender: fact.message.sender, mentions: [...fact.mentions] }
|
|
115
|
-
:
|
|
195
|
+
: activityFactView(fact.sequence, fact.activity)), { cursor: history.cursor, hasMore: history.hasMore });
|
|
116
196
|
}
|
|
117
197
|
if (args.beforeSequence !== undefined || args.limit !== undefined)
|
|
118
198
|
throw new Error('read does not accept history arguments');
|
|
119
199
|
const read = await host.readThreadForAgent(agent, { requestId: requestId(agent.id, exec.callId), ...base });
|
|
120
200
|
return threadResult('read', read, read.attention, read.facts.map(entry => entry.fact.kind === 'message'
|
|
121
201
|
? { 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 }
|
|
122
|
-
:
|
|
202
|
+
: activityFactView(entry.fact.sequence, entry.fact.activity, { unread: entry.unread, direct: entry.direct })), { readThroughSequence: read.readThroughSequence, remainingUnreadCount: read.remainingUnreadCount, ...(read.contextAdvice === undefined ? {} : { contextAdvice: adviceView(read.contextAdvice) }) });
|
|
123
203
|
},
|
|
124
204
|
});
|
|
125
205
|
function threadResult(kind, snapshot, attention, facts, extra = {}) {
|
|
@@ -134,17 +214,25 @@ function threadResult(kind, snapshot, attention, facts, extra = {}) {
|
|
|
134
214
|
facts,
|
|
135
215
|
};
|
|
136
216
|
}
|
|
217
|
+
/** Render one read-time acceptance advice; unavailable never prints a fabricated number. */
|
|
218
|
+
function adviceLines(advice) {
|
|
219
|
+
const measured = advice.usageTokens !== undefined && advice.taskBoundaryThreshold !== undefined && advice.handoffAt !== undefined && advice.hardLimit !== undefined;
|
|
220
|
+
const summary = measured
|
|
221
|
+
? `${advice.usageTokens.toLocaleString('en-US')} tokens used; Task-boundary threshold ${advice.taskBoundaryThreshold.toLocaleString('en-US')}; normal handoff at ${advice.handoffAt.toLocaleString('en-US')}; hard limit ${advice.hardLimit.toLocaleString('en-US')}.`
|
|
222
|
+
: 'Context usage could not be measured for this acceptance.';
|
|
223
|
+
return [`Context guidance — ${summary}`, `Action: ${advice.action}. ${advice.guidance}`];
|
|
224
|
+
}
|
|
137
225
|
const teamMessage = markAgentTeamPreset(defineTool({
|
|
138
226
|
name: 'team_message',
|
|
139
227
|
description: 'Start a top-level Thread, reply to an existing Thread, or send a direct message (DM). start defaults to a taskless Thread; pass asTask true to create a Task in the same send. Read the Thread first; replies require its current revision (an internal concurrency token carried by baseRevision, never quoted in bodies). A top-level start may mention related Agents directly; in replies, only a Human can invite an unfollowed Agent. Pass Member refs in mentions and spell their handles inside the body; only mentioned Members render as mention chips. dm sends a private direct message to one enabled Agent Member in your Workspace: use it for quick clarifications and status syncs — never for task work, decisions, or anything that needs team visibility or traceability (use a Thread); if a DM exchange with the same Member exceeds about 3 exchanges, move it to a Thread, because every DM costs the recipient a full agent turn.',
|
|
140
228
|
parameters: {
|
|
141
229
|
action: { type: 'string', required: true, enum: ['start', 'reply', 'dm'] },
|
|
142
|
-
channelRef: { type: 'string', description: "Full branded Channel ref exactly as returned by Team tools, including the 'channel:' prefix." },
|
|
143
|
-
threadRef: { type: 'string', description: "Full branded Thread ref exactly as returned by Team tools, including the 'thread:' prefix." },
|
|
144
|
-
taskRef: { type: 'string', description: "Optional Task ref alias for reply on a Taskful Thread. Prefer threadRef." },
|
|
145
|
-
memberRef: { type: 'string', description: "Full branded Member ref exactly as returned by Team tools, including the 'member:' prefix. Required for dm; the Member must be an enabled Agent in your Workspace (the Human cannot be DMed)." },
|
|
230
|
+
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." },
|
|
231
|
+
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." },
|
|
232
|
+
taskRef: { type: 'string', description: "Optional Task ref alias for reply on a Taskful Thread. Prefer threadRef; an unambiguous abbreviation of the first 6+ UUID hex characters also resolves." },
|
|
233
|
+
memberRef: { type: 'string', description: "Full branded Member ref exactly as returned by Team tools, including the 'member:' prefix. An unambiguous abbreviation of the first 6+ UUID hex characters also resolves. Required for dm; the Member must be an enabled Agent in your Workspace (the Human cannot be DMed)." },
|
|
146
234
|
asTask: { type: 'boolean', description: 'When true, start creates a Task with the Thread. Default false creates a taskless Thread.' },
|
|
147
|
-
body: { type: 'string', required: true, description: "Markdown body. 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. Spell each mentioned Member's handle in the prose so the mention renders inline." }, baseRevision: { type: 'number', description: "Positive integer; use the current Thread revision as shown by the latest team_inbox or team_thread result for this Thread. The revision is an internal concurrency token, not a citable fact." },
|
|
235
|
+
body: { type: 'string', required: true, description: "Markdown body. 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. Spell each mentioned Member's handle in the prose so the mention renders inline." }, baseRevision: { type: 'number', description: "Positive integer; use the current Thread revision as shown by the latest team_inbox or team_thread result for this Thread. The revision is an internal concurrency token, not a citable fact." },
|
|
148
236
|
mentions: { type: 'array', items: { type: 'string' }, description: 'Member refs to mention. Mentioned Agents receive the Message directly; write their handles in the body (any casing, optional @) so the mention renders inline.' },
|
|
149
237
|
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.' },
|
|
150
238
|
},
|
|
@@ -155,8 +243,18 @@ const teamMessage = markAgentTeamPreset(defineTool({
|
|
|
155
243
|
recipientMemberId: { type: 'string' }, recipientHandle: { type: 'string' }, delivered: { type: 'boolean' }, deliveryNote: { type: 'string' },
|
|
156
244
|
} },
|
|
157
245
|
render: (_args, value) => [{ type: 'text', text: value.kind === 'dm-sent' ? `DM ${value.delivered === false ? 'recorded but not delivered' : 'delivered'} to @${value.recipientHandle} (${value.recipientMemberId})${value.deliveryNote === undefined ? '' : `: ${value.deliveryNote}`}`
|
|
158
|
-
: value.kind === 'committed' ? `Message ${value.messageRef} committed at revision ${value.revision}.`
|
|
159
|
-
:
|
|
246
|
+
: value.kind === 'committed' ? `Message ${value.messageRef} committed at revision ${value.revision} on ${value.threadRef}${value.taskRef === undefined ? '' : ` (${value.taskRef})`}.`
|
|
247
|
+
: value.kind === 'unread_required' ? `unread_required: ${value.threadRef}${value.taskRef === undefined ? '' : ` (${value.taskRef})`} has ${value.unreadCount} unread update(s), ${value.directCount} direct at revision ${value.revision}. Read the pending updates (team_thread read) before retrying this send.`
|
|
248
|
+
: value.kind === 'stale_revision' ? `stale_revision: your baseRevision ${value.expectedRevision} is obsolete; ${value.threadRef}${value.taskRef === undefined ? '' : ` (${value.taskRef})`} is now at revision ${value.revision}. Read the Thread, then retry with baseRevision ${value.revision}.`
|
|
249
|
+
: value.kind === 'member_not_following' ? `member_not_following: ${(value.memberIds ?? []).join(', ')} not following; the message was not committed. Only a Human can invite an unfollowed Agent — retry without mentioning them, or ask the Human.`
|
|
250
|
+
: `${value.kind}: ${value.memberIds?.join(', ') ?? `${value.threadRef ?? ''}${value.taskRef === undefined ? '' : ` · Task ${value.taskRef}`} revision ${value.revision ?? ''}`}` }],
|
|
251
|
+
// Minimal durable projection for the Host's context timeline: the
|
|
252
|
+
// structured outcome identity (never the render text). The effect-anchor
|
|
253
|
+
// fold reads `kind === 'committed'` + threadRef from the persisted
|
|
254
|
+
// tool/result meta — a start's Thread is born here, in the result.
|
|
255
|
+
presentationMeta: (_args, value) => value.kind === 'committed' && value.threadRef !== undefined
|
|
256
|
+
? { kind: value.kind, threadRef: value.threadRef, ...(value.taskRef === undefined ? {} : { taskRef: value.taskRef }) }
|
|
257
|
+
: { kind: value.kind },
|
|
160
258
|
},
|
|
161
259
|
async execute(args, exec) {
|
|
162
260
|
const agent = exec.agent;
|
|
@@ -225,9 +323,9 @@ const teamClaim = defineTool({
|
|
|
225
323
|
description: 'List or mutate your Direction Claims. Read the Thread first; every mutation uses the current Thread revision. A Claim is your one-sentence direction statement on a Task — "the angle I am taking" — so others can spot collisions and track progress: Tasks define scope (owned by Humans), Claims declare the angle (owned by you). Good direction: "Unify the four form dialogs on shared field components before wiring submits." Bad direction: a multi-paragraph plan with step order, file lists, or acceptance criteria — those belong in Thread messages, not the Claim.',
|
|
226
324
|
parameters: {
|
|
227
325
|
action: { type: 'string', required: true, enum: ['list', 'claim', 'done', 'release'] },
|
|
228
|
-
taskRef: { type: 'string', required: true, description: "Full branded Task ref exactly as returned by Team tools, including the 'task:' prefix." },
|
|
326
|
+
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." },
|
|
229
327
|
baseRevision: { type: 'number', description: "Positive integer; use the current Thread revision as shown by the latest team_inbox or team_thread result for this Task. The revision is an internal concurrency token, not a citable fact." }, direction: { type: 'string' },
|
|
230
|
-
claimRef: { type: 'string', description: "Full branded Claim ref exactly as returned by team_claim, including the 'claim:' prefix." },
|
|
328
|
+
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." },
|
|
231
329
|
},
|
|
232
330
|
output: {
|
|
233
331
|
schema: { type: 'object', additionalProperties: false, properties: {
|
|
@@ -236,8 +334,14 @@ const teamClaim = defineTool({
|
|
|
236
334
|
unreadCount: { type: 'number' }, directCount: { type: 'number' },
|
|
237
335
|
claims: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: { claimRef: { type: 'string', required: true }, direction: { type: 'string', required: true }, state: { type: 'string', required: true }, owner: { type: 'string', required: true } } } },
|
|
238
336
|
} },
|
|
239
|
-
render: (_args, value) => [{ type: 'text', text: [
|
|
240
|
-
|
|
337
|
+
render: (_args, value) => [{ type: 'text', text: [
|
|
338
|
+
value.kind === 'unread_required'
|
|
339
|
+
? `unread_required: ${value.taskRef} (${value.threadRef}) has ${value.unreadCount} unread update(s), ${value.directCount} direct at revision ${value.revision}. Read the pending updates (team_thread read) before retrying this Claim mutation.`
|
|
340
|
+
: value.kind === 'stale_revision'
|
|
341
|
+
? `stale_revision: your baseRevision ${value.expectedRevision} is obsolete; ${value.taskRef} (${value.threadRef}) is now at revision ${value.revision}. Read the Thread, then retry with baseRevision ${value.revision}.`
|
|
342
|
+
: `${value.kind}: ${value.taskRef} (${value.threadRef}) · ${value.status}, revision ${value.revision}`,
|
|
343
|
+
...value.claims.map(claim => `${claim.claimRef} · ${claim.state} — ${claim.owner}: ${claim.direction}`)
|
|
344
|
+
].join('\n') }],
|
|
241
345
|
},
|
|
242
346
|
async execute(args, exec) {
|
|
243
347
|
const agent = exec.agent;
|
|
@@ -276,9 +380,9 @@ const teamClaim = defineTool({
|
|
|
276
380
|
});
|
|
277
381
|
const teamView = defineTool({
|
|
278
382
|
name: 'team_view',
|
|
279
|
-
description: 'Discover authorized Team Channels, Tasks, and Members. It is not a substitute for team_thread reading.',
|
|
383
|
+
description: 'Discover authorized Team Channels, top-level Threads, Tasks, and Members. It is not a substitute for team_thread reading.',
|
|
280
384
|
parameters: {
|
|
281
|
-
channelRef: { type: 'string', description: "Full branded Channel ref exactly as returned by Team tools, including the 'channel:' prefix." },
|
|
385
|
+
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." },
|
|
282
386
|
limit: { type: 'number' }, cursor: { type: 'number' },
|
|
283
387
|
},
|
|
284
388
|
output: {
|
|
@@ -287,15 +391,23 @@ const teamView = defineTool({
|
|
|
287
391
|
members: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: {
|
|
288
392
|
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 },
|
|
289
393
|
} } },
|
|
394
|
+
threads: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: {
|
|
395
|
+
threadRef: { type: 'string', required: true }, channelRef: { type: 'string', required: true }, revision: { type: 'number', required: true }, messageCount: { type: 'number', required: true },
|
|
396
|
+
taskRef: { type: 'string' }, status: { type: 'string' }, taskNumber: { type: 'number' },
|
|
397
|
+
} } },
|
|
290
398
|
tasks: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: { taskRef: { type: 'string', required: true }, threadRef: { type: 'string', required: true }, channelRef: { type: 'string', required: true }, status: { type: 'string', required: true }, revision: { type: 'number', required: true } } } },
|
|
291
399
|
cursor: { type: 'number', required: true }, hasMore: { type: 'boolean', required: true },
|
|
292
400
|
} },
|
|
293
401
|
render: (_args, value) => [{ type: 'text', text: [
|
|
294
402
|
...value.channels.map(channel => `${channel.channelRef} · ${channel.name}`),
|
|
295
403
|
...value.members.map(m => `${m.memberId} · ${m.handle} (${m.kind}, ${m.presence})${m.description === '' ? '' : ` — ${m.description}`}`),
|
|
404
|
+
...(value.threads.length > 0
|
|
405
|
+
? value.threads.map(thread => `${thread.threadRef} · ${thread.channelRef}${thread.taskRef === undefined ? '' : ` · ${thread.taskRef}${thread.taskNumber === undefined ? '' : ` (#${thread.taskNumber})`} (${thread.status})`} · ${thread.messageCount} message(s), revision ${thread.revision}`)
|
|
406
|
+
: ['No Team Threads.']),
|
|
296
407
|
...(value.tasks.length > 0
|
|
297
|
-
? value.tasks.map(task => `${task.taskRef} · ${task.status}`)
|
|
408
|
+
? value.tasks.map(task => `${task.taskRef} · ${task.threadRef} · ${task.channelRef} · ${task.status}, revision ${task.revision}`)
|
|
298
409
|
: ['No Team Tasks.']),
|
|
410
|
+
`cursor ${value.cursor}, hasMore=${value.hasMore ? 'true' : 'false'}${value.hasMore ? ' — more items exist; call team_view again with cursor set to this value.' : ' — no further pages.'}`,
|
|
299
411
|
].join('\n') }],
|
|
300
412
|
},
|
|
301
413
|
async execute(args, exec) {
|
|
@@ -313,6 +425,12 @@ const teamView = defineTool({
|
|
|
313
425
|
...host.members().filter(status => visibleMemberIds.has(status.member.memberId)).map(status => ({ memberId: status.member.memberId,
|
|
314
426
|
kind: 'agent', handle: status.member.handle, description: status.member.description, presence: status.presence })),
|
|
315
427
|
],
|
|
428
|
+
threads: view.items.map(item => {
|
|
429
|
+
const thread = item.thread;
|
|
430
|
+
const task = item.task;
|
|
431
|
+
return { threadRef: thread.threadRef, channelRef: item.message.channelRef, revision: thread.revision, messageCount: item.messageCount,
|
|
432
|
+
...(task === undefined ? {} : { taskRef: task.taskRef, status: task.status, ...(item.taskNumber === undefined ? {} : { taskNumber: item.taskNumber }) }) };
|
|
433
|
+
}),
|
|
316
434
|
tasks: view.tasks.map(task => ({ taskRef: task.taskRef, threadRef: task.threadRef, channelRef: task.channelRef,
|
|
317
435
|
status: task.status, revision: view.threads.find(thread => thread.threadRef === task.threadRef)?.revision ?? 0 })),
|
|
318
436
|
cursor: view.cursor, hasMore: view.hasMore,
|
|
@@ -325,4 +443,5 @@ export function apply(ctx) {
|
|
|
325
443
|
ctx.tools.register(teamMessage);
|
|
326
444
|
ctx.tools.register(teamClaim);
|
|
327
445
|
ctx.tools.register(teamView);
|
|
446
|
+
registerContextTools(ctx);
|
|
328
447
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing context-management tools for Team Members. Thin adapters
|
|
3
|
+
* only: validation runs in the Host adapter, the successful result is the
|
|
4
|
+
* durable intent, and every lifecycle side effect — generation swap, Session
|
|
5
|
+
* creation, inbox handling — happens in the Host coordinator after the
|
|
6
|
+
* result is durably appended. `concludeTurn()` rides the success result of
|
|
7
|
+
* `context_rollover` and `context_checkpoint`, so sibling calls settle in model
|
|
8
|
+
* order before the turn closes.
|
|
9
|
+
* @module @wowyuarm/dsh-agent-team/context-tools
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerContextTools(ctx: {
|
|
12
|
+
readonly tools: {
|
|
13
|
+
register(tool: unknown): void;
|
|
14
|
+
};
|
|
15
|
+
}): void;
|
|
16
|
+
//# sourceMappingURL=context-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-tools.d.ts","sourceRoot":"","sources":["../../src/context-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoKH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,CAIrG"}
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAalD,eAAO,MAAM,IAAI,8BAA8B,CAAA;AAC/C,eAAO,MAAM,MAAM,UAAY,CAAA;AAmd/B,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAOxC"}
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { ManualCompactionError } from '@deepseek-ai/dsh-compaction';
|
|
2
|
-
/** Strict threshold: 200K exactly does not compact. */
|
|
3
|
-
export const AUTO_COMPACTION_TOKEN_LIMIT = 200_000;
|
|
4
|
-
/**
|
|
5
|
-
* Suggested summary marker of the pre-compaction memory hint. Same shape as
|
|
6
|
-
* the inbox and recovery notices so future turns can recognize it.
|
|
7
|
-
*/
|
|
8
|
-
export const PRE_COMPACTION_NOTICE_SUMMARY = 'Compaction is imminent; consider persisting key conclusions.';
|
|
9
|
-
/** Suggested pre-compaction hint body; writing is the Agent's own call. */
|
|
10
|
-
export function preCompactionNoticeText() {
|
|
11
|
-
return 'This session is about to be compacted, which will summarize away the current conversation detail. If there are durable conclusions worth keeping — validated facts, decisions, or reusable details — consider writing them to your memory or notes now. This is only a reminder: decide yourself whether anything is worth persisting, and do nothing if not. Do not start new work because of this message.';
|
|
12
|
-
}
|
|
13
|
-
/** Claim owners of a durable Human acceptance, including early-completed ones. */
|
|
14
|
-
export function acceptedTaskCompactionMembers(operation) {
|
|
15
|
-
if (operation.kind !== 'team/task-changed' || operation.actor.kind !== 'human' || operation.data.activity.kind !== 'accept')
|
|
16
|
-
return undefined;
|
|
17
|
-
return [...new Set(operation.data.claims.map(claim => claim.owner))];
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Small Host-side accepted-Task follow-up. This is deliberately not a generic
|
|
21
|
-
* workflow/state-machine: a pending set deduplicates Members, an error map is
|
|
22
|
-
* owned by the Host, and each worker is only whenIdle → measure → compactNow.
|
|
23
|
-
*/
|
|
24
|
-
export class AutoCompactionCoordinator {
|
|
25
|
-
options;
|
|
26
|
-
pending = new Set();
|
|
27
|
-
workers = new Map();
|
|
28
|
-
controller = new AbortController();
|
|
29
|
-
constructor(options) {
|
|
30
|
-
this.options = options;
|
|
31
|
-
}
|
|
32
|
-
/** Queue all unique claim owners; unavailable Members stay pending. */
|
|
33
|
-
schedule(memberIds) {
|
|
34
|
-
for (const memberId of new Set(memberIds)) {
|
|
35
|
-
this.pending.add(memberId);
|
|
36
|
-
this.start(memberId);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/** Activation/recovery calls this after a live handle becomes available. */
|
|
40
|
-
activated(memberId) {
|
|
41
|
-
this.start(memberId);
|
|
42
|
-
}
|
|
43
|
-
async dispose() {
|
|
44
|
-
this.controller.abort();
|
|
45
|
-
await Promise.all([...this.workers.values()].map(worker => worker.catch(() => { })));
|
|
46
|
-
this.workers.clear();
|
|
47
|
-
this.pending.clear();
|
|
48
|
-
}
|
|
49
|
-
start(memberId) {
|
|
50
|
-
if (this.controller.signal.aborted || this.workers.has(memberId) || !this.pending.has(memberId))
|
|
51
|
-
return;
|
|
52
|
-
// No live handle is not a failure: member activation/recovery calls activated().
|
|
53
|
-
if (this.options.agentForMember(memberId) === undefined)
|
|
54
|
-
return;
|
|
55
|
-
const worker = this.work(memberId).finally(() => { this.workers.delete(memberId); });
|
|
56
|
-
this.workers.set(memberId, worker);
|
|
57
|
-
}
|
|
58
|
-
async work(memberId) {
|
|
59
|
-
let reactivated = false;
|
|
60
|
-
while (!this.controller.signal.aborted && this.pending.has(memberId)) {
|
|
61
|
-
const agent = this.options.agentForMember(memberId);
|
|
62
|
-
if (agent === undefined)
|
|
63
|
-
return;
|
|
64
|
-
try {
|
|
65
|
-
if (!(await waitForIdleOrAbort(agent, this.controller.signal)))
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
this.fail(memberId, agent, `automatic compaction idle wait failed: ${describe(error)}`);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
// A restart can replace the handle while it waited; restart the turn on
|
|
73
|
-
// the current agent so measurement and compactNow share one scope.
|
|
74
|
-
if (this.options.agentForMember(memberId) !== agent)
|
|
75
|
-
continue;
|
|
76
|
-
const meter = agent.ctx.get('tokenMeter');
|
|
77
|
-
if (meter === undefined) {
|
|
78
|
-
this.fail(memberId, agent, 'automatic compaction failed: tokenMeter is unavailable in the Member scope');
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
let before = meter.measure(agent.session).totalTokens;
|
|
82
|
-
if (before <= AUTO_COMPACTION_TOKEN_LIMIT) {
|
|
83
|
-
this.complete(memberId, agent);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
// The session is over the threshold and about to be compacted: steer one
|
|
87
|
-
// advisory hint so the Agent can persist its own key conclusions first.
|
|
88
|
-
// The hint turn itself is ordinary agent work — its failure is contained
|
|
89
|
-
// by the agent loop and never blocks compaction; only the fresh idle
|
|
90
|
-
// boundary is awaited before the final measurement and compactNow.
|
|
91
|
-
if (this.options.steerPreCompaction !== undefined) {
|
|
92
|
-
try {
|
|
93
|
-
this.options.steerPreCompaction(agent);
|
|
94
|
-
}
|
|
95
|
-
catch (error) {
|
|
96
|
-
this.options.log(`pre-compaction memory hint failed: ${describe(error)} (member ${memberId}, session ${agent.id})`);
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
99
|
-
if (!(await waitForIdleOrAbort(agent, this.controller.signal)))
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
catch (error) {
|
|
103
|
-
this.options.log(`pre-compaction hint idle wait failed: ${describe(error)} (member ${memberId}, session ${agent.id})`);
|
|
104
|
-
}
|
|
105
|
-
if (this.controller.signal.aborted)
|
|
106
|
-
return;
|
|
107
|
-
if (this.options.agentForMember(memberId) !== agent)
|
|
108
|
-
continue;
|
|
109
|
-
before = meter.measure(agent.session).totalTokens;
|
|
110
|
-
if (before <= AUTO_COMPACTION_TOKEN_LIMIT) {
|
|
111
|
-
this.complete(memberId, agent);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
const engine = this.options.compactionForAgent(agent);
|
|
116
|
-
if (engine === undefined) {
|
|
117
|
-
// A roster-subtree teardown (bundle-row reload, config hot-apply)
|
|
118
|
-
// prunes the standing mount while this agent's scope binding still
|
|
119
|
-
// names the dead key, so resolution can never succeed again for this
|
|
120
|
-
// handle. One in-place re-activation rebuilds the binding from the
|
|
121
|
-
// live roster; a preset that mounts no compaction row keeps failing
|
|
122
|
-
// and lands in the terminal diagnostic below.
|
|
123
|
-
if (!reactivated && await this.tryReactivate(memberId)) {
|
|
124
|
-
reactivated = true;
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
this.fail(memberId, agent, 'automatic compaction failed: compaction is unavailable in the Member scope');
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
try {
|
|
131
|
-
const result = await engine.compactNow(agent, this.controller.signal);
|
|
132
|
-
if (this.controller.signal.aborted)
|
|
133
|
-
return;
|
|
134
|
-
if (result === null) {
|
|
135
|
-
this.fail(memberId, agent, `automatic compaction returned no compactable range above ${AUTO_COMPACTION_TOKEN_LIMIT} tokens`);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
if (this.controller.signal.aborted)
|
|
141
|
-
return;
|
|
142
|
-
if (error instanceof ManualCompactionError && error.code === 'busy')
|
|
143
|
-
continue;
|
|
144
|
-
this.fail(memberId, agent, `automatic compaction failed: ${describe(error)}`);
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const after = meter.measure(agent.session).totalTokens;
|
|
148
|
-
if (after >= before) {
|
|
149
|
-
this.fail(memberId, agent, `automatic compaction did not reduce context (${before} → ${after} tokens)`);
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
this.options.cleared(memberId, agent.id);
|
|
153
|
-
// Still over the strict threshold: next iteration waits at a fresh idle
|
|
154
|
-
// boundary before attempting another compactNow.
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
complete(memberId, agent) {
|
|
158
|
-
this.pending.delete(memberId);
|
|
159
|
-
this.options.cleared(memberId, agent.id);
|
|
160
|
-
}
|
|
161
|
-
tryReactivate(memberId) {
|
|
162
|
-
return this.options.reactivate(memberId).catch(error => {
|
|
163
|
-
this.options.log(`automatic compaction re-activation failed: ${describe(error)} (member ${memberId})`);
|
|
164
|
-
return false;
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
fail(memberId, agent, diagnostic) {
|
|
168
|
-
this.pending.delete(memberId);
|
|
169
|
-
this.options.log(`${diagnostic} (member ${memberId}, session ${agent.id})`);
|
|
170
|
-
this.options.failed(memberId, agent.id, diagnostic);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
/** Abortable idle wait; the Agent owns its idle promise and is never cancelled. */
|
|
174
|
-
function waitForIdleOrAbort(agent, signal) {
|
|
175
|
-
const idle = agent.whenIdle();
|
|
176
|
-
if (signal.aborted) {
|
|
177
|
-
// The agent may settle or reject after teardown; keep that late outcome
|
|
178
|
-
// observed without making it a coordinator failure.
|
|
179
|
-
void idle.catch(() => { });
|
|
180
|
-
return Promise.resolve(false);
|
|
181
|
-
}
|
|
182
|
-
return new Promise((resolve, reject) => {
|
|
183
|
-
let settled = false;
|
|
184
|
-
const finish = (outcome, error) => {
|
|
185
|
-
if (settled)
|
|
186
|
-
return;
|
|
187
|
-
settled = true;
|
|
188
|
-
signal.removeEventListener('abort', onAbort);
|
|
189
|
-
if (outcome === 'error')
|
|
190
|
-
reject(error);
|
|
191
|
-
else
|
|
192
|
-
resolve(outcome === 'idle');
|
|
193
|
-
};
|
|
194
|
-
const onAbort = () => { finish('aborted'); };
|
|
195
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
196
|
-
idle.then(() => { finish('idle'); }, error => { finish('error', error); });
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
function describe(error) {
|
|
200
|
-
return error instanceof Error ? error.message : String(error);
|
|
201
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
2
|
-
import { type CompactionEngine } from '@deepseek-ai/dsh-compaction';
|
|
3
|
-
import type { AgentTeamMemberId, AgentTeamOperation } from './types.ts';
|
|
4
|
-
/** Strict threshold: 200K exactly does not compact. */
|
|
5
|
-
export declare const AUTO_COMPACTION_TOKEN_LIMIT = 200000;
|
|
6
|
-
/**
|
|
7
|
-
* Suggested summary marker of the pre-compaction memory hint. Same shape as
|
|
8
|
-
* the inbox and recovery notices so future turns can recognize it.
|
|
9
|
-
*/
|
|
10
|
-
export declare const PRE_COMPACTION_NOTICE_SUMMARY = "Compaction is imminent; consider persisting key conclusions.";
|
|
11
|
-
/** Suggested pre-compaction hint body; writing is the Agent's own call. */
|
|
12
|
-
export declare function preCompactionNoticeText(): string;
|
|
13
|
-
/** Claim owners of a durable Human acceptance, including early-completed ones. */
|
|
14
|
-
export declare function acceptedTaskCompactionMembers(operation: AgentTeamOperation): readonly AgentTeamMemberId[] | undefined;
|
|
15
|
-
/**
|
|
16
|
-
* Small Host-side accepted-Task follow-up. This is deliberately not a generic
|
|
17
|
-
* workflow/state-machine: a pending set deduplicates Members, an error map is
|
|
18
|
-
* owned by the Host, and each worker is only whenIdle → measure → compactNow.
|
|
19
|
-
*/
|
|
20
|
-
export declare class AutoCompactionCoordinator {
|
|
21
|
-
private readonly options;
|
|
22
|
-
private readonly pending;
|
|
23
|
-
private readonly workers;
|
|
24
|
-
private readonly controller;
|
|
25
|
-
constructor(options: {
|
|
26
|
-
readonly agentForMember: (memberId: AgentTeamMemberId) => Agent | undefined;
|
|
27
|
-
readonly compactionForAgent: (agent: Agent) => CompactionEngine | undefined;
|
|
28
|
-
/** Rebuild one enabled Member in place from its persisted Session; false when it stays unusable. */
|
|
29
|
-
readonly reactivate: (memberId: AgentTeamMemberId) => Promise<boolean>;
|
|
30
|
-
readonly failed: (memberId: AgentTeamMemberId, sessionId: Agent['id'], diagnostic: string) => void;
|
|
31
|
-
readonly cleared: (memberId: AgentTeamMemberId, sessionId: Agent['id']) => void;
|
|
32
|
-
readonly log: (message: string) => void;
|
|
33
|
-
/** Steer the pre-compaction memory hint; absent skips the hint entirely. */
|
|
34
|
-
readonly steerPreCompaction?: (agent: Agent) => void;
|
|
35
|
-
});
|
|
36
|
-
/** Queue all unique claim owners; unavailable Members stay pending. */
|
|
37
|
-
schedule(memberIds: readonly AgentTeamMemberId[]): void;
|
|
38
|
-
/** Activation/recovery calls this after a live handle becomes available. */
|
|
39
|
-
activated(memberId: AgentTeamMemberId): void;
|
|
40
|
-
dispose(): Promise<void>;
|
|
41
|
-
private start;
|
|
42
|
-
private work;
|
|
43
|
-
private complete;
|
|
44
|
-
private tryReactivate;
|
|
45
|
-
private fail;
|
|
46
|
-
}
|
|
47
|
-
//# sourceMappingURL=auto-compaction.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auto-compaction.d.ts","sourceRoot":"","sources":["../../src/auto-compaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAG1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEvE,uDAAuD;AACvD,eAAO,MAAM,2BAA2B,SAAU,CAAA;AAElD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,iEAAiE,CAAA;AAE3G,2EAA2E;AAC3E,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED,kFAAkF;AAClF,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,kBAAkB,GAAG,SAAS,iBAAiB,EAAE,GAAG,SAAS,CAGrH;AAED;;;;GAIG;AACH,qBAAa,yBAAyB;IAKxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8C;IACtE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwB;gBAEtB,OAAO,EAAE;QACpC,QAAQ,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,KAAK,GAAG,SAAS,CAAA;QAC3E,QAAQ,CAAC,kBAAkB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,gBAAgB,GAAG,SAAS,CAAA;QAC3E,oGAAoG;QACpG,QAAQ,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;QACtE,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;QAClG,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAA;QAC/E,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;QACvC,4EAA4E;QAC5E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;KACrD;IAED,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,EAAE,GAAG,IAAI;IAOvD,4EAA4E;IAC5E,SAAS,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAItC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B,OAAO,CAAC,KAAK;YAQC,IAAI;IAuFlB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,IAAI;CAKb"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
-
/** Public input-dock slot currency for the Member Session strip. */
|
|
3
|
-
export type TeamMemberDockProps = PropsRuntime<'conversation.input.dock'> & PropsLocale<'team'>;
|
|
4
|
-
/**
|
|
5
|
-
* Team-owned strip above the shipped composer while a Member Session is
|
|
6
|
-
* embedded. The shipped InputBar keeps the whole input surface (draft,
|
|
7
|
-
* trigger menus, submit); this dock only carries what the shipped bar cannot
|
|
8
|
-
* know — the Member-Session vocabulary hint and the Member turn's prompt
|
|
9
|
-
* error, read through the standard useSession hook.
|
|
10
|
-
*/
|
|
11
|
-
export declare function TeamMemberDock({ t, useSession }: TeamMemberDockProps): import("react").JSX.Element;
|
|
12
|
-
//# sourceMappingURL=TeamMemberDock.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TeamMemberDock.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberDock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAGjF,oEAAoE;AACpE,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC,yBAAyB,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;AAE/F;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,mBAAmB,+BAQpE"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import css from './composer.module.css';
|
|
3
|
-
/**
|
|
4
|
-
* Team-owned strip above the shipped composer while a Member Session is
|
|
5
|
-
* embedded. The shipped InputBar keeps the whole input surface (draft,
|
|
6
|
-
* trigger menus, submit); this dock only carries what the shipped bar cannot
|
|
7
|
-
* know — the Member-Session vocabulary hint and the Member turn's prompt
|
|
8
|
-
* error, read through the standard useSession hook.
|
|
9
|
-
*/
|
|
10
|
-
export function TeamMemberDock({ t, useSession }) {
|
|
11
|
-
const promptError = useSession(state => state.promptError ?? null);
|
|
12
|
-
return _jsxs("div", { className: css.memberSessionDock, "data-team-member-dock": "true", children: [_jsx("span", { className: css.memberSessionHint, children: t('memberSessionHint') }), promptError !== null && (_jsx("p", { className: css.memberSessionStatus, role: "alert", children: promptError.error.message }))] });
|
|
13
|
-
}
|