@lemoncat7/dsh-partner 1.9.2 → 1.10.2
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 +12 -1
- package/docs/architecture.md +14 -0
- package/lib/agent-runtime.d.ts +3 -2
- package/lib/agent-runtime.d.ts.map +1 -1
- package/lib/agent-runtime.js +24 -66
- package/lib/agent-runtime.js.map +1 -1
- package/lib/api/features/workspace-api.d.ts +2 -0
- package/lib/api/features/workspace-api.d.ts.map +1 -1
- package/lib/api/features/workspace-api.js +49 -2
- package/lib/api/features/workspace-api.js.map +1 -1
- package/lib/api.d.ts +2 -0
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js.map +1 -1
- package/lib/channels/delivery-policy.d.ts +7 -0
- package/lib/channels/delivery-policy.d.ts.map +1 -1
- package/lib/channels/delivery-policy.js +44 -9
- package/lib/channels/delivery-policy.js.map +1 -1
- package/lib/channels/manager.d.ts +2 -0
- package/lib/channels/manager.d.ts.map +1 -1
- package/lib/channels/manager.js +36 -9
- package/lib/channels/manager.js.map +1 -1
- package/lib/channels/outbound-media.d.ts +6 -0
- package/lib/channels/outbound-media.d.ts.map +1 -0
- package/lib/channels/outbound-media.js +118 -0
- package/lib/channels/outbound-media.js.map +1 -0
- package/lib/client-api.d.ts +3 -0
- package/lib/client-api.d.ts.map +1 -1
- package/lib/client-api.js.map +1 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +1424 -1067
- package/lib/client.js.map +4 -4
- package/lib/collaboration/composition.d.ts +3 -1
- package/lib/collaboration/composition.d.ts.map +1 -1
- package/lib/collaboration/composition.js +30 -13
- package/lib/collaboration/composition.js.map +1 -1
- package/lib/collaboration/service.d.ts +3 -0
- package/lib/collaboration/service.d.ts.map +1 -1
- package/lib/collaboration/service.js +46 -10
- package/lib/collaboration/service.js.map +1 -1
- package/lib/domain.d.ts +2 -0
- package/lib/domain.d.ts.map +1 -1
- package/lib/domain.js.map +1 -1
- package/lib/execution/agent-support.d.ts.map +1 -1
- package/lib/execution/agent-support.js +27 -3
- package/lib/execution/agent-support.js.map +1 -1
- package/lib/execution/service.d.ts +1 -0
- package/lib/execution/service.d.ts.map +1 -1
- package/lib/execution/service.js +8 -0
- package/lib/execution/service.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +19 -3
- package/lib/index.js.map +1 -1
- package/lib/pendant/renderer.d.ts +1 -0
- package/lib/pendant/renderer.d.ts.map +1 -1
- package/lib/pendant/renderer.js +4 -0
- package/lib/pendant/renderer.js.map +1 -1
- package/lib/pendant/widget.js +3 -1
- package/lib/pendant/widget.js.map +1 -1
- package/lib/pendant-renderer.js +6 -6
- package/lib/requirements/archive.d.ts +4 -0
- package/lib/requirements/archive.d.ts.map +1 -0
- package/lib/requirements/archive.js +14 -0
- package/lib/requirements/archive.js.map +1 -0
- package/lib/requirements/consolidation.d.ts +11 -0
- package/lib/requirements/consolidation.d.ts.map +1 -0
- package/lib/requirements/consolidation.js +50 -0
- package/lib/requirements/consolidation.js.map +1 -0
- package/lib/requirements/domain.d.ts +41 -0
- package/lib/requirements/domain.d.ts.map +1 -0
- package/lib/requirements/domain.js +2 -0
- package/lib/requirements/domain.js.map +1 -0
- package/lib/requirements/progress.d.ts +7 -0
- package/lib/requirements/progress.d.ts.map +1 -0
- package/lib/requirements/progress.js +17 -0
- package/lib/requirements/progress.js.map +1 -0
- package/lib/requirements/service.d.ts +23 -0
- package/lib/requirements/service.d.ts.map +1 -0
- package/lib/requirements/service.js +163 -0
- package/lib/requirements/service.js.map +1 -0
- package/lib/requirements/tool.d.ts +5 -0
- package/lib/requirements/tool.d.ts.map +1 -0
- package/lib/requirements/tool.js +49 -0
- package/lib/requirements/tool.js.map +1 -0
- package/lib/requirements/worker.d.ts +30 -0
- package/lib/requirements/worker.d.ts.map +1 -0
- package/lib/requirements/worker.js +164 -0
- package/lib/requirements/worker.js.map +1 -0
- package/lib/skills/builtin.js +2 -2
- package/lib/skills/builtin.js.map +1 -1
- package/lib/skills/task-planning.d.ts +3 -1
- package/lib/skills/task-planning.d.ts.map +1 -1
- package/lib/skills/task-planning.js +29 -13
- package/lib/skills/task-planning.js.map +1 -1
- package/lib/store.js +2 -0
- package/lib/store.js.map +1 -1
- package/lib/tasks/context.d.ts +8 -0
- package/lib/tasks/context.d.ts.map +1 -0
- package/lib/tasks/context.js +48 -0
- package/lib/tasks/context.js.map +1 -0
- package/lib/tasks/domain.d.ts +10 -0
- package/lib/tasks/domain.d.ts.map +1 -1
- package/lib/tasks/removal.d.ts +5 -0
- package/lib/tasks/removal.d.ts.map +1 -0
- package/lib/tasks/removal.js +44 -0
- package/lib/tasks/removal.js.map +1 -0
- package/lib/tasks/requirement-link.d.ts +4 -0
- package/lib/tasks/requirement-link.d.ts.map +1 -0
- package/lib/tasks/requirement-link.js +11 -0
- package/lib/tasks/requirement-link.js.map +1 -0
- package/lib/tasks/service.d.ts +13 -5
- package/lib/tasks/service.d.ts.map +1 -1
- package/lib/tasks/service.js +91 -27
- package/lib/tasks/service.js.map +1 -1
- package/lib/ui/board-refresh.d.ts +3 -0
- package/lib/ui/board-refresh.d.ts.map +1 -0
- package/lib/ui/board-refresh.js +42 -0
- package/lib/ui/board-refresh.js.map +1 -0
- package/lib/ui/requirement-board.d.ts +5 -0
- package/lib/ui/requirement-board.d.ts.map +1 -0
- package/lib/ui/requirement-board.js +47 -0
- package/lib/ui/requirement-board.js.map +1 -0
- package/lib/ui/requirement-dialog.d.ts +15 -0
- package/lib/ui/requirement-dialog.d.ts.map +1 -0
- package/lib/ui/requirement-dialog.js +53 -0
- package/lib/ui/requirement-dialog.js.map +1 -0
- package/lib/ui/requirement-tasks-panel.d.ts +8 -0
- package/lib/ui/requirement-tasks-panel.d.ts.map +1 -0
- package/lib/ui/requirement-tasks-panel.js +269 -0
- package/lib/ui/requirement-tasks-panel.js.map +1 -0
- package/lib/ui/task-board-panel.d.ts +2 -4
- package/lib/ui/task-board-panel.d.ts.map +1 -1
- package/lib/ui/task-board-panel.js +2 -288
- package/lib/ui/task-board-panel.js.map +1 -1
- package/lib/ui/workspace-components.js +1 -1
- package/lib/ui/workspace-components.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BoardRequirement } from './domain.js';
|
|
2
|
+
/** Keep actual archived evidence, without unbounded growth or silent history eviction. */
|
|
3
|
+
export declare function retainRequirementArchive(target: BoardRequirement, archived: BoardRequirement): void;
|
|
4
|
+
//# sourceMappingURL=archive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/requirements/archive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD,0FAA0F;AAC1F,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CASnG"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Keep actual archived evidence, without unbounded growth or silent history eviction. */
|
|
2
|
+
export function retainRequirementArchive(target, archived) {
|
|
3
|
+
if (archived.status !== 'done' || !archived.archivedAt)
|
|
4
|
+
throw new Error('只能保留已完成归档的需求');
|
|
5
|
+
const history = [...(target.archiveHistory ?? []), ...(target === archived ? [] : archived.archiveHistory ?? [])];
|
|
6
|
+
if (history.length >= 20)
|
|
7
|
+
throw new Error('需求已有 20 次历史归档,请创建独立后续需求,不自动删除历史成果');
|
|
8
|
+
history.push({ requirementId: archived.id, title: archived.title, description: archived.description, revision: archived.revision,
|
|
9
|
+
archivedAt: archived.archivedAt, ...(archived.summary ? { summary: archived.summary } : {}),
|
|
10
|
+
...(archived.results ? { results: structuredClone(archived.results) } : {}),
|
|
11
|
+
...(archived.notifiedAt ? { notifiedAt: archived.notifiedAt } : {}) });
|
|
12
|
+
target.archiveHistory = history;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/requirements/archive.ts"],"names":[],"mappings":"AAEA,0FAA0F;AAC1F,MAAM,UAAU,wBAAwB,CAAC,MAAwB,EAAE,QAA0B;IAC3F,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAA;IACvF,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,CAAA;IACjH,IAAI,OAAO,CAAC,MAAM,IAAI,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IAC9E,OAAO,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC9H,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3F,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IACxE,MAAM,CAAC,cAAc,GAAG,OAAO,CAAA;AACjC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PartnerState } from '../domain.js';
|
|
2
|
+
/** Explicit maintenance only, never a title-based startup migration or an agent tool. */
|
|
3
|
+
export declare function consolidateCompletedRequirements(state: PartnerState, input: {
|
|
4
|
+
targetId: string;
|
|
5
|
+
sourceId: string;
|
|
6
|
+
targetRevision: number;
|
|
7
|
+
sourceRevision: number;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
}): void;
|
|
11
|
+
//# sourceMappingURL=consolidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidation.d.ts","sourceRoot":"","sources":["../../src/requirements/consolidation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAMhD,yFAAyF;AACzF,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE;IAC3E,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;IAClF,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CACnC,GAAG,IAAI,CA2BP"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { appendBounded } from '../core/collections.js';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { retainRequirementArchive } from './archive.js';
|
|
4
|
+
import { requirementProgressKey } from './progress.js';
|
|
5
|
+
/** Explicit maintenance only, never a title-based startup migration or an agent tool. */
|
|
6
|
+
export function consolidateCompletedRequirements(state, input) {
|
|
7
|
+
if (input.sourceId === input.targetId)
|
|
8
|
+
throw new Error('不能合并同一需求');
|
|
9
|
+
const target = state.requirements?.find(r => r.id === input.targetId);
|
|
10
|
+
const source = state.requirements?.find(r => r.id === input.sourceId);
|
|
11
|
+
if (!target)
|
|
12
|
+
throw new Error('目标需求不存在');
|
|
13
|
+
if (!source && target.archiveHistory?.some(h => h.requirementId === input.sourceId) && !state.tasks.some(t => t.requirementId === input.sourceId))
|
|
14
|
+
return;
|
|
15
|
+
if (!source)
|
|
16
|
+
throw new Error('来源需求不存在');
|
|
17
|
+
if (target.revision !== input.targetRevision || source.revision !== input.sourceRevision)
|
|
18
|
+
throw new Error('需求已改变,请重新核对,未合并');
|
|
19
|
+
if (target.status !== 'planning' || source.status !== 'done' || !source.notifiedAt)
|
|
20
|
+
throw new Error('只合并已通知的完成需求到规划中的原需求');
|
|
21
|
+
if (!target.ownerCompanionId || target.ownerCompanionId !== source.ownerCompanionId || !target.creatorSessionId || target.creatorSessionId !== source.creatorSessionId)
|
|
22
|
+
throw new Error('不同负责人或来源会话的需求不能通过此修复合并');
|
|
23
|
+
const children = state.tasks.filter(t => t.requirementId === source.id || t.requirementId === target.id);
|
|
24
|
+
const ids = new Set(children.map(t => t.id));
|
|
25
|
+
if (!children.some(t => t.requirementId === source.id) || children.some(t => t.status !== 'done') || state.delegations.some(d => ids.has(d.taskId) && ['running', 'queued'].includes(d.status)))
|
|
26
|
+
throw new Error('任务尚未全部完成,不能合并');
|
|
27
|
+
if (!input.title.trim() || input.title.length > 200 || input.description.length > 8000)
|
|
28
|
+
throw new Error('合并后的需求说明无效');
|
|
29
|
+
const oldBatchWasDelivered = Boolean(target.stageReport?.notifiedAt && target.stageReport.key === requirementProgressKey(state, target));
|
|
30
|
+
retainRequirementArchive(target, source);
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
for (const task of children.filter(t => t.requirementId === source.id)) {
|
|
33
|
+
task.requirementId = target.id;
|
|
34
|
+
task.revision++;
|
|
35
|
+
task.updatedAt = now;
|
|
36
|
+
appendBounded(state.taskActivities, { id: `activity-${randomUUID()}`, taskId: task.id, actor: 'user', kind: 'updated',
|
|
37
|
+
message: `修正续做归属:${source.id} → ${target.id};保留已验收成果,不重新执行`, at: now }, 2000);
|
|
38
|
+
}
|
|
39
|
+
target.title = input.title.trim();
|
|
40
|
+
target.description = input.description;
|
|
41
|
+
target.revision++;
|
|
42
|
+
target.updatedAt = now;
|
|
43
|
+
delete target.lastError;
|
|
44
|
+
delete target.nextAttemptAt;
|
|
45
|
+
delete target.attempts;
|
|
46
|
+
if (oldBatchWasDelivered)
|
|
47
|
+
target.reportBaselineKey = requirementProgressKey(state, target);
|
|
48
|
+
state.requirements = state.requirements.filter(r => r.id !== source.id);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=consolidation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidation.js","sourceRoot":"","sources":["../../src/requirements/consolidation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { BoardTask } from '../tasks/domain.js';
|
|
2
|
+
export interface BoardRequirement {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
ownerCompanionId?: string;
|
|
7
|
+
creatorSessionId?: string;
|
|
8
|
+
status: 'planning' | 'active' | 'review' | 'done';
|
|
9
|
+
revision: number;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
updatedAt: number;
|
|
12
|
+
summary?: string;
|
|
13
|
+
archivedAt?: number;
|
|
14
|
+
/** Immutable accepted deliverables survive deleting their original task cards. */
|
|
15
|
+
results?: Array<Pick<BoardTask, 'id' | 'title' | 'assigneeCompanionId' | 'resultSummary' | 'resultAbstract'>>;
|
|
16
|
+
nextAttemptAt?: number;
|
|
17
|
+
attempts?: number;
|
|
18
|
+
lastError?: string;
|
|
19
|
+
notifiedAt?: number;
|
|
20
|
+
/** Durable stage receipt, independent of final archiving. */
|
|
21
|
+
stageReport?: {
|
|
22
|
+
key: string;
|
|
23
|
+
summary: string;
|
|
24
|
+
createdAt: number;
|
|
25
|
+
notifiedAt?: number;
|
|
26
|
+
};
|
|
27
|
+
/** Explicit continuation/migration must not announce the same delivered batch again. */
|
|
28
|
+
reportBaselineKey?: string;
|
|
29
|
+
archiveHistory?: RequirementArchive[];
|
|
30
|
+
}
|
|
31
|
+
export interface RequirementArchive {
|
|
32
|
+
requirementId: string;
|
|
33
|
+
title: string;
|
|
34
|
+
description: string;
|
|
35
|
+
revision: number;
|
|
36
|
+
summary?: string;
|
|
37
|
+
results?: BoardRequirement['results'];
|
|
38
|
+
archivedAt: number;
|
|
39
|
+
notifiedAt?: number;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=domain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../src/requirements/domain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;IACjD,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,OAAO,GAAG,qBAAqB,GAAG,eAAe,GAAG,gBAAgB,CAAC,CAAC,CAAA;IAC7G,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6DAA6D;IAC7D,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACtF,wFAAwF;IACxF,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,cAAc,CAAC,EAAE,kBAAkB,EAAE,CAAA;CACtC;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain.js","sourceRoot":"","sources":["../../src/requirements/domain.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PartnerState } from '../domain.js';
|
|
2
|
+
import type { BoardRequirement } from './domain.js';
|
|
3
|
+
/** Ignore bookkeeping timestamps: unchanged deliverables must not produce new notifications. */
|
|
4
|
+
export declare function requirementProgressKey(state: PartnerState, item: BoardRequirement): string;
|
|
5
|
+
/** A gap between dependency dispatches is not the end of a work batch. */
|
|
6
|
+
export declare function requirementIsIdle(state: PartnerState, item: BoardRequirement): boolean;
|
|
7
|
+
//# sourceMappingURL=progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/requirements/progress.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD,gGAAgG;AAChG,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAI1F;AAED,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAMtF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/** Ignore bookkeeping timestamps: unchanged deliverables must not produce new notifications. */
|
|
3
|
+
export function requirementProgressKey(state, item) {
|
|
4
|
+
const tasks = state.tasks.filter(t => t.requirementId === item.id).sort((a, b) => a.id.localeCompare(b.id));
|
|
5
|
+
return createHash('sha256').update(JSON.stringify([item.title, item.description, tasks.map(t => [t.id, t.title, t.description, t.status, t.resultSummary, t.resultAbstract, t.rejectionReason, t.dependencyTaskIds, t.autoRun])])).digest('hex');
|
|
6
|
+
}
|
|
7
|
+
/** A gap between dependency dispatches is not the end of a work batch. */
|
|
8
|
+
export function requirementIsIdle(state, item) {
|
|
9
|
+
const tasks = state.tasks.filter(t => t.requirementId === item.id);
|
|
10
|
+
if (!tasks.length || tasks.some(t => t.status !== 'done' && t.status !== 'blocked'))
|
|
11
|
+
return false;
|
|
12
|
+
const ids = new Set(tasks.map(t => t.id));
|
|
13
|
+
if (state.delegations.some(d => ids.has(d.taskId) && ['running', 'queued'].includes(d.status)))
|
|
14
|
+
return false;
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/requirements/progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAIxC,gGAAgG;AAChG,MAAM,UAAU,sBAAsB,CAAC,KAAmB,EAAE,IAAsB;IAChF,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAC3G,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAC7F,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,iBAAiB,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACpJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,iBAAiB,CAAC,KAAmB,EAAE,IAAsB;IAC3E,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC,EAAE,CAAC,CAAA;IAClE,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC;QAAE,OAAO,KAAK,CAAA;IACjG,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACzC,IAAI,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IAC5G,OAAO,IAAI,CAAA;AACb,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PartnerStore } from '../store.js';
|
|
2
|
+
import type { TaskActor } from '../tasks/service.js';
|
|
3
|
+
import type { BoardRequirement } from './domain.js';
|
|
4
|
+
export declare function requirementDraft(title: string, description: string, ownerCompanionId?: string, creatorSessionId?: string): BoardRequirement;
|
|
5
|
+
export declare class RequirementService {
|
|
6
|
+
private readonly store;
|
|
7
|
+
constructor(store: PartnerStore);
|
|
8
|
+
list(): BoardRequirement[];
|
|
9
|
+
require(id: string): BoardRequirement;
|
|
10
|
+
create(value: unknown, actor: TaskActor, sessionId?: string): Promise<BoardRequirement>;
|
|
11
|
+
submit(id: string, revision: number, actor: TaskActor): Promise<BoardRequirement>;
|
|
12
|
+
update(id: string, revision: number, value: unknown, actor: TaskActor): Promise<BoardRequirement>;
|
|
13
|
+
assignOwner(id: string, revision: number, owner: string | undefined): Promise<BoardRequirement>;
|
|
14
|
+
reopen(id: string, revision: number, actor: TaskActor, value?: unknown): Promise<BoardRequirement>;
|
|
15
|
+
finish(id: string, revision: number, summary: string, actor: TaskActor): Promise<BoardRequirement>;
|
|
16
|
+
retry(id: string): Promise<void>;
|
|
17
|
+
private change;
|
|
18
|
+
}
|
|
19
|
+
export declare class RequirementError extends Error {
|
|
20
|
+
readonly status: number;
|
|
21
|
+
constructor(status: number, message: string);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/requirements/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAMnD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAI3I;AAED,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,YAAY;IAChD,IAAI,IAAI,gBAAgB,EAAE;IAC1B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB;IAK/B,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAYvF,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAOjF,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuBjG,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAU/F,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAclG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;IASlG,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAOxB,MAAM;CAYrB;AACD,qBAAa,gBAAiB,SAAQ,KAAK;IAAe,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAAsB"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { optionalText, record, requiredText } from '../core/validation.js';
|
|
3
|
+
import { invalidateTaskWork } from '../tasks/context.js';
|
|
4
|
+
import { retainRequirementArchive } from './archive.js';
|
|
5
|
+
import { requirementProgressKey } from './progress.js';
|
|
6
|
+
export function requirementDraft(title, description, ownerCompanionId, creatorSessionId) {
|
|
7
|
+
const now = Date.now();
|
|
8
|
+
return { id: `requirement-${randomUUID()}`, title, description, status: 'planning', revision: 1, createdAt: now, updatedAt: now,
|
|
9
|
+
...(ownerCompanionId ? { ownerCompanionId } : {}), ...(creatorSessionId ? { creatorSessionId } : {}) };
|
|
10
|
+
}
|
|
11
|
+
export class RequirementService {
|
|
12
|
+
store;
|
|
13
|
+
constructor(store) {
|
|
14
|
+
this.store = store;
|
|
15
|
+
}
|
|
16
|
+
list() { return this.store.snapshot().requirements ?? []; }
|
|
17
|
+
require(id) {
|
|
18
|
+
const item = this.list().find(item => item.id === id);
|
|
19
|
+
if (!item)
|
|
20
|
+
throw new RequirementError(404, '需求已删除或不存在,请刷新看板');
|
|
21
|
+
return item;
|
|
22
|
+
}
|
|
23
|
+
async create(value, actor, sessionId) {
|
|
24
|
+
const input = record(value, 'requirement');
|
|
25
|
+
const owner = actor.companionId ?? optionalText(input.ownerCompanionId, 'ownerCompanionId', 120);
|
|
26
|
+
const item = requirementDraft(requiredText(input.title, 'title', 200), optionalText(input.description, 'description', 8000) ?? '', owner, sessionId);
|
|
27
|
+
await this.store.update(state => {
|
|
28
|
+
if (owner && !state.companions.some(c => c.id === owner))
|
|
29
|
+
throw new RequirementError(400, '需求负责人不存在');
|
|
30
|
+
state.requirements ??= [];
|
|
31
|
+
if (state.requirements.length >= 500)
|
|
32
|
+
throw new RequirementError(400, '需求数量已达上限,请删除不再需要的归档');
|
|
33
|
+
state.requirements.push(item);
|
|
34
|
+
});
|
|
35
|
+
return item;
|
|
36
|
+
}
|
|
37
|
+
async submit(id, revision, actor) {
|
|
38
|
+
return this.change(id, revision, actor, (item, tasks) => {
|
|
39
|
+
if (item.status !== 'planning')
|
|
40
|
+
throw new RequirementError(409, '该需求已经提交,请刷新查看');
|
|
41
|
+
if (!tasks.length)
|
|
42
|
+
throw new RequirementError(409, '请先安排子任务,再提交规划');
|
|
43
|
+
item.status = 'active';
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async update(id, revision, value, actor) {
|
|
47
|
+
const input = record(value, 'requirement');
|
|
48
|
+
let result;
|
|
49
|
+
await this.store.update(state => {
|
|
50
|
+
const item = state.requirements?.find(r => r.id === id);
|
|
51
|
+
if (!item)
|
|
52
|
+
throw new RequirementError(404, '需求不存在');
|
|
53
|
+
if (actor.kind !== 'user' && item.ownerCompanionId !== actor.companionId)
|
|
54
|
+
throw new RequirementError(403, '只有需求负责人可以修改需求');
|
|
55
|
+
if (revision !== item.revision)
|
|
56
|
+
throw new RequirementError(409, '需求内容已更新,请重新读取');
|
|
57
|
+
if (item.status === 'done')
|
|
58
|
+
throw new RequirementError(409, '已归档需求不能直接改写;同一目标续做请 reopen 保留历史后追加任务');
|
|
59
|
+
const title = input.title === undefined ? item.title : requiredText(input.title, 'title', 200);
|
|
60
|
+
const description = input.description === undefined ? item.description : optionalText(input.description, 'description', 8000) ?? '';
|
|
61
|
+
if (title !== item.title || description !== item.description) {
|
|
62
|
+
item.title = title;
|
|
63
|
+
item.description = description;
|
|
64
|
+
item.status = 'planning';
|
|
65
|
+
item.revision++;
|
|
66
|
+
item.updatedAt = Date.now();
|
|
67
|
+
delete item.lastError;
|
|
68
|
+
delete item.nextAttemptAt;
|
|
69
|
+
for (const task of state.tasks.filter(t => t.requirementId === id)) {
|
|
70
|
+
invalidateTaskWork(state, task, '所属需求已更新,请根据最新需求重新执行和验收');
|
|
71
|
+
task.revision++;
|
|
72
|
+
task.updatedAt = Date.now();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
result = structuredClone(item);
|
|
76
|
+
});
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
async assignOwner(id, revision, owner) {
|
|
80
|
+
return this.change(id, revision, { kind: 'user' }, item => {
|
|
81
|
+
if (item.status === 'done')
|
|
82
|
+
throw new RequirementError(409, '已归档需求不能更改负责人');
|
|
83
|
+
if (owner && !this.store.snapshot().companions.some(c => c.id === owner))
|
|
84
|
+
throw new RequirementError(400, '需求负责人不存在');
|
|
85
|
+
if (owner)
|
|
86
|
+
item.ownerCompanionId = owner;
|
|
87
|
+
else
|
|
88
|
+
delete item.ownerCompanionId;
|
|
89
|
+
if (item.status === 'review')
|
|
90
|
+
item.status = 'active';
|
|
91
|
+
delete item.lastError;
|
|
92
|
+
delete item.nextAttemptAt;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async reopen(id, revision, actor, value) {
|
|
96
|
+
const input = value === undefined ? {} : record(value, 'continuation');
|
|
97
|
+
return this.change(id, revision, actor, item => {
|
|
98
|
+
if (item.status === 'done') {
|
|
99
|
+
retainRequirementArchive(item, item);
|
|
100
|
+
delete item.summary;
|
|
101
|
+
delete item.results;
|
|
102
|
+
delete item.archivedAt;
|
|
103
|
+
delete item.notifiedAt;
|
|
104
|
+
}
|
|
105
|
+
if (input.title !== undefined)
|
|
106
|
+
item.title = requiredText(input.title, 'title', 200);
|
|
107
|
+
if (input.description !== undefined)
|
|
108
|
+
item.description = optionalText(input.description, 'description', 8000) ?? '';
|
|
109
|
+
item.reportBaselineKey = requirementProgressKey(this.store.snapshot(), item);
|
|
110
|
+
item.status = 'planning';
|
|
111
|
+
delete item.lastError;
|
|
112
|
+
delete item.nextAttemptAt;
|
|
113
|
+
delete item.attempts;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async finish(id, revision, summary, actor) {
|
|
117
|
+
const text = requiredText(summary, 'summary', 12000);
|
|
118
|
+
return this.change(id, revision, actor, (item, tasks) => {
|
|
119
|
+
if (item.status === 'done' || !tasks.length || tasks.some(t => t.status !== 'done'))
|
|
120
|
+
throw new RequirementError(409, '需求已归档或仍有未验收任务,不能完成归档');
|
|
121
|
+
item.summary = text;
|
|
122
|
+
item.status = 'done';
|
|
123
|
+
item.archivedAt = Date.now();
|
|
124
|
+
item.results = tasks.map(({ id, title, assigneeCompanionId, resultSummary, resultAbstract }) => ({ id, title, ...(assigneeCompanionId ? { assigneeCompanionId } : {}), ...(resultSummary ? { resultSummary } : {}), ...(resultAbstract ? { resultAbstract } : {}) }));
|
|
125
|
+
delete item.lastError;
|
|
126
|
+
delete item.nextAttemptAt;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
async retry(id) {
|
|
130
|
+
await this.store.update(state => {
|
|
131
|
+
const item = state.requirements?.find(r => r.id === id);
|
|
132
|
+
if (!item)
|
|
133
|
+
throw new RequirementError(404, '需求已删除或不存在');
|
|
134
|
+
delete item.nextAttemptAt;
|
|
135
|
+
delete item.lastError;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async change(id, revision, actor, change) {
|
|
139
|
+
let result;
|
|
140
|
+
await this.store.update(state => {
|
|
141
|
+
const item = state.requirements?.find(r => r.id === id);
|
|
142
|
+
if (!item)
|
|
143
|
+
throw new RequirementError(404, '需求已删除或不存在,请刷新看板');
|
|
144
|
+
if (actor.kind !== 'user' && item.ownerCompanionId !== actor.companionId)
|
|
145
|
+
throw new RequirementError(403, '只有需求负责人可以提交、调整或完成需求');
|
|
146
|
+
if (revision !== item.revision)
|
|
147
|
+
throw new RequirementError(409, '需求内容已更新,请刷新后重试');
|
|
148
|
+
change(item, state.tasks.filter(t => t.requirementId === id));
|
|
149
|
+
item.revision++;
|
|
150
|
+
item.updatedAt = Date.now();
|
|
151
|
+
result = structuredClone(item);
|
|
152
|
+
});
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
export class RequirementError extends Error {
|
|
157
|
+
status;
|
|
158
|
+
constructor(status, message) {
|
|
159
|
+
super(message);
|
|
160
|
+
this.status = status;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/requirements/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
|
|
2
|
+
import type { RequirementService } from './service.js';
|
|
3
|
+
import type { TaskBoardService } from '../tasks/service.js';
|
|
4
|
+
export declare function requirementTool(companionId: string, service: RequirementService, tasks: TaskBoardService): ToolDefinition;
|
|
5
|
+
//# sourceMappingURL=tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/requirements/tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAE5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAE3D,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,GAAG,cAAc,CA+BzH"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { record, requiredText } from '../core/validation.js';
|
|
2
|
+
export function requirementTool(companionId, service, tasks) {
|
|
3
|
+
return {
|
|
4
|
+
name: 'partner_requirements',
|
|
5
|
+
description: 'Manage one requirement containing all tasks for a user deliverable. Create a requirement and attach child board tasks. submit confirms the complete scope: when all children are accepted the system summarizes and archives automatically. A requirement containing only done/blocked tasks sends one stage result per changed batch even without submit. Any backlog, ready, doing, review or queued execution suppresses stage delivery. Never report individual review/rework steps. Stage delivery does NOT require archiving and permits further tasks. When the owner confirms the whole requirement is complete, finish saves a real final summary and archives if all children are done, including planning requirements. Never finish merely because work is queued or blocked. update changes the authoritative requirement and invalidates old child execution/reviews; use it for changed scope, not a comment alone. For continued work on the same user goal, list and reuse the original requirement instead of creating another per specialist phase. reopen supports submitted AND archived requirements, preserving previous archives and accepted tasks; optional title/description extends the overall scope for NEW work without invalidating old deliverables. Use update only when changing acceptance requirements for existing work. remove permanently deletes only on explicit user request and is idempotent. Use the enabled task-planning Skill for decomposition.',
|
|
6
|
+
parameters: { type: 'object', additionalProperties: false, required: ['action'], properties: {
|
|
7
|
+
action: { type: 'string', enum: ['list', 'create', 'update', 'submit', 'reopen', 'finish', 'retry', 'remove'] },
|
|
8
|
+
requirementId: { type: 'string' }, title: { type: 'string' }, description: { type: 'string' },
|
|
9
|
+
expectedRevision: { type: 'integer' }, summary: { type: 'string' },
|
|
10
|
+
} },
|
|
11
|
+
output: { schema: { type: 'string' }, render: (_args, value) => [{ type: 'text', text: String(value) }] },
|
|
12
|
+
presentCall: args => ({ card: 'generic', title: `需求 · ${String(args.action ?? '操作')}` }),
|
|
13
|
+
async execute(raw, exec) {
|
|
14
|
+
const input = record(raw, 'arguments'), action = requiredText(input.action, 'action', 20);
|
|
15
|
+
const actor = { kind: 'companion', companionId };
|
|
16
|
+
if (action === 'list')
|
|
17
|
+
return JSON.stringify({ requirements: service.list() });
|
|
18
|
+
if (action === 'create')
|
|
19
|
+
return JSON.stringify(await service.create(input, actor, exec.agent?.session.id));
|
|
20
|
+
const id = requiredText(input.requirementId, 'requirementId', 160);
|
|
21
|
+
const item = service.list().find(r => r.id === id);
|
|
22
|
+
if (!item)
|
|
23
|
+
return JSON.stringify({ id, status: 'removed', message: '需求已删除或不存在,不要重建或继续旧任务' });
|
|
24
|
+
if (item.ownerCompanionId !== companionId)
|
|
25
|
+
throw new Error('当前伙伴不是此需求负责人');
|
|
26
|
+
if (action === 'remove') {
|
|
27
|
+
await tasks.removeRequirement(id);
|
|
28
|
+
return JSON.stringify({ id, removed: true });
|
|
29
|
+
}
|
|
30
|
+
if (action === 'retry') {
|
|
31
|
+
await service.retry(id);
|
|
32
|
+
return JSON.stringify({ id, retry: true });
|
|
33
|
+
}
|
|
34
|
+
const revision = input.expectedRevision;
|
|
35
|
+
if (!Number.isInteger(revision))
|
|
36
|
+
throw new Error('请先查询需求并提供 expectedRevision');
|
|
37
|
+
if (action === 'update')
|
|
38
|
+
return JSON.stringify(await service.update(id, revision, input, actor));
|
|
39
|
+
if (action === 'submit')
|
|
40
|
+
return JSON.stringify(await service.submit(id, revision, actor));
|
|
41
|
+
if (action === 'reopen')
|
|
42
|
+
return JSON.stringify(await service.reopen(id, revision, actor, input));
|
|
43
|
+
if (action === 'finish')
|
|
44
|
+
return JSON.stringify(await service.finish(id, revision, requiredText(input.summary, 'summary', 12000), actor));
|
|
45
|
+
throw new Error('需求操作无效');
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/requirements/tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { PartnerStore } from '../store.js';
|
|
2
|
+
import type { BoardTask } from '../tasks/domain.js';
|
|
3
|
+
import type { BoardRequirement } from './domain.js';
|
|
4
|
+
import { RequirementService } from './service.js';
|
|
5
|
+
interface RequirementEffects {
|
|
6
|
+
summarize(item: BoardRequirement, tasks: BoardTask[], signal: AbortSignal, stage?: boolean): Promise<string>;
|
|
7
|
+
deliver(item: BoardRequirement): Promise<void>;
|
|
8
|
+
warn(message: string): void;
|
|
9
|
+
isBusy?(companionId: string): boolean;
|
|
10
|
+
}
|
|
11
|
+
/** One bounded worker. Durable state is the retry queue, not transient callbacks. */
|
|
12
|
+
export declare class RequirementWorker {
|
|
13
|
+
private readonly store;
|
|
14
|
+
private readonly service;
|
|
15
|
+
private readonly effects;
|
|
16
|
+
private timer?;
|
|
17
|
+
private job;
|
|
18
|
+
private controller;
|
|
19
|
+
private closed;
|
|
20
|
+
private unsubscribe;
|
|
21
|
+
constructor(store: PartnerStore, service: RequirementService, effects: RequirementEffects);
|
|
22
|
+
start(): void;
|
|
23
|
+
tick(): Promise<void>;
|
|
24
|
+
beginShutdown(): void;
|
|
25
|
+
close(): Promise<void>;
|
|
26
|
+
private run;
|
|
27
|
+
}
|
|
28
|
+
export declare function requirementSummaryPrompt(item: BoardRequirement, tasks: BoardTask[], stage?: boolean): string;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/requirements/worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAGjD,UAAU,kBAAkB;IAC1B,SAAS,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC5G,OAAO,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,MAAM,CAAC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAA;CACtC;AAED,qFAAqF;AACrF,qBAAa,iBAAiB;IAMhB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAAgB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAsB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALxH,OAAO,CAAC,KAAK,CAAC,CAAgB;IAC9B,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,WAAW,CAA0B;gBAChB,KAAK,EAAE,YAAY,EAAmB,OAAO,EAAE,kBAAkB,EAAmB,OAAO,EAAE,kBAAkB;IAC5I,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrB,aAAa,IAAI,IAAI;IACf,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YACd,GAAG;CAoFlB;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,KAAK,UAAQ,GAAG,MAAM,CAe1G"}
|