@co0ontty/wand 4.76.0-beta.ge98b32f → 4.77.0-beta.g6c4df97
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/dist/agent-dispatch.d.ts +46 -0
- package/dist/agent-dispatch.js +84 -0
- package/dist/ai-team-availability.d.ts +69 -0
- package/dist/ai-team-availability.js +100 -0
- package/dist/ai-team-prompts.d.ts +113 -0
- package/dist/ai-team-prompts.js +435 -0
- package/dist/ai-team-runner.d.ts +241 -0
- package/dist/ai-team-runner.js +1370 -0
- package/dist/ai-team-types.d.ts +147 -0
- package/dist/ai-team-types.js +32 -0
- package/dist/attention.d.ts +40 -0
- package/dist/attention.js +86 -0
- package/dist/build-info.json +3 -3
- package/dist/claude-sdk-runner.d.ts +5 -0
- package/dist/claude-sdk-runner.js +2 -1
- package/dist/commit-task-archive.d.ts +3 -0
- package/dist/commit-task-archive.js +43 -0
- package/dist/config.js +0 -2
- package/dist/git-quick-commit.d.ts +2 -1
- package/dist/git-quick-commit.js +72 -60
- package/dist/message-truncator.d.ts +41 -5
- package/dist/message-truncator.js +255 -38
- package/dist/missions.d.ts +4 -2
- package/dist/missions.js +2 -2
- package/dist/process-manager.d.ts +1 -0
- package/dist/process-manager.js +17 -8
- package/dist/prompt-optimizer.js +13 -13
- package/dist/server-ai-team-routes.d.ts +11 -0
- package/dist/server-ai-team-routes.js +283 -0
- package/dist/server-attention-routes.d.ts +9 -0
- package/dist/server-attention-routes.js +26 -0
- package/dist/server-file-routes.js +1 -1
- package/dist/server-session-routes.js +41 -5
- package/dist/server-settings-routes.js +1 -1
- package/dist/server-task-routes.d.ts +2 -0
- package/dist/server-task-routes.js +6 -40
- package/dist/server-workspace-routes.js +13 -5
- package/dist/server.js +43 -0
- package/dist/session-topic.js +13 -13
- package/dist/session-transport.d.ts +3 -0
- package/dist/session-transport.js +1 -0
- package/dist/storage.d.ts +54 -1
- package/dist/storage.js +413 -2
- package/dist/structured-claude-adapter.d.ts +5 -0
- package/dist/structured-claude-adapter.js +13 -2
- package/dist/structured-claude-protocol.d.ts +2 -0
- package/dist/structured-claude-protocol.js +4 -0
- package/dist/structured-codex-adapter.js +2 -2
- package/dist/structured-grok-adapter.js +2 -1
- package/dist/structured-opencode-adapter.js +2 -2
- package/dist/structured-pi-adapter.js +2 -1
- package/dist/structured-provider-common.d.ts +17 -0
- package/dist/structured-provider-common.js +52 -0
- package/dist/structured-qoder-adapter.js +2 -1
- package/dist/structured-session-manager.d.ts +18 -1
- package/dist/structured-session-manager.js +67 -10
- package/dist/system-ai.d.ts +13 -2
- package/dist/system-ai.js +9 -5
- package/dist/task-title.js +10 -10
- package/dist/types.d.ts +25 -2
- package/dist/wand-task-sync.d.ts +1 -0
- package/dist/wand-task-sync.js +1 -0
- package/dist/web-ui/content/ai-teams.js +12 -0
- package/dist/web-ui/content/scripts.js +86 -5301
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -0
- package/dist/web-ui/embedded-assets.js +3 -2
- package/dist/web-ui/scripts.d.ts +8 -0
- package/dist/web-ui/scripts.js +28 -2
- package/dist/ws-broadcast.js +52 -19
- package/package.json +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ProcessManager } from "./process-manager.js";
|
|
2
|
+
import type { SessionRegistry } from "./session-registry.js";
|
|
3
|
+
import type { WandStorage } from "./storage.js";
|
|
4
|
+
import type { StructuredSessionManager } from "./structured-session-manager.js";
|
|
5
|
+
import type { WandTask, WandTaskAgent } from "./task-types.js";
|
|
6
|
+
import type { SessionSnapshot, WandConfig } from "./types.js";
|
|
7
|
+
export interface AgentDispatchDeps {
|
|
8
|
+
storage: WandStorage;
|
|
9
|
+
config: WandConfig;
|
|
10
|
+
structured: StructuredSessionManager | null;
|
|
11
|
+
processes: ProcessManager | null;
|
|
12
|
+
}
|
|
13
|
+
export interface AgentDispatchResult {
|
|
14
|
+
session: SessionSnapshot;
|
|
15
|
+
cwd: string;
|
|
16
|
+
workspaceId: string;
|
|
17
|
+
}
|
|
18
|
+
/** 任务卡的执行目录:工作任务的 worktree / 目录 → 所属项目目录 → 全局默认目录。 */
|
|
19
|
+
export declare function resolveTaskDispatchTarget(deps: Pick<AgentDispatchDeps, "storage" | "config">, task: WandTask): {
|
|
20
|
+
cwd: string;
|
|
21
|
+
workspaceId: string;
|
|
22
|
+
workspaceTaskId: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* 为任务卡开一个新会话(structured 或 PTY,按 agent.kind),发送首条提示词,并把会话绑定回任务卡。
|
|
26
|
+
* cwd 取任务的工作任务目录 / 所属项目目录;未指定项目时用全局默认目录。
|
|
27
|
+
* 结构化会话的整轮 completion 不等待:进度与失败走正常会话事件。
|
|
28
|
+
*/
|
|
29
|
+
export declare function dispatchAgentForTask(deps: AgentDispatchDeps, input: {
|
|
30
|
+
task: WandTask;
|
|
31
|
+
agent: WandTaskAgent;
|
|
32
|
+
prompt: string;
|
|
33
|
+
automationId: string;
|
|
34
|
+
systemPrompt?: string;
|
|
35
|
+
}): Promise<AgentDispatchResult>;
|
|
36
|
+
/**
|
|
37
|
+
* 给已有会话追加一条消息。结构化会话走 sendMessage(不等整轮结束);
|
|
38
|
+
* PTY 按输入契约先写文本、再单独写 "\r",不用 text + "\n" 代替回车。
|
|
39
|
+
*/
|
|
40
|
+
export declare function sendToAgentSession(deps: Pick<AgentDispatchDeps, "structured" | "processes"> & {
|
|
41
|
+
sessions: SessionRegistry;
|
|
42
|
+
}, sessionId: string, text: string): Promise<void>;
|
|
43
|
+
/** 与 /api/sessions/:id/stop 同样按会话归属分派到对应 manager。 */
|
|
44
|
+
export declare function stopAgentSession(deps: Pick<AgentDispatchDeps, "structured" | "processes"> & {
|
|
45
|
+
sessions: SessionRegistry;
|
|
46
|
+
}, sessionId: string): void;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { getDefaultModelForProvider } from "./config.js";
|
|
2
|
+
import { providerCliCommand } from "./session-provider.js";
|
|
3
|
+
import { ensureWorkspaceTaskForBoardTask } from "./wand-task-sync.js";
|
|
4
|
+
/** 任务卡的执行目录:工作任务的 worktree / 目录 → 所属项目目录 → 全局默认目录。 */
|
|
5
|
+
export function resolveTaskDispatchTarget(deps, task) {
|
|
6
|
+
const workspace = task.workspaceId ? deps.storage.getWorkspace(task.workspaceId) : null;
|
|
7
|
+
if (task.workspaceId && !workspace)
|
|
8
|
+
throw new Error("任务所属项目已被删除,请重新指定。");
|
|
9
|
+
const group = ensureWorkspaceTaskForBoardTask(deps.storage, task);
|
|
10
|
+
const cwd = group.worktree?.path || group.cwd || workspace?.cwd || deps.config.defaultCwd;
|
|
11
|
+
return { cwd, workspaceId: group.workspaceId, workspaceTaskId: group.id };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 为任务卡开一个新会话(structured 或 PTY,按 agent.kind),发送首条提示词,并把会话绑定回任务卡。
|
|
15
|
+
* cwd 取任务的工作任务目录 / 所属项目目录;未指定项目时用全局默认目录。
|
|
16
|
+
* 结构化会话的整轮 completion 不等待:进度与失败走正常会话事件。
|
|
17
|
+
*/
|
|
18
|
+
export async function dispatchAgentForTask(deps, input) {
|
|
19
|
+
const { storage, config, structured, processes } = deps;
|
|
20
|
+
const { agent } = input;
|
|
21
|
+
const systemPrompt = input.systemPrompt?.trim() || undefined;
|
|
22
|
+
if (agent.kind === "structured" && !structured)
|
|
23
|
+
throw new Error("当前服务未启用结构化会话,无法派发 Agent。");
|
|
24
|
+
if (agent.kind === "pty" && !processes)
|
|
25
|
+
throw new Error("当前服务未启用终端会话,无法派发 Agent。");
|
|
26
|
+
const group = resolveTaskDispatchTarget(deps, input.task);
|
|
27
|
+
const cwd = group.cwd;
|
|
28
|
+
const provider = agent.provider;
|
|
29
|
+
const model = agent.model === "default" ? "" : agent.model;
|
|
30
|
+
const resolvedModel = model || getDefaultModelForProvider(config, provider) || undefined;
|
|
31
|
+
const session = agent.kind === "pty"
|
|
32
|
+
? await processes.start(providerCliCommand(provider), cwd, agent.mode, input.prompt, {
|
|
33
|
+
provider,
|
|
34
|
+
model: resolvedModel,
|
|
35
|
+
thinkingEffort: agent.thinkingEffort,
|
|
36
|
+
sessionSource: "automation",
|
|
37
|
+
automationId: input.automationId,
|
|
38
|
+
systemPrompt,
|
|
39
|
+
workspaceId: group.workspaceId,
|
|
40
|
+
workspaceTaskId: group.workspaceTaskId,
|
|
41
|
+
})
|
|
42
|
+
: structured.createSession({
|
|
43
|
+
cwd,
|
|
44
|
+
mode: agent.mode,
|
|
45
|
+
provider,
|
|
46
|
+
model: resolvedModel,
|
|
47
|
+
thinkingEffort: agent.thinkingEffort,
|
|
48
|
+
worktreeEnabled: false,
|
|
49
|
+
sessionSource: "automation",
|
|
50
|
+
automationId: input.automationId,
|
|
51
|
+
systemPrompt,
|
|
52
|
+
workspaceId: group.workspaceId,
|
|
53
|
+
workspaceTaskId: group.workspaceTaskId,
|
|
54
|
+
});
|
|
55
|
+
storage.bindWandTaskSession(input.task.id, session.id);
|
|
56
|
+
if (agent.kind !== "pty") {
|
|
57
|
+
const completion = structured.sendMessage(session.id, input.prompt);
|
|
58
|
+
completion.catch((error) => console.error(`[AgentDispatch] ${input.automationId} failed:`, error));
|
|
59
|
+
}
|
|
60
|
+
return { session, cwd, workspaceId: group.workspaceId };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 给已有会话追加一条消息。结构化会话走 sendMessage(不等整轮结束);
|
|
64
|
+
* PTY 按输入契约先写文本、再单独写 "\r",不用 text + "\n" 代替回车。
|
|
65
|
+
*/
|
|
66
|
+
export async function sendToAgentSession(deps, sessionId, text) {
|
|
67
|
+
if (deps.sessions.ownerOf(sessionId) === "structured") {
|
|
68
|
+
const completion = deps.structured.sendMessage(sessionId, text);
|
|
69
|
+
completion.catch((error) => console.error(`[AgentDispatch] message to ${sessionId} failed:`, error));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!deps.processes)
|
|
73
|
+
throw new Error("当前服务未启用终端会话。");
|
|
74
|
+
await deps.processes.sendInputConfirmed(sessionId, text, "terminal");
|
|
75
|
+
await deps.processes.sendInputConfirmed(sessionId, "\r", "terminal", "enter_text");
|
|
76
|
+
}
|
|
77
|
+
/** 与 /api/sessions/:id/stop 同样按会话归属分派到对应 manager。 */
|
|
78
|
+
export function stopAgentSession(deps, sessionId) {
|
|
79
|
+
if (deps.sessions.ownerOf(sessionId) === "structured") {
|
|
80
|
+
deps.structured?.stop(sessionId);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
deps.processes?.stop(sessionId);
|
|
84
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { CandidateFailureKind } from "./ai-team-types.js";
|
|
2
|
+
import type { WandTaskAgent, WandTaskAgentKind } from "./task-types.js";
|
|
3
|
+
/**
|
|
4
|
+
* v2 启动失败分类与候选降级判定(设计文档 §3.4)。
|
|
5
|
+
* 本模块只放纯函数:不接 runner、不读全局状态、不碰时钟与文件系统;
|
|
6
|
+
* 时间窗与快照一律由调用方算好传入。
|
|
7
|
+
*/
|
|
8
|
+
/** 类别真源在 ai-team-types.ts(StepDispatchInfo 引用),这里按文档口径对外再导出一次。 */
|
|
9
|
+
export type { CandidateFailureKind } from "./ai-team-types.js";
|
|
10
|
+
/**
|
|
11
|
+
* startup-timeout 判定的启动窗口(§3.4、§11-Q7):固定常量,刻意不开 config 口子。
|
|
12
|
+
* 窗口内「会话已没 + 从没出过 assistant 回复」才视为启动失败可降级;窗口外按 runtime-failure 对待。
|
|
13
|
+
*/
|
|
14
|
+
export declare const AI_TEAM_STARTUP_WINDOW_MS = 45000;
|
|
15
|
+
export declare function isSessionGoneStatus(status: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 按错误文案分类。只认 spawn-missing 与 host-disabled 两类稳定信号;
|
|
18
|
+
* 无法归类的启动期错误一律 runtime-failure(不降级,宁失败交回 Leader)。
|
|
19
|
+
* model-unknown 只由事前快照比对产生(isModelUnknownBeforeDispatch),事后一律不判。
|
|
20
|
+
*/
|
|
21
|
+
export declare function classifyCandidateFailure(errorMessage: string | null | undefined): CandidateFailureKind;
|
|
22
|
+
export interface StartupFailureProbe {
|
|
23
|
+
/** 会话快照状态(SessionSnapshot.status 字面量)。 */
|
|
24
|
+
status: string;
|
|
25
|
+
/** snapshot.messages 是否出现过 assistant 回复(有任何输出即算)。 */
|
|
26
|
+
hasAssistantReply: boolean;
|
|
27
|
+
/** now - step.startedAt(毫秒);由调用方计算。 */
|
|
28
|
+
elapsedMs: number;
|
|
29
|
+
/** structuredState.lastError 等错误文案;没有则留空。 */
|
|
30
|
+
errorMessage?: string | null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 异步启动失败的组合判定:先按文案(ENOENT 优先于窗口),再看 startup window。
|
|
34
|
+
* 结论只会是 spawn-missing / host-disabled / startup-timeout / runtime-failure。
|
|
35
|
+
*/
|
|
36
|
+
export declare function classifyStartupFailure(probe: StartupFailureProbe): CandidateFailureKind;
|
|
37
|
+
export interface WorkFailureInput {
|
|
38
|
+
/** 只有 work 步骤参与降级;leader 步骤走既有格式重试/waiting_user 路径。 */
|
|
39
|
+
stepKind: "leader" | "work";
|
|
40
|
+
/** PTY 候选一律不异步分类、不降级(§3.4 修正 B4)。 */
|
|
41
|
+
agentKind: WandTaskAgentKind;
|
|
42
|
+
/** stepOutcome 给出的会话失败信号。 */
|
|
43
|
+
sessionError: boolean;
|
|
44
|
+
/** 报告文件已有产出 → 会话启动是成功的,不降级。 */
|
|
45
|
+
hasReportOutput: boolean;
|
|
46
|
+
/** classifyStartupFailure / classifyCandidateFailure 的结果。 */
|
|
47
|
+
failure: CandidateFailureKind;
|
|
48
|
+
/** 本步实际使用的候选下标。 */
|
|
49
|
+
usedCandidate: number;
|
|
50
|
+
/** 该成员的候选总数(memberAgents(member).length)。 */
|
|
51
|
+
candidateCount: number;
|
|
52
|
+
/** 下一候选是否已在 run 级黑名单里。 */
|
|
53
|
+
nextCandidateBlocked: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 降级判定核(§3.4 触发点 2):全部条件满足才允许「降级开新步」。
|
|
57
|
+
* host-disabled / model-unknown / runtime-failure / format-error / user-stop 一律 false。
|
|
58
|
+
*/
|
|
59
|
+
export declare function isDegradableWorkFailure(input: WorkFailureInput): boolean;
|
|
60
|
+
/** 事前比对用的最小快照视图:一列模型 id(字符串或带 id 字段的条目)。 */
|
|
61
|
+
export type ModelCatalogView = ReadonlyArray<string | {
|
|
62
|
+
id?: unknown;
|
|
63
|
+
} | null | undefined> | null | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* model-unknown 的事前检查(§3.4 修正 B3),方向是「拿不准就放行」:
|
|
66
|
+
* 仅当快照存在、清单非空、model 不是 "default" 且精确匹配失败时才返回 true。
|
|
67
|
+
* 快照缺失 / 空清单(冷启动)、"default"、命中清单 → false,照常派发交给事后降级。
|
|
68
|
+
*/
|
|
69
|
+
export declare function isModelUnknownBeforeDispatch(agent: WandTaskAgent, snapshot: ModelCatalogView): boolean;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* startup-timeout 判定的启动窗口(§3.4、§11-Q7):固定常量,刻意不开 config 口子。
|
|
3
|
+
* 窗口内「会话已没 + 从没出过 assistant 回复」才视为启动失败可降级;窗口外按 runtime-failure 对待。
|
|
4
|
+
*/
|
|
5
|
+
export const AI_TEAM_STARTUP_WINDOW_MS = 45_000;
|
|
6
|
+
/** 与 runner isSessionGone 同语义的状态清单(本地镜像,保持本模块零依赖 runner)。 */
|
|
7
|
+
const SESSION_GONE_STATUSES = ["exited", "failed", "stopped"];
|
|
8
|
+
export function isSessionGoneStatus(status) {
|
|
9
|
+
return SESSION_GONE_STATUSES.includes(status);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* CLI 缺失类文案(spawn-missing)。来源样例(只引用不改):
|
|
13
|
+
* - structured-session-manager.ts: codex exec ENOENT →「PATH 中找不到 … 可执行文件」
|
|
14
|
+
* - git-quick-commit.ts: error.code === "ENOENT" →「未找到 <cmd> CLI。」
|
|
15
|
+
* - claude-sdk-runner.ts:「Claude Code native binary not found」/ ENOENT →「未找到 claude CLI。」
|
|
16
|
+
*/
|
|
17
|
+
const SPAWN_MISSING_PATTERNS = [
|
|
18
|
+
/\bENOENT\b/i,
|
|
19
|
+
/No such file or directory/i,
|
|
20
|
+
/command not found/i,
|
|
21
|
+
/native binary not found/i,
|
|
22
|
+
/找不到[^\n]{0,24}可执行文件/,
|
|
23
|
+
/未找到[^\n]{0,24}CLI/,
|
|
24
|
+
/(cli|binary|executable|可执行文件)[^\n]{0,24}not found/i,
|
|
25
|
+
/not found[^\n]{0,24}(cli|binary|executable)/i,
|
|
26
|
+
];
|
|
27
|
+
/** host-disabled 文案源:agent-dispatch.ts 的「当前服务未启用结构化/终端会话,无法派发 Agent。」 */
|
|
28
|
+
const HOST_DISABLED_PATTERNS = [/未启用[^\n]{0,8}会话/];
|
|
29
|
+
/**
|
|
30
|
+
* 按错误文案分类。只认 spawn-missing 与 host-disabled 两类稳定信号;
|
|
31
|
+
* 无法归类的启动期错误一律 runtime-failure(不降级,宁失败交回 Leader)。
|
|
32
|
+
* model-unknown 只由事前快照比对产生(isModelUnknownBeforeDispatch),事后一律不判。
|
|
33
|
+
*/
|
|
34
|
+
export function classifyCandidateFailure(errorMessage) {
|
|
35
|
+
const text = (errorMessage ?? "").trim();
|
|
36
|
+
if (!text)
|
|
37
|
+
return "runtime-failure";
|
|
38
|
+
if (HOST_DISABLED_PATTERNS.some((pattern) => pattern.test(text)))
|
|
39
|
+
return "host-disabled";
|
|
40
|
+
if (SPAWN_MISSING_PATTERNS.some((pattern) => pattern.test(text)))
|
|
41
|
+
return "spawn-missing";
|
|
42
|
+
return "runtime-failure";
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 异步启动失败的组合判定:先按文案(ENOENT 优先于窗口),再看 startup window。
|
|
46
|
+
* 结论只会是 spawn-missing / host-disabled / startup-timeout / runtime-failure。
|
|
47
|
+
*/
|
|
48
|
+
export function classifyStartupFailure(probe) {
|
|
49
|
+
const byText = classifyCandidateFailure(probe.errorMessage);
|
|
50
|
+
if (byText !== "runtime-failure")
|
|
51
|
+
return byText;
|
|
52
|
+
if (isSessionGoneStatus(probe.status)
|
|
53
|
+
&& !probe.hasAssistantReply
|
|
54
|
+
&& probe.elapsedMs < AI_TEAM_STARTUP_WINDOW_MS) {
|
|
55
|
+
return "startup-timeout";
|
|
56
|
+
}
|
|
57
|
+
return "runtime-failure";
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 降级判定核(§3.4 触发点 2):全部条件满足才允许「降级开新步」。
|
|
61
|
+
* host-disabled / model-unknown / runtime-failure / format-error / user-stop 一律 false。
|
|
62
|
+
*/
|
|
63
|
+
export function isDegradableWorkFailure(input) {
|
|
64
|
+
if (input.stepKind !== "work")
|
|
65
|
+
return false;
|
|
66
|
+
if (input.agentKind !== "structured")
|
|
67
|
+
return false;
|
|
68
|
+
if (!input.sessionError)
|
|
69
|
+
return false;
|
|
70
|
+
if (input.hasReportOutput)
|
|
71
|
+
return false;
|
|
72
|
+
if (input.failure !== "spawn-missing" && input.failure !== "startup-timeout")
|
|
73
|
+
return false;
|
|
74
|
+
if (input.usedCandidate >= input.candidateCount - 1)
|
|
75
|
+
return false;
|
|
76
|
+
if (input.nextCandidateBlocked)
|
|
77
|
+
return false;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* model-unknown 的事前检查(§3.4 修正 B3),方向是「拿不准就放行」:
|
|
82
|
+
* 仅当快照存在、清单非空、model 不是 "default" 且精确匹配失败时才返回 true。
|
|
83
|
+
* 快照缺失 / 空清单(冷启动)、"default"、命中清单 → false,照常派发交给事后降级。
|
|
84
|
+
*/
|
|
85
|
+
export function isModelUnknownBeforeDispatch(agent, snapshot) {
|
|
86
|
+
if (!Array.isArray(snapshot) || snapshot.length === 0)
|
|
87
|
+
return false;
|
|
88
|
+
if (agent.model === "default")
|
|
89
|
+
return false;
|
|
90
|
+
const ids = new Set();
|
|
91
|
+
for (const entry of snapshot) {
|
|
92
|
+
if (typeof entry === "string")
|
|
93
|
+
ids.add(entry);
|
|
94
|
+
else if (entry && typeof entry === "object" && typeof entry.id === "string")
|
|
95
|
+
ids.add(entry.id);
|
|
96
|
+
}
|
|
97
|
+
if (ids.size === 0)
|
|
98
|
+
return false;
|
|
99
|
+
return !ids.has(agent.model);
|
|
100
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { type AiTeam, type AiTeamMember, type AiTeamRun, type AiTeamRunStatus, type AiTeamStep } from "./ai-team-types.js";
|
|
2
|
+
import type { ConversationTurn } from "./types.js";
|
|
3
|
+
export declare const AI_TEAM_REPORT_DIR = ".wand-team";
|
|
4
|
+
export declare const AI_TEAM_MAX_ASSIGN_STEPS = 5;
|
|
5
|
+
export interface AiTeamAssignedStep {
|
|
6
|
+
memberId: string;
|
|
7
|
+
title: string;
|
|
8
|
+
instructions: string;
|
|
9
|
+
/** 依赖本次 steps 里的哪些项(0 起下标);空数组 = 立刻开始。 */
|
|
10
|
+
after: number[];
|
|
11
|
+
}
|
|
12
|
+
export type AiTeamLeaderDecision = {
|
|
13
|
+
action: "assign";
|
|
14
|
+
message: string;
|
|
15
|
+
steps: AiTeamAssignedStep[];
|
|
16
|
+
} | {
|
|
17
|
+
action: "ask";
|
|
18
|
+
message: string;
|
|
19
|
+
} | {
|
|
20
|
+
action: "finish";
|
|
21
|
+
message: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 团队这一轮要做的事。
|
|
25
|
+
* 指派框 / 群聊里刚输入的文字就是本轮任务,不再把任务卡上的旧描述静默拼进去。
|
|
26
|
+
* 没有单独输入时(创建任务后立刻交给团队)才用标题 + 描述。
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildAiTeamObjective(task: {
|
|
29
|
+
title: string;
|
|
30
|
+
description: string;
|
|
31
|
+
}, note?: string): string;
|
|
32
|
+
export declare function leaderOf(team: AiTeam): AiTeamMember;
|
|
33
|
+
export declare function workersOf(team: AiTeam): AiTeamMember[];
|
|
34
|
+
/** 每步的约定报告文件(相对 cwd)。所有 CLI 都能写文件,因此是通用的交接方式。 */
|
|
35
|
+
export declare function aiTeamReportPath(runId: string, seq: number, kind: "leader" | "work", memberId: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* 交接文件(相对 cwd):上一批步骤的报告全文汇总,服务端在派发前写好,
|
|
38
|
+
* 下一步的提示词只给这条路径,让执行者自己去读,不再把报告贴进提示词。
|
|
39
|
+
*/
|
|
40
|
+
export declare function aiTeamHandoffPath(runId: string, seq: number, kind: "leader" | "work"): string;
|
|
41
|
+
/**
|
|
42
|
+
* 群聊续跑的上下文文件(相对 cwd):用户在同一个群聊里接着说话时,服务端把之前几轮的
|
|
43
|
+
* 步骤摘要与群聊原文写在这里,新一轮的负责人与成员先读它再动手,不从零重来。
|
|
44
|
+
* 文件名由运行 id 决定,所以在库里不用额外记一列;「文件在不在」就是「是不是续跑」。
|
|
45
|
+
*/
|
|
46
|
+
export declare function aiTeamChatHistoryPath(runId: string): string;
|
|
47
|
+
/** 一条待交接的上游步骤:提示词里只给元信息,正文进交接文件。 */
|
|
48
|
+
export interface AiTeamHandoffStep {
|
|
49
|
+
seq: number;
|
|
50
|
+
title: string;
|
|
51
|
+
memberName: string;
|
|
52
|
+
status: AiTeamStep["status"];
|
|
53
|
+
reportPath: string;
|
|
54
|
+
}
|
|
55
|
+
/** 交接文件正文用的条目:元信息 + 报告全文。 */
|
|
56
|
+
export interface AiTeamHandoffEntry extends AiTeamHandoffStep {
|
|
57
|
+
report: string;
|
|
58
|
+
}
|
|
59
|
+
/** 交接文件正文(Markdown)。纯函数,便于单测。 */
|
|
60
|
+
export declare function renderHandoffFile(entries: AiTeamHandoffEntry[], generatedAt: string): string;
|
|
61
|
+
/** 成员步骤的上游交接(依赖步骤);没有依赖时为 null。 */
|
|
62
|
+
export interface AiTeamUpstream {
|
|
63
|
+
handoffPath: string;
|
|
64
|
+
steps: AiTeamHandoffStep[];
|
|
65
|
+
}
|
|
66
|
+
/** 群聊上下文文件里的一段「之前几轮运行」摘要:只列步骤与状态,报告正文在群聊原文里。 */
|
|
67
|
+
export interface AiTeamHistoryRun {
|
|
68
|
+
status: AiTeamRunStatus;
|
|
69
|
+
steps: Array<{
|
|
70
|
+
seq: number;
|
|
71
|
+
memberName: string;
|
|
72
|
+
title: string;
|
|
73
|
+
status: AiTeamStep["status"];
|
|
74
|
+
}>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 群聊续跑的交接文件(Markdown,纯函数便于单测):先列之前几轮的步骤摘要(含上一轮没做完的),
|
|
78
|
+
* 再给群聊原文;原文只留最近的这一段,超预算时从最早处丢并注明。
|
|
79
|
+
*/
|
|
80
|
+
export declare function renderChatHistoryFile(input: {
|
|
81
|
+
runs: AiTeamHistoryRun[];
|
|
82
|
+
turns: ConversationTurn[];
|
|
83
|
+
generatedAt: string;
|
|
84
|
+
maxBytes?: number;
|
|
85
|
+
}): string;
|
|
86
|
+
/**
|
|
87
|
+
* 一轮提示词:`system` 交给 CLI 的系统提示通道(角色、规则、回复格式),`message` 是这一轮的
|
|
88
|
+
* 用户消息(目标、指派、报告路径)。provider 支持系统提示时必须走 `system`,不要把规则塞进消息里。
|
|
89
|
+
*/
|
|
90
|
+
export interface AiTeamPrompt {
|
|
91
|
+
system: string;
|
|
92
|
+
message: string;
|
|
93
|
+
}
|
|
94
|
+
/** `chatHistoryPath` 有值时说明这一轮是同一个群聊的续跑(见 `aiTeamChatHistoryPath`)。 */
|
|
95
|
+
export declare function buildLeaderKickoffPrompt(run: AiTeamRun, reportPath: string, fresh: boolean, chatHistoryPath?: string | null): AiTeamPrompt;
|
|
96
|
+
export interface AiTeamFinishedStepSummary {
|
|
97
|
+
step: AiTeamStep;
|
|
98
|
+
memberName: string;
|
|
99
|
+
}
|
|
100
|
+
export declare function buildLeaderFollowupPrompt(run: AiTeamRun, finished: AiTeamFinishedStepSummary[], reportPath: string, userNote: string | undefined, fresh: boolean, handoffPath?: string | null, chatHistoryPath?: string | null): AiTeamPrompt;
|
|
101
|
+
export declare function buildLeaderFormatRetryPrompt(run: AiTeamRun, error: string, reportPath: string, fresh: boolean, chatHistoryPath?: string | null): AiTeamPrompt;
|
|
102
|
+
/**
|
|
103
|
+
* fresh=false 时成员会话已有上下文,省掉目标段落。`upstream` 有值时只给交接文件路径与
|
|
104
|
+
* 各步报告文件,不在提示词里复述上游报告正文(上一个成员的信息走文件交接)。
|
|
105
|
+
*/
|
|
106
|
+
export declare function buildMemberPrompt(run: AiTeamRun, step: AiTeamStep, member: AiTeamMember, fresh: boolean, upstream?: AiTeamUpstream | null, chatHistoryPath?: string | null): AiTeamPrompt;
|
|
107
|
+
export declare function parseLeaderDecision(text: string, team: AiTeam): {
|
|
108
|
+
ok: true;
|
|
109
|
+
decision: AiTeamLeaderDecision;
|
|
110
|
+
} | {
|
|
111
|
+
ok: false;
|
|
112
|
+
error: string;
|
|
113
|
+
};
|