@co0ontty/wand 4.83.1 → 4.84.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.
- package/README.md +4 -0
- package/dist/agent-dispatch.js +5 -1
- package/dist/ai-team-availability.d.ts +1 -1
- package/dist/ai-team-prompts.js +13 -4
- package/dist/ai-team-runner.d.ts +80 -5
- package/dist/ai-team-runner.js +332 -55
- package/dist/ai-team-types.d.ts +92 -5
- package/dist/ai-team-types.js +9 -2
- package/dist/build-info.json +3 -3
- package/dist/cli-api.d.ts +1 -0
- package/dist/cli-api.js +3 -0
- package/dist/cli.js +18 -1
- package/dist/commit-task-archive.d.ts +3 -1
- package/dist/commit-task-archive.js +21 -10
- package/dist/config.d.ts +1 -1
- package/dist/config.js +73 -0
- package/dist/conversation-mentions.d.ts +16 -0
- package/dist/conversation-mentions.js +32 -0
- package/dist/conversation-service.d.ts +90 -0
- package/dist/conversation-service.js +759 -0
- package/dist/conversation-session-preview.d.ts +9 -0
- package/dist/conversation-session-preview.js +47 -0
- package/dist/conversation-task-preview.d.ts +5 -0
- package/dist/conversation-task-preview.js +8 -0
- package/dist/conversation-types.d.ts +80 -0
- package/dist/conversation-types.js +7 -0
- package/dist/core-extension-host.d.ts +27 -0
- package/dist/core-extension-host.js +138 -0
- package/dist/core-runner.d.ts +86 -0
- package/dist/core-runner.js +757 -0
- package/dist/core-status-cli.d.ts +9 -0
- package/dist/core-status-cli.js +280 -0
- package/dist/core-turn-tracker.d.ts +12 -0
- package/dist/core-turn-tracker.js +36 -0
- package/dist/decision-runner.d.ts +6 -0
- package/dist/decision-runner.js +3 -1
- package/dist/decision-tool.d.ts +2 -0
- package/dist/decision-tool.js +49 -22
- package/dist/env-utils.d.ts +7 -3
- package/dist/env-utils.js +30 -5
- package/dist/git-quick-commit.d.ts +5 -0
- package/dist/git-quick-commit.js +9 -3
- package/dist/git-utils.js +2 -1
- package/dist/harness-engine.d.ts +110 -0
- package/dist/harness-engine.js +331 -0
- package/dist/mission-diff.js +2 -1
- package/dist/model-auto-assign.d.ts +50 -0
- package/dist/model-auto-assign.js +163 -0
- package/dist/model-group-runner.d.ts +4 -0
- package/dist/model-group-runner.js +72 -0
- package/dist/model-groups.d.ts +38 -0
- package/dist/model-groups.js +138 -0
- package/dist/models.d.ts +12 -0
- package/dist/models.js +102 -8
- package/dist/npm-update-utils.js +2 -1
- package/dist/openrouter-free-cli.d.ts +25 -0
- package/dist/openrouter-free-cli.js +109 -0
- package/dist/openrouter-free-models.d.ts +85 -0
- package/dist/openrouter-free-models.js +458 -0
- package/dist/openrouter-free-selection.d.ts +5 -0
- package/dist/openrouter-free-selection.js +7 -0
- package/dist/path-repair.d.ts +10 -0
- package/dist/path-repair.js +50 -5
- package/dist/pi-auto-resources.d.ts +12 -0
- package/dist/pi-auto-resources.js +151 -0
- package/dist/pi-execution-types.d.ts +48 -0
- package/dist/pi-execution-types.js +1 -0
- package/dist/pi-execution.d.ts +9 -0
- package/dist/pi-execution.js +187 -0
- package/dist/pi-extension-resources.d.ts +13 -0
- package/dist/pi-extension-resources.js +71 -0
- package/dist/pi-openrouter-free-bridge.d.ts +2 -0
- package/dist/pi-openrouter-free-bridge.js +80 -0
- package/dist/pi-resource-bridge.d.ts +2 -0
- package/dist/pi-resource-bridge.js +73 -0
- package/dist/pi-resource-catalog.d.ts +18 -0
- package/dist/pi-resource-catalog.js +82 -0
- package/dist/pi-resource-recommendation.d.ts +29 -0
- package/dist/pi-resource-recommendation.js +169 -0
- package/dist/pi-resource-run.d.ts +8 -0
- package/dist/pi-resource-run.js +28 -0
- package/dist/pi-session-settings.d.ts +157 -0
- package/dist/pi-session-settings.js +166 -0
- package/dist/platform-action-catalog.d.ts +24 -0
- package/dist/platform-action-catalog.js +141 -0
- package/dist/platform-action-service.d.ts +37 -0
- package/dist/platform-action-service.js +138 -0
- package/dist/platform-action-types.d.ts +125 -0
- package/dist/platform-action-types.js +7 -0
- package/dist/process-manager.d.ts +21 -1
- package/dist/process-manager.js +168 -15
- package/dist/provider-catalog.js +1 -1
- package/dist/provider-cli-updater.js +29 -8
- package/dist/provider-history-scanner.d.ts +9 -0
- package/dist/provider-history-scanner.js +107 -1
- package/dist/retention.d.ts +19 -7
- package/dist/retention.js +171 -28
- package/dist/server-ai-team-routes.js +6 -2
- package/dist/server-conversation-routes.d.ts +5 -0
- package/dist/server-conversation-routes.js +91 -0
- package/dist/server-employee-routes.d.ts +2 -0
- package/dist/server-employee-routes.js +85 -11
- package/dist/server-pi-recommendation-routes.d.ts +13 -0
- package/dist/server-pi-recommendation-routes.js +67 -0
- package/dist/server-resume-routes.js +1 -1
- package/dist/server-session-routes.d.ts +4 -3
- package/dist/server-session-routes.js +182 -37
- package/dist/server-settings-routes.d.ts +5 -0
- package/dist/server-settings-routes.js +46 -0
- package/dist/server-task-routes.js +49 -4
- package/dist/server-team-dispatch-routes.d.ts +19 -0
- package/dist/server-team-dispatch-routes.js +71 -0
- package/dist/server-update-routes.d.ts +1 -1
- package/dist/server-update-routes.js +10 -5
- package/dist/server-workspace-routes.js +44 -24
- package/dist/server.d.ts +1 -0
- package/dist/server.js +148 -30
- package/dist/session-ai-context.d.ts +5 -1
- package/dist/session-ai-context.js +9 -4
- package/dist/session-registry.d.ts +4 -0
- package/dist/session-registry.js +47 -13
- package/dist/session-transport.js +8 -1
- package/dist/settings-web-access.d.ts +15 -0
- package/dist/settings-web-access.js +37 -0
- package/dist/silicon-employee-defaults.d.ts +29 -0
- package/dist/silicon-employee-defaults.js +41 -0
- package/dist/silicon-employee-dispatch.d.ts +5 -1
- package/dist/silicon-employee-dispatch.js +10 -4
- package/dist/silicon-employee-draft.d.ts +21 -4
- package/dist/silicon-employee-draft.js +103 -16
- package/dist/storage.d.ts +73 -2
- package/dist/storage.js +362 -26
- package/dist/structured-claude-protocol.js +1 -1
- package/dist/structured-client-protocol.js +11 -33
- package/dist/structured-content.d.ts +23 -0
- package/dist/structured-content.js +72 -0
- package/dist/structured-failure.d.ts +16 -0
- package/dist/structured-failure.js +36 -0
- package/dist/structured-pi-adapter.d.ts +8 -3
- package/dist/structured-pi-adapter.js +131 -30
- package/dist/structured-provider-common.d.ts +6 -0
- package/dist/structured-provider-common.js +23 -1
- package/dist/structured-runner.d.ts +45 -0
- package/dist/structured-session-manager.d.ts +78 -4
- package/dist/structured-session-manager.js +513 -129
- package/dist/structured-thinking.d.ts +15 -0
- package/dist/structured-thinking.js +46 -0
- package/dist/subagent-dispatch.d.ts +56 -0
- package/dist/subagent-dispatch.js +102 -0
- package/dist/system-employee.d.ts +4 -3
- package/dist/system-employee.js +7 -4
- package/dist/task-retention.d.ts +23 -0
- package/dist/task-retention.js +69 -0
- package/dist/task-types.d.ts +4 -0
- package/dist/task-types.js +3 -0
- package/dist/team-dispatch.d.ts +101 -0
- package/dist/team-dispatch.js +269 -0
- package/dist/tool-preview.js +7 -1
- package/dist/tui/commands.js +50 -1
- package/dist/tui/ipc-protocol.d.ts +1 -1
- package/dist/tui/ipc-server.d.ts +4 -0
- package/dist/tui/ipc-server.js +23 -0
- package/dist/turn-heartbeat.d.ts +35 -0
- package/dist/turn-heartbeat.js +64 -0
- package/dist/types.d.ts +147 -1
- package/dist/user-profile.d.ts +33 -0
- package/dist/user-profile.js +70 -0
- package/dist/web-ui/chat-message-meta.d.ts +11 -0
- package/dist/web-ui/chat-message-meta.js +47 -0
- package/dist/web-ui/content/ai-teams.js +13 -13
- package/dist/web-ui/content/scripts.js +795 -95
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/content/tailwind.css +1 -6808
- package/dist/web-ui/content/vendor/qrcode/qrcode.bundle.js +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +5 -5
- package/dist/web-ui/index.d.ts +1 -1
- package/dist/web-ui/index.js +3 -3
- package/dist/web-ui/markdown.d.ts +2 -0
- package/dist/web-ui/markdown.js +111 -0
- package/dist/web-ui/page.d.ts +4 -0
- package/dist/web-ui/page.js +8 -0
- package/dist/web-ui/react/local-preview/controller.d.ts +30 -0
- package/dist/web-ui/react/local-preview/controller.js +159 -0
- package/dist/web-ui/running-activity.d.ts +63 -0
- package/dist/web-ui/running-activity.js +134 -0
- package/dist/web-ui/styles.js +2 -11
- package/package.json +7 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Express, RequestHandler } from "express";
|
|
2
|
+
import type { AuthPrincipal } from "./storage.js";
|
|
3
|
+
export declare const SETTINGS_WEB_COOKIE = "wand_settings_access";
|
|
4
|
+
/** A WebView-only proof bound to the existing, live client session, not a new administrator login. */
|
|
5
|
+
export declare class SettingsWebAccess {
|
|
6
|
+
private readonly key;
|
|
7
|
+
issue(sessionToken: string): string;
|
|
8
|
+
accepts(sessionToken: string | undefined, cookieHeader: string | undefined): boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function registerSettingsWebAccessRoute(app: Express, deps: {
|
|
11
|
+
requireAuth: RequestHandler;
|
|
12
|
+
useHttps: boolean;
|
|
13
|
+
access: SettingsWebAccess;
|
|
14
|
+
authenticateSession(token: string | undefined): AuthPrincipal | null;
|
|
15
|
+
}): void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { principalHasScope, readSessionCookie } from "./auth.js";
|
|
3
|
+
export const SETTINGS_WEB_COOKIE = "wand_settings_access";
|
|
4
|
+
/** A WebView-only proof bound to the existing, live client session, not a new administrator login. */
|
|
5
|
+
export class SettingsWebAccess {
|
|
6
|
+
key = crypto.randomBytes(32);
|
|
7
|
+
issue(sessionToken) {
|
|
8
|
+
return crypto.createHmac("sha256", this.key).update(sessionToken).digest("base64url");
|
|
9
|
+
}
|
|
10
|
+
accepts(sessionToken, cookieHeader) {
|
|
11
|
+
if (!sessionToken || !cookieHeader)
|
|
12
|
+
return false;
|
|
13
|
+
const prefix = `${SETTINGS_WEB_COOKIE}=`;
|
|
14
|
+
const value = cookieHeader.split(";").map((part) => part.trim()).find((part) => part.startsWith(prefix))?.slice(prefix.length);
|
|
15
|
+
if (!value || !/^[A-Za-z0-9_-]{43}$/.test(value))
|
|
16
|
+
return false;
|
|
17
|
+
return crypto.timingSafeEqual(Buffer.from(value), Buffer.from(this.issue(sessionToken)));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function registerSettingsWebAccessRoute(app, deps) {
|
|
21
|
+
app.post("/api/settings/webview-session", deps.requireAuth, (req, res) => {
|
|
22
|
+
const token = readSessionCookie(req, deps.useHttps);
|
|
23
|
+
const principal = deps.authenticateSession(token);
|
|
24
|
+
if (!token || !principal) {
|
|
25
|
+
res.status(401).json({ error: "客户端登录已失效,请重新连接客户端。" });
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (!principalHasScope(principal, "session-preferences")) {
|
|
29
|
+
res.status(403).json({ error: "当前客户端连接不能访问完整设置。" });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// The parent session remains unchanged; revocation/expiry immediately invalidates this proof.
|
|
33
|
+
res.set("Cache-Control", "no-store").cookie(SETTINGS_WEB_COOKIE, deps.access.issue(token), {
|
|
34
|
+
httpOnly: true, sameSite: "strict", secure: deps.useHttps, path: "/", maxAge: 1000 * 60 * 60 * 12,
|
|
35
|
+
}).json({ ok: true });
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ProviderModelField } from "./config.js";
|
|
2
|
+
import type { WandTaskAgent } from "./task-types.js";
|
|
3
|
+
import type { SessionProvider, WandConfig } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* 员工创建默认执行配置(用户显式保存的那份)。与看板的
|
|
6
|
+
* `pref:taskBoardLastAgent` 分开:任务默认跟随最近派发,员工默认只跟用户确认过的创建配置。
|
|
7
|
+
*/
|
|
8
|
+
export declare const EMPLOYEE_CREATION_DEFAULTS_PREF = "pref:employeeCreationAgent";
|
|
9
|
+
export type EmployeeDefaultsConfig = Pick<WandConfig, "defaultProvider" | ProviderModelField>;
|
|
10
|
+
export interface SiliconEmployeeDefaults {
|
|
11
|
+
configured: boolean;
|
|
12
|
+
/** false 时唯一合法动作是让用户自己选;服务端不替任何端硬编码默认。 */
|
|
13
|
+
requiresUserChoice: boolean;
|
|
14
|
+
source?: "saved" | "server";
|
|
15
|
+
provider?: SessionProvider;
|
|
16
|
+
/** 新员工将使用的执行候选;与「生成草稿用的系统 AI 模型」是两条独立链路。 */
|
|
17
|
+
agent?: WandTaskAgent;
|
|
18
|
+
/** 该 provider 当前生效的服务端默认模型,仅作展示(agent.model 仍是 "default")。 */
|
|
19
|
+
defaultModel?: string;
|
|
20
|
+
reason?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 两端唯一的默认解析:已保存的员工创建配置 → 服务端 defaultProvider/defaultModel → 需要用户选择。
|
|
24
|
+
* 纯函数;saved 的解析(parseTaskAgent 等)由调用方完成后传入。
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveSiliconEmployeeDefaults(input: {
|
|
27
|
+
savedAgent?: WandTaskAgent | null;
|
|
28
|
+
config?: EmployeeDefaultsConfig;
|
|
29
|
+
}): SiliconEmployeeDefaults;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { getDefaultModelForProvider } from "./config.js";
|
|
2
|
+
import { employeeAgentFor } from "./silicon-employee-draft.js";
|
|
3
|
+
import { isSessionProvider } from "./session-provider.js";
|
|
4
|
+
/**
|
|
5
|
+
* 员工创建默认执行配置(用户显式保存的那份)。与看板的
|
|
6
|
+
* `pref:taskBoardLastAgent` 分开:任务默认跟随最近派发,员工默认只跟用户确认过的创建配置。
|
|
7
|
+
*/
|
|
8
|
+
export const EMPLOYEE_CREATION_DEFAULTS_PREF = "pref:employeeCreationAgent";
|
|
9
|
+
/**
|
|
10
|
+
* 两端唯一的默认解析:已保存的员工创建配置 → 服务端 defaultProvider/defaultModel → 需要用户选择。
|
|
11
|
+
* 纯函数;saved 的解析(parseTaskAgent 等)由调用方完成后传入。
|
|
12
|
+
*/
|
|
13
|
+
export function resolveSiliconEmployeeDefaults(input) {
|
|
14
|
+
const saved = input.savedAgent;
|
|
15
|
+
if (saved && saved.kind === "structured" && isSessionProvider(saved.provider)) {
|
|
16
|
+
return {
|
|
17
|
+
configured: true,
|
|
18
|
+
requiresUserChoice: false,
|
|
19
|
+
source: "saved",
|
|
20
|
+
provider: saved.provider,
|
|
21
|
+
agent: saved,
|
|
22
|
+
defaultModel: getDefaultModelForProvider(input.config ?? {}, saved.provider),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const provider = input.config?.defaultProvider;
|
|
26
|
+
if (provider && isSessionProvider(provider)) {
|
|
27
|
+
return {
|
|
28
|
+
configured: true,
|
|
29
|
+
requiresUserChoice: false,
|
|
30
|
+
source: "server",
|
|
31
|
+
provider,
|
|
32
|
+
agent: employeeAgentFor(provider),
|
|
33
|
+
defaultModel: getDefaultModelForProvider(input.config ?? {}, provider),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
configured: false,
|
|
38
|
+
requiresUserChoice: true,
|
|
39
|
+
reason: "服务端未配置默认执行工具,请在创建员工时自行选择执行配置。",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { SiliconEmployee } from "./ai-team-types.js";
|
|
2
2
|
import type { WandTaskAgent } from "./task-types.js";
|
|
3
|
+
export interface EmployeeCandidateSelectionOptions {
|
|
4
|
+
/** 普通任务派发只走 CLI;显式员工会话可允许已配置的 SDK。 */
|
|
5
|
+
skipSdk?: boolean;
|
|
6
|
+
}
|
|
3
7
|
/** Match child-process PATH without starting a shell or trusting a client availability claim. */
|
|
4
8
|
export declare function employeeCliAvailable(agent: WandTaskAgent, pathValue?: string): boolean;
|
|
5
9
|
/** First installed candidate wins; when none is installed retain the preferred candidate for a recoverable failure. */
|
|
6
|
-
export declare function selectEmployeeCandidate(employee: Pick<SiliconEmployee, "agents">, available?: (agent: WandTaskAgent) => boolean): {
|
|
10
|
+
export declare function selectEmployeeCandidate(employee: Pick<SiliconEmployee, "agents">, available?: (agent: WandTaskAgent) => boolean, options?: EmployeeCandidateSelectionOptions): {
|
|
7
11
|
agent: WandTaskAgent;
|
|
8
12
|
index: number;
|
|
9
13
|
};
|
|
@@ -4,10 +4,16 @@ export function employeeCliAvailable(agent, pathValue = process.env.PATH ?? "")
|
|
|
4
4
|
return providerCliInstalled(agent.provider, pathValue);
|
|
5
5
|
}
|
|
6
6
|
/** First installed candidate wins; when none is installed retain the preferred candidate for a recoverable failure. */
|
|
7
|
-
export function selectEmployeeCandidate(employee, available = employeeCliAvailable) {
|
|
8
|
-
|
|
7
|
+
export function selectEmployeeCandidate(employee, available = employeeCliAvailable, options = {}) {
|
|
8
|
+
const candidates = options.skipSdk ? employee.agents.filter((agent) => agent.engine !== "sdk") : employee.agents;
|
|
9
|
+
if (!candidates.length)
|
|
10
|
+
throw new Error("硅基员工没有可用的 CLI 执行候选。");
|
|
11
|
+
if (!candidates.length || candidates.some((agent) => agent.kind !== "structured")) {
|
|
9
12
|
throw new Error("硅基员工没有可用的结构化执行候选。");
|
|
10
13
|
}
|
|
11
|
-
const index =
|
|
12
|
-
|
|
14
|
+
const index = candidates.findIndex((agent) => available(agent));
|
|
15
|
+
const selectedIndex = index < 0 ? 0 : index;
|
|
16
|
+
const agent = candidates[selectedIndex];
|
|
17
|
+
const originalIndex = employee.agents.indexOf(agent);
|
|
18
|
+
return { agent, index: originalIndex };
|
|
13
19
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type QuickCommitAiOptions } from "./git-quick-commit.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type SiliconEmployeeDraft } from "./ai-team-types.js";
|
|
3
3
|
import type { AiTextRequest } from "./types.js";
|
|
4
4
|
import type { WandTaskAgent } from "./task-types.js";
|
|
5
5
|
import type { SessionProvider } from "./types.js";
|
|
6
6
|
export declare class SiliconEmployeeDraftError extends Error {
|
|
7
7
|
readonly code: string;
|
|
8
|
-
|
|
8
|
+
readonly field?: string | undefined;
|
|
9
|
+
constructor(message: string, code: string, field?: string | undefined);
|
|
9
10
|
}
|
|
10
11
|
/** 硅基员工只能跑结构化会话;候选沿用看板的「跟随服务端默认」。 */
|
|
11
12
|
export declare function employeeAgentFor(provider: SessionProvider): WandTaskAgent;
|
|
@@ -16,14 +17,30 @@ export declare function employeeAgentFor(provider: SessionProvider): WandTaskAge
|
|
|
16
17
|
export declare function availableEmployeeProviders(preferred?: SessionProvider, isAvailable?: (agent: WandTaskAgent) => boolean): SessionProvider[];
|
|
17
18
|
/** 从模型输出里取第一个 JSON 对象,容忍前后夹带解释文字。 */
|
|
18
19
|
export declare function extractDraftJson(raw: string): Record<string, unknown>;
|
|
20
|
+
export interface EmployeeDraftNormalizeOptions {
|
|
21
|
+
allowedProviders?: SessionProvider[];
|
|
22
|
+
fallbackProvider?: SessionProvider;
|
|
23
|
+
existingNames?: string[];
|
|
24
|
+
/** 结构化输入(HR 草稿 / API 直传)走严格模式:非法资源直接拒绝,不静默兜底。 */
|
|
25
|
+
strict?: boolean;
|
|
26
|
+
}
|
|
19
27
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
28
|
+
* 唯一的草稿规范化/校验入口:模型输出与结构化草稿都经过这里。
|
|
29
|
+
* 名字 / 角色设定缺失即视为不可用;strict=false 时 provider 不在允许集合内按候选顺序兜底,
|
|
30
|
+
* strict=true 时拒绝并提示选择,避免把非法或越费用边界的资源悄悄换成别的。
|
|
31
|
+
*/
|
|
32
|
+
export declare function normalizeSiliconEmployeeDraft(record: Record<string, unknown>, options?: EmployeeDraftNormalizeOptions): SiliconEmployeeDraft;
|
|
33
|
+
/**
|
|
34
|
+
* 校验并收敛模型输出(宽松路径:非法 provider 在允许集合内按候选顺序兜底,
|
|
35
|
+
* 不让员工带着跑不起来的 CLI 落库)。
|
|
22
36
|
*/
|
|
23
37
|
export declare function parseSiliconEmployeeDraft(raw: string, options?: {
|
|
24
38
|
allowedProviders?: SessionProvider[];
|
|
25
39
|
fallbackProvider?: SessionProvider;
|
|
40
|
+
existingNames?: string[];
|
|
26
41
|
}): SiliconEmployeeDraft;
|
|
42
|
+
/** HR / 客户端已给出完整结构化草稿时走同一校验,不再调用模型。 */
|
|
43
|
+
export declare function validateStructuredEmployeeDraft(input: unknown, options?: Omit<EmployeeDraftNormalizeOptions, "strict">): SiliconEmployeeDraft;
|
|
27
44
|
/** 规则走系统提示,用户的期望走用户消息;一次性调用沿用系统 AI 通道。 */
|
|
28
45
|
export declare function buildEmployeeDraftPrompt(expectation: string, providers: SessionProvider[], existingNames?: string[]): AiTextRequest;
|
|
29
46
|
/** 按自然语言期望生成员工配置;调用方负责随后落库。 */
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { callConfiguredAiText } from "./git-quick-commit.js";
|
|
2
2
|
import { employeeCliAvailable } from "./silicon-employee-dispatch.js";
|
|
3
3
|
import { SESSION_PROVIDERS, isSessionProvider } from "./session-provider.js";
|
|
4
|
+
import { isThinkingEffort } from "./structured-provider-common.js";
|
|
4
5
|
import { DEFAULT_WAND_TASK_AGENT_KIND, DEFAULT_WAND_TASK_AGENT_MODE, normalizeWandTaskAgentMode, } from "./task-types.js";
|
|
5
6
|
import { clipAtWordBoundary } from "./text-utils.js";
|
|
7
|
+
import { agentKey } from "./ai-team-types.js";
|
|
6
8
|
export class SiliconEmployeeDraftError extends Error {
|
|
7
9
|
code;
|
|
8
|
-
|
|
10
|
+
field;
|
|
11
|
+
constructor(message, code, field) {
|
|
9
12
|
super(message);
|
|
10
13
|
this.code = code;
|
|
14
|
+
this.field = field;
|
|
11
15
|
this.name = "SiliconEmployeeDraftError";
|
|
12
16
|
}
|
|
13
17
|
}
|
|
@@ -79,27 +83,108 @@ function oneLine(record, key) {
|
|
|
79
83
|
const value = record[key];
|
|
80
84
|
return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
|
|
81
85
|
}
|
|
86
|
+
function stringValue(value) {
|
|
87
|
+
return typeof value === "string" ? value.trim() : "";
|
|
88
|
+
}
|
|
82
89
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
90
|
+
* 解析一个执行候选。员工候选只允许结构化会话,provider 必须在允许集合内;
|
|
91
|
+
* 模型只接受字符串(空即 "default",跟随服务端默认),不静默改成别的资源。
|
|
85
92
|
*/
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
function parseEmployeeCandidate(raw, allowed) {
|
|
94
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
95
|
+
throw new SiliconEmployeeDraftError("执行候选必须是对象。", "DRAFT_INVALID_AGENT", "agents");
|
|
96
|
+
}
|
|
97
|
+
const body = raw;
|
|
98
|
+
const provider = stringValue(body.provider).toLowerCase();
|
|
99
|
+
if (!isSessionProvider(provider)) {
|
|
100
|
+
throw new SiliconEmployeeDraftError("请选择有效的执行工具。", "DRAFT_INVALID_PROVIDER", "provider");
|
|
101
|
+
}
|
|
102
|
+
if (allowed.length && !allowed.includes(provider)) {
|
|
103
|
+
throw new SiliconEmployeeDraftError(`执行工具「${provider}」不在当前允许的候选集合(${allowed.join("、")})里,请重新选择。`, "DRAFT_PROVIDER_NOT_ALLOWED", "provider");
|
|
104
|
+
}
|
|
105
|
+
const model = stringValue(body.model) || "default";
|
|
106
|
+
if (model.length > 128) {
|
|
107
|
+
throw new SiliconEmployeeDraftError("模型名称过长。", "DRAFT_MODEL_TOO_LONG", "model");
|
|
108
|
+
}
|
|
109
|
+
const effort = stringValue(body.thinkingEffort) || "off";
|
|
110
|
+
if (!isThinkingEffort(effort)) {
|
|
111
|
+
throw new SiliconEmployeeDraftError("思考深度无效。", "DRAFT_INVALID_THINKING", "thinkingEffort");
|
|
112
|
+
}
|
|
113
|
+
const engine = stringValue(body.engine);
|
|
114
|
+
if (engine && engine !== "cli" && engine !== "sdk") {
|
|
115
|
+
throw new SiliconEmployeeDraftError("执行引擎无效。", "DRAFT_INVALID_ENGINE", "engine");
|
|
116
|
+
}
|
|
117
|
+
if (engine === "sdk" && provider !== "pi") {
|
|
118
|
+
throw new SiliconEmployeeDraftError("SDK 执行引擎仅支持 Pi 结构化会话。", "DRAFT_INVALID_ENGINE", "engine");
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
provider,
|
|
122
|
+
model,
|
|
123
|
+
thinkingEffort: effort,
|
|
124
|
+
mode: normalizeWandTaskAgentMode(provider, body.mode ?? DEFAULT_WAND_TASK_AGENT_MODE),
|
|
125
|
+
kind: DEFAULT_WAND_TASK_AGENT_KIND,
|
|
126
|
+
...(engine === "sdk" ? { engine: "sdk" } : {}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 唯一的草稿规范化/校验入口:模型输出与结构化草稿都经过这里。
|
|
131
|
+
* 名字 / 角色设定缺失即视为不可用;strict=false 时 provider 不在允许集合内按候选顺序兜底,
|
|
132
|
+
* strict=true 时拒绝并提示选择,避免把非法或越费用边界的资源悄悄换成别的。
|
|
133
|
+
*/
|
|
134
|
+
export function normalizeSiliconEmployeeDraft(record, options = {}) {
|
|
88
135
|
const name = clipAtWordBoundary(oneLine(record, "name"), NAME_MAX);
|
|
89
136
|
if (!name)
|
|
90
|
-
throw new SiliconEmployeeDraftError("AI 没有给出员工名字,请重试。", "DRAFT_MISSING_NAME");
|
|
137
|
+
throw new SiliconEmployeeDraftError("AI 没有给出员工名字,请重试。", "DRAFT_MISSING_NAME", "name");
|
|
91
138
|
const duty = clipAtWordBoundary(oneLine(record, "duty"), DUTY_MAX);
|
|
92
139
|
const prompt = typeof record.prompt === "string" ? record.prompt.trim().slice(0, PROMPT_MAX) : "";
|
|
93
|
-
if (!prompt)
|
|
94
|
-
throw new SiliconEmployeeDraftError("AI 没有给出员工角色设定,请重试。", "DRAFT_MISSING_PROMPT");
|
|
140
|
+
if (!prompt) {
|
|
141
|
+
throw new SiliconEmployeeDraftError(options.strict ? "员工草稿缺少角色设定。" : "AI 没有给出员工角色设定,请重试。", "DRAFT_MISSING_PROMPT", "prompt");
|
|
142
|
+
}
|
|
143
|
+
const duplicate = (options.existingNames ?? []).some((existing) => existing.trim().toLowerCase() === name.toLowerCase());
|
|
144
|
+
if (duplicate) {
|
|
145
|
+
throw new SiliconEmployeeDraftError(`已有名叫「${name}」的员工,请换个名字。`, "DRAFT_DUPLICATE_NAME", "name");
|
|
146
|
+
}
|
|
95
147
|
const allowed = options.allowedProviders?.length ? options.allowedProviders : [...SESSION_PROVIDERS];
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
148
|
+
let candidates;
|
|
149
|
+
if (Array.isArray(record.agents) && record.agents.length > 0) {
|
|
150
|
+
candidates = record.agents.map((raw) => parseEmployeeCandidate(raw, allowed));
|
|
151
|
+
if (new Set(candidates.map(agentKey)).size !== candidates.length) {
|
|
152
|
+
throw new SiliconEmployeeDraftError("执行候选存在重复。", "DRAFT_DUPLICATE_AGENT", "agents");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else if (record.agent !== undefined && record.agent !== null) {
|
|
156
|
+
candidates = [parseEmployeeCandidate(record.agent, allowed)];
|
|
157
|
+
}
|
|
158
|
+
else if (options.strict) {
|
|
159
|
+
if (!stringValue(record.provider)) {
|
|
160
|
+
throw new SiliconEmployeeDraftError("员工草稿缺少执行候选。", "DRAFT_MISSING_AGENT", "provider");
|
|
161
|
+
}
|
|
162
|
+
candidates = [parseEmployeeCandidate({ provider: record.provider, model: record.model, thinkingEffort: record.thinkingEffort, mode: record.mode }, allowed)];
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const requested = oneLine(record, "provider").toLowerCase();
|
|
166
|
+
const provider = isSessionProvider(requested) && allowed.includes(requested)
|
|
167
|
+
? requested
|
|
168
|
+
: options.fallbackProvider && allowed.includes(options.fallbackProvider)
|
|
169
|
+
? options.fallbackProvider
|
|
170
|
+
: allowed[0];
|
|
171
|
+
candidates = [employeeAgentFor(provider)];
|
|
172
|
+
}
|
|
173
|
+
return { name, duty, prompt, agent: candidates[0], candidates };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 校验并收敛模型输出(宽松路径:非法 provider 在允许集合内按候选顺序兜底,
|
|
177
|
+
* 不让员工带着跑不起来的 CLI 落库)。
|
|
178
|
+
*/
|
|
179
|
+
export function parseSiliconEmployeeDraft(raw, options = {}) {
|
|
180
|
+
return normalizeSiliconEmployeeDraft(extractDraftJson(raw), options);
|
|
181
|
+
}
|
|
182
|
+
/** HR / 客户端已给出完整结构化草稿时走同一校验,不再调用模型。 */
|
|
183
|
+
export function validateStructuredEmployeeDraft(input, options = {}) {
|
|
184
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
185
|
+
throw new SiliconEmployeeDraftError("员工草稿必须是对象。", "DRAFT_INVALID_DRAFT");
|
|
186
|
+
}
|
|
187
|
+
return normalizeSiliconEmployeeDraft(input, { ...options, strict: true });
|
|
103
188
|
}
|
|
104
189
|
/** 规则走系统提示,用户的期望走用户消息;一次性调用沿用系统 AI 通道。 */
|
|
105
190
|
export function buildEmployeeDraftPrompt(expectation, providers, existingNames = []) {
|
|
@@ -133,7 +218,9 @@ export async function generateSiliconEmployeeDraft(expectation, ai = {}, options
|
|
|
133
218
|
const providers = available.length ? available : [options.preferredProvider ?? "claude", ...SESSION_PROVIDERS];
|
|
134
219
|
const request = buildEmployeeDraftPrompt(trimmed, providers, options.existingNames);
|
|
135
220
|
try {
|
|
136
|
-
return await callConfiguredAiText(request, options.cwd || process.cwd(), options.language ?? "", ai, (raw) => parseSiliconEmployeeDraft(raw, {
|
|
221
|
+
return await callConfiguredAiText(request, options.cwd || process.cwd(), options.language ?? "", ai, (raw) => parseSiliconEmployeeDraft(raw, {
|
|
222
|
+
allowedProviders: providers, fallbackProvider: providers[0], existingNames: options.existingNames,
|
|
223
|
+
}));
|
|
137
224
|
}
|
|
138
225
|
catch (error) {
|
|
139
226
|
if (error instanceof SiliconEmployeeDraftError)
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type ConversationInstance, type ConversationRequest } from "./conversation-types.js";
|
|
1
2
|
import { SessionSnapshot, ConversationTurn, SessionProvider, StructuredSessionState, Workspace, LayoutNode, TaskWindowLayout, WorkspaceDefaultProvider, WorkspaceKind, WorkspaceTask, WorkspaceTaskWorktree, WorkspaceTaskStatus } from "./types.js";
|
|
3
|
+
import { type PiSessionSettings } from "./pi-session-settings.js";
|
|
2
4
|
import type { AiTeam, AiTeamRun, AiTeamRunChatMarker, AiTeamRunStatus, AiTeamStep, AiTeamStepSessionMarker, CandidateFailureKind, SiliconEmployee, StepDispatchInfo } from "./ai-team-types.js";
|
|
3
5
|
import type { WandTask, WandTaskAgent, WandTaskAgentKind, WandTaskTitleSource } from "./task-types.js";
|
|
4
6
|
import { type SystemEmployeeSeed } from "./system-employee.js";
|
|
@@ -74,6 +76,43 @@ export declare class WandStorage {
|
|
|
74
76
|
* be nested because SQLite does not support a second BEGIN on this connection.
|
|
75
77
|
*/
|
|
76
78
|
transaction<T>(action: () => T): T;
|
|
79
|
+
conversationListState(id: string): {
|
|
80
|
+
pinnedAt: string | null;
|
|
81
|
+
dissolvedAt: string | null;
|
|
82
|
+
dissolvedBy: string | null;
|
|
83
|
+
deleting: boolean;
|
|
84
|
+
};
|
|
85
|
+
/** Separate from channel snapshots, so late run saves cannot overwrite lifecycle state. */
|
|
86
|
+
updateConversationListState(id: string, patch: {
|
|
87
|
+
pinned?: boolean;
|
|
88
|
+
dissolved?: boolean;
|
|
89
|
+
deleting?: boolean;
|
|
90
|
+
dissolvedBy?: string;
|
|
91
|
+
}): void;
|
|
92
|
+
deleteConversation(id: string): void;
|
|
93
|
+
listConversations(): ConversationInstance[];
|
|
94
|
+
getConversation(id: string): ConversationInstance | null;
|
|
95
|
+
getConversationBySession(sessionId: string): ConversationInstance | null;
|
|
96
|
+
saveConversation(instance: ConversationInstance): void;
|
|
97
|
+
conversationTasks(id: string): Array<{
|
|
98
|
+
taskId: string;
|
|
99
|
+
linkedAt: string;
|
|
100
|
+
}>;
|
|
101
|
+
linkConversationTask(conversationId: string, taskId: string, linkedAt: string): void;
|
|
102
|
+
/** One canonical task/workspace/instance association commit, before any model input. */
|
|
103
|
+
createConversationTask(instance: ConversationInstance, input: CreateWandTaskInput, accept?: (task: WandTask) => void): WandTask;
|
|
104
|
+
/** Commit the first accepted fact, identifiers and DM link together, before any execution I/O. */
|
|
105
|
+
acceptConversationDispatch(instance: ConversationInstance, input: CreateWandTaskInput | {
|
|
106
|
+
continueTaskId: string;
|
|
107
|
+
}, request: ConversationRequest, source?: ConversationInstance): WandTask;
|
|
108
|
+
/** Message + reply + exact session association + receipt are committed before any model input. */
|
|
109
|
+
acceptConversationSession(instance: ConversationInstance, createSession: () => string, title: string, input: string, request: ConversationRequest): void;
|
|
110
|
+
conversationSessionIds(id: string): string[];
|
|
111
|
+
conversationTaskStartup(taskId: string): ConversationRequest["receipt"] | null;
|
|
112
|
+
appendConversationEvent(id: string, turn: ConversationTurn): void;
|
|
113
|
+
conversationEvents(id: string): ConversationTurn[];
|
|
114
|
+
getConversationRequest(id: string): ConversationRequest | null;
|
|
115
|
+
saveConversationRequest(request: ConversationRequest): void;
|
|
77
116
|
/** Get a config value from database */
|
|
78
117
|
getConfigValue(key: string): string | null;
|
|
79
118
|
/** Set a config value in database */
|
|
@@ -86,6 +125,14 @@ export declare class WandStorage {
|
|
|
86
125
|
setPreference<T>(key: string, value: T | null | undefined): void;
|
|
87
126
|
/** 判断偏好是否在 DB 中存在(区别于值为 null/false/"")。 */
|
|
88
127
|
hasPreference(key: string): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Pi CLI 会话的「上次设置」:用户在任意 Pi 会话里改过设置后,新建会话以它起步,
|
|
130
|
+
* 不必每开一个会话都重配自动选择 / CodeMode。返回 null 表示沿用出厂默认
|
|
131
|
+
* (跟随 Pi 自身配置),不是「设置全关」。只存设置字段本身,不含路径、命令或凭据。
|
|
132
|
+
*/
|
|
133
|
+
getPiSessionDefaults(): PiSessionSettings | null;
|
|
134
|
+
/** 写入 Pi 会话默认设置;null / undefined 清除,回到出厂默认。 */
|
|
135
|
+
setPiSessionDefaults(settings: PiSessionSettings | null | undefined): void;
|
|
89
136
|
/**
|
|
90
137
|
* 用户在首页拖动排序后的目录组顺序(服务端偏好,所有客户端共用)。
|
|
91
138
|
* 存的是客户端看到的组 id:真实工作区用 workspace id,合成目录用 `cwd:<规范化路径>`。
|
|
@@ -123,8 +170,10 @@ export declare class WandStorage {
|
|
|
123
170
|
/** 显式更新某会话的工作空间归属(用于创建时绑定)。 */
|
|
124
171
|
getSessionWorkspace(sessionId: string): Pick<SessionSnapshot, "workspaceId" | "workspaceTaskId"> | null;
|
|
125
172
|
setSessionWorkspaceId(sessionId: string, workspaceId: string | null): void;
|
|
126
|
-
/**
|
|
127
|
-
countSessionsByWorkspace(
|
|
173
|
+
/** Normal workspace counts exclude archived sessions and sessions in archived tasks. */
|
|
174
|
+
countSessionsByWorkspace(options?: {
|
|
175
|
+
includeArchived?: boolean;
|
|
176
|
+
}): Map<string, number>;
|
|
128
177
|
/** Count user-initiated CLI launches (one per session), including archived sessions. */
|
|
129
178
|
countInteractiveSessionsByProvider(): Record<SessionProvider, number>;
|
|
130
179
|
/** Sessions not yet attached to a project. Omits messages/output. */
|
|
@@ -190,7 +239,21 @@ export declare class WandStorage {
|
|
|
190
239
|
updateWandTask(id: string, patch: WandTaskPatch): WandTask | null;
|
|
191
240
|
/** Commit completion archives only the selected completed cards, atomically. */
|
|
192
241
|
archiveCompletedWandTasks(ids: readonly string[]): string[];
|
|
242
|
+
/**
|
|
243
|
+
* Archive the given cards. Already-archived ids are skipped.
|
|
244
|
+
* `completedOnly` keeps the historical commit helper from closing in-progress work.
|
|
245
|
+
*/
|
|
246
|
+
archiveWandTasks(ids: readonly string[], options?: {
|
|
247
|
+
completedOnly?: boolean;
|
|
248
|
+
}): string[];
|
|
193
249
|
deleteWandTask(id: string): void;
|
|
250
|
+
/**
|
|
251
|
+
* 批量硬删除卡片:一次事务,返回实际删除的 id(不存在或已消失的跳过)。
|
|
252
|
+
* 归档目录的「清空」和保留期扫描都走这里,调用方自己决定哪些 id 可以删。
|
|
253
|
+
*/
|
|
254
|
+
deleteWandTasks(ids: readonly string[]): string[];
|
|
255
|
+
/** Single-card hard delete body; callers own the transaction (SQLite forbids nesting). */
|
|
256
|
+
private removeWandTaskRow;
|
|
194
257
|
/** One-time id-based reconciliation; normal reads never create tasks or repair membership. */
|
|
195
258
|
private migrateTaskRecords;
|
|
196
259
|
/**
|
|
@@ -349,6 +412,12 @@ export declare class WandStorage {
|
|
|
349
412
|
issueDecisionAccess(sessionId: string, now?: number): string;
|
|
350
413
|
resolveDecisionAccess(token: string, now?: number): string | null;
|
|
351
414
|
revokeDecisionAccess(token: string): void;
|
|
415
|
+
/** CLI-only free inference scope, durable across Server restarts but not reusable by another run. */
|
|
416
|
+
issueOpenRouterFreeAccess(sessionId: string, selector: string, credentialHash: string, requirements: {
|
|
417
|
+
preferReasoning?: boolean;
|
|
418
|
+
allowedSelectors?: readonly string[];
|
|
419
|
+
}, now?: number): string;
|
|
420
|
+
revokeOpenRouterFreeAccess(token: string): void;
|
|
352
421
|
/** No IO on input hot paths; queued observations still CAS against the database before writing. */
|
|
353
422
|
userMemoryCaptureState(): Readonly<{
|
|
354
423
|
enabled: boolean;
|
|
@@ -374,6 +443,8 @@ export declare class WandStorage {
|
|
|
374
443
|
saveAiTeam(team: AiTeam): void;
|
|
375
444
|
/** 只删团队定义;已有运行保存了团队快照,不受影响。 */
|
|
376
445
|
deleteAiTeam(id: string): void;
|
|
446
|
+
/** 保留期清理:删掉一次运行及其步骤。关联会话由调用方按运行所有权删除。 */
|
|
447
|
+
deleteAiTeamRun(id: string): void;
|
|
377
448
|
saveAiTeamRun(run: AiTeamRun): void;
|
|
378
449
|
/** 群聊会话对应的最近一次运行(同一个群聊可以接着开新运行)。 */
|
|
379
450
|
getLatestAiTeamRunByChat(sessionId: string): AiTeamRun | null;
|