@wowyuarm/dsh-agent-team 0.1.15 → 0.2.1
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 +8 -4
- package/README.zh.md +8 -4
- package/cordis.patch.yml +26 -12
- package/package.json +52 -47
- package/packages/agent-team/lib/attachments.js +63 -4
- package/packages/agent-team/lib/compaction-engine.js +18 -0
- package/packages/agent-team/lib/context-continuity-host.js +198 -4
- package/packages/agent-team/lib/context-judge.js +150 -0
- package/packages/agent-team/lib/context-source.js +11 -6
- package/packages/agent-team/lib/environment-check.js +102 -0
- package/packages/agent-team/lib/human-profile.js +23 -37
- package/packages/agent-team/lib/index.js +510 -99
- package/packages/agent-team/lib/installed-manifest.js +32 -0
- package/packages/agent-team/lib/invariant.js +36 -12
- package/packages/agent-team/lib/ledger.js +37 -1
- package/packages/agent-team/lib/member-context.js +2 -4
- package/packages/agent-team/lib/member-runtime.js +2 -1
- package/packages/agent-team/lib/member-time-context.js +2 -4
- package/packages/agent-team/lib/recovery.js +53 -4
- package/packages/agent-team/lib/typert.host.js +225 -54
- package/packages/agent-team/lib/typert.remote-client.d.ts +5 -1
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +127 -39
- package/packages/agent-team/lib/types/attachments.d.ts +32 -2
- package/packages/agent-team/lib/types/attachments.d.ts.map +1 -1
- package/packages/agent-team/lib/types/compaction-engine.d.ts +19 -0
- package/packages/agent-team/lib/types/compaction-engine.d.ts.map +1 -0
- package/packages/agent-team/lib/types/context-continuity-host.d.ts +131 -4
- package/packages/agent-team/lib/types/context-continuity-host.d.ts.map +1 -1
- package/packages/agent-team/lib/types/context-judge.d.ts +82 -0
- package/packages/agent-team/lib/types/context-judge.d.ts.map +1 -0
- package/packages/agent-team/lib/types/context-source.d.ts +7 -4
- package/packages/agent-team/lib/types/context-source.d.ts.map +1 -1
- package/packages/agent-team/lib/types/environment-check.d.ts +66 -0
- package/packages/agent-team/lib/types/environment-check.d.ts.map +1 -0
- package/packages/agent-team/lib/types/human-profile.d.ts +21 -27
- package/packages/agent-team/lib/types/human-profile.d.ts.map +1 -1
- package/packages/agent-team/lib/types/index.d.ts +281 -18
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/installed-manifest.d.ts +16 -0
- package/packages/agent-team/lib/types/installed-manifest.d.ts.map +1 -0
- package/packages/agent-team/lib/types/invariant.d.ts +27 -5
- package/packages/agent-team/lib/types/invariant.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +12 -0
- 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 +1 -1
- package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/recovery.d.ts +19 -6
- package/packages/agent-team/lib/types/recovery.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/entities.d.ts +1 -8
- package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/requests-results.d.ts +94 -2
- package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types.d.ts +9 -0
- package/packages/agent-team/lib/types/types.d.ts.map +1 -1
- package/packages/client-agent-team/lib/client.js +1264 -383
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/EnvironmentCheck.d.ts +18 -0
- package/packages/client-agent-team/lib/types/client/EnvironmentCheck.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/EnvironmentCheck.js +58 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +0 -5
- 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/TeamPresenceDot.js +2 -7
- package/packages/client-agent-team/lib/types/client/TeamSettingsSection.d.ts +26 -0
- package/packages/client-agent-team/lib/types/client/TeamSettingsSection.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamSettingsSection.js +195 -0
- package/packages/client-agent-team/lib/types/client/attachment-preview.d.ts +7 -11
- package/packages/client-agent-team/lib/types/client/attachment-preview.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/attachment-preview.js +13 -3
- package/packages/client-agent-team/lib/types/client/context-judge.d.ts +64 -0
- package/packages/client-agent-team/lib/types/client/context-judge.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/context-judge.js +78 -0
- package/packages/client-agent-team/lib/types/client/environment-check.d.ts +65 -0
- package/packages/client-agent-team/lib/types/client/environment-check.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/environment-check.js +78 -0
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +84 -11
- package/packages/client-agent-team/lib/types/client/judge-form.d.ts +99 -0
- package/packages/client-agent-team/lib/types/client/judge-form.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/judge-form.js +208 -0
- package/packages/client-agent-team/lib/types/client/locales.d.ts +70 -14
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +76 -14
- package/packages/tool-agent-team/README.md +6 -3
- package/packages/tool-agent-team/README.zh.md +6 -3
- package/packages/tool-agent-team/lib/context-tools.js +148 -164
- package/packages/tool-agent-team/lib/index.js +17 -2
- package/packages/tool-agent-team/lib/types/context-tools.d.ts +32 -7
- package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/pressure-policy.js +0 -230
- package/packages/agent-team/lib/types/pressure-policy.d.ts +0 -130
- package/packages/agent-team/lib/types/pressure-policy.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts +0 -15
- package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/HumanSettingsSection.js +0 -115
|
@@ -1,197 +1,181 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Model-facing context-management tools for Team Members.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
2
|
+
* Model-facing context-management tools for Team Members.
|
|
3
|
+
*
|
|
4
|
+
* All four are the published engine's tools, built by `createContinuityTools`:
|
|
5
|
+
* the engine owns their argument contract, the anti-forgery gate on a cited ref,
|
|
6
|
+
* the `concludeTurn()` timing, and the render shapes, while the Team supplies
|
|
7
|
+
* its own vocabulary (`TEAM_CONTINUITY_TEXT`) and the mechanism behind
|
|
8
|
+
* `ContinuityToolAdapter`. Hand-written copies of those descriptions used to
|
|
9
|
+
* live here and drifted from the engine's defaults, so the Team's guidance now
|
|
10
|
+
* travels only through the engine's text seams — including the Team glossary the
|
|
11
|
+
* engine splices into `context_status` as `timelineGuidance`.
|
|
12
|
+
*
|
|
13
|
+
* One Team rule survives only in the adapter: for a row the engine renders as
|
|
14
|
+
* not restorable it prints that row's `ref` verbatim (`brief` truncates at 120
|
|
15
|
+
* characters, and a Team ref is 83), and a printed ref is exactly what a model
|
|
16
|
+
* copies into `checkpointRef`. The adapter therefore answers such a row with a
|
|
17
|
+
* short digest instead of its ref — the same digest that keeps two rows sharing
|
|
18
|
+
* a label and a price apart — so no non-selectable row carries a citable string.
|
|
9
19
|
* @module @wowyuarm/dsh-agent-team/context-tools
|
|
10
20
|
*/
|
|
11
21
|
import { createHash } from 'node:crypto';
|
|
12
|
-
import {
|
|
22
|
+
import { compactibleNow, createContinuityTools, } from '@wowyuarm/dsh-context-continuity';
|
|
23
|
+
import { MAX_TIMELINE_LIMIT } from '@wowyuarm/dsh-agent-team/host';
|
|
13
24
|
import { member, service } from "./host-access.js";
|
|
14
|
-
const MAX_HANDOFF_CHARS = 32 * 1024;
|
|
15
|
-
const MAX_RELATED_FILES = 32;
|
|
16
25
|
/**
|
|
17
26
|
* Short stable identifier for one timeline row: a digest of the anchor's own
|
|
18
27
|
* ref. Rows routinely share a label (`Team message` is a constant) and a price
|
|
19
28
|
* (the same completed turn prices both), so without it two distinct anchors
|
|
20
|
-
* read as one duplicated row.
|
|
21
|
-
*
|
|
22
|
-
* in full
|
|
29
|
+
* read as one duplicated row. It is also what a row that is NOT restorable
|
|
30
|
+
* answers as its `ref`, because the engine's render prints that field for every
|
|
31
|
+
* row: a ref only means something on a restorable row, which prints it in full
|
|
32
|
+
* for `context_rollover`.
|
|
23
33
|
*/
|
|
24
34
|
function anchorId(checkpointRef) {
|
|
25
35
|
return createHash('sha256').update(checkpointRef).digest('hex').slice(0, 6);
|
|
26
36
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
/** The calling Agent, or a model-visible rejection — these tools only exist in a Member Session. */
|
|
38
|
+
function agentOf(exec) {
|
|
39
|
+
const agent = exec.agent;
|
|
40
|
+
if (agent === undefined)
|
|
41
|
+
throw new Error('context tool requires an Agent session');
|
|
42
|
+
return agent;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The compaction capability in one calling Agent's preset scope, priced by the
|
|
46
|
+
* same meter the pressure policy reads. The Host owns the address: a preset
|
|
47
|
+
* revision publishes its services behind its own isolate, so the engine is asked
|
|
48
|
+
* for through the one method that knows which realm holds it. A Member whose
|
|
49
|
+
* composition mounts none reports "not available in this scope" — never a
|
|
50
|
+
* failure, never a silent no-op.
|
|
51
|
+
*
|
|
52
|
+
* The meter travels with the scope because two engine decisions read it: how
|
|
53
|
+
* much of the newest surface a compaction keeps verbatim, and the price a status
|
|
54
|
+
* reports for a compaction started now. A scope without one still compacts —
|
|
55
|
+
* keeping the newest instruction and everything after it — and prices nothing.
|
|
56
|
+
*/
|
|
57
|
+
function compactionScopeFor(agent) {
|
|
58
|
+
const engine = service(agent).compactionForAgent(agent);
|
|
59
|
+
if (engine === undefined)
|
|
60
|
+
return undefined;
|
|
61
|
+
const meter = agent.ctx.get('tokenMeter');
|
|
62
|
+
return { engine, ...(meter === undefined ? {} : { meter }) };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* One Host timeline item in the engine's own vocabulary. Team's four sources
|
|
66
|
+
* collapse onto the engine's three kinds, and the two that say something a
|
|
67
|
+
* reader needs (`handoff`, `compaction`) ride its opaque `kind` field rather
|
|
68
|
+
* than being flattened silently.
|
|
69
|
+
*
|
|
70
|
+
* The `ref` of a row that is not restorable is a short digest rather than the
|
|
71
|
+
* ref itself: the engine's render prints this field on every row, and a printed
|
|
72
|
+
* ref is what a model copies into `checkpointRef`. Only a restorable row — the
|
|
73
|
+
* one kind of row whose ref `context_rollover` accepts — carries a citable
|
|
74
|
+
* string.
|
|
75
|
+
*/
|
|
76
|
+
export function engineTimelineItems(items) {
|
|
77
|
+
return items.map(item => ({
|
|
78
|
+
ref: item.restorable ? item.checkpointRef : anchorId(item.checkpointRef),
|
|
79
|
+
label: item.name,
|
|
80
|
+
source: item.source === 'agent' ? 'checkpoint' : item.source === 'head' ? 'head' : 'boundary',
|
|
81
|
+
...(item.source === 'handoff' || item.source === 'compaction' ? { kind: item.source } : {}),
|
|
82
|
+
retainedTokens: item.retainedTokens,
|
|
83
|
+
discardedTokens: item.discardedTokens,
|
|
84
|
+
affectedTopics: [...item.affectedThreads],
|
|
85
|
+
restorable: item.restorable,
|
|
86
|
+
...(item.reason === undefined ? {} : { reason: item.reason }),
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Team's half of the engine's contract: resolve the calling execution to its
|
|
91
|
+
* Member and Host, answer the ref gate from the one policy that owns it, and run
|
|
92
|
+
* the effects. Every method resolves its own caller, because one adapter serves
|
|
93
|
+
* all four tools.
|
|
94
|
+
*/
|
|
95
|
+
const adapter = {
|
|
96
|
+
/**
|
|
97
|
+
* One policy, two readers: a ref is restorable exactly when the Team timeline
|
|
98
|
+
* — the same list the model picked from — offers it as such. The walk is asked
|
|
99
|
+
* for the widest window `context_status` can show, so any ref a status read
|
|
100
|
+
* could have printed is answered here.
|
|
101
|
+
*/
|
|
102
|
+
async isRestorableRef(checkpointRef, exec) {
|
|
103
|
+
const agent = agentOf(exec);
|
|
104
|
+
const current = member(agent);
|
|
105
|
+
const timeline = await service(agent).contextTimelineForAgent(agent, { memberId: current.memberId, limit: MAX_TIMELINE_LIMIT });
|
|
106
|
+
return timeline.items.some(item => item.checkpointRef === checkpointRef && item.restorable);
|
|
40
107
|
},
|
|
41
|
-
async
|
|
42
|
-
const agent = exec
|
|
43
|
-
if (agent === undefined)
|
|
44
|
-
throw new Error('context_rollover requires an Agent session');
|
|
108
|
+
async requestRollover(request, exec) {
|
|
109
|
+
const agent = agentOf(exec);
|
|
45
110
|
const current = member(agent);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (handoff.length > MAX_HANDOFF_CHARS)
|
|
51
|
-
throw new Error(`context_rollover handoff exceeds ${MAX_HANDOFF_CHARS} characters`);
|
|
52
|
-
const relatedFilesInput = Array.isArray(args.relatedFiles) ? args.relatedFiles : [];
|
|
53
|
-
if (relatedFilesInput.length > MAX_RELATED_FILES)
|
|
54
|
-
throw new Error(`context_rollover accepts at most ${MAX_RELATED_FILES} related files`);
|
|
55
|
-
// Tool argument validation is layered: the Harness schema (required and
|
|
56
|
-
// type checks) rejects at the execute boundary, and this body adds the
|
|
57
|
-
// checks the schema cannot express — each related file is validated
|
|
58
|
-
// here, so a blank path/reason rejects instead of seeding the handoff
|
|
59
|
-
// envelope with empty fields.
|
|
60
|
-
const relatedFiles = [];
|
|
61
|
-
for (const [index, entry] of relatedFilesInput.entries()) {
|
|
62
|
-
if (typeof entry !== 'object' || entry === null)
|
|
63
|
-
throw new Error(`context_rollover relatedFiles[${index}] must be an object with path and reason`);
|
|
64
|
-
const candidate = entry;
|
|
65
|
-
if (typeof candidate.path !== 'string' || candidate.path.trim() === '')
|
|
66
|
-
throw new Error(`context_rollover relatedFiles[${index}].path must be a non-empty string`);
|
|
67
|
-
if (typeof candidate.reason !== 'string' || candidate.reason.trim() === '')
|
|
68
|
-
throw new Error(`context_rollover relatedFiles[${index}].reason must be a non-empty string`);
|
|
69
|
-
relatedFiles.push({ path: candidate.path, reason: candidate.reason });
|
|
70
|
-
}
|
|
71
|
-
// Tool schemas are open at the root (Harness parameter specs set no
|
|
72
|
-
// `additionalProperties: false`), so an undeclared shape can still reach
|
|
73
|
-
// the body. Any supplied value that is not a non-empty string rejects
|
|
74
|
-
// here rather than being treated as absent — an absent ref means fresh,
|
|
75
|
-
// which is not what the model asked for.
|
|
76
|
-
const raw = args;
|
|
77
|
-
const suppliedRef = Object.hasOwn(raw, 'checkpointRef') ? raw.checkpointRef : undefined;
|
|
78
|
-
if (suppliedRef !== undefined && (typeof suppliedRef !== 'string' || suppliedRef.trim() === '')) {
|
|
79
|
-
throw new Error('context_rollover checkpointRef must be a non-empty string when supplied');
|
|
80
|
-
}
|
|
81
|
-
const checkpointRef = typeof suppliedRef === 'string' ? suppliedRef.trim() : undefined;
|
|
82
|
-
const outcome = await host.requestNewContext(agent, {
|
|
111
|
+
// The engine already validated the argument shape and the cited ref; the
|
|
112
|
+
// Host owns the durable intent and the generation swap that follows it at
|
|
113
|
+
// the idle boundary.
|
|
114
|
+
const outcome = await service(agent).requestNewContext(agent, {
|
|
83
115
|
memberId: current.memberId,
|
|
84
|
-
...(checkpointRef === undefined
|
|
85
|
-
...(relatedFiles.length === 0 ? {} : { relatedFiles }),
|
|
116
|
+
...(request.checkpointRef === undefined ? {} : { checkpointRef: request.checkpointRef }),
|
|
117
|
+
...(request.relatedFiles.length === 0 ? {} : { relatedFiles: [...request.relatedFiles] }),
|
|
86
118
|
});
|
|
87
|
-
|
|
88
|
-
return { mode: outcome.mode, status: 'scheduled' };
|
|
119
|
+
return { mode: outcome.mode };
|
|
89
120
|
},
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
name: 'context_checkpoint',
|
|
93
|
-
description: 'Record a named checkpoint at the end of the current turn: an opaque, private, restorable anchor for this Member\'s context lineage. Use it before a noisy or risky phase — a broad refactor, an experiment whose value is unproven — when returning to the current completed state may later be useful. The checkpoint resolves only when this turn completes; the Host continues work in the next turn automatically. A checkpoint never snapshots files, git, jobs, or any external state: returning to one (via context_rollover with its checkpointRef) resumes the conversation prefix and nothing else. Checkpoints are private context structure, not Team facts, and are never visible to other Members.',
|
|
94
|
-
parameters: {
|
|
95
|
-
name: { type: 'string', required: true, description: 'Short semantic label for this checkpoint, shown in context_timeline.' },
|
|
96
|
-
},
|
|
97
|
-
output: {
|
|
98
|
-
schema: { type: 'object', additionalProperties: false, properties: {
|
|
99
|
-
checkpointRef: { type: 'string', required: true }, name: { type: 'string', required: true },
|
|
100
|
-
} },
|
|
101
|
-
// The ref is the selection surface for `context_rollover`: rendering only the
|
|
102
|
-
// name left the model with no legitimate way to cite the anchor it just
|
|
103
|
-
// recorded. Renders are the only channel results reach the model through.
|
|
104
|
-
render: (_args, value) => [{ type: 'text', text: `Checkpoint recorded: ${value.name} (ref: ${value.checkpointRef}). Work continues in the next turn; the Host will continue automatically.` }],
|
|
105
|
-
},
|
|
106
|
-
async execute(args, exec) {
|
|
107
|
-
const agent = exec.agent;
|
|
108
|
-
if (agent === undefined)
|
|
109
|
-
throw new Error('context_checkpoint requires an Agent session');
|
|
121
|
+
async recordCheckpoint(request, exec) {
|
|
122
|
+
const agent = agentOf(exec);
|
|
110
123
|
const current = member(agent);
|
|
111
|
-
const host = service(agent);
|
|
112
|
-
const name = typeof args.name === 'string' ? args.name : '';
|
|
113
124
|
// The Host validates binding, running-turn fencing, and the name budget;
|
|
114
125
|
// the durable checkpoint is the successful call/result pair the Session
|
|
115
126
|
// projection folds, and the ref derives from the tool call id.
|
|
116
|
-
const outcome =
|
|
117
|
-
exec.concludeTurn();
|
|
127
|
+
const outcome = service(agent).recordCheckpointForAgent(agent, { memberId: current.memberId, callId: request.callId, name: request.name });
|
|
118
128
|
return { checkpointRef: outcome.checkpointRef, name: outcome.name };
|
|
119
129
|
},
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
name: 'context_timeline',
|
|
123
|
-
description: 'Inspect the bounded structural timeline of this Member\'s context lineage: named checkpoints you recorded, Team boundaries (effect anchors: a committed team_message, a successful team_claim mutation, a follow/unfollow — rendered as `Team message`, `Team task claim change`, `Team attention change`; plus a Thread\'s first delivered notice, rendered as `First arrival: <refs>`), handoff and compaction boundaries, and the current head — across the current generation and its archived ancestors. Only the first delivery of a Thread\'s facts anchors; later re-deliveries and reminders produce no boundary. Returns approximate retained/discarded token estimates, current usage against the pressure budget, the Threads whose facts entered your context by each anchor, and which anchors are restorable. A Team boundary is a selectable default checkpoint exactly when the retained prefix through it stays inside one Thread and the return would shrink the working set below the handoff budget; a boundary spanning several Threads, or attributable to none, states its reason instead. Every row carries a short `anchor` id: it distinguishes rows that share a label and a price, and it is NOT a ref — only the `checkpointRef` printed on a restorable row may be cited to context_rollover. Structural only: no transcript content. A fresh context_rollover (no checkpointRef) never requires consulting this timeline first — call it directly. Use this tool only when you specifically intend a checkpointRef return: to pick the smallest sufficient ref, or to confirm that a fresh handoff is the better path when every anchor is marked non-restorable.',
|
|
124
|
-
parameters: {
|
|
125
|
-
limit: { type: 'number', description: 'Maximum number of items to return (default 12, at most 24).' },
|
|
126
|
-
},
|
|
127
|
-
output: {
|
|
128
|
-
schema: { type: 'object', additionalProperties: false, properties: {
|
|
129
|
-
usageTokens: { type: 'number', required: true },
|
|
130
|
-
hardLimit: { type: 'number', required: true },
|
|
131
|
-
handoffAt: { type: 'number', required: true },
|
|
132
|
-
items: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: {
|
|
133
|
-
checkpointRef: { type: 'string', required: true },
|
|
134
|
-
name: { type: 'string', required: true },
|
|
135
|
-
source: { type: 'string', required: true },
|
|
136
|
-
retainedTokens: { type: 'number', required: true },
|
|
137
|
-
discardedTokens: { type: 'number', required: true },
|
|
138
|
-
affectedThreads: { type: 'array', required: true, items: { type: 'string' } },
|
|
139
|
-
restorable: { type: 'boolean', required: true },
|
|
140
|
-
reason: { type: 'string' },
|
|
141
|
-
sourceSessionId: { type: 'string' },
|
|
142
|
-
} } },
|
|
143
|
-
incompleteFrom: { type: 'object', additionalProperties: false, properties: {
|
|
144
|
-
sessionId: { type: 'string', required: true },
|
|
145
|
-
reason: { type: 'string', required: true },
|
|
146
|
-
} },
|
|
147
|
-
} },
|
|
148
|
-
// The item list is the whole decision surface: without each anchor's
|
|
149
|
-
// ref, label, source, size estimates, affected Threads, and
|
|
150
|
-
// restorable/reason verdict, the model cannot pick a `checkpointRef` for
|
|
151
|
-
// `context_rollover` — the summary line alone left the tool unusable for
|
|
152
|
-
// seeded returns. The short anchor id distinguishes rows that share a
|
|
153
|
-
// label and a price without ever printing a ref that is not usable. The
|
|
154
|
-
// Host bounds items (default 12, at most 24), so the list cannot grow
|
|
155
|
-
// unbounded. `incompleteFrom` states where and why the lineage walk
|
|
156
|
-
// stopped early, so history read up to that ancestor is known to be a
|
|
157
|
-
// truncation, not everything that exists.
|
|
158
|
-
render: (_args, value) => {
|
|
159
|
-
const lines = [`Context timeline: ${value.usageTokens} tokens used (handoff at ${value.handoffAt}, hard limit ${value.hardLimit}). ${value.items.length} item(s):`];
|
|
160
|
-
for (const item of value.items) {
|
|
161
|
-
const threads = item.affectedThreads.length === 0 ? 'no Threads' : `Threads ${item.affectedThreads.join(', ')}`;
|
|
162
|
-
const size = `retained ~${item.retainedTokens}, discarded ~${item.discardedTokens}`;
|
|
163
|
-
const restorable = item.restorable
|
|
164
|
-
? `restorable — ref: ${item.checkpointRef}`
|
|
165
|
-
: `not restorable — ${item.reason ?? 'no reason given'}`;
|
|
166
|
-
lines.push(`- ${item.name} [source: ${item.source}; anchor ${anchorId(item.checkpointRef)}] (${size}; ${threads}) — ${restorable}`);
|
|
167
|
-
}
|
|
168
|
-
if (value.incompleteFrom !== undefined) {
|
|
169
|
-
lines.push(`History incomplete: the lineage walk stopped at Session ${value.incompleteFrom.sessionId} (${value.incompleteFrom.reason}); ancestors before it could not be read and are not reflected above.`);
|
|
170
|
-
}
|
|
171
|
-
return [{ type: 'text', text: lines.join('\n') }];
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
async execute(args, exec) {
|
|
175
|
-
const agent = exec.agent;
|
|
176
|
-
if (agent === undefined)
|
|
177
|
-
throw new Error('context_timeline requires an Agent session');
|
|
130
|
+
async timeline(request, exec) {
|
|
131
|
+
const agent = agentOf(exec);
|
|
178
132
|
const current = member(agent);
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
//
|
|
133
|
+
const result = await service(agent).contextTimelineForAgent(agent, {
|
|
134
|
+
memberId: current.memberId,
|
|
135
|
+
...(request.limit === undefined ? {} : { limit: request.limit }),
|
|
136
|
+
});
|
|
137
|
+
// "This scope cannot price a compaction" and "a compaction would replace
|
|
138
|
+
// nothing" are different facts, and only the second is worth showing, so an
|
|
139
|
+
// unpriced scope omits the field and the status omits its line.
|
|
140
|
+
const scope = compactionScopeFor(agent);
|
|
141
|
+
const compactible = scope === undefined ? undefined : compactibleNow(agent.session, scope);
|
|
184
142
|
return {
|
|
185
143
|
usageTokens: result.usageTokens,
|
|
186
|
-
hardLimit: result.hardLimit,
|
|
187
144
|
handoffAt: result.handoffAt,
|
|
188
|
-
|
|
145
|
+
hardLimit: result.hardLimit,
|
|
146
|
+
...(compactible === undefined ? {} : { compactible }),
|
|
147
|
+
items: engineTimelineItems(result.items),
|
|
189
148
|
...(result.incompleteFrom === undefined ? {} : { incompleteFrom: result.incompleteFrom }),
|
|
190
149
|
};
|
|
191
150
|
},
|
|
192
|
-
|
|
151
|
+
compactionFor(agent) {
|
|
152
|
+
return compactionScopeFor(agent);
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Team vocabulary for the engine's tools. `carriedContext` names the channels a
|
|
157
|
+
* fresh generation already receives — without it the engine's "seeded only by
|
|
158
|
+
* your handoff" sentence reads as "everything must be restated", which is what
|
|
159
|
+
* our own corpus showed members doing. The checklist carries the one item the
|
|
160
|
+
* engine's default does not ask for and the corpus showed missing: which facts
|
|
161
|
+
* were verified and which were only trusted. `timelineGuidance` is the glossary
|
|
162
|
+
* the engine splices into `context_status`: the engine names the structure, and
|
|
163
|
+
* only the Team can say what its own rows mean.
|
|
164
|
+
*/
|
|
165
|
+
const TEAM_CONTINUITY_TEXT = {
|
|
166
|
+
subjectNoun: 'Team Member',
|
|
167
|
+
carriedContext: 'You stay the same Team Member: your @handle and role, your private memory index, your skills catalog, and the Team and Workspace instructions carry across a rollover — they are re-injected at birth — and the Team ledger (Threads, Tasks, Claims, your inbox, your owner jobs) is one query away (team_view, team_inbox). Do not restate any of it.',
|
|
168
|
+
rolloverChecklist: 'the objective and the atomic action in flight; facts and evidence not already recorded elsewhere; which items you verified and which you only trusted; inferences and unresolved conflicts; current external side effects and their verification state (files, git, jobs, browser state, remote calls); one explicit next step',
|
|
169
|
+
timelineGuidance: ' Team rows carry Team sources: `checkpoint` is one you recorded, `boundary` is a Team boundary this Host contributed — a committed team_message, a successful team_claim mutation, or a follow/unfollow, rendered as `Team message`, `Team task claim change`, `Team attention change` — and `head` is the current generation. A Thread\'s first delivered notice is a boundary too, rendered `First arrival: <refs>`; later re-deliveries and reminders produce none. A Team boundary is a selectable default exactly when the retained prefix through it stays inside one Thread and the return would shrink the working set below the handoff budget; a boundary spanning several Threads, or attributable to none, states its reason instead. The `anchor` shown on a row that is not restorable is a short digest, never a ref: cite only a ref printed on a restorable row.',
|
|
170
|
+
topicNoun: 'Thread',
|
|
171
|
+
topicNounPlural: 'Threads',
|
|
172
|
+
};
|
|
173
|
+
const engineTools = createContinuityTools(adapter, TEAM_CONTINUITY_TEXT);
|
|
193
174
|
export function registerContextTools(ctx) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
ctx.tools.register(
|
|
175
|
+
// The engine's four, and nothing else: the roster the Host validates against
|
|
176
|
+
// carries the same names, so no description names a tool this surface lacks.
|
|
177
|
+
ctx.tools.register(engineTools.rollover);
|
|
178
|
+
ctx.tools.register(engineTools.checkpoint);
|
|
179
|
+
ctx.tools.register(engineTools.status);
|
|
180
|
+
ctx.tools.register(engineTools.compact);
|
|
197
181
|
}
|
|
@@ -114,7 +114,7 @@ function rejectionLines(noun, value) {
|
|
|
114
114
|
}
|
|
115
115
|
const teamInbox = defineTool({
|
|
116
116
|
name: 'team_inbox',
|
|
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.',
|
|
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. This is your own unread queue; to see work others have in flight (whether or not you have unread on it), use the active-task-threads section of team_view.',
|
|
118
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: {
|
|
@@ -554,7 +554,7 @@ const teamClaim = defineTool({
|
|
|
554
554
|
});
|
|
555
555
|
const teamView = defineTool({
|
|
556
556
|
name: 'team_view',
|
|
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
|
|
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), current Members, and an「active task threads」section listing the work in flight right now — every in_progress / in_review Task Thread in your Channels, with its subject and the Members already on it. This is an address book, not a work queue: unread work lives in team_inbox, and a Thread is read with team_thread read. Consult the active-task-threads section when a Workspace change surprises you, or before you claim or commit, to tell in-flight work from a real conflict and avoid riding onto someone else\'s unpushed work — it is independent of your own unread. 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
560
|
limit: { type: 'number' }, cursor: { type: 'number' }, workspace: workspaceParam,
|
|
@@ -571,6 +571,11 @@ const teamView = defineTool({
|
|
|
571
571
|
threadRef: { type: 'string', required: true }, channelRef: { type: 'string', required: true }, revision: { type: 'number', required: true }, messageCount: { type: 'number', required: true }, subject: { type: 'string', required: true },
|
|
572
572
|
taskRef: { type: 'string' }, status: { type: 'string' }, taskNumber: { type: 'number' }, lastActivityAt: { type: 'string' },
|
|
573
573
|
} } },
|
|
574
|
+
activeTaskThreads: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: {
|
|
575
|
+
taskRef: { type: 'string', required: true }, threadRef: { type: 'string', required: true }, channelRef: { type: 'string', required: true }, status: { type: 'string', required: true }, subject: { type: 'string', required: true },
|
|
576
|
+
taskNumber: { type: 'number' }, lastActivityAt: { type: 'string', required: true },
|
|
577
|
+
members: { type: 'array', required: true, items: { type: 'object', additionalProperties: false, properties: { memberId: { type: 'string', required: true }, name: { type: 'string', required: true } } } },
|
|
578
|
+
} } },
|
|
574
579
|
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' } } } },
|
|
575
580
|
cursor: { type: 'number', required: true }, hasMore: { type: 'boolean', required: true }, page: { type: 'string' },
|
|
576
581
|
} },
|
|
@@ -608,6 +613,12 @@ const teamView = defineTool({
|
|
|
608
613
|
lines.push('No visible Members.');
|
|
609
614
|
else
|
|
610
615
|
lines.push(...value.members.map(m => `${m.memberId} · @${m.handle} (${m.kind}, ${m.presence})${m.description === '' ? '' : ` — ${m.description}`}`));
|
|
616
|
+
lines.push('', 'Active task threads — work in flight right now');
|
|
617
|
+
if (value.activeTaskThreads.length === 0)
|
|
618
|
+
lines.push('No task thread is in flight in this Workspace.');
|
|
619
|
+
else
|
|
620
|
+
lines.push(...value.activeTaskThreads.map(t => `${t.threadRef} · ${t.channelRef} · ${t.status}${t.taskNumber === undefined ? '' : ` · #${t.taskNumber}`}${t.members.length === 0 ? '' : ` · ${t.members.map(m => `@${m.name}`).join(', ')}`} — ${t.subject} · last activity ${formatTeamTimestamp(t.lastActivityAt)}`));
|
|
621
|
+
lines.push('Use this when a Workspace change surprises you, or before you claim or commit — to see who is already on a Task rather than riding onto their in-flight work. It is not the unread queue; unread work still lives in team_inbox.');
|
|
611
622
|
}
|
|
612
623
|
return [{ type: 'text', text: lines.join('\n') }];
|
|
613
624
|
},
|
|
@@ -645,6 +656,10 @@ const teamView = defineTool({
|
|
|
645
656
|
return { taskRef: task.taskRef, threadRef: task.threadRef, channelRef: task.channelRef, status: task.status,
|
|
646
657
|
...(revision === undefined ? {} : { revision }) };
|
|
647
658
|
}),
|
|
659
|
+
activeTaskThreads: view.activeTaskThreads.map(t => ({ taskRef: t.taskRef, threadRef: t.threadRef, channelRef: t.channelRef,
|
|
660
|
+
status: t.status, subject: t.subject, lastActivityAt: t.lastActivityAt,
|
|
661
|
+
...(t.taskNumber === undefined ? {} : { taskNumber: t.taskNumber }),
|
|
662
|
+
members: t.members.map(m => ({ memberId: m.memberId, name: m.name })) })),
|
|
648
663
|
cursor: view.cursor, hasMore: view.hasMore,
|
|
649
664
|
...(args.cursor === undefined ? {} : { page: 'threads' }),
|
|
650
665
|
};
|
|
@@ -1,13 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Model-facing context-management tools for Team Members.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
2
|
+
* Model-facing context-management tools for Team Members.
|
|
3
|
+
*
|
|
4
|
+
* All four are the published engine's tools, built by `createContinuityTools`:
|
|
5
|
+
* the engine owns their argument contract, the anti-forgery gate on a cited ref,
|
|
6
|
+
* the `concludeTurn()` timing, and the render shapes, while the Team supplies
|
|
7
|
+
* its own vocabulary (`TEAM_CONTINUITY_TEXT`) and the mechanism behind
|
|
8
|
+
* `ContinuityToolAdapter`. Hand-written copies of those descriptions used to
|
|
9
|
+
* live here and drifted from the engine's defaults, so the Team's guidance now
|
|
10
|
+
* travels only through the engine's text seams — including the Team glossary the
|
|
11
|
+
* engine splices into `context_status` as `timelineGuidance`.
|
|
12
|
+
*
|
|
13
|
+
* One Team rule survives only in the adapter: for a row the engine renders as
|
|
14
|
+
* not restorable it prints that row's `ref` verbatim (`brief` truncates at 120
|
|
15
|
+
* characters, and a Team ref is 83), and a printed ref is exactly what a model
|
|
16
|
+
* copies into `checkpointRef`. The adapter therefore answers such a row with a
|
|
17
|
+
* short digest instead of its ref — the same digest that keeps two rows sharing
|
|
18
|
+
* a label and a price apart — so no non-selectable row carries a citable string.
|
|
9
19
|
* @module @wowyuarm/dsh-agent-team/context-tools
|
|
10
20
|
*/
|
|
21
|
+
import { type ContextTimelineItem } from '@wowyuarm/dsh-context-continuity';
|
|
22
|
+
import { type AgentTeamTimelineItem } from '@wowyuarm/dsh-agent-team/host';
|
|
23
|
+
/**
|
|
24
|
+
* One Host timeline item in the engine's own vocabulary. Team's four sources
|
|
25
|
+
* collapse onto the engine's three kinds, and the two that say something a
|
|
26
|
+
* reader needs (`handoff`, `compaction`) ride its opaque `kind` field rather
|
|
27
|
+
* than being flattened silently.
|
|
28
|
+
*
|
|
29
|
+
* The `ref` of a row that is not restorable is a short digest rather than the
|
|
30
|
+
* ref itself: the engine's render prints this field on every row, and a printed
|
|
31
|
+
* ref is what a model copies into `checkpointRef`. Only a restorable row — the
|
|
32
|
+
* one kind of row whose ref `context_rollover` accepts — carries a citable
|
|
33
|
+
* string.
|
|
34
|
+
*/
|
|
35
|
+
export declare function engineTimelineItems(items: readonly AgentTeamTimelineItem[]): ContextTimelineItem[];
|
|
11
36
|
export declare function registerContextTools(ctx: {
|
|
12
37
|
readonly tools: {
|
|
13
38
|
register(tool: unknown): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-tools.d.ts","sourceRoot":"","sources":["../../src/context-tools.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"context-tools.d.ts","sourceRoot":"","sources":["../../src/context-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAKL,KAAK,mBAAmB,EAIzB,MAAM,kCAAkC,CAAA;AAEzC,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AA6C9F;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,qBAAqB,EAAE,GAAG,mBAAmB,EAAE,CAYlG;AA0FD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,CAOrG"}
|
|
@@ -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;AA2rB/B,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAOxC"}
|