@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,166 @@
|
|
|
1
|
+
/** Shared, data-only contract for per-session Pi settings. */
|
|
2
|
+
export const PI_BUILTIN_TOOLS = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
3
|
+
/**
|
|
4
|
+
* pi-goal 注册的工具名。Pi 的 `--tools` 是白名单(会替换默认集合),所以 CLI 会话要开启目标模式
|
|
5
|
+
* 必须逐个点名;名字与 `core-extension-host.ts` 的排除列表保持同一份。
|
|
6
|
+
*/
|
|
7
|
+
export const PI_GOAL_TOOLS = ["create_goal", "get_goal", "update_goal"];
|
|
8
|
+
export function piSkillMode(settings, id) {
|
|
9
|
+
return !settings.resources?.skills.includes(id) ? "off" : settings.lockedSkills?.includes(id) ? "locked" : "on";
|
|
10
|
+
}
|
|
11
|
+
/** A single manual gesture changes selection and lock atomically, never mutating its snapshot. */
|
|
12
|
+
export function piSkillModePatch(settings, id, mode) {
|
|
13
|
+
const resources = settings.resources ?? { skills: [], mcpServers: [] };
|
|
14
|
+
const locks = settings.lockedSkills ?? [];
|
|
15
|
+
return {
|
|
16
|
+
resources: { ...resources, skills: mode === "off" ? resources.skills.filter((value) => value !== id)
|
|
17
|
+
: [...new Set([...resources.skills, id])] },
|
|
18
|
+
lockedSkills: mode === "locked" ? [...new Set([...locks, id])] : locks.filter((value) => value !== id),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function patchPiResourceSelection(raw) {
|
|
22
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
23
|
+
throw new Error("请选择有效的 Skills / MCP 列表。");
|
|
24
|
+
const item = raw;
|
|
25
|
+
if (Object.keys(item).some((key) => key !== "skills" && key !== "mcpServers")) {
|
|
26
|
+
throw new Error("资源选择不能包含路径、命令或其他设置。");
|
|
27
|
+
}
|
|
28
|
+
const ids = (key) => {
|
|
29
|
+
const value = item[key];
|
|
30
|
+
if (!Array.isArray(value) || value.length > 64 || new Set(value).size !== value.length
|
|
31
|
+
|| value.some((id) => typeof id !== "string" || !/^(?:skill-[a-f0-9]{24}|mcp-[a-f0-9]{24})$/.test(id))) {
|
|
32
|
+
throw new Error("资源选择必须是无重复的已安装资源 ID(每类最多64项)。");
|
|
33
|
+
}
|
|
34
|
+
return [...value];
|
|
35
|
+
};
|
|
36
|
+
return { skills: ids("skills"), mcpServers: ids("mcpServers") };
|
|
37
|
+
}
|
|
38
|
+
export function defaultPiSessionSettings(autoCompaction = true) {
|
|
39
|
+
return { codemode: "off", globalTools: false, goalMode: false,
|
|
40
|
+
tools: ["read", "bash", "edit", "write"], localDecision: true, autoCompaction };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* CLI 会话的出厂默认:完全跟随 Pi 自身配置(默认工具 + 已安装的全局扩展 + codemode),
|
|
44
|
+
* 与直接在终端里跑 `pi` 一致——用户已经装好的子代理、待办、CodeMode 不该因为从 Wand 启动就消失。
|
|
45
|
+
* 想逐项挑选工具时再关掉「全局扩展工具」,会话就切到 Wand 托管的 `--tools` 白名单。
|
|
46
|
+
* 进程内 SDK 会话仍用 `defaultPiSessionSettings`:员工会话不静默继承用户的全局扩展。
|
|
47
|
+
*/
|
|
48
|
+
export function defaultPiCliSessionSettings(autoCompaction = true) {
|
|
49
|
+
return { ...defaultPiSessionSettings(autoCompaction), globalTools: true,
|
|
50
|
+
resources: { skills: [], mcpServers: [] }, autoResources: false };
|
|
51
|
+
}
|
|
52
|
+
/** Strict partial updates: an invalid/unknown field never silently enables a capability. */
|
|
53
|
+
export function patchPiSessionSettings(current, raw) {
|
|
54
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
55
|
+
throw new Error("Pi 设置必须是对象。");
|
|
56
|
+
const next = { ...current, tools: [...current.tools] };
|
|
57
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
58
|
+
if (key === "codemodeOverride") {
|
|
59
|
+
if (value === null)
|
|
60
|
+
delete next.codemodeOverride;
|
|
61
|
+
else if (value === "off" || value === "on" || value === "only")
|
|
62
|
+
next.codemodeOverride = value;
|
|
63
|
+
else
|
|
64
|
+
throw new Error("CodeMode 请选择跟随 Pi、关闭、启用或仅 CodeMode。");
|
|
65
|
+
}
|
|
66
|
+
else if (key === "resources") {
|
|
67
|
+
next.resources = patchPiResourceSelection(value);
|
|
68
|
+
}
|
|
69
|
+
else if (key === "lockedSkills") {
|
|
70
|
+
if (!Array.isArray(value) || value.length > 64 || new Set(value).size !== value.length
|
|
71
|
+
|| value.some((id) => typeof id !== "string" || !/^skill-[a-f0-9]{24}$/.test(id))) {
|
|
72
|
+
throw new Error("锁定技能必须是无重复的已安装 Skill ID(最多64项)。");
|
|
73
|
+
}
|
|
74
|
+
next.lockedSkills = [...value];
|
|
75
|
+
}
|
|
76
|
+
else if (key === "codemode") {
|
|
77
|
+
if (value !== "off" && value !== "on" && value !== "only")
|
|
78
|
+
throw new Error("CodeMode 必须是 off、on 或 only。");
|
|
79
|
+
next.codemode = value;
|
|
80
|
+
}
|
|
81
|
+
else if (key === "tools") {
|
|
82
|
+
if (!Array.isArray(value) || value.length > PI_BUILTIN_TOOLS.length
|
|
83
|
+
|| value.some((tool) => !PI_BUILTIN_TOOLS.includes(tool)) || new Set(value).size !== value.length) {
|
|
84
|
+
throw new Error("基础工具必须是无重复的 Pi 工具列表。");
|
|
85
|
+
}
|
|
86
|
+
next.tools = [...value];
|
|
87
|
+
}
|
|
88
|
+
else if (key === "globalTools" || key === "goalMode" || key === "localDecision" || key === "autoCompaction" || key === "autoResources") {
|
|
89
|
+
if (typeof value !== "boolean")
|
|
90
|
+
throw new Error(`${key} 必须是是否启用(boolean)。`);
|
|
91
|
+
next[key] = value;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
throw new Error(`未知 Pi 设置:${key}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return next;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* `codemode: "only"`(连声明都藏起来、只从脚本调用)由 Pi 的 `codemode.mode` 设置决定,
|
|
101
|
+
* 没有对应的 CLI 开关:CLI 会话一律按「启用」处理,读取时也归一到 `on`,不谎报模式。
|
|
102
|
+
*/
|
|
103
|
+
export function cliCodemode(codemode) {
|
|
104
|
+
return codemode === "off" ? "off" : "on";
|
|
105
|
+
}
|
|
106
|
+
/** 会话设置在该引擎下真正生效的形态(目前只有 CLI 的 CodeMode 需要归一)。 */
|
|
107
|
+
export function effectivePiSessionSettings(settings, engine) {
|
|
108
|
+
return engine === "cli" ? { ...settings, codemode: cliCodemode(settings.codemode) } : settings;
|
|
109
|
+
}
|
|
110
|
+
export function piToolSelection(settings) {
|
|
111
|
+
if (settings.globalTools)
|
|
112
|
+
return { mode: "config", names: [] };
|
|
113
|
+
const names = [...settings.tools];
|
|
114
|
+
const codemode = settings.codemodeOverride ?? cliCodemode(settings.codemode);
|
|
115
|
+
if (codemode !== "off")
|
|
116
|
+
names.push("codemode");
|
|
117
|
+
if (settings.goalMode)
|
|
118
|
+
names.push(...PI_GOAL_TOOLS);
|
|
119
|
+
return { mode: "allowlist", names };
|
|
120
|
+
}
|
|
121
|
+
export const NO_PI_SETTINGS_CONTROLS = Object.freeze({
|
|
122
|
+
tools: false, codemode: false, codemodeOnly: false, globalTools: false,
|
|
123
|
+
goalMode: false, localDecision: false, autoCompaction: false,
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* 面板逐项可用性。新服务端直接给 `controls`;没有这个字段的旧服务端按它当时的语义退回:
|
|
127
|
+
* SDK 原生开关看 `available`,CLI 只开放基础工具列表,其余一律不可改。
|
|
128
|
+
*/
|
|
129
|
+
export function piSettingsControls(response) {
|
|
130
|
+
if (!response)
|
|
131
|
+
return NO_PI_SETTINGS_CONTROLS;
|
|
132
|
+
if (response.controls)
|
|
133
|
+
return response.controls;
|
|
134
|
+
const sdk = response.available === true;
|
|
135
|
+
return {
|
|
136
|
+
tools: sdk || response.toolsAvailable === true,
|
|
137
|
+
codemode: sdk,
|
|
138
|
+
codemodeOnly: sdk,
|
|
139
|
+
globalTools: sdk,
|
|
140
|
+
goalMode: sdk && response.goalAvailable === true,
|
|
141
|
+
localDecision: sdk && response.localDecisionAvailable === true,
|
|
142
|
+
autoCompaction: sdk,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** CLI 会话能表达的设置字段;其余字段只对进程内 SDK 会话有意义。 */
|
|
146
|
+
export const PI_CLI_SETTING_KEYS = ["tools", "codemode", "globalTools", "goalMode", "localDecision", "resources", "lockedSkills", "codemodeOverride", "autoResources"];
|
|
147
|
+
/**
|
|
148
|
+
* CLI 会话的写入裁决:无法表达的字段/取值直接拒绝,不能存下一条永远不会生效的设置。
|
|
149
|
+
* 返回拒绝原因,允许时返回 null。
|
|
150
|
+
*/
|
|
151
|
+
export function cliPiSettingsRejection(raw, next) {
|
|
152
|
+
const keys = raw && typeof raw === "object" && !Array.isArray(raw) ? Object.keys(raw) : [];
|
|
153
|
+
const unsupported = keys.filter((key) => !PI_CLI_SETTING_KEYS.includes(key));
|
|
154
|
+
if (unsupported.length) {
|
|
155
|
+
return `Pi CLI 会话不支持这些设置:${unsupported.join("、")}(仅原生 SDK 可用)。`;
|
|
156
|
+
}
|
|
157
|
+
if (keys.includes("codemode") && next.codemode === "only") {
|
|
158
|
+
return "「仅 CodeMode」需要原生 SDK;CLI 会话只能选择启用或关闭。";
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
/** Only settings commands, never paths, extension commands, or ordinary slash-prefixed text. */
|
|
163
|
+
export function isPiSettingsDraft(text) {
|
|
164
|
+
const value = text.trim();
|
|
165
|
+
return value === "/" || ["/settings", "/codemode", "/tools", "/goal-mode"].some((command) => value.length > 1 && command.startsWith(value));
|
|
166
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 平台动作注册表与严格输入校验(IM-07)。
|
|
3
|
+
* validateActionInput 是所有调用方(HTTP/CLI/原生工具/CodeMode)共用的唯一校验入口;
|
|
4
|
+
* 任何适配器都不得绕过 invokePlatformAction 另造执行通路。
|
|
5
|
+
*/
|
|
6
|
+
import { type PlatformActionDefinition, type PlatformActionError, type StrictObjectSchema } from "./platform-action-types.js";
|
|
7
|
+
export declare class PlatformActionRegistry {
|
|
8
|
+
private readonly definitions;
|
|
9
|
+
/** 定义与自身安全属性矛盾时抛错,防止元数据谎报“只读/免费”。 */
|
|
10
|
+
register(definition: PlatformActionDefinition): void;
|
|
11
|
+
get(name: string): PlatformActionDefinition | null;
|
|
12
|
+
has(name: string): boolean;
|
|
13
|
+
list(): PlatformActionDefinition[];
|
|
14
|
+
}
|
|
15
|
+
export type ActionInput = Record<string, unknown>;
|
|
16
|
+
export interface ValidatedActionInput {
|
|
17
|
+
ok: boolean;
|
|
18
|
+
value?: ActionInput;
|
|
19
|
+
error?: PlatformActionError;
|
|
20
|
+
}
|
|
21
|
+
/** 根输入必须是普通 JSON 对象;未知键、缺失必填、类型/取值错误逐层拒绝。 */
|
|
22
|
+
export declare function validateActionInput(definition: PlatformActionDefinition, input: unknown): ValidatedActionInput;
|
|
23
|
+
export declare const EMPLOYEE_SEARCH_INPUT_SCHEMA: StrictObjectSchema;
|
|
24
|
+
export declare const EMPLOYEE_SEARCH_DEFINITION: PlatformActionDefinition;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 平台动作注册表与严格输入校验(IM-07)。
|
|
3
|
+
* validateActionInput 是所有调用方(HTTP/CLI/原生工具/CodeMode)共用的唯一校验入口;
|
|
4
|
+
* 任何适配器都不得绕过 invokePlatformAction 另造执行通路。
|
|
5
|
+
*/
|
|
6
|
+
import { PLATFORM_ACTION_SCHEMA_VERSION, } from "./platform-action-types.js";
|
|
7
|
+
const ACTION_NAME_RE = /^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)*$/;
|
|
8
|
+
export class PlatformActionRegistry {
|
|
9
|
+
definitions = new Map();
|
|
10
|
+
/** 定义与自身安全属性矛盾时抛错,防止元数据谎报“只读/免费”。 */
|
|
11
|
+
register(definition) {
|
|
12
|
+
if (!ACTION_NAME_RE.test(definition.name))
|
|
13
|
+
throw new Error(`动作名不合法: ${definition.name}`);
|
|
14
|
+
if (definition.schemaVersion < 1)
|
|
15
|
+
throw new Error(`动作 ${definition.name} 缺少有效 schemaVersion`);
|
|
16
|
+
if (!definition.summary.trim())
|
|
17
|
+
throw new Error(`动作 ${definition.name} 缺少说明`);
|
|
18
|
+
if (definition.scopes.length === 0)
|
|
19
|
+
throw new Error(`动作 ${definition.name} 必须声明所需 scope`);
|
|
20
|
+
if (definition.inputSchema.type !== "object" || definition.inputSchema.additionalProperties !== false) {
|
|
21
|
+
throw new Error(`动作 ${definition.name} 的输入 schema 必须 additionalProperties:false`);
|
|
22
|
+
}
|
|
23
|
+
if (definition.kind === "read" && (definition.requiresConfirmation || definition.chargesModel)) {
|
|
24
|
+
throw new Error(`只读动作 ${definition.name} 不得声明确认或模型费用`);
|
|
25
|
+
}
|
|
26
|
+
if (definition.kind !== "read" && !definition.requiresConfirmation) {
|
|
27
|
+
throw new Error(`写/执行动作 ${definition.name} 必须 requiresConfirmation`);
|
|
28
|
+
}
|
|
29
|
+
if (this.definitions.has(definition.name))
|
|
30
|
+
throw new Error(`动作重复注册: ${definition.name}`);
|
|
31
|
+
this.definitions.set(definition.name, definition);
|
|
32
|
+
}
|
|
33
|
+
get(name) {
|
|
34
|
+
return this.definitions.get(name) ?? null;
|
|
35
|
+
}
|
|
36
|
+
has(name) { return this.definitions.has(name); }
|
|
37
|
+
list() { return [...this.definitions.values()]; }
|
|
38
|
+
}
|
|
39
|
+
function err(code, message, field) {
|
|
40
|
+
return field === undefined ? { code, message } : { code, message, field };
|
|
41
|
+
}
|
|
42
|
+
function validateNode(schema, value, field) {
|
|
43
|
+
switch (schema.type) {
|
|
44
|
+
case "object": {
|
|
45
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
46
|
+
return err("invalid_type", `字段 ${field || "input"} 必须是对象。`, field || undefined);
|
|
47
|
+
}
|
|
48
|
+
const record = value;
|
|
49
|
+
for (const required of schema.required ?? []) {
|
|
50
|
+
if (!(required in record))
|
|
51
|
+
return err("missing_required_field", `缺少必填字段 ${required}。`, required);
|
|
52
|
+
}
|
|
53
|
+
for (const key of Object.keys(record)) {
|
|
54
|
+
if (!(key in schema.properties))
|
|
55
|
+
return err("unknown_field", `参数字段 ${key} 未定义,已拒绝。`, key);
|
|
56
|
+
}
|
|
57
|
+
for (const [key, child] of Object.entries(schema.properties)) {
|
|
58
|
+
if (!(key in record))
|
|
59
|
+
continue;
|
|
60
|
+
const childField = field ? `${field}.${key}` : key;
|
|
61
|
+
const failure = validateNode(child, record[key], childField);
|
|
62
|
+
if (failure)
|
|
63
|
+
return failure;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
case "string": {
|
|
68
|
+
if (typeof value !== "string")
|
|
69
|
+
return err("invalid_type", `字段 ${field} 必须是文字。`, field);
|
|
70
|
+
if (schema.minLength !== undefined && value.length < schema.minLength) {
|
|
71
|
+
return err("invalid_value", `字段 ${field} 长度不足。`, field);
|
|
72
|
+
}
|
|
73
|
+
if (schema.maxLength !== undefined && value.length > schema.maxLength) {
|
|
74
|
+
return err("invalid_value", `字段 ${field} 超出长度上限 ${schema.maxLength}。`, field);
|
|
75
|
+
}
|
|
76
|
+
if (schema.enum && !schema.enum.includes(value)) {
|
|
77
|
+
return err("invalid_value", `字段 ${field} 不在允许的取值内。`, field);
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
case "integer": {
|
|
82
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
83
|
+
return err("invalid_type", `字段 ${field} 必须是整数。`, field);
|
|
84
|
+
}
|
|
85
|
+
if (schema.minimum !== undefined && value < schema.minimum) {
|
|
86
|
+
return err("invalid_value", `字段 ${field} 不能小于 ${schema.minimum}。`, field);
|
|
87
|
+
}
|
|
88
|
+
if (schema.maximum !== undefined && value > schema.maximum) {
|
|
89
|
+
return err("invalid_value", `字段 ${field} 不能大于 ${schema.maximum}。`, field);
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
case "boolean": {
|
|
94
|
+
return typeof value === "boolean" ? null : err("invalid_type", `字段 ${field} 必须是布尔值。`, field);
|
|
95
|
+
}
|
|
96
|
+
case "array": {
|
|
97
|
+
if (!Array.isArray(value))
|
|
98
|
+
return err("invalid_type", `字段 ${field} 必须是数组。`, field);
|
|
99
|
+
if (schema.maxItems !== undefined && value.length > schema.maxItems) {
|
|
100
|
+
return err("invalid_value", `字段 ${field} 条目数超过上限 ${schema.maxItems}。`, field);
|
|
101
|
+
}
|
|
102
|
+
for (let i = 0; i < value.length; i++) {
|
|
103
|
+
const failure = validateNode(schema.items, value[i], `${field}[${i}]`);
|
|
104
|
+
if (failure)
|
|
105
|
+
return failure;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** 根输入必须是普通 JSON 对象;未知键、缺失必填、类型/取值错误逐层拒绝。 */
|
|
112
|
+
export function validateActionInput(definition, input) {
|
|
113
|
+
if (input !== undefined && (typeof input !== "object" || input === null || Array.isArray(input))) {
|
|
114
|
+
return { ok: false, error: err("invalid_request", "动作输入必须是 JSON 对象。") };
|
|
115
|
+
}
|
|
116
|
+
const value = (input ?? {});
|
|
117
|
+
const failure = validateNode(definition.inputSchema, value, "");
|
|
118
|
+
if (failure)
|
|
119
|
+
return { ok: false, error: failure };
|
|
120
|
+
return { ok: true, value };
|
|
121
|
+
}
|
|
122
|
+
export const EMPLOYEE_SEARCH_INPUT_SCHEMA = {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
query: { type: "string", maxLength: 120 },
|
|
126
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
127
|
+
},
|
|
128
|
+
required: [],
|
|
129
|
+
additionalProperties: false,
|
|
130
|
+
};
|
|
131
|
+
export const EMPLOYEE_SEARCH_DEFINITION = {
|
|
132
|
+
name: "employee.search",
|
|
133
|
+
schemaVersion: PLATFORM_ACTION_SCHEMA_VERSION,
|
|
134
|
+
summary: "按名字、职责、标签只读检索公开员工目录;不读取私人知识与私聊。",
|
|
135
|
+
group: "people",
|
|
136
|
+
kind: "read",
|
|
137
|
+
requiresConfirmation: false,
|
|
138
|
+
chargesModel: false,
|
|
139
|
+
scopes: ["employee.search"],
|
|
140
|
+
inputSchema: EMPLOYEE_SEARCH_INPUT_SCHEMA,
|
|
141
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PlatformActionRegistry } from "./platform-action-catalog.js";
|
|
2
|
+
import { type PlatformActionCatalog, type PlatformActorContext, type PlatformActionRequest, type PlatformActionResult } from "./platform-action-types.js";
|
|
3
|
+
export declare const EMPLOYEE_SEARCH_SCOPE: string;
|
|
4
|
+
/** 员工目录的只读投影端口;结构上由 WandStorage.listSiliconEmployees 满足。 */
|
|
5
|
+
export interface EmployeeDirectoryRecord {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
duty: string;
|
|
9
|
+
tags?: string[];
|
|
10
|
+
systemKey?: string;
|
|
11
|
+
archivedAt?: string;
|
|
12
|
+
agents: readonly unknown[];
|
|
13
|
+
}
|
|
14
|
+
export interface EmployeeDirectoryPort {
|
|
15
|
+
listSiliconEmployees(options?: {
|
|
16
|
+
includeArchived?: boolean;
|
|
17
|
+
}): EmployeeDirectoryRecord[];
|
|
18
|
+
}
|
|
19
|
+
export interface PlatformActionExecContext {
|
|
20
|
+
actor: PlatformActorContext;
|
|
21
|
+
deps: PlatformActionDeps;
|
|
22
|
+
}
|
|
23
|
+
export interface PlatformActionDeps {
|
|
24
|
+
employees: EmployeeDirectoryPort;
|
|
25
|
+
registry: PlatformActionRegistry;
|
|
26
|
+
}
|
|
27
|
+
export declare function createReadOnlyPlatformActionDeps(employees: EmployeeDirectoryPort): PlatformActionDeps;
|
|
28
|
+
/**
|
|
29
|
+
* 两阶段发现的目录视图:只返回“本作用域真实已注册且已授予”的动作。
|
|
30
|
+
* 空权限得到空目录,而不是默认管理员视角;未注册写动作永不出现在这里。
|
|
31
|
+
*/
|
|
32
|
+
export declare function describePlatformCatalog(actor: PlatformActorContext, deps: PlatformActionDeps): PlatformActionCatalog;
|
|
33
|
+
/**
|
|
34
|
+
* 唯一校验+执行入口。CodeMode 脚本、原生工具、CLI、HTTP 适配器共用本函数,
|
|
35
|
+
* 不得另造绕过 validateActionInput/scope 检查的执行通路。
|
|
36
|
+
*/
|
|
37
|
+
export declare function invokePlatformAction(request: PlatformActionRequest, actor: PlatformActorContext, deps: PlatformActionDeps): Promise<PlatformActionResult>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 只读平台动作服务(IM-07):能力目录发现 + employee.search 唯一注册动作。
|
|
3
|
+
* 本模块不接 HTTP/CLI;09/10 的适配器必须调用这里的同一函数。
|
|
4
|
+
* 身份与授权只来自 PlatformActorContext(可信宿主传入),input 永不参与推导。
|
|
5
|
+
*/
|
|
6
|
+
import { isBuiltinSiliconEmployee, siliconEmployeeTags } from "./ai-team-types.js";
|
|
7
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
8
|
+
import { EMPLOYEE_SEARCH_DEFINITION, PlatformActionRegistry, validateActionInput, } from "./platform-action-catalog.js";
|
|
9
|
+
import { PLATFORM_ACTION_SCHEMA_VERSION, } from "./platform-action-types.js";
|
|
10
|
+
export const EMPLOYEE_SEARCH_SCOPE = EMPLOYEE_SEARCH_DEFINITION.scopes[0];
|
|
11
|
+
const EMPLOYEE_SEARCH_DEFAULT_LIMIT = 20;
|
|
12
|
+
const EMPLOYEE_NAME_MAX_CHARS = 80;
|
|
13
|
+
const EMPLOYEE_DUTY_MAX_CHARS = 200;
|
|
14
|
+
const REQUEST_ID_RE = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
15
|
+
function fail(code, message, field) {
|
|
16
|
+
return field === undefined ? { code, message } : { code, message, field };
|
|
17
|
+
}
|
|
18
|
+
function truncate(value, max) {
|
|
19
|
+
return value.length > max ? value.slice(0, max) : value;
|
|
20
|
+
}
|
|
21
|
+
function executeEmployeeSearch(input, ctx) {
|
|
22
|
+
const query = typeof input.query === "string" ? input.query.trim().toLowerCase() : "";
|
|
23
|
+
const limit = typeof input.limit === "number" ? input.limit : EMPLOYEE_SEARCH_DEFAULT_LIMIT;
|
|
24
|
+
// 默认口径即排除归档;不读取 prompt、知识与任何私人内容。
|
|
25
|
+
const records = ctx.deps.employees.listSiliconEmployees();
|
|
26
|
+
const matched = [];
|
|
27
|
+
for (const record of records) {
|
|
28
|
+
if (!query) {
|
|
29
|
+
matched.push(record);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const tags = siliconEmployeeTags(record).map((tag) => tag.toLowerCase());
|
|
33
|
+
const haystack = [record.name.toLowerCase(), record.duty.toLowerCase(), ...tags];
|
|
34
|
+
if (haystack.some((part) => part.includes(query)))
|
|
35
|
+
matched.push(record);
|
|
36
|
+
}
|
|
37
|
+
const limited = matched.slice(0, limit);
|
|
38
|
+
const matches = limited.map((record) => ({
|
|
39
|
+
employeeId: record.id,
|
|
40
|
+
name: truncate(record.name, EMPLOYEE_NAME_MAX_CHARS),
|
|
41
|
+
duty: truncate(record.duty, EMPLOYEE_DUTY_MAX_CHARS),
|
|
42
|
+
tags: [...siliconEmployeeTags(record)],
|
|
43
|
+
builtin: isBuiltinSiliconEmployee(record),
|
|
44
|
+
availability: record.agents.length > 0 ? "available" : "unconfigured",
|
|
45
|
+
}));
|
|
46
|
+
const nameCounts = new Map();
|
|
47
|
+
for (const match of matches)
|
|
48
|
+
nameCounts.set(match.name, (nameCounts.get(match.name) ?? 0) + 1);
|
|
49
|
+
return {
|
|
50
|
+
schemaVersion: PLATFORM_ACTION_SCHEMA_VERSION,
|
|
51
|
+
matches,
|
|
52
|
+
totalMatched: matched.length,
|
|
53
|
+
truncated: matched.length > limited.length,
|
|
54
|
+
duplicateNames: [...nameCounts.entries()].filter(([, count]) => count >= 2).map(([name]) => name),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** 已注册但此处未接线执行器的动作一律 action_unavailable,不冒充可调用。 */
|
|
58
|
+
const READ_ONLY_EXECUTORS = {
|
|
59
|
+
[EMPLOYEE_SEARCH_DEFINITION.name]: executeEmployeeSearch,
|
|
60
|
+
};
|
|
61
|
+
export function createReadOnlyPlatformActionDeps(employees) {
|
|
62
|
+
const registry = new PlatformActionRegistry();
|
|
63
|
+
registry.register(EMPLOYEE_SEARCH_DEFINITION);
|
|
64
|
+
return { employees, registry };
|
|
65
|
+
}
|
|
66
|
+
function hasAllScopes(definition, grantedScopes) {
|
|
67
|
+
const granted = new Set(grantedScopes);
|
|
68
|
+
return definition.scopes.every((scope) => granted.has(scope));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 两阶段发现的目录视图:只返回“本作用域真实已注册且已授予”的动作。
|
|
72
|
+
* 空权限得到空目录,而不是默认管理员视角;未注册写动作永不出现在这里。
|
|
73
|
+
*/
|
|
74
|
+
export function describePlatformCatalog(actor, deps) {
|
|
75
|
+
const entries = deps.registry
|
|
76
|
+
.list()
|
|
77
|
+
.filter((definition) => hasAllScopes(definition, actor.grantedScopes))
|
|
78
|
+
.map((definition) => ({
|
|
79
|
+
name: definition.name,
|
|
80
|
+
schemaVersion: definition.schemaVersion,
|
|
81
|
+
summary: definition.summary,
|
|
82
|
+
group: definition.group,
|
|
83
|
+
kind: definition.kind,
|
|
84
|
+
requiresConfirmation: definition.requiresConfirmation,
|
|
85
|
+
chargesModel: definition.chargesModel,
|
|
86
|
+
scopes: [...definition.scopes],
|
|
87
|
+
inputSchema: definition.inputSchema,
|
|
88
|
+
available: true,
|
|
89
|
+
}));
|
|
90
|
+
return {
|
|
91
|
+
schemaVersion: PLATFORM_ACTION_SCHEMA_VERSION,
|
|
92
|
+
entries,
|
|
93
|
+
note: "目录仅列出当前授权下真实可调用的已注册动作;未列出的动作(包括任何写操作)不可调用。",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 唯一校验+执行入口。CodeMode 脚本、原生工具、CLI、HTTP 适配器共用本函数,
|
|
98
|
+
* 不得另造绕过 validateActionInput/scope 检查的执行通路。
|
|
99
|
+
*/
|
|
100
|
+
export function invokePlatformAction(request, actor, deps) {
|
|
101
|
+
const envelope = (schemaVersion) => ({
|
|
102
|
+
requestId: typeof request?.requestId === "string" ? request.requestId : "",
|
|
103
|
+
action: typeof request?.action === "string" ? request.action : "",
|
|
104
|
+
schemaVersion,
|
|
105
|
+
});
|
|
106
|
+
const rejected = (error, definition) => Promise.resolve({ ...envelope(definition?.schemaVersion ?? PLATFORM_ACTION_SCHEMA_VERSION), state: "rejected", error });
|
|
107
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
108
|
+
return rejected(fail("invalid_request", "动作请求必须是 JSON 对象。"), null);
|
|
109
|
+
}
|
|
110
|
+
if (typeof request.requestId !== "string" || !REQUEST_ID_RE.test(request.requestId)) {
|
|
111
|
+
return rejected(fail("invalid_request", "requestId 必须是非空、不超过 128 字符的稳定标识。", "requestId"), null);
|
|
112
|
+
}
|
|
113
|
+
if (typeof request.action !== "string") {
|
|
114
|
+
return rejected(fail("invalid_request", "action 必须是字符串。", "action"), null);
|
|
115
|
+
}
|
|
116
|
+
const definition = deps.registry.get(request.action);
|
|
117
|
+
if (!definition) {
|
|
118
|
+
return rejected(fail("unknown_action", `动作 ${request.action} 未注册,当前不可调用。`, "action"), null);
|
|
119
|
+
}
|
|
120
|
+
if (!hasAllScopes(definition, actor.grantedScopes)) {
|
|
121
|
+
return rejected(fail("scope_denied", `当前授权缺少动作 ${definition.name} 所需 scope。`), definition);
|
|
122
|
+
}
|
|
123
|
+
const validated = validateActionInput(definition, request.input);
|
|
124
|
+
if (!validated.ok || !validated.value) {
|
|
125
|
+
return rejected(validated.error ?? fail("invalid_request", "动作输入校验失败。"), definition);
|
|
126
|
+
}
|
|
127
|
+
const executor = READ_ONLY_EXECUTORS[definition.name];
|
|
128
|
+
if (!executor) {
|
|
129
|
+
return rejected(fail("action_unavailable", `动作 ${definition.name} 已注册但当前作用域未启用。`), definition);
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const output = executor(validated.value, { actor, deps });
|
|
133
|
+
return Promise.resolve({ ...envelope(definition.schemaVersion), state: "completed", output });
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return rejected(fail("internal_error", `动作 ${definition.name} 执行失败:${getErrorMessage(error)}`), definition);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 平台动作类型合同(IM-07)。
|
|
3
|
+
* 合同全文见 output/im-huajie-implementation-20261006/results/07/CONTRACT.md;
|
|
4
|
+
* 08/09/10/12/13 复用这里的类型与错误码,不再另定义平行信封。
|
|
5
|
+
*/
|
|
6
|
+
/** 动作信封与定义共用的 schema 版本。 */
|
|
7
|
+
export declare const PLATFORM_ACTION_SCHEMA_VERSION = 1;
|
|
8
|
+
export type PlatformActionGroup = "people" | "chat" | "task";
|
|
9
|
+
export type PlatformActionKind = "read" | "write" | "execute";
|
|
10
|
+
export interface StrictObjectSchema {
|
|
11
|
+
type: "object";
|
|
12
|
+
properties: Record<string, StrictInputSchemaNode>;
|
|
13
|
+
required?: string[];
|
|
14
|
+
additionalProperties: false;
|
|
15
|
+
}
|
|
16
|
+
export type StrictInputSchemaNode = StrictObjectSchema | {
|
|
17
|
+
type: "string";
|
|
18
|
+
minLength?: number;
|
|
19
|
+
maxLength?: number;
|
|
20
|
+
enum?: readonly string[];
|
|
21
|
+
} | {
|
|
22
|
+
type: "integer";
|
|
23
|
+
minimum?: number;
|
|
24
|
+
maximum?: number;
|
|
25
|
+
} | {
|
|
26
|
+
type: "boolean";
|
|
27
|
+
} | {
|
|
28
|
+
type: "array";
|
|
29
|
+
items: StrictInputSchemaNode;
|
|
30
|
+
maxItems?: number;
|
|
31
|
+
};
|
|
32
|
+
export interface PlatformActionDefinition {
|
|
33
|
+
name: string;
|
|
34
|
+
schemaVersion: number;
|
|
35
|
+
summary: string;
|
|
36
|
+
group: PlatformActionGroup;
|
|
37
|
+
kind: PlatformActionKind;
|
|
38
|
+
/** read 必须 false;write/execute 必须 true。注册时强制。 */
|
|
39
|
+
requiresConfirmation: boolean;
|
|
40
|
+
/** 是否可能触发模型调用;read 必须 false。 */
|
|
41
|
+
chargesModel: boolean;
|
|
42
|
+
/** 全部授予才可调用;当前约定 scope 字符串与动作名一致。 */
|
|
43
|
+
scopes: readonly string[];
|
|
44
|
+
inputSchema: StrictObjectSchema;
|
|
45
|
+
}
|
|
46
|
+
export type PlatformActorKind = "employee_session" | "conversation_member" | "codemode" | "host";
|
|
47
|
+
/**
|
|
48
|
+
* 调用者身份与授权只由可信宿主构造;request.input 里的任何同名字段不参与推导,
|
|
49
|
+
* 且会被严格输入 schema 作为 unknown_field 拒绝。
|
|
50
|
+
*/
|
|
51
|
+
export interface PlatformActorContext {
|
|
52
|
+
actorKind: PlatformActorKind;
|
|
53
|
+
/** 可信绑定到的调用员工;null = 无员工身份,不因此获得或失去任何 scope。 */
|
|
54
|
+
employeeId: string | null;
|
|
55
|
+
sessionId: string | null;
|
|
56
|
+
conversationId: string | null;
|
|
57
|
+
/** 用户权限 ∩ 本次委托 ∩ 引擎/资源限制;空数组 = 无权限,不默认管理员。 */
|
|
58
|
+
grantedScopes: readonly string[];
|
|
59
|
+
}
|
|
60
|
+
export interface PlatformActionRequest {
|
|
61
|
+
/** ≤128 字符、[A-Za-z0-9._:-];write 动作按 requestId+fingerprint 幂等(08)。 */
|
|
62
|
+
requestId: string;
|
|
63
|
+
action: string;
|
|
64
|
+
input?: unknown;
|
|
65
|
+
}
|
|
66
|
+
export type PlatformActionErrorCode = "invalid_request" | "unknown_action" | "scope_denied" | "action_unavailable" | "unknown_field" | "missing_required_field" | "invalid_type" | "invalid_value" | "internal_error";
|
|
67
|
+
export interface PlatformActionError {
|
|
68
|
+
code: PlatformActionErrorCode;
|
|
69
|
+
message: string;
|
|
70
|
+
field?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface PlatformActionCompleted {
|
|
73
|
+
requestId: string;
|
|
74
|
+
action: string;
|
|
75
|
+
schemaVersion: number;
|
|
76
|
+
state: "completed";
|
|
77
|
+
output: unknown;
|
|
78
|
+
}
|
|
79
|
+
export interface PlatformActionRejected {
|
|
80
|
+
requestId: string;
|
|
81
|
+
action: string;
|
|
82
|
+
schemaVersion: number;
|
|
83
|
+
state: "rejected";
|
|
84
|
+
error: PlatformActionError;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 只读动作同步产出 completed;write 动作(08+)扩展 needs_confirmation/accepted/
|
|
88
|
+
* unconfirmed,与 ConversationReceipt 的映射见 CONTRACT.md §5。
|
|
89
|
+
*/
|
|
90
|
+
export type PlatformActionResult = PlatformActionCompleted | PlatformActionRejected;
|
|
91
|
+
export interface PlatformActionCatalogEntry {
|
|
92
|
+
name: string;
|
|
93
|
+
schemaVersion: number;
|
|
94
|
+
summary: string;
|
|
95
|
+
group: PlatformActionGroup;
|
|
96
|
+
kind: PlatformActionKind;
|
|
97
|
+
requiresConfirmation: boolean;
|
|
98
|
+
chargesModel: boolean;
|
|
99
|
+
scopes: readonly string[];
|
|
100
|
+
inputSchema: StrictObjectSchema;
|
|
101
|
+
/** 目录只列真实已注册且已授予的动作;未注册动作永远不会以可调用出现。 */
|
|
102
|
+
available: true;
|
|
103
|
+
}
|
|
104
|
+
export interface PlatformActionCatalog {
|
|
105
|
+
schemaVersion: number;
|
|
106
|
+
entries: PlatformActionCatalogEntry[];
|
|
107
|
+
note: string;
|
|
108
|
+
}
|
|
109
|
+
export interface EmployeeSearchMatch {
|
|
110
|
+
employeeId: string;
|
|
111
|
+
name: string;
|
|
112
|
+
duty: string;
|
|
113
|
+
tags: string[];
|
|
114
|
+
builtin: boolean;
|
|
115
|
+
availability: "available" | "unconfigured";
|
|
116
|
+
}
|
|
117
|
+
export interface EmployeeSearchOutput {
|
|
118
|
+
schemaVersion: number;
|
|
119
|
+
matches: EmployeeSearchMatch[];
|
|
120
|
+
/** 截断前命中数。 */
|
|
121
|
+
totalMatched: number;
|
|
122
|
+
truncated: boolean;
|
|
123
|
+
/** matches 内出现 ≥2 次的名字,供上层追问选择。 */
|
|
124
|
+
duplicateNames: string[];
|
|
125
|
+
}
|