@wowyuarm/dsh-agent-team 0.1.14 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +8 -4
  2. package/README.zh.md +8 -4
  3. package/cordis.patch.yml +187 -2
  4. package/package.json +52 -51
  5. package/packages/agent-team/README.md +1 -1
  6. package/packages/agent-team/README.zh.md +1 -1
  7. package/packages/agent-team/lib/context-continuity-host.js +2 -3
  8. package/packages/agent-team/lib/context-projection.js +3 -3
  9. package/packages/agent-team/lib/context-source.js +39 -13
  10. package/packages/agent-team/lib/environment-check.js +102 -0
  11. package/packages/agent-team/lib/human-profile.js +110 -13
  12. package/packages/agent-team/lib/index.js +192 -100
  13. package/packages/agent-team/lib/installed-manifest.js +32 -0
  14. package/packages/agent-team/lib/ledger.js +35 -0
  15. package/packages/agent-team/lib/member-context.js +3 -3
  16. package/packages/agent-team/lib/member-time-context.js +3 -2
  17. package/packages/agent-team/lib/pressure-policy.js +18 -11
  18. package/packages/agent-team/lib/stored-session-reader.js +1 -2
  19. package/packages/agent-team/lib/typert.host.js +479 -260
  20. package/packages/agent-team/lib/typert.remote-client.d.ts +8 -3
  21. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  22. package/packages/agent-team/lib/typert.remote-client.js +375 -228
  23. package/packages/agent-team/lib/types/context-continuity-host.d.ts.map +1 -1
  24. package/packages/agent-team/lib/types/context-source.d.ts +49 -13
  25. package/packages/agent-team/lib/types/context-source.d.ts.map +1 -1
  26. package/packages/agent-team/lib/types/environment-check.d.ts +66 -0
  27. package/packages/agent-team/lib/types/environment-check.d.ts.map +1 -0
  28. package/packages/agent-team/lib/types/human-profile.d.ts +87 -21
  29. package/packages/agent-team/lib/types/human-profile.d.ts.map +1 -1
  30. package/packages/agent-team/lib/types/index.d.ts +85 -26
  31. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  32. package/packages/agent-team/lib/types/installed-manifest.d.ts +16 -0
  33. package/packages/agent-team/lib/types/installed-manifest.d.ts.map +1 -0
  34. package/packages/agent-team/lib/types/ledger.d.ts +12 -0
  35. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  36. package/packages/agent-team/lib/types/member-context.d.ts +14 -0
  37. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  38. package/packages/agent-team/lib/types/member-time-context.d.ts +15 -0
  39. package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -1
  40. package/packages/agent-team/lib/types/pressure-policy.d.ts +5 -1
  41. package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
  42. package/packages/agent-team/lib/types/stored-session-reader.d.ts +1 -2
  43. package/packages/agent-team/lib/types/stored-session-reader.d.ts.map +1 -1
  44. package/packages/agent-team/lib/types/types/entities.d.ts +1 -8
  45. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  46. package/packages/agent-team/lib/types/types/requests-results.d.ts +84 -0
  47. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  48. package/packages/client-agent-team/README.md +1 -1
  49. package/packages/client-agent-team/README.zh.md +1 -1
  50. package/packages/client-agent-team/lib/client.js +615 -275
  51. package/packages/client-agent-team/lib/client.js.map +1 -1
  52. package/packages/client-agent-team/lib/types/client/EnvironmentCheck.d.ts +18 -0
  53. package/packages/client-agent-team/lib/types/client/EnvironmentCheck.d.ts.map +1 -0
  54. package/packages/client-agent-team/lib/types/client/EnvironmentCheck.js +58 -0
  55. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts +3 -0
  56. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts.map +1 -1
  57. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.js +7 -4
  58. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +6 -11
  59. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +3 -3
  60. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +4 -4
  61. package/packages/client-agent-team/lib/types/client/TeamComposer.js +3 -3
  62. package/packages/client-agent-team/lib/types/client/TeamFooterAction.js +2 -2
  63. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +3 -3
  64. package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +2 -2
  65. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  66. package/packages/client-agent-team/lib/types/client/TeamMessage.js +1 -1
  67. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +1 -1
  68. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
  69. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +2 -7
  70. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts.map +1 -1
  71. package/packages/client-agent-team/lib/types/client/TeamRowMenu.js +2 -2
  72. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.js +2 -2
  73. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +3 -3
  74. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +3 -3
  75. package/packages/client-agent-team/lib/types/client/TeamWorkspaceSelector.js +2 -2
  76. package/packages/client-agent-team/lib/types/client/environment-check.d.ts +65 -0
  77. package/packages/client-agent-team/lib/types/client/environment-check.d.ts.map +1 -0
  78. package/packages/client-agent-team/lib/types/client/environment-check.js +78 -0
  79. package/packages/client-agent-team/lib/types/client/human-identity.d.ts +4 -8
  80. package/packages/client-agent-team/lib/types/client/human-identity.d.ts.map +1 -1
  81. package/packages/client-agent-team/lib/types/client/human-identity.js +0 -21
  82. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  83. package/packages/client-agent-team/lib/types/client/index.js +59 -49
  84. package/packages/client-agent-team/lib/types/client/locales.d.ts +18 -8
  85. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  86. package/packages/client-agent-team/lib/types/client/locales.js +21 -8
  87. package/packages/client-agent-team/lib/types/client/multi-menu-field.js +2 -2
  88. package/packages/client-agent-team/lib/types/client/slots.d.ts +1 -1
  89. package/packages/tool-agent-team/README.md +1 -1
  90. package/packages/tool-agent-team/README.zh.md +1 -1
  91. package/packages/tool-agent-team/lib/context-tools.js +106 -94
  92. package/packages/tool-agent-team/lib/index.js +17 -2
  93. package/packages/tool-agent-team/lib/types/context-tools.d.ts +16 -7
  94. package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
  95. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
  96. package/packages/agent-team/lib/preset-roster.js +0 -15
  97. package/packages/agent-team/lib/session-remediation.js +0 -493
  98. package/packages/agent-team/lib/types/preset-roster.d.ts +0 -5
  99. package/packages/agent-team/lib/types/preset-roster.d.ts.map +0 -1
  100. package/packages/agent-team/lib/types/session-remediation.d.ts +0 -172
  101. package/packages/agent-team/lib/types/session-remediation.d.ts.map +0 -1
  102. package/packages/agent-team/preset/team-member/agent.cordis.yml +0 -166
@@ -58,7 +58,7 @@ export type TeamSidebarProps = PropsRuntime<'sidebar.workspaces'> & PropsLocale<
58
58
  /**
59
59
  * Host clear-context remote kept as a hidden migration escape hatch.
60
60
  * The visible row action is retired: Members manage their own context
61
- * through the context_rollover tool (see docs/architecture.md).
61
+ * through the context_rollover tool (see docs/architecture/README.md).
62
62
  */
63
63
  clearMemberContext: (request: AgentTeamClearMemberContextRequest) => Promise<RemoteResult<AgentTeamClearMemberContextResult>>;
64
64
  archiveMember: (request: AgentTeamArchiveMemberRequest) => Promise<RemoteResult<AgentTeamArchiveMemberResult>>;
@@ -21,7 +21,7 @@ Every agent-facing render carries absolute event instants in the fixed Team coor
21
21
 
22
22
  Canonical results expose stable refs, optional Task status, Thread revision, Claim history, Attention, and unread facts as structured fields. The model-visible renders follow the action decision surface: the write token appears only on a fully drained `team_thread read` and a committed public mutation; browsing results (`team_view`, `team_inbox`, status/follow/unfollow, `history`, `team_claim list`) and all typed rejections render no revision and no token. Typed `unread_required` and `stale_revision` results keep the structured fields needed to reread and retry deliberately. Tool execution resolves the exact live `exec.agent`; arguments cannot select or impersonate the actor, and the optional `workspace` selector can only pick among that actor's participated Workspaces. Write request identity derives from sessionId plus tool callId. `context_rollover` and `context_checkpoint` conclude the Agent turn (a rollover must not be followed by old-generation work, and a checkpoint resolves at its containing turn's end); every other Team tool returns to the model loop.
23
23
 
24
- The complete implemented protocol is documented in [`../../docs/team-collaboration.md`](../../docs/team-collaboration.md).
24
+ The complete implemented protocol is documented in [`../../docs/team-collaboration/README.md`](../../docs/team-collaboration/README.md).
25
25
 
26
26
  ## Composition
27
27
 
@@ -21,7 +21,7 @@ mention 在 Message 正文里撰写,形如 `@Handle`(`@` 必需、大小写
21
21
 
22
22
  Canonical result 以结构化字段暴露稳定 refs、可选 Task status、Thread revision、Claim history、Attention 和未读 facts。模型可见的渲染遵循 action decision surface:写令牌只出现在未读清零的 `team_thread read` 与一次已提交的 public mutation 上;浏览类结果(`team_view`、`team_inbox`、status/follow/unfollow、`history`、`team_claim list`)与所有类型化拒绝都不渲染 revision 与令牌。类型化的 `unread_required` 与 `stale_revision` 结果保留重新读取和审慎重试所需的结构化字段。工具执行通过准确的 live `exec.agent` 解析 actor;参数不能选择或冒充 actor,可选的 `workspace` 选择器只能在该 actor 已参与的 Workspace 中选择。写操作的 request identity 由 sessionId 与 tool callId 派生。`context_rollover` 与 `context_checkpoint` 结束 Agent turn;其余 Team tools 将结果返回模型循环,不主动结束 turn。
23
23
 
24
- 完整的已实现协议见 [`../../docs/team-collaboration.md`](../../docs/team-collaboration.md)。
24
+ 完整的已实现协议见 [`../../docs/team-collaboration/README.md`](../../docs/team-collaboration/README.md)。
25
25
 
26
26
  ## Composition
27
27
 
@@ -1,18 +1,27 @@
1
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.
2
+ * Model-facing context-management tools for Team Members.
3
+ *
4
+ * `context_rollover` and `context_checkpoint` are the published engine's tools,
5
+ * built by `createContinuityTools`: the engine owns their argument contract, the
6
+ * anti-forgery gate on a cited ref, the `concludeTurn()` timing, and the render
7
+ * shapes, while the Team supplies its own vocabulary (`TEAM_CONTINUITY_TEXT`)
8
+ * and the mechanism behind `ContinuityToolAdapter`. Hand-written copies of those
9
+ * two descriptions used to live here and drifted from the engine's defaults, so
10
+ * the Team's guidance now travels only through the engine's text seams.
11
+ *
12
+ * `context_timeline` deliberately stays Team-owned. Its render never prints a
13
+ * ref-shaped string for a non-restorable row — a short digest names the row
14
+ * instead, because a printed ref is exactly what a model copies into
15
+ * `checkpointRef` — and the engine's render has no switch for that. The
16
+ * adapter's own `timeline` member is still implemented: the engine's contract
17
+ * requires it, and the shape it returns is the one the engine's render reads.
9
18
  * @module @wowyuarm/dsh-agent-team/context-tools
10
19
  */
11
20
  import { createHash } from 'node:crypto';
21
+ import { createContinuityTools, } from '@wowyuarm/dsh-context-continuity';
22
+ import { MAX_TIMELINE_LIMIT } from '@wowyuarm/dsh-agent-team/host';
12
23
  import { defineTool } from '@deepseek-ai/dsh-tools';
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
@@ -24,100 +33,101 @@ const MAX_RELATED_FILES = 32;
24
33
  function anchorId(checkpointRef) {
25
34
  return createHash('sha256').update(checkpointRef).digest('hex').slice(0, 6);
26
35
  }
27
- const contextRollover = defineTool({
28
- name: 'context_rollover',
29
- description: 'context_rollover: end this context generation and continue as the same Team Member in a new one. Without checkpointRef the context starts fresh and empty, seeded only by your handoff — this is the default, cheapest path at context pressure, and the right choice for ordinary generation changes and pressure-driven handoffs. Omit checkpointRef unless you are deliberately returning to a restorable anchor you just selected from a context_timeline result: supply a checkpointRef only when that timeline listed it as restorable and you are citing its exact ref — never synthesize, guess, or reconstruct one; a fabricated ref rejects as a model-visible error. Write the handoff as one prose string covering: current objective and every active Thread/Claim; verified facts and evidence; inferences and unresolved conflicts; current external side effects and their verification state (files, git, jobs, browser state, remote calls); one explicit next step. A context change never rolls back any external effect — describe current state so the next generation can re-verify. Record anything worth keeping in your private memory/notes first. Collect or stop your background jobs before calling: a rollover is refused while jobs this Member owns are still running.',
30
- parameters: {
31
- handoff: { type: 'string', required: true, description: 'Prose handoff for the next context generation: objective, active Threads/Claims, verified facts, inferences, external side effects and their verification state, next step.' },
32
- checkpointRef: { type: 'string', description: 'Optional. Omit for the default fresh rollover — ordinary generation changes and pressure-driven handoffs must not supply this. Provide it only to resume from a restorable anchor you just selected in a context_timeline result, citing that exact ref; never synthesize or guess a ref.' },
33
- relatedFiles: { type: 'array', items: { type: 'object', additionalProperties: false, properties: { path: { type: 'string', required: true }, reason: { type: 'string', required: true } } }, description: 'Workspace paths the next generation should look at first, each with one reason.' },
34
- },
35
- output: {
36
- schema: { type: 'object', additionalProperties: false, properties: {
37
- mode: { type: 'string', required: true }, status: { type: 'string', required: true },
38
- } },
39
- render: (_args, value) => [{ type: 'text', text: `Context rollover scheduled (${value.mode}). Finish this turn; the Host switches you to the next context generation afterward.` }],
36
+ /** The calling Agent, or a model-visible rejection — these tools only exist in a Member Session. */
37
+ function agentOf(exec) {
38
+ const agent = exec.agent;
39
+ if (agent === undefined)
40
+ throw new Error('context tool requires an Agent session');
41
+ return agent;
42
+ }
43
+ /**
44
+ * Team's half of the engine's contract: resolve the calling execution to its
45
+ * Member and Host, answer the ref gate from the one policy that owns it, and run
46
+ * the effects. Every method resolves its own caller, because one adapter serves
47
+ * all three tools.
48
+ */
49
+ const adapter = {
50
+ /**
51
+ * One policy, two readers: a ref is restorable exactly when the Team timeline
52
+ * — the same list the model picked from — offers it as such. The walk is asked
53
+ * for the widest window the timeline tool can show, so any ref a timeline read
54
+ * could have printed is answered here.
55
+ */
56
+ async isRestorableRef(checkpointRef, exec) {
57
+ const agent = agentOf(exec);
58
+ const current = member(agent);
59
+ const timeline = await service(agent).contextTimelineForAgent(agent, { memberId: current.memberId, limit: MAX_TIMELINE_LIMIT });
60
+ return timeline.items.some(item => item.checkpointRef === checkpointRef && item.restorable);
40
61
  },
41
- async execute(args, exec) {
42
- const agent = exec.agent;
43
- if (agent === undefined)
44
- throw new Error('context_rollover requires an Agent session');
62
+ async requestRollover(request, exec) {
63
+ const agent = agentOf(exec);
45
64
  const current = member(agent);
46
- const host = service(agent);
47
- const handoff = typeof args.handoff === 'string' ? args.handoff : '';
48
- if (handoff.trim() === '')
49
- throw new Error('context_rollover requires a non-empty handoff');
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, {
65
+ // The engine already validated the argument shape and the cited ref; the
66
+ // Host owns the durable intent and the generation swap that follows it at
67
+ // the idle boundary.
68
+ const outcome = await service(agent).requestNewContext(agent, {
83
69
  memberId: current.memberId,
84
- ...(checkpointRef === undefined || checkpointRef === '' ? {} : { checkpointRef: checkpointRef }),
85
- ...(relatedFiles.length === 0 ? {} : { relatedFiles }),
70
+ ...(request.checkpointRef === undefined ? {} : { checkpointRef: request.checkpointRef }),
71
+ ...(request.relatedFiles.length === 0 ? {} : { relatedFiles: [...request.relatedFiles] }),
86
72
  });
87
- exec.concludeTurn();
88
- return { mode: outcome.mode, status: 'scheduled' };
73
+ return { mode: outcome.mode };
89
74
  },
90
- });
91
- const contextCheckpoint = defineTool({
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');
75
+ async recordCheckpoint(request, exec) {
76
+ const agent = agentOf(exec);
110
77
  const current = member(agent);
111
- const host = service(agent);
112
- const name = typeof args.name === 'string' ? args.name : '';
113
78
  // The Host validates binding, running-turn fencing, and the name budget;
114
79
  // the durable checkpoint is the successful call/result pair the Session
115
80
  // projection folds, and the ref derives from the tool call id.
116
- const outcome = host.recordCheckpointForAgent(agent, { memberId: current.memberId, callId: exec.callId, name });
117
- exec.concludeTurn();
81
+ const outcome = service(agent).recordCheckpointForAgent(agent, { memberId: current.memberId, callId: request.callId, name: request.name });
118
82
  return { checkpointRef: outcome.checkpointRef, name: outcome.name };
119
83
  },
120
- });
84
+ async timeline(request, exec) {
85
+ const agent = agentOf(exec);
86
+ const current = member(agent);
87
+ const result = await service(agent).contextTimelineForAgent(agent, {
88
+ memberId: current.memberId,
89
+ ...(request.limit === undefined ? {} : { limit: request.limit }),
90
+ });
91
+ // Team's item vocabulary is its own (`agent` / `team-boundary` / `handoff` /
92
+ // `compaction` / `head`); the engine names the same anchors in its terms —
93
+ // four Team sources collapse onto the engine's three kinds, and the two that
94
+ // say something a reader needs (`handoff`, `compaction`) ride its opaque
95
+ // `kind` field rather than being flattened silently.
96
+ return {
97
+ usageTokens: result.usageTokens,
98
+ handoffAt: result.handoffAt,
99
+ hardLimit: result.hardLimit,
100
+ items: result.items.map(item => ({
101
+ ref: item.checkpointRef,
102
+ label: item.name,
103
+ source: item.source === 'agent' ? 'checkpoint' : item.source === 'head' ? 'head' : 'boundary',
104
+ ...(item.source === 'handoff' || item.source === 'compaction' ? { kind: item.source } : {}),
105
+ retainedTokens: item.retainedTokens,
106
+ discardedTokens: item.discardedTokens,
107
+ affectedTopics: [...item.affectedThreads],
108
+ restorable: item.restorable,
109
+ ...(item.reason === undefined ? {} : { reason: item.reason }),
110
+ })),
111
+ ...(result.incompleteFrom === undefined ? {} : { incompleteFrom: result.incompleteFrom }),
112
+ };
113
+ },
114
+ };
115
+ /**
116
+ * Team vocabulary for the engine's tools. `carriedContext` names the channels a
117
+ * fresh generation already receives — without it the engine's "seeded only by
118
+ * your handoff" sentence reads as "everything must be restated", which is what
119
+ * our own corpus showed members doing. The checklist carries the one item the
120
+ * engine's default does not ask for and the corpus showed missing: which facts
121
+ * were verified and which were only trusted.
122
+ */
123
+ const TEAM_CONTINUITY_TEXT = {
124
+ subjectNoun: 'Team Member',
125
+ 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.',
126
+ 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',
127
+ topicNoun: 'Thread',
128
+ topicNounPlural: 'Threads',
129
+ };
130
+ const engineTools = createContinuityTools(adapter, TEAM_CONTINUITY_TEXT);
121
131
  const contextTimeline = defineTool({
122
132
  name: 'context_timeline',
123
133
  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.',
@@ -191,7 +201,9 @@ const contextTimeline = defineTool({
191
201
  },
192
202
  });
193
203
  export function registerContextTools(ctx) {
194
- ctx.tools.register(contextRollover);
195
- ctx.tools.register(contextCheckpoint);
204
+ // The engine's two, then the Team's own timeline: one registration each, and
205
+ // the roster the Host validates against stays the same three names.
206
+ ctx.tools.register(engineTools.rollover);
207
+ ctx.tools.register(engineTools.checkpoint);
196
208
  ctx.tools.register(contextTimeline);
197
209
  }
@@ -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 current Members. This is an address book, not a work queue — unread work lives in team_inbox, and a Thread is read with team_thread read. The cursor pages Thread rows only.',
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,11 +1,20 @@
1
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.
2
+ * Model-facing context-management tools for Team Members.
3
+ *
4
+ * `context_rollover` and `context_checkpoint` are the published engine's tools,
5
+ * built by `createContinuityTools`: the engine owns their argument contract, the
6
+ * anti-forgery gate on a cited ref, the `concludeTurn()` timing, and the render
7
+ * shapes, while the Team supplies its own vocabulary (`TEAM_CONTINUITY_TEXT`)
8
+ * and the mechanism behind `ContinuityToolAdapter`. Hand-written copies of those
9
+ * two descriptions used to live here and drifted from the engine's defaults, so
10
+ * the Team's guidance now travels only through the engine's text seams.
11
+ *
12
+ * `context_timeline` deliberately stays Team-owned. Its render never prints a
13
+ * ref-shaped string for a non-restorable row — a short digest names the row
14
+ * instead, because a printed ref is exactly what a model copies into
15
+ * `checkpointRef` — and the engine's render has no switch for that. The
16
+ * adapter's own `timeline` member is still implemented: the engine's contract
17
+ * requires it, and the shape it returns is the one the engine's render reads.
9
18
  * @module @wowyuarm/dsh-agent-team/context-tools
10
19
  */
11
20
  export declare function registerContextTools(ctx: {
@@ -1 +1 @@
1
- {"version":3,"file":"context-tools.d.ts","sourceRoot":"","sources":["../../src/context-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsLH,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
+ {"version":3,"file":"context-tools.d.ts","sourceRoot":"","sources":["../../src/context-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAqMH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,CAMrG"}
@@ -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;AA8qB/B,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAOxC"}
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"}
@@ -1,15 +0,0 @@
1
- import { dirname, resolve } from 'node:path';
2
- import { fileURLToPath } from 'node:url';
3
- import AgentPresets from '@deepseek-ai/dsh-agent-presets';
4
- export const inject = ['loader'];
5
- /** Bundle-private Agent preset roster. The composition must isolate `agentPresets` around this provider and AgentTeam. */
6
- export function apply(ctx) {
7
- const here = dirname(fileURLToPath(import.meta.url));
8
- const root = resolve(here, '../preset');
9
- ctx.plugin(AgentPresets, {
10
- default: 'team-member',
11
- roots: [{ path: root, trust: 'system' }],
12
- includeShippedRoot: false,
13
- includeUserRoot: false,
14
- });
15
- }