@co0ontty/wand 4.94.0 → 4.95.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 -1
- package/dist/ai-text-runner.d.ts +4 -0
- package/dist/ai-text-runner.js +44 -4
- package/dist/build-info.json +3 -3
- package/dist/cli-api.d.ts +1 -0
- package/dist/cli-api.js +7 -2
- package/dist/cli.js +47 -0
- package/dist/conversation-execution-preference.d.ts +7 -0
- package/dist/conversation-execution-preference.js +26 -0
- package/dist/conversation-organize-contract.d.ts +70 -0
- package/dist/conversation-organize-contract.js +1 -0
- package/dist/conversation-organize-service.d.ts +46 -0
- package/dist/conversation-organize-service.js +505 -0
- package/dist/conversation-organize.d.ts +41 -0
- package/dist/conversation-organize.js +97 -0
- package/dist/conversation-service.d.ts +2 -1
- package/dist/conversation-service.js +10 -4
- package/dist/core-turn-tracker.d.ts +5 -1
- package/dist/core-turn-tracker.js +17 -1
- package/dist/decision-expert-service.d.ts +2 -0
- package/dist/decision-expert-service.js +17 -1
- package/dist/decision-skill.d.ts +0 -1
- package/dist/decision-skill.js +5 -64
- package/dist/deployment-lock.js +4 -1
- package/dist/employee-im-config.d.ts +17 -0
- package/dist/employee-im-config.js +85 -0
- package/dist/employee-im-lifecycle.d.ts +35 -0
- package/dist/employee-im-lifecycle.js +168 -0
- package/dist/employee-im-types.d.ts +80 -0
- package/dist/employee-im-types.js +32 -0
- package/dist/employee-im.d.ts +35 -0
- package/dist/employee-im.js +157 -0
- package/dist/employee-knowledge.js +3 -2
- package/dist/employee-text.d.ts +1 -0
- package/dist/employee-text.js +28 -13
- package/dist/git-quick-commit.js +21 -3
- package/dist/managed-skill.d.ts +6 -0
- package/dist/managed-skill.js +99 -0
- package/dist/model-auto-assign.js +1 -1
- package/dist/process-manager.js +1 -1
- package/dist/prompt-optimizer.js +1 -1
- package/dist/scheduled-task-activity.d.ts +16 -0
- package/dist/scheduled-task-activity.js +42 -0
- package/dist/scheduled-task-agenda.d.ts +20 -0
- package/dist/scheduled-task-agenda.js +82 -0
- package/dist/scheduled-task-calendar.d.ts +37 -0
- package/dist/scheduled-task-calendar.js +131 -0
- package/dist/scheduled-task-read-range.d.ts +7 -0
- package/dist/scheduled-task-read-range.js +35 -0
- package/dist/scheduled-task-service.d.ts +6 -1
- package/dist/scheduled-task-service.js +61 -1
- package/dist/scheduled-task-state.d.ts +1 -1
- package/dist/scheduled-task-types.d.ts +38 -0
- package/dist/server-asset-routes.js +3 -2
- package/dist/server-conversation-organize-routes.d.ts +4 -0
- package/dist/server-conversation-organize-routes.js +75 -0
- package/dist/server-conversation-routes.js +2 -1
- package/dist/server-employee-im-routes.d.ts +9 -0
- package/dist/server-employee-im-routes.js +40 -0
- package/dist/server-employee-routes.js +1 -1
- package/dist/server-scheduled-task-routes.js +24 -0
- package/dist/server-session-lifecycle-routes.js +8 -3
- package/dist/server-session-routes.js +18 -0
- package/dist/server-session-worktree-routes.js +3 -3
- package/dist/server-workspace-routes.d.ts +3 -0
- package/dist/server-workspace-routes.js +14 -1
- package/dist/server.js +39 -5
- package/dist/session-ai-context.d.ts +11 -1
- package/dist/session-ai-context.js +21 -2
- package/dist/session-metadata-service.js +1 -1
- package/dist/session-registry.js +6 -0
- package/dist/session-request.js +3 -0
- package/dist/session-topic.js +1 -1
- package/dist/silicon-employee-draft.js +1 -1
- package/dist/speech-polisher-service.d.ts +2 -0
- package/dist/speech-polisher-service.js +28 -6
- package/dist/storage/conversation-organization-repository.d.ts +13 -0
- package/dist/storage/conversation-organization-repository.js +33 -0
- package/dist/storage/migrations.js +10 -0
- package/dist/storage/secrets-repository.d.ts +7 -0
- package/dist/storage/secrets-repository.js +28 -1
- package/dist/storage/tasks-repository.js +4 -0
- package/dist/storage-schedules.d.ts +8 -1
- package/dist/storage-schedules.js +24 -0
- package/dist/storage-schema.js +9 -0
- package/dist/storage.d.ts +6 -1
- package/dist/storage.js +13 -0
- package/dist/structured-session-manager.d.ts +1 -0
- package/dist/structured-session-manager.js +18 -4
- package/dist/system-ai-call.d.ts +49 -0
- package/dist/system-ai-call.js +28 -0
- package/dist/system-ai-session-recorder.d.ts +30 -0
- package/dist/system-ai-session-recorder.js +239 -0
- package/dist/system-cli.d.ts +6 -0
- package/dist/system-cli.js +93 -0
- package/dist/system-skill.d.ts +5 -0
- package/dist/system-skill.js +19 -0
- package/dist/task-organize-contract.js +2 -2
- package/dist/task-title-organize-service.d.ts +1 -1
- package/dist/task-title-organize-service.js +9 -5
- package/dist/task-title-organize.js +13 -6
- package/dist/task-title-service.js +1 -1
- package/dist/task-title.js +1 -1
- package/dist/user-memory.js +1 -1
- package/dist/wand-task-sync.js +2 -1
- package/dist/web-ui/content/ai-teams.js +12 -12
- package/dist/web-ui/content/module-manifest.json +1 -1
- package/dist/web-ui/content/modules/{0051bd3d55f10ab7/library-bridge-DTMQIHVJ.js → 09842858f9d34a4e/library-bridge.js} +2 -2
- package/dist/web-ui/content/modules/1021ae40c5761e3f/sha256.js +1 -0
- package/dist/web-ui/content/modules/134f6120c0acaf57/host.js +1 -0
- package/dist/web-ui/content/modules/1d518ae6ed66fe5a/chunk.js +3 -0
- package/dist/web-ui/content/modules/1f93b5bd70381bb6/runtime.js +539 -0
- package/dist/web-ui/content/modules/26c68d4decdd18b4/markdown.js +1 -0
- package/dist/web-ui/content/modules/2ed8bafd5b4fecc5/workspace-window.js +1 -0
- package/dist/web-ui/content/modules/31ffc2f05f87652c/host.js +1 -0
- package/dist/web-ui/content/modules/35aaa85b9d0937af/chunk.js +20 -0
- package/dist/web-ui/content/modules/3664d0620f124292/chunk.js +3 -0
- package/dist/web-ui/content/modules/3cea480770cb184c/chunk.js +1 -0
- package/dist/web-ui/content/modules/3d12adf54647c1e2/host-bridge.js +1 -0
- package/dist/web-ui/content/modules/459cca2482a9a4ba/host.js +18 -0
- package/dist/web-ui/content/modules/474c9647130ef3e9/task-board-host.js +2 -0
- package/dist/web-ui/content/modules/4f74d2517f29134c/chunk.js +15 -0
- package/dist/web-ui/content/modules/52a218d74002a011/host.js +30 -0
- package/dist/web-ui/content/modules/54f2b10659ef8475/employee-card.js +1 -0
- package/dist/web-ui/content/modules/703fb1c670f7b199/chunk.js +1 -0
- package/dist/web-ui/content/modules/75511da23f051718/host.js +1 -0
- package/dist/web-ui/content/modules/{0051bd3d55f10ab7/host-2IVIZ2YC.js → 799b19da948bec8d/host.js} +3 -2
- package/dist/web-ui/content/modules/{0051bd3d55f10ab7/host-XNIR3NTJ.js → 7ef73b6c20d2504f/host.js} +3 -2
- package/dist/web-ui/content/modules/8f8f7a0a8874d29a/host.js +1 -0
- package/dist/web-ui/content/modules/90f2cd73cdf3a2cd/chat-render.js +25 -0
- package/dist/web-ui/content/modules/9510efce6caaa9ec/chunk.js +1 -0
- package/dist/web-ui/content/modules/9a0e65188739957f/chunk.js +67 -0
- package/dist/web-ui/content/modules/9eae1033f47e4526/host.js +1 -0
- package/dist/web-ui/content/modules/a2c5b246e57fa165/panel.js +1 -0
- package/dist/web-ui/content/modules/a50ce28f45bfeb2e/host.js +1 -0
- package/dist/web-ui/content/modules/adfc53789d5bedb2/chunk.js +1 -0
- package/dist/web-ui/content/modules/bcecb0cc14b7d2bd/chunk.js +1 -0
- package/dist/web-ui/content/modules/c4706f47c23f33e4/host.js +1 -0
- package/dist/web-ui/content/modules/{0051bd3d55f10ab7/host-Q524TMSR.js → c7de873095c061ab/host.js} +4 -3
- package/dist/web-ui/content/modules/d789f84a3f8765ae/page.js +6 -0
- package/dist/web-ui/content/modules/db4e2dbc7aed6353/chunk.js +1 -0
- package/dist/web-ui/content/modules/e4fd9906521b4aa3/chunk.js +1 -0
- package/dist/web-ui/content/modules/eb95c3ee0d42827a/chunk.js +6 -0
- package/dist/web-ui/content/modules/{0051bd3d55f10ab7/host-PHHNGVNL.js → ec68d93a95c1d5d6/host.js} +2 -2
- package/dist/web-ui/content/modules/f33ed89d1ccd3a2d/chunk.js +1 -0
- package/dist/web-ui/content/modules/f6502dab121f9d91/chunk.js +1 -0
- package/dist/web-ui/content/modules/fac83e61c0a8c8a7/host.js +1 -0
- package/dist/web-ui/content/scripts.js +1 -1
- package/dist/web-ui/embedded-assets.d.ts +200 -27
- package/dist/web-ui/embedded-assets.js +5 -4
- package/dist/web-ui/react/workspaces/sidebar-manage.d.ts +5 -0
- package/dist/web-ui/react/workspaces/sidebar-manage.js +27 -3
- package/dist/web-ui/running-activity.d.ts +22 -9
- package/dist/web-ui/running-activity.js +58 -42
- package/dist/web-ui/running-current-action.d.ts +12 -0
- package/dist/web-ui/running-current-action.js +74 -0
- package/dist/web-ui/scripts.d.ts +4 -0
- package/dist/web-ui/scripts.js +17 -8
- package/package.json +4 -2
- package/scripts/recover-web-update.mjs +26 -19
- package/scripts/recover-web-update.sh +9 -6
- package/scripts/update-node.sh +3 -1
- package/skills/wand-system/SKILL.md +69 -0
- package/skills/wand-system/references/operations.md +100 -0
- package/skills/wand-system/scripts/wand.mjs +34 -0
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chat-render-RY2I2CF3.js +0 -24
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-2F6J2MKX.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-6C6A2RHG.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-7AFM245W.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-ECWYA3U5.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-MHX2QXIS.js +0 -111
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-RAAPETNH.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-RXKBHYUR.js +0 -38
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-T35D57FW.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-U4EDP4TI.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-WHXQR7JT.js +0 -20
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-YOXXNXOX.js +0 -2
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-YQJ6XYV7.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/employee-card-NV34TZIH.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-2AQAIUNO.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-AZXNFCRW.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-EPEQX22R.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-FZYIMOLC.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-G6GL4SNL.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-PEILC5NU.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-RO5MB6RB.js +0 -69
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-WOQRK6NG.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-XRVOIEP6.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-ZDKOQEMS.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-bridge-PMQZRG5G.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/page-PY4JUL4W.js +0 -3
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/panel-BBQFC55Q.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/runtime.js +0 -974
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/sha256-ANXV72UC.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/task-board-host-PXZSWBBG.js +0 -2
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/workspace-agent-picker-DJSECSY6.js +0 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/workspace-window-MNMKSOBG.js +0 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Shared employee IM configuration contract. Credentials are write-only. */
|
|
2
|
+
export declare const EMPLOYEE_IM_EVENTS: readonly [{
|
|
3
|
+
readonly value: "task_started";
|
|
4
|
+
readonly label: "开始处理任务";
|
|
5
|
+
}, {
|
|
6
|
+
readonly value: "task_completed";
|
|
7
|
+
readonly label: "任务完成";
|
|
8
|
+
}, {
|
|
9
|
+
readonly value: "task_failed";
|
|
10
|
+
readonly label: "任务失败";
|
|
11
|
+
}, {
|
|
12
|
+
readonly value: "task_stopped";
|
|
13
|
+
readonly label: "任务停止";
|
|
14
|
+
}, {
|
|
15
|
+
readonly value: "waiting_permission";
|
|
16
|
+
readonly label: "等待权限或计划审批";
|
|
17
|
+
}, {
|
|
18
|
+
readonly value: "waiting_input";
|
|
19
|
+
readonly label: "等待用户输入";
|
|
20
|
+
}, {
|
|
21
|
+
readonly value: "message_queued";
|
|
22
|
+
readonly label: "消息进入队列";
|
|
23
|
+
}, {
|
|
24
|
+
readonly value: "step_completed";
|
|
25
|
+
readonly label: "步骤完成";
|
|
26
|
+
}, {
|
|
27
|
+
readonly value: "candidate_switched";
|
|
28
|
+
readonly label: "执行候选切换";
|
|
29
|
+
}, {
|
|
30
|
+
readonly value: "team_started";
|
|
31
|
+
readonly label: "团队开始任务";
|
|
32
|
+
}, {
|
|
33
|
+
readonly value: "team_completed";
|
|
34
|
+
readonly label: "团队任务完成";
|
|
35
|
+
}, {
|
|
36
|
+
readonly value: "team_failed";
|
|
37
|
+
readonly label: "团队任务失败";
|
|
38
|
+
}, {
|
|
39
|
+
readonly value: "team_stopped";
|
|
40
|
+
readonly label: "团队任务停止";
|
|
41
|
+
}];
|
|
42
|
+
export type EmployeeImEvent = typeof EMPLOYEE_IM_EVENTS[number]["value"];
|
|
43
|
+
export type EmployeeImProvider = "telegram" | "dingtalk";
|
|
44
|
+
/** Shared notification choices; selected events go to every enabled channel for this employee. */
|
|
45
|
+
export declare const EMPLOYEE_IM_EVENT_GROUPS: ReadonlyArray<{
|
|
46
|
+
label: string;
|
|
47
|
+
events: readonly EmployeeImEvent[];
|
|
48
|
+
}>;
|
|
49
|
+
export declare const EMPLOYEE_IM_PRIMARY_EVENTS: readonly EmployeeImEvent[];
|
|
50
|
+
export declare const EMPLOYEE_IM_MORE_EVENT_GROUPS: {
|
|
51
|
+
events: ("task_started" | "task_completed" | "task_failed" | "task_stopped" | "waiting_permission" | "waiting_input" | "message_queued" | "step_completed" | "candidate_switched" | "team_started" | "team_completed" | "team_failed" | "team_stopped")[];
|
|
52
|
+
label: string;
|
|
53
|
+
}[];
|
|
54
|
+
export interface EmployeeImChannel {
|
|
55
|
+
enabled: boolean;
|
|
56
|
+
/** Telegram destination; not a credential. */
|
|
57
|
+
chatId?: string;
|
|
58
|
+
botTokenConfigured?: boolean;
|
|
59
|
+
webhookConfigured?: boolean;
|
|
60
|
+
secretConfigured?: boolean;
|
|
61
|
+
}
|
|
62
|
+
export interface EmployeeImConfig {
|
|
63
|
+
events: EmployeeImEvent[];
|
|
64
|
+
telegram: EmployeeImChannel;
|
|
65
|
+
dingtalk: EmployeeImChannel;
|
|
66
|
+
}
|
|
67
|
+
export interface EmployeeImChannelInput {
|
|
68
|
+
enabled: boolean;
|
|
69
|
+
chatId?: string;
|
|
70
|
+
/** undefined/empty preserves an existing credential; null explicitly clears it. */
|
|
71
|
+
botToken?: string | null;
|
|
72
|
+
webhook?: string | null;
|
|
73
|
+
secret?: string | null;
|
|
74
|
+
}
|
|
75
|
+
export interface EmployeeImConfigInput {
|
|
76
|
+
events: EmployeeImEvent[];
|
|
77
|
+
telegram: EmployeeImChannelInput;
|
|
78
|
+
dingtalk: EmployeeImChannelInput;
|
|
79
|
+
}
|
|
80
|
+
export declare function emptyEmployeeImConfig(): EmployeeImConfig;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Shared employee IM configuration contract. Credentials are write-only. */
|
|
2
|
+
export const EMPLOYEE_IM_EVENTS = [
|
|
3
|
+
{ value: "task_started", label: "开始处理任务" },
|
|
4
|
+
{ value: "task_completed", label: "任务完成" },
|
|
5
|
+
{ value: "task_failed", label: "任务失败" },
|
|
6
|
+
{ value: "task_stopped", label: "任务停止" },
|
|
7
|
+
{ value: "waiting_permission", label: "等待权限或计划审批" },
|
|
8
|
+
{ value: "waiting_input", label: "等待用户输入" },
|
|
9
|
+
{ value: "message_queued", label: "消息进入队列" },
|
|
10
|
+
{ value: "step_completed", label: "步骤完成" },
|
|
11
|
+
{ value: "candidate_switched", label: "执行候选切换" },
|
|
12
|
+
{ value: "team_started", label: "团队开始任务" },
|
|
13
|
+
{ value: "team_completed", label: "团队任务完成" },
|
|
14
|
+
{ value: "team_failed", label: "团队任务失败" },
|
|
15
|
+
{ value: "team_stopped", label: "团队任务停止" },
|
|
16
|
+
];
|
|
17
|
+
/** Shared notification choices; selected events go to every enabled channel for this employee. */
|
|
18
|
+
export const EMPLOYEE_IM_EVENT_GROUPS = [
|
|
19
|
+
{ label: "处理进度", events: ["task_started", "message_queued", "step_completed", "candidate_switched"] },
|
|
20
|
+
{ label: "需要你介入", events: ["waiting_permission", "waiting_input"] },
|
|
21
|
+
{ label: "执行结果", events: ["task_completed", "task_failed", "task_stopped"] },
|
|
22
|
+
{ label: "团队任务", events: ["team_started", "team_completed", "team_failed", "team_stopped"] },
|
|
23
|
+
];
|
|
24
|
+
export const EMPLOYEE_IM_PRIMARY_EVENTS = [
|
|
25
|
+
"task_started", "task_completed", "task_failed", "task_stopped", "waiting_permission", "waiting_input",
|
|
26
|
+
];
|
|
27
|
+
export const EMPLOYEE_IM_MORE_EVENT_GROUPS = EMPLOYEE_IM_EVENT_GROUPS
|
|
28
|
+
.map(group => ({ ...group, events: group.events.filter(event => !EMPLOYEE_IM_PRIMARY_EVENTS.includes(event)) }))
|
|
29
|
+
.filter(group => group.events.length > 0);
|
|
30
|
+
export function emptyEmployeeImConfig() {
|
|
31
|
+
return { events: [], telegram: { enabled: false, chatId: "" }, dingtalk: { enabled: false } };
|
|
32
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type EmployeeImEvent, type EmployeeImProvider } from "./employee-im-types.js";
|
|
2
|
+
import type { WandStorage } from "./storage.js";
|
|
3
|
+
interface Notice {
|
|
4
|
+
employeeId: string;
|
|
5
|
+
provider: EmployeeImProvider;
|
|
6
|
+
event: EmployeeImEvent;
|
|
7
|
+
context: {
|
|
8
|
+
sessionId?: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
interface EmployeeImOptions {
|
|
13
|
+
fetch?: typeof fetch;
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
now?: () => number;
|
|
16
|
+
onFailure?: () => void;
|
|
17
|
+
}
|
|
18
|
+
/** Best-effort, bounded and non-blocking. No automatic retry after an unknown delivery. */
|
|
19
|
+
export declare class EmployeeImService {
|
|
20
|
+
private readonly storage;
|
|
21
|
+
private readonly options;
|
|
22
|
+
private readonly queue;
|
|
23
|
+
private readonly controllers;
|
|
24
|
+
private active;
|
|
25
|
+
private disposed;
|
|
26
|
+
constructor(storage: WandStorage, options?: EmployeeImOptions);
|
|
27
|
+
notify(employeeId: string, event: EmployeeImEvent, context?: Notice["context"]): void;
|
|
28
|
+
test(employeeId: string, provider: EmployeeImProvider): Promise<void>;
|
|
29
|
+
dispose(): void;
|
|
30
|
+
private failure;
|
|
31
|
+
private pump;
|
|
32
|
+
private sendNotice;
|
|
33
|
+
private deliver;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createHmac } from "node:crypto";
|
|
2
|
+
import { dingTalkWebhook } from "./employee-im-config.js";
|
|
3
|
+
import { EMPLOYEE_IM_EVENTS } from "./employee-im-types.js";
|
|
4
|
+
/** Best-effort, bounded and non-blocking. No automatic retry after an unknown delivery. */
|
|
5
|
+
export class EmployeeImService {
|
|
6
|
+
storage;
|
|
7
|
+
options;
|
|
8
|
+
queue = [];
|
|
9
|
+
controllers = new Set();
|
|
10
|
+
active = 0;
|
|
11
|
+
disposed = false;
|
|
12
|
+
constructor(storage, options = {}) {
|
|
13
|
+
this.storage = storage;
|
|
14
|
+
this.options = options;
|
|
15
|
+
}
|
|
16
|
+
notify(employeeId, event, context = {}) {
|
|
17
|
+
if (this.disposed)
|
|
18
|
+
return;
|
|
19
|
+
try {
|
|
20
|
+
const employee = this.storage.getSiliconEmployeeDefinition(employeeId);
|
|
21
|
+
if (!employee || employee.archivedAt)
|
|
22
|
+
return;
|
|
23
|
+
const config = this.storage.getEmployeeImConfig(employeeId);
|
|
24
|
+
if (!config.events.includes(event))
|
|
25
|
+
return;
|
|
26
|
+
for (const provider of ["telegram", "dingtalk"]) {
|
|
27
|
+
if (!config[provider].enabled)
|
|
28
|
+
continue;
|
|
29
|
+
if (this.queue.length >= 100) {
|
|
30
|
+
this.failure();
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
this.queue.push({ employeeId, provider, event, context: { sessionId: context.sessionId, title: context.title?.slice(0, 200) } });
|
|
34
|
+
}
|
|
35
|
+
this.pump();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
this.failure();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async test(employeeId, provider) {
|
|
42
|
+
if (this.disposed || this.active >= 4)
|
|
43
|
+
throw new Error("IM 发送繁忙,请稍后重试。");
|
|
44
|
+
this.active += 1;
|
|
45
|
+
try {
|
|
46
|
+
await this.deliver(employeeId, provider, "Wand · IM 测试通知\n此员工的消息渠道已连通。");
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
this.active -= 1;
|
|
50
|
+
this.pump();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
dispose() {
|
|
54
|
+
this.disposed = true;
|
|
55
|
+
this.queue.length = 0;
|
|
56
|
+
for (const controller of this.controllers)
|
|
57
|
+
controller.abort();
|
|
58
|
+
this.controllers.clear();
|
|
59
|
+
}
|
|
60
|
+
failure() {
|
|
61
|
+
try {
|
|
62
|
+
this.options.onFailure?.();
|
|
63
|
+
}
|
|
64
|
+
catch { /* observers cannot affect execution */ }
|
|
65
|
+
}
|
|
66
|
+
pump() {
|
|
67
|
+
while (!this.disposed && this.active < 4 && this.queue.length) {
|
|
68
|
+
const item = this.queue.shift();
|
|
69
|
+
this.active += 1;
|
|
70
|
+
void this.sendNotice(item).catch(() => this.failure()).finally(() => { this.active -= 1; this.pump(); });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async sendNotice(item) {
|
|
74
|
+
// Re-read before delivery: queued notices cannot survive disable/archive/delete or a changed selection.
|
|
75
|
+
const employee = this.storage.getSiliconEmployeeDefinition(item.employeeId);
|
|
76
|
+
const config = this.storage.getEmployeeImConfig(item.employeeId);
|
|
77
|
+
if (!employee || employee.archivedAt || !config[item.provider].enabled || !config.events.includes(item.event))
|
|
78
|
+
return;
|
|
79
|
+
const label = EMPLOYEE_IM_EVENTS.find(event => event.value === item.event)?.label;
|
|
80
|
+
if (!label)
|
|
81
|
+
return;
|
|
82
|
+
const lines = [`Wand · ${label}`, `员工:${employee.name.slice(0, 40)}`, `任务:${item.context.title || "员工任务"}`];
|
|
83
|
+
if (item.context.sessionId)
|
|
84
|
+
lines.push(`会话:${item.context.sessionId.slice(0, 100)}`);
|
|
85
|
+
await this.deliver(item.employeeId, item.provider, lines.join("\n").slice(0, 3000));
|
|
86
|
+
}
|
|
87
|
+
async deliver(employeeId, provider, text) {
|
|
88
|
+
if (!this.storage.getSiliconEmployeeDefinition(employeeId))
|
|
89
|
+
throw new Error("员工不存在。");
|
|
90
|
+
const { config, credentials } = this.storage.getEmployeeImSettings(employeeId);
|
|
91
|
+
let url;
|
|
92
|
+
let body;
|
|
93
|
+
if (provider === "telegram") {
|
|
94
|
+
if (!credentials.botToken || !config.telegram.chatId)
|
|
95
|
+
throw new Error("请先保存 Telegram Bot Token 和 Chat ID。");
|
|
96
|
+
url = new URL(`https://api.telegram.org/bot${credentials.botToken}/sendMessage`);
|
|
97
|
+
body = { chat_id: config.telegram.chatId, text };
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
if (!credentials.webhook)
|
|
101
|
+
throw new Error("请先保存钉钉机器人 Webhook。");
|
|
102
|
+
url = dingTalkWebhook(credentials.webhook);
|
|
103
|
+
if (credentials.secret) {
|
|
104
|
+
const timestamp = String((this.options.now ?? Date.now)());
|
|
105
|
+
const sign = createHmac("sha256", credentials.secret).update(`${timestamp}\n${credentials.secret}`).digest("base64");
|
|
106
|
+
url.searchParams.set("timestamp", timestamp);
|
|
107
|
+
url.searchParams.set("sign", sign);
|
|
108
|
+
}
|
|
109
|
+
body = { msgtype: "text", text: { content: text } };
|
|
110
|
+
}
|
|
111
|
+
const controller = new AbortController();
|
|
112
|
+
this.controllers.add(controller);
|
|
113
|
+
const timer = setTimeout(() => controller.abort(), this.options.timeoutMs ?? 8000);
|
|
114
|
+
timer.unref?.();
|
|
115
|
+
try {
|
|
116
|
+
const response = await (this.options.fetch ?? fetch)(url, {
|
|
117
|
+
method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
|
|
118
|
+
redirect: "error", signal: controller.signal,
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
await response.body?.cancel();
|
|
122
|
+
throw new Error("IM 发送失败,请检查机器人凭据、接收目标及网络。");
|
|
123
|
+
}
|
|
124
|
+
const reader = response.body?.getReader();
|
|
125
|
+
if (!reader)
|
|
126
|
+
throw new Error("IM 服务未返回有效回执。");
|
|
127
|
+
const chunks = [];
|
|
128
|
+
let size = 0;
|
|
129
|
+
try {
|
|
130
|
+
while (true) {
|
|
131
|
+
const { done, value } = await reader.read();
|
|
132
|
+
if (done)
|
|
133
|
+
break;
|
|
134
|
+
size += value.byteLength;
|
|
135
|
+
if (size > 16_384)
|
|
136
|
+
throw new Error("IM 回执过大。");
|
|
137
|
+
chunks.push(value);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
await reader.cancel().catch(() => { });
|
|
142
|
+
reader.releaseLock();
|
|
143
|
+
}
|
|
144
|
+
const payload = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
145
|
+
if (provider === "telegram" ? payload.ok !== true : payload.errcode !== 0)
|
|
146
|
+
throw new Error("IM 服务拒绝了消息。");
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Provider bodies and errors may include the credential-bearing request URL: never echo/log them.
|
|
150
|
+
throw new Error("IM 发送未确认,请检查机器人凭据、接收目标及网络;不会自动重发。");
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
clearTimeout(timer);
|
|
154
|
+
this.controllers.delete(controller);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -51,7 +51,8 @@ export function rememberExplicitEmployeeRequest(storage, employeeId, prompt) {
|
|
|
51
51
|
export function startEmployeeKnowledgeRunner(storage, runner, context, observer) {
|
|
52
52
|
const employeeId = context.session.employeeId;
|
|
53
53
|
// Strip any inherited credentials from the parent environment (including nested Wand runs).
|
|
54
|
-
|
|
54
|
+
// Non-secret identity, overwritten for nested executions; never infer a current session by title/time.
|
|
55
|
+
const env = { ...context.env, WAND_SESSION_ID: context.session.id };
|
|
55
56
|
delete env.WAND_KNOWLEDGE_DB;
|
|
56
57
|
delete env.WAND_KNOWLEDGE_TOKEN;
|
|
57
58
|
if (!employeeId)
|
|
@@ -69,7 +70,7 @@ export function startEmployeeKnowledgeRunner(storage, runner, context, observer)
|
|
|
69
70
|
try {
|
|
70
71
|
const receipt = (!context.session.automationId || context.session.automationId.startsWith("wand-task:"))
|
|
71
72
|
? rememberExplicitEmployeeRequest(storage, employeeId, context.prompt) : null;
|
|
72
|
-
const runtimeSystemPrompt = [context.session.runtimeSystemPrompt, receipt, knowledgeContext(storage, employeeId, context.prompt)]
|
|
73
|
+
const runtimeSystemPrompt = [context.session.runtimeSystemPrompt, `当前 Wand 会话 ID:${context.session.id}(非凭据;创建后续检查计划时用作精确续接目标)。`, receipt, knowledgeContext(storage, employeeId, context.prompt)]
|
|
73
74
|
.filter(Boolean).join("\n\n");
|
|
74
75
|
const execution = runner.start({ ...context,
|
|
75
76
|
session: { ...context.session, runtimeSystemPrompt },
|
package/dist/employee-text.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface EmployeeTextDeps {
|
|
|
13
13
|
outputTokens: number;
|
|
14
14
|
model: string;
|
|
15
15
|
}) => void;
|
|
16
|
+
callSessionId?: string;
|
|
16
17
|
}
|
|
17
18
|
/** Expand each configured slot once; equivalent leaves execute once in first occurrence order. */
|
|
18
19
|
export declare function employeeTextCandidates(agents: readonly WandTaskAgent[], config: Parameters<typeof getDefaultModelForProvider>[0] & Pick<WandConfig, "modelGroups">): Array<{
|
package/dist/employee-text.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { coreHarnessRuntime, loadPiAi, resolveCoreModel } from "./harness-engine.js";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { CoreTurnTracker } from "./core-turn-tracker.js";
|
|
2
4
|
import { agentKey } from "./ai-team-types.js";
|
|
3
5
|
import { getDefaultModelForProvider } from "./config.js";
|
|
4
6
|
import { freeModelOrder, resolveModelGroupModels } from "./model-groups.js";
|
|
@@ -84,6 +86,7 @@ export async function prepareEmployeeTextCandidate(deps, agent, request, signal)
|
|
|
84
86
|
}
|
|
85
87
|
if (agent.provider !== "pi")
|
|
86
88
|
throw new Error("Wand Agent只支持Pi provider。");
|
|
89
|
+
CoreTurnTracker.assertSystemCallAdmission();
|
|
87
90
|
const pi = await loadPiAi();
|
|
88
91
|
const context = pi.normalizeContext({ systemPrompt: request.system,
|
|
89
92
|
messages: [{ role: "user", content: request.prompt, timestamp: Date.now() }], tools: [] });
|
|
@@ -96,14 +99,20 @@ export async function prepareEmployeeTextCandidate(deps, agent, request, signal)
|
|
|
96
99
|
const { streamSimple } = await import("@earendil-works/pi-ai/api/openai-completions");
|
|
97
100
|
return async () => {
|
|
98
101
|
signal.throwIfAborted();
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
const token = CoreTurnTracker.startTurn(deps.callSessionId ?? `system-ai:${randomUUID()}`, true);
|
|
103
|
+
try {
|
|
104
|
+
let status;
|
|
105
|
+
const message = await streamSimple(checked.model, context, { apiKey: checked.apiKey, signal,
|
|
106
|
+
maxRetries: 0, fetch: async (input, init) => { const response = await fetch(input, init); status = response.status; return response; },
|
|
107
|
+
maxTokens: Math.min(4096, checked.model.maxTokens), reasoning: reasoning === "off" ? undefined : reasoning,
|
|
108
|
+
toolChoice: "none", onPayload: payload => ({ ...payload, provider: OPENROUTER_FREE_ROUTING }) }).result();
|
|
109
|
+
const text = sdkText(message, status);
|
|
110
|
+
deps.onUsage?.({ inputTokens: message.usage.input, outputTokens: message.usage.output, model: `${checked.model.provider}/${checked.model.id}` });
|
|
111
|
+
return text;
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
CoreTurnTracker.endTurn(token);
|
|
115
|
+
}
|
|
107
116
|
};
|
|
108
117
|
}
|
|
109
118
|
const resolved = await resolveCoreModel(config.harness, agent.model, { cwd: config.defaultCwd });
|
|
@@ -113,10 +122,16 @@ export async function prepareEmployeeTextCandidate(deps, agent, request, signal)
|
|
|
113
122
|
const level = pi.clampThinkingLevel(resolved.model, (reasoning ?? "off"));
|
|
114
123
|
return async () => {
|
|
115
124
|
signal.throwIfAborted();
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
125
|
+
const token = CoreTurnTracker.startTurn(deps.callSessionId ?? `system-ai:${randomUUID()}`, true);
|
|
126
|
+
try {
|
|
127
|
+
const message = await runtime.streamSimple(resolved.model, context, { signal, maxRetries: 0, maxTokens: Math.min(4096, resolved.model.maxTokens),
|
|
128
|
+
reasoning: level === "off" ? undefined : level, toolChoice: "none" }).result();
|
|
129
|
+
const text = sdkText(message);
|
|
130
|
+
deps.onUsage?.({ inputTokens: message.usage.input, outputTokens: message.usage.output, model: `${resolved.model.provider}/${resolved.model.id}` });
|
|
131
|
+
return text;
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
CoreTurnTracker.endTurn(token);
|
|
135
|
+
}
|
|
121
136
|
};
|
|
122
137
|
}
|
package/dist/git-quick-commit.js
CHANGED
|
@@ -354,7 +354,7 @@ function commitTaskLine(lang, usedIteration) {
|
|
|
354
354
|
async function generateCommitMessage(cwd, language, ai = {}, options = {}) {
|
|
355
355
|
const { input, usedIteration } = await buildCommitPromptInput(cwd, options);
|
|
356
356
|
const lang = language.trim() || "中文";
|
|
357
|
-
return callConfiguredAiText({ system: commitTaskLine(lang, usedIteration), prompt: input }, cwd, language, ai, (raw) => {
|
|
357
|
+
return callConfiguredAiText({ system: commitTaskLine(lang, usedIteration), prompt: input }, cwd, language, { ...ai, purpose: "git-commit" }, (raw) => {
|
|
358
358
|
const message = normalizeAiText(raw);
|
|
359
359
|
if (!message)
|
|
360
360
|
throw new QuickCommitError("AI 返回了空的 commit message。", "EMPTY_AI_MESSAGE");
|
|
@@ -407,7 +407,7 @@ async function generateCommitMessageWithTag(cwd, language, ai = {}, options = {}
|
|
|
407
407
|
|
|
408
408
|
请严格输出**单行 JSON 对象**,不要 Markdown 代码块、不要任何解释文字、不要多余引号。格式:
|
|
409
409
|
{"message":"...","tag":"v1.2.3"}`;
|
|
410
|
-
return callConfiguredAiText({ system, prompt: `${tagHint}\n\n${input}` }, cwd, language, ai, (raw) => {
|
|
410
|
+
return callConfiguredAiText({ system, prompt: `${tagHint}\n\n${input}` }, cwd, language, { ...ai, purpose: "git-tag" }, (raw) => {
|
|
411
411
|
const parsed = tryParseJson(raw);
|
|
412
412
|
// Preserve the plain-message fallback for older CLIs that do not return JSON.
|
|
413
413
|
const message = normalizeAiText(parsed && typeof parsed.message === "string" ? parsed.message : raw);
|
|
@@ -464,7 +464,7 @@ async function generateTagAfterCommit(cwd, language, commitMessage, ai = {}) {
|
|
|
464
464
|
请用${lang}思考但严格输出**单行 JSON 对象**,不要 Markdown 代码块、不要任何解释文字、不要多余引号。格式:
|
|
465
465
|
{"tag":"v1.2.3"}`;
|
|
466
466
|
const prompt = `${tagHint}\n\ncommit message:${commitMessage}\n\ngit diff:\n${diff}`;
|
|
467
|
-
return callConfiguredAiText({ system, prompt }, cwd, language, ai, (raw) => {
|
|
467
|
+
return callConfiguredAiText({ system, prompt }, cwd, language, { ...ai, purpose: "git-tag" }, (raw) => {
|
|
468
468
|
const parsed = tryParseJson(raw);
|
|
469
469
|
const suggested = sanitizeSuggestedTag(parsed && typeof parsed.tag === "string" ? parsed.tag : raw);
|
|
470
470
|
if (!suggested)
|
|
@@ -875,6 +875,24 @@ async function getLatestTagAtHead(cwd) {
|
|
|
875
875
|
}
|
|
876
876
|
}
|
|
877
877
|
async function runQuickCommitFallbackCli(opts, priorError) {
|
|
878
|
+
const trace = opts.traceFactory?.start("git-fallback", buildFallbackPrompt(opts, priorError), opts.cwd);
|
|
879
|
+
const candidate = { provider: defaultProvider(opts.provider), engine: "cli", model: opts.model ?? undefined, thinkingEffort: opts.thinkingEffort ?? undefined };
|
|
880
|
+
try {
|
|
881
|
+
if (trace)
|
|
882
|
+
opts.onCallSessionCreated?.(trace.sessionId);
|
|
883
|
+
trace?.candidateStarted(candidate);
|
|
884
|
+
const result = await runQuickCommitFallbackCliImpl(opts, priorError);
|
|
885
|
+
trace?.applicationResult(result);
|
|
886
|
+
trace?.complete();
|
|
887
|
+
return result;
|
|
888
|
+
}
|
|
889
|
+
catch (error) {
|
|
890
|
+
trace?.candidateFailed(candidate, error, "execution");
|
|
891
|
+
trace?.fail(error);
|
|
892
|
+
throw error;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
async function runQuickCommitFallbackCliImpl(opts, priorError) {
|
|
878
896
|
await assertGitWorkTreeAsync(opts.cwd);
|
|
879
897
|
const beforeHead = await getHead(opts.cwd);
|
|
880
898
|
const request = withOpsPersona(buildFallbackPrompt(opts, priorError), opts.opsPersona);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type SessionProvider } from "./provider-catalog.js";
|
|
2
|
+
/** Install trusted bundled files without overwriting user content or changing tool permissions. */
|
|
3
|
+
export declare function installManagedSkill(configDir: string, name: string, files: Record<string, string>, providers?: readonly SessionProvider[], home?: string): Promise<{
|
|
4
|
+
installed: string[];
|
|
5
|
+
providers: readonly SessionProvider[];
|
|
6
|
+
}>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile, mkdir, lstat, readlink, writeFile, symlink, realpath } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { SESSION_PROVIDERS } from "./provider-catalog.js";
|
|
6
|
+
const sha = (data) => createHash("sha256").update(data).digest("hex");
|
|
7
|
+
/** Install trusted bundled files without overwriting user content or changing tool permissions. */
|
|
8
|
+
export async function installManagedSkill(configDir, name, files, providers = SESSION_PROVIDERS, home = homedir()) {
|
|
9
|
+
if (!providers.length || providers.some((provider) => !SESSION_PROVIDERS.includes(provider)))
|
|
10
|
+
throw new Error("未知CLI技能目标。");
|
|
11
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name) || Object.keys(files).some(file => path.isAbsolute(file) || file.split(/[\\/]/).some(part => !part || part === ".." || part === "."))) {
|
|
12
|
+
throw new Error("无效的托管技能路径。");
|
|
13
|
+
}
|
|
14
|
+
const canonical = path.join(configDir, "skills", name);
|
|
15
|
+
const targets = new Set();
|
|
16
|
+
for (const provider of providers) {
|
|
17
|
+
const directory = provider === "claude" ? ".claude" : provider === "qoder" ? ".qoder" : ".agents";
|
|
18
|
+
targets.add(path.join(home, directory, "skills", name));
|
|
19
|
+
}
|
|
20
|
+
// Preflight every target before touching anything; existing user content is never replaced.
|
|
21
|
+
for (const target of targets) {
|
|
22
|
+
try {
|
|
23
|
+
const info = await lstat(target);
|
|
24
|
+
if (!info.isSymbolicLink() || path.resolve(path.dirname(target), await readlink(target)) !== path.resolve(canonical)) {
|
|
25
|
+
throw new Error(`已有同名技能,保留原内容,请先手动处理:${target}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error.code !== "ENOENT")
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const directories = new Set([canonical]);
|
|
34
|
+
for (const file of Object.keys(files)) {
|
|
35
|
+
let directory = path.dirname(path.join(canonical, file));
|
|
36
|
+
while (directory !== canonical) {
|
|
37
|
+
directories.add(directory);
|
|
38
|
+
directory = path.dirname(directory);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const directory of directories) {
|
|
42
|
+
try {
|
|
43
|
+
if (!(await lstat(directory)).isDirectory())
|
|
44
|
+
throw new Error(`技能目录不是普通目录,保留原内容:${directory}`);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error.code !== "ENOENT")
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const file of [...Object.keys(files), ".wand-managed.json"]) {
|
|
52
|
+
try {
|
|
53
|
+
if (!(await lstat(path.join(canonical, file))).isFile())
|
|
54
|
+
throw new Error(`技能文件不是普通文件,保留原内容:${file}`);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error.code !== "ENOENT")
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
let previous = {};
|
|
62
|
+
try {
|
|
63
|
+
previous = JSON.parse(await readFile(path.join(canonical, ".wand-managed.json"), "utf8"));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error.code !== "ENOENT")
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
const contents = Object.entries(files).map(([file, content]) => ({ file, content }));
|
|
70
|
+
for (const { file, content } of contents) {
|
|
71
|
+
try {
|
|
72
|
+
const old = await readFile(path.join(canonical, file), "utf8");
|
|
73
|
+
if (old !== content && sha(old) !== previous[file])
|
|
74
|
+
throw new Error(`技能已有用户修改,拒绝覆盖:${file}`);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (error.code !== "ENOENT")
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const { file, content } of contents) {
|
|
82
|
+
await mkdir(path.dirname(path.join(canonical, file)), { recursive: true });
|
|
83
|
+
await writeFile(path.join(canonical, file), content, { mode: 0o644 });
|
|
84
|
+
}
|
|
85
|
+
await writeFile(path.join(canonical, ".wand-managed.json"), JSON.stringify(Object.fromEntries(contents.map(({ file, content }) => [file, sha(content)]))), { mode: 0o600 });
|
|
86
|
+
for (const target of targets) {
|
|
87
|
+
await mkdir(path.dirname(target), { recursive: true });
|
|
88
|
+
try {
|
|
89
|
+
await symlink(canonical, target, "dir");
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (error.code !== "EEXIST")
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
if (await realpath(target) !== await realpath(canonical))
|
|
96
|
+
throw new Error("技能链接目标校验失败。");
|
|
97
|
+
}
|
|
98
|
+
return { installed: [...targets], providers };
|
|
99
|
+
}
|
|
@@ -117,7 +117,7 @@ async function decideWithAi(request, candidates) {
|
|
|
117
117
|
const message = `任务提示词:\n${task}\n\n可选模型分组:\n${list}\n\n只输出上面列出的分组名之一;都不合适就输出 -。`;
|
|
118
118
|
const text = call
|
|
119
119
|
? await call({ system, prompt: message }, ai)
|
|
120
|
-
: await callConfiguredAiText({ system, prompt: message }, request.cwd ?? process.cwd(), request.language ?? "", { ...ai, budgetMs: AUTO_ASSIGN_AI_BUDGET_MS }, (raw) => String(raw).trim());
|
|
120
|
+
: await callConfiguredAiText({ system, prompt: message }, request.cwd ?? process.cwd(), request.language ?? "", { ...ai, purpose: "model-auto-assign", budgetMs: AUTO_ASSIGN_AI_BUDGET_MS }, (raw) => String(raw).trim());
|
|
121
121
|
const flat = collapseWhitespace(text);
|
|
122
122
|
if (!flat || flat === "-" || flat === "无")
|
|
123
123
|
return { group: null, calls: 1 };
|
package/dist/process-manager.js
CHANGED
|
@@ -718,7 +718,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
718
718
|
ai: resolveSystemAiContext({
|
|
719
719
|
provider: groupProvider, runner: undefined, command: providerCliCommand(groupProvider),
|
|
720
720
|
structuredState: undefined, selectedModel: null, thinkingEffort: opts?.thinkingEffort,
|
|
721
|
-
}, this.config, this.storage.getSystemSiliconEmployee(), this.free),
|
|
721
|
+
}, this.config, this.storage.getSystemSiliconEmployee(), this.free, { storage: this.storage }),
|
|
722
722
|
cwd: resolveSessionCwd(cwd, this.config.defaultCwd),
|
|
723
723
|
language: this.config.language,
|
|
724
724
|
});
|
package/dist/prompt-optimizer.js
CHANGED
|
@@ -32,7 +32,7 @@ export async function optimizePrompt(rawText, language, cwd, ai = {}) {
|
|
|
32
32
|
}
|
|
33
33
|
const request = buildOptimizePrompt(text, language);
|
|
34
34
|
try {
|
|
35
|
-
return await callConfiguredAiText(request, cwd ?? process.cwd(), language, ai, (raw) => {
|
|
35
|
+
return await callConfiguredAiText(request, cwd ?? process.cwd(), language, { ...ai, purpose: "prompt-optimize" }, (raw) => {
|
|
36
36
|
const cleaned = raw
|
|
37
37
|
.replace(/^```[a-zA-Z]*\n?/, "")
|
|
38
38
|
.replace(/\n?```$/, "")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ScheduledTaskRunView, ScheduledTaskRunFactView } from "./scheduled-task-types.js";
|
|
2
|
+
export type DayOutcome = ScheduledTaskRunView["executionStatus"] | "mixed" | "empty";
|
|
3
|
+
export interface ExecutionDay<T extends ScheduledTaskRunFactView = ScheduledTaskRunView> {
|
|
4
|
+
key: string;
|
|
5
|
+
records: T[];
|
|
6
|
+
outcome: DayOutcome;
|
|
7
|
+
counts: Partial<Record<ScheduledTaskRunView["executionStatus"], number>>;
|
|
8
|
+
available: boolean;
|
|
9
|
+
level: number;
|
|
10
|
+
}
|
|
11
|
+
/** A date-only key in the explicit display zone, never the browser's implicit zone. */
|
|
12
|
+
export declare function dateKey(value: string | Date, timeZone: string): string;
|
|
13
|
+
export declare function addDateDays(key: string, days: number): string;
|
|
14
|
+
export declare function latestExecution<T extends ScheduledTaskRunFactView>(records: readonly T[]): T | undefined;
|
|
15
|
+
/** Executions belong to the actual claim date. A late catch-up is not painted into its old scheduled day. */
|
|
16
|
+
export declare function executionDays<T extends ScheduledTaskRunFactView>(records: readonly T[], now: Date, createdAt: string, timeZone: string, weeks?: number): ExecutionDay<T>[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** A date-only key in the explicit display zone, never the browser's implicit zone. */
|
|
2
|
+
export function dateKey(value, timeZone) {
|
|
3
|
+
const date = typeof value === "string" ? new Date(value) : value;
|
|
4
|
+
const parts = new Intl.DateTimeFormat("en-CA", { timeZone, year: "numeric", month: "2-digit", day: "2-digit" }).formatToParts(date);
|
|
5
|
+
return ["year", "month", "day"].map(type => parts.find(part => part.type === type).value).join("-");
|
|
6
|
+
}
|
|
7
|
+
export function addDateDays(key, days) {
|
|
8
|
+
const date = new Date(`${key}T12:00:00Z`);
|
|
9
|
+
date.setUTCDate(date.getUTCDate() + days);
|
|
10
|
+
return date.toISOString().slice(0, 10);
|
|
11
|
+
}
|
|
12
|
+
export function latestExecution(records) {
|
|
13
|
+
return [...records].filter(record => Number.isFinite(Date.parse(record.claimedAt))).sort((a, b) => Date.parse(b.claimedAt) - Date.parse(a.claimedAt))[0];
|
|
14
|
+
}
|
|
15
|
+
/** Executions belong to the actual claim date. A late catch-up is not painted into its old scheduled day. */
|
|
16
|
+
export function executionDays(records, now, createdAt, timeZone, weeks = 12) {
|
|
17
|
+
weeks = Number.isFinite(weeks) ? Math.max(1, Math.min(53, Math.trunc(weeks))) : 12;
|
|
18
|
+
const today = dateKey(now, timeZone);
|
|
19
|
+
const weekday = (new Date(`${today}T12:00:00Z`).getUTCDay() + 6) % 7;
|
|
20
|
+
const start = addDateDays(today, -weekday - (weeks - 1) * 7);
|
|
21
|
+
const created = dateKey(createdAt, timeZone);
|
|
22
|
+
const buckets = new Map();
|
|
23
|
+
for (const record of records) {
|
|
24
|
+
if (!Number.isFinite(Date.parse(record.claimedAt)))
|
|
25
|
+
continue;
|
|
26
|
+
const key = dateKey(record.claimedAt, timeZone);
|
|
27
|
+
const bucket = buckets.get(key) ?? [];
|
|
28
|
+
bucket.push(record);
|
|
29
|
+
buckets.set(key, bucket);
|
|
30
|
+
}
|
|
31
|
+
return Array.from({ length: weeks * 7 }, (_, index) => {
|
|
32
|
+
const key = addDateDays(start, index);
|
|
33
|
+
const daily = [...(buckets.get(key) ?? [])].sort((a, b) => Date.parse(b.claimedAt) - Date.parse(a.claimedAt));
|
|
34
|
+
const available = key >= created && key <= today || daily.length > 0;
|
|
35
|
+
const counts = {};
|
|
36
|
+
for (const record of daily)
|
|
37
|
+
counts[record.executionStatus] = (counts[record.executionStatus] ?? 0) + 1;
|
|
38
|
+
const states = Object.keys(counts);
|
|
39
|
+
const outcome = states.length > 1 ? "mixed" : states[0] ?? "empty";
|
|
40
|
+
return { key, records: daily, counts, outcome, available, level: Math.min(3, daily.length) };
|
|
41
|
+
});
|
|
42
|
+
}
|