@suifei/dsh-ai-team 0.3.17 → 0.3.18

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.18 — 2026-11-10
4
+
5
+ - 打开 Project 时回填已有 Thread 中对 Human 的拍板请求到收件箱;
6
+ - 修复升级前已有的“Thread 里等 Human 决定,但收件箱为空”。
7
+
3
8
  ## 0.3.17 — 2026-11-10
4
9
 
5
10
  - Thread 中 Agent 明确提到 Human / 请你拍板时,自动创建 Human 决策收件箱项;
package/index.js CHANGED
@@ -34,7 +34,7 @@ export const name = 'ai-team';
34
34
  /** The workbench only makes sense where the Web carrier exists. */
35
35
  export const inject = ['webServer', 'permissionPresets', 'sandboxPolicy'];
36
36
 
37
- const VERSION = '0.3.17';
37
+ const VERSION = '0.3.18';
38
38
  const PROJECT_ID = 'prj_main';
39
39
  const HUMAN_ID = 'mem_human';
40
40
 
@@ -61,6 +61,7 @@ export class ProjectRuntime {
61
61
  });
62
62
  }
63
63
  await seedDefaultRoster(service, projectId, { humanId: HUMAN_ID });
64
+ await service.backfillHumanMentions();
64
65
  }
65
66
  return runtime;
66
67
  }
@@ -19,6 +19,11 @@ import { boardView, quadrantsView } from './views.js';
19
19
  import { applyMemberConfigPatch, normalizeMemberConfig } from '../domain/member-config.js';
20
20
  import { isQaMember, unfinishedTasksFor } from '../domain/member-profile.js';
21
21
 
22
+ function mentionsHuman(body) {
23
+ return typeof body === 'string'
24
+ && /(^|[^A-Za-z])(human|@你|待你|需要你|请你|等你|你拍板)([^A-Za-z]|$)/i.test(body);
25
+ }
26
+
22
27
  /**
23
28
  * Team domain service on top of a Project ledger.
24
29
  *
@@ -131,6 +136,56 @@ export class TeamService {
131
136
  };
132
137
  }
133
138
 
139
+ /**
140
+ * Create missing Human decision items for existing Agent Thread messages
141
+ * that explicitly asked Human to decide. Runs on Project open once; the
142
+ * per-entry dedupe makes it idempotent.
143
+ */
144
+ async backfillHumanMentions() {
145
+ const humanId = this.state.humanMemberId
146
+ ?? Object.values(this.state.members ?? {}).find((member) => member.kind === 'human')?.id
147
+ ?? null;
148
+ if (humanId === null) return 0;
149
+ const projectId = Object.keys(this.state.projects ?? {})[0];
150
+ const existing = new Set(
151
+ Object.values(this.state.inbox ?? {})
152
+ .filter((item) => item.memberId === humanId && item.ref?.entrySeq !== undefined)
153
+ .map((item) => `${item.ref?.taskId}:${item.ref?.entrySeq}`),
154
+ );
155
+ let created = 0;
156
+ for (const task of Object.values(this.state.tasks ?? {})) {
157
+ if (task.tombstone === true) continue;
158
+ for (const entry of this.state.threads?.[task.id]?.entries ?? []) {
159
+ const author = this.state.members?.[entry.authorId];
160
+ if (author?.kind !== 'agent' || !mentionsHuman(entry.body)) continue;
161
+ const key = `${task.id}:${entry.seq}`;
162
+ if (existing.has(key)) continue;
163
+ existing.add(key);
164
+ const item = createInboxItem({
165
+ memberId: humanId,
166
+ type: 'decision_required',
167
+ ref: {
168
+ taskId: task.id,
169
+ entrySeq: entry.seq,
170
+ memberId: entry.authorId,
171
+ category: 'thread_mention',
172
+ title: String(entry.body).slice(0, 180),
173
+ },
174
+ requiresDecision: true,
175
+ }, { at: this.now() });
176
+ await this.ledger.append({
177
+ type: 'inbox/item-created',
178
+ payload: { itemId: item.id, ...item },
179
+ actor: { kind: 'system' },
180
+ projectId,
181
+ at: this.now(),
182
+ });
183
+ created += 1;
184
+ }
185
+ }
186
+ return created;
187
+ }
188
+
134
189
  /**
135
190
  * Dispatch one command. The caller is an exact Team member (or Human) and
136
191
  * the ledger serializes commands per Project.
@@ -800,9 +855,7 @@ export class TeamService {
800
855
  // Human inbox; otherwise the board looks busy while the Human never sees
801
856
  // the question (the exact failure mode behind “对话面板没有反馈”).
802
857
  const body = typeof payload.body === 'string' ? payload.body : '';
803
- const mentionsHuman = actorKind === 'agent'
804
- && /(^|[^A-Za-z])(human|@你|待你|需要你|请你|等你|你拍板)([^A-Za-z]|$)/i.test(body);
805
- if (mentionsHuman) {
858
+ if (actorKind === 'agent' && mentionsHuman(body)) {
806
859
  const humanId = this.state.humanMemberId
807
860
  ?? Object.values(this.state.members ?? {}).find((candidate) => candidate.kind === 'human')?.id
808
861
  ?? null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suifei/dsh-ai-team",
3
- "version": "0.3.17",
3
+ "version": "0.3.18",
4
4
  "type": "module",
5
5
  "description": "文渊阁 (Wenyuan Pavilion) — AI Team mode for DeepSeek Harness: concurrent projects, owned tasks, threads, inbox and an append-only team ledger",
6
6
  "license": "MIT",