@zhushanwen/pi-subagent-workflow 9.1.1 → 9.2.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/package.json +4 -4
- package/src/injectors/__tests__/workflow-list-injector.test.ts +1 -1
- package/src/injectors/subagent-list-injector.ts +1 -1
- package/src/interface/gui/subagent-actions.ts +5 -1
- package/src/interface/tool/__tests__/tool-render.test.ts +13 -5
- package/src/interface/tool/__tests__/tool-workflow.test.ts +63 -0
- package/src/interface/tool/subagent-tool-schema.ts +1 -1
- package/src/interface/tool/subagent-tool.ts +1 -1
- package/src/interface/tool/tool-render.ts +18 -6
- package/src/interface/tool/tool-subagents.ts +1 -1
- package/src/interface/tool/tool-workflow.ts +8 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/pi-subagent-workflow",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"description": "Unified subagent execution and multi-agent workflow orchestration for Pi — spawned-process agent runtime with sync/background modes, stateful workflow management with persistence, state machine, and execution tracing.",
|
|
@@ -58,12 +58,12 @@
|
|
|
58
58
|
]
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@zhushanwen/extension-protocol": "0.17.0",
|
|
62
61
|
"@zhushanwen/session-delivery": "1.0.0",
|
|
62
|
+
"@zhushanwen/extension-protocol": "0.17.0",
|
|
63
63
|
"@zhushanwen/pi-ext-guards": "0.4.2",
|
|
64
|
+
"@zhushanwen/pi-notify-ledger-host": "0.2.3",
|
|
64
65
|
"@zhushanwen/pi-extension-logger": "0.6.1",
|
|
65
|
-
"@zhushanwen/
|
|
66
|
-
"@zhushanwen/subagent-core": "1.1.0",
|
|
66
|
+
"@zhushanwen/subagent-core": "1.2.0",
|
|
67
67
|
"@zhushanwen/pi-subagent-cli": "0.5.0",
|
|
68
68
|
"@zhushanwen/zcode-subagent-cli": "0.4.4"
|
|
69
69
|
},
|
|
@@ -243,7 +243,7 @@ describe("formatAgentList CA2 快照(agent guide 壳自有文案唯一字节
|
|
|
243
243
|
// guide 段硬编码(锁文本):期望值不插值生产常量——改写 SUBAGENT_LIST_GUIDE
|
|
244
244
|
// 即红灯,避免「两侧同源插值」下文案漂移无守卫
|
|
245
245
|
expect(out).toBe(
|
|
246
|
-
`\n\n<available_subagents>\nThe following subagents are available. PRIORITY: when a task involves reading 3+ files, writing 100+ lines, parallel research, or specialized review, delegate to a matching subagent FIRST instead of doing it yourself — this keeps your context focused on orchestration. Do NOT call list to discover available subagents; use list only for running state. When using the subagent tool,
|
|
246
|
+
`\n\n<available_subagents>\nThe following subagents are available. PRIORITY: when a task involves reading 3+ files, writing 100+ lines, parallel research, or specialized review, delegate to a matching subagent FIRST instead of doing it yourself — this keeps your context focused on orchestration. Do NOT call list to discover available subagents; use list only for running state. When using the subagent tool, pass the <location> path from this list as the agent param (usually the right choice); an absolute .md path outside this list also works if the file is a readable agent definition. If no agent matches your task, omit agent (a general-purpose agent is used) and put all role-specific instructions in the task text.\n${expectedItems.join("\n")}\n</available_subagents>`,
|
|
247
247
|
);
|
|
248
248
|
|
|
249
249
|
// location 前缀替换(资产来源迁移:pi-sw 安装目录 → core 包)后其余字节零变化
|
|
@@ -43,7 +43,7 @@ import { createResourceListInjector } from "./resource-list-injector.ts";
|
|
|
43
43
|
* 动态指引经 task 文本 / appendSystemPrompt 参数承载。
|
|
44
44
|
*/
|
|
45
45
|
export const SUBAGENT_LIST_GUIDE =
|
|
46
|
-
"The following subagents are available. PRIORITY: when a task involves reading 3+ files, writing 100+ lines, parallel research, or specialized review, delegate to a matching subagent FIRST instead of doing it yourself — this keeps your context focused on orchestration. Do NOT call list to discover available subagents; use list only for running state. When using the subagent tool,
|
|
46
|
+
"The following subagents are available. PRIORITY: when a task involves reading 3+ files, writing 100+ lines, parallel research, or specialized review, delegate to a matching subagent FIRST instead of doing it yourself — this keeps your context focused on orchestration. Do NOT call list to discover available subagents; use list only for running state. When using the subagent tool, pass the <location> path from this list as the agent param (usually the right choice); an absolute .md path outside this list also works if the file is a readable agent definition. If no agent matches your task, omit agent (a general-purpose agent is used) and put all role-specific instructions in the task text.";
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* agent 清单实例:缓存生命周期 / 三 handler 经工厂骨架;装配循环经 assemble 覆写
|
|
@@ -139,7 +139,11 @@ export function buildGuiComponent(
|
|
|
139
139
|
const listResp = input.domain;
|
|
140
140
|
return guiComponent("list-tree", {
|
|
141
141
|
items: listResp.response.items.map((it) => ({
|
|
142
|
-
label
|
|
142
|
+
// GUI 详情层:label 保留完整 agent ref(摘要短名的取舍见 TUI compact/expanded
|
|
143
|
+
// 分层——GUI list-tree 是详情展示,全路径信息优先)
|
|
144
|
+
label: it.slug
|
|
145
|
+
? `${it.agent} · ${it.slug} · ${it.subagentId}`
|
|
146
|
+
: `${it.agent} · ${it.subagentId}`,
|
|
143
147
|
status: mapRunStatus(it.status),
|
|
144
148
|
icon: mapRunIcon(it.status),
|
|
145
149
|
})),
|
|
@@ -200,7 +200,7 @@ describe("renderSubagentResult — expanded 与 compact 的分支关系(字节
|
|
|
200
200
|
expect(expanded.join("\n")).toContain("/tmp/sessions/old-abc.jsonl");
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
-
it("list:expanded
|
|
203
|
+
it("list:expanded agent 用完整 ref、并追加 session 行;compact agent 用 basename 短名(分层锁定)", () => {
|
|
204
204
|
const details = {
|
|
205
205
|
action: "list",
|
|
206
206
|
subagentId: null,
|
|
@@ -209,7 +209,7 @@ describe("renderSubagentResult — expanded 与 compact 的分支关系(字节
|
|
|
209
209
|
running: 1,
|
|
210
210
|
items: [{
|
|
211
211
|
subagentId: "sa-1",
|
|
212
|
-
agent: "worker",
|
|
212
|
+
agent: "/Users/x/.agents/agents/worker.md",
|
|
213
213
|
slug: "scan",
|
|
214
214
|
status: "running",
|
|
215
215
|
duration: 12,
|
|
@@ -218,10 +218,18 @@ describe("renderSubagentResult — expanded 与 compact 的分支关系(字节
|
|
|
218
218
|
},
|
|
219
219
|
};
|
|
220
220
|
const { compact, expanded } = renderBoth(details);
|
|
221
|
+
// compact:basename 短名(无目录、无 .md),无 session 行。强断言带边界形态:
|
|
222
|
+
// agent 值以完整 accent 包裹出现(防 "worker" 作为其他字段子串命中弱化回归)
|
|
223
|
+
expect(compact.join("\n")).toMatch(/<accent>worker<\/accent>/);
|
|
224
|
+
expect(compact.join("\n")).not.toContain("/Users/x/.agents/");
|
|
225
|
+
expect(compact.join("\n")).not.toContain(".md");
|
|
221
226
|
expect(compact.join("\n")).not.toContain("session: ");
|
|
227
|
+
// expanded:agent 完整 ref(详情层)+ session 路径行
|
|
228
|
+
expect(expanded.join("\n")).toContain("/Users/x/.agents/agents/worker.md");
|
|
229
|
+
expect(expanded.join("\n")).toContain("session: ");
|
|
222
230
|
expect(expanded.join("\n")).toContain("/tmp/sessions/sa-1.jsonl");
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
expect(expanded.
|
|
231
|
+
// 标题行两态一致(首行),差异只在 item agent 行与追加 session 行
|
|
232
|
+
expect(expanded[0]).toBe(compact[0]);
|
|
233
|
+
expect(expanded.length).toBe(compact.length + 1);
|
|
226
234
|
});
|
|
227
235
|
});
|
|
@@ -906,3 +906,66 @@ describe("TC3i: 宿主 TOOL_TOP_LEVEL reservedKeys 接线锚(M-3 回归)", (
|
|
|
906
906
|
expect(argKeysFromMeta(rflParams(), OPT).exact.has("name")).toBe(false);
|
|
907
907
|
});
|
|
908
908
|
});
|
|
909
|
+
|
|
910
|
+
// ══════════════════════════════════════════════════════════════
|
|
911
|
+
// renderCall 标题行(displayWorkflowName 短名 + runId/slug 不受影响)
|
|
912
|
+
// ══════════════════════════════════════════════════════════════
|
|
913
|
+
//
|
|
914
|
+
// 锁 2026-09 display 分层:name 是绝对 .js 路径,标题行只出 basename 短名
|
|
915
|
+
// (displayWorkflowName);runId 截断预览与 slug 后缀形态不变。
|
|
916
|
+
|
|
917
|
+
/** 最小 theme stub(fg/bold 直包文本——断言不依赖真实着色映射)。 */
|
|
918
|
+
function makeRenderTheme(): Record<string, (color: string | ((t: string) => string), text?: string) => string> {
|
|
919
|
+
return {
|
|
920
|
+
fg: (_color, text) => (typeof text === "string" ? `<${_color}>${text}</${_color}>` : ""),
|
|
921
|
+
bold: (text) => (typeof text === "string" ? `<b>${text}</b>` : ""),
|
|
922
|
+
} as never;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/** pi-tui Text 反射取构造串(tool-render.test.ts 同款 helper——本文件独立内联)。 */
|
|
926
|
+
function textOf(component: unknown): string {
|
|
927
|
+
const obj = component as Record<string, unknown>;
|
|
928
|
+
for (const key of Object.keys(obj)) {
|
|
929
|
+
const v = obj[key];
|
|
930
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
931
|
+
}
|
|
932
|
+
return "";
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
describe("renderCall 标题行:displayWorkflowName 短名 + runId/slug 不受影响", () => {
|
|
936
|
+
const tool = captureWorkflowTool(makeDeps(stubRegistry()), { isProcessing: false });
|
|
937
|
+
const theme = makeRenderTheme();
|
|
938
|
+
|
|
939
|
+
it("绝对路径 name → basename 短名(去 .js、去目录)", () => {
|
|
940
|
+
const out = textOf(
|
|
941
|
+
(tool as unknown as { renderCall: (args: Record<string, unknown>, theme: unknown) => unknown })
|
|
942
|
+
.renderCall({ action: "run", name: "/home/u/wf/batch-report.js" }, theme),
|
|
943
|
+
);
|
|
944
|
+
expect(out).toMatch(/batch-report<\/accent>/); // name 落在 accent 段收口前(前缀空格也在段内)
|
|
945
|
+
expect(out).not.toContain("/home/u/wf/");
|
|
946
|
+
expect(out).not.toContain(".js");
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
it("runId / slug 后缀不受短名影响(runId 截断预览 + slug 全文)", () => {
|
|
950
|
+
const out = textOf(
|
|
951
|
+
(tool as unknown as { renderCall: (args: Record<string, unknown>, theme: unknown) => unknown })
|
|
952
|
+
.renderCall(
|
|
953
|
+
{ action: "run", name: "/home/u/wf/batch-report.js", slug: "nightly", runId: "wf-run-0123456789" },
|
|
954
|
+
theme,
|
|
955
|
+
),
|
|
956
|
+
);
|
|
957
|
+
// 短名 + slug 全文在位
|
|
958
|
+
expect(out).toContain("batch-report");
|
|
959
|
+
expect(out).toContain("nightly");
|
|
960
|
+
// runId 只出截断预览(ID_PREVIEW_LENGTH = 8 → "wf-run-0"),不泄漏全文
|
|
961
|
+
expect(out).toContain("wf-run-0");
|
|
962
|
+
expect(out).not.toContain("wf-run-0123456789");
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
it("缺 name(status 等无参 action)不崩、不出短名", () => {
|
|
966
|
+
expect(() =>
|
|
967
|
+
(tool as unknown as { renderCall: (args: Record<string, unknown>, theme: unknown) => unknown })
|
|
968
|
+
.renderCall({ action: "status" }, theme),
|
|
969
|
+
).not.toThrow();
|
|
970
|
+
});
|
|
971
|
+
});
|
|
@@ -55,7 +55,7 @@ export const SubagentParams = Type.Object({
|
|
|
55
55
|
maxLength: SLUG_MAX_LENGTH,
|
|
56
56
|
})),
|
|
57
57
|
agent: Type.Optional(Type.String({
|
|
58
|
-
description: 'Agent ref: absolute path to
|
|
58
|
+
description: 'Agent ref: absolute path to an agent .md file — usually a <location> from <available_subagents>, but ANY absolute .md path works if the file is a readable agent definition (the list is not required). Relative paths and bare names are rejected. If omitted, defaults to "general-purpose" — a generic agent that inherits the main agent\'s model and project context.',
|
|
59
59
|
})),
|
|
60
60
|
model: Type.Optional(Type.String({
|
|
61
61
|
description: 'Model override in "provider/modelId" format. CASE-SENSITIVE: the string must equal a registry entry exactly, including letter case (e.g. "zai-coding-cn/GLM-5.3-Flash", NOT "zai-coding-cn/glm-5.3-flash"). A non-exact match is rejected immediately with "Did you mean" suggestions — retry with the exact suggested string; the system never auto-corrects your input. Resolution order (top wins): (1) this param, (2) agent .md frontmatter model, (3) the main agent\'s current model (zero-config default). An explicit model (param or frontmatter) that is missing or unauthorized THROWS — there is no silent fallback to the main model. Omit this param to inherit the main model.',
|
|
@@ -149,7 +149,7 @@ export function registerSubagentTool(pi: ExtensionAPI): void {
|
|
|
149
149
|
pi.registerTool({
|
|
150
150
|
name: "subagent",
|
|
151
151
|
label: "Subagent",
|
|
152
|
-
promptSnippet: "Delegate to specialized subagents (agentRef = absolute .md path from <available_subagents>)",
|
|
152
|
+
promptSnippet: "Delegate to specialized subagents (agentRef = absolute .md path, e.g. a <location> from <available_subagents>)",
|
|
153
153
|
description: `Delegate a task to a specialized subagent — when to delegate rather than do it yourself.
|
|
154
154
|
|
|
155
155
|
CRITICAL — executionMode "sequential": multiple \`subagent\` calls in the SAME message run one-after-another. For concurrency, start runs in background and tasks run concurrently in the pool (default maxConcurrent=6).
|
|
@@ -230,7 +230,8 @@ function buildCompactLines(d: SubagentToolResult, theme: ThemeLike): string[] {
|
|
|
230
230
|
/**
|
|
231
231
|
* 展开视图行内容生成。除下列两个特例外与 compact 逐字节相同(一次性 block 无
|
|
232
232
|
* 细节可展开),测试锁字节等价:
|
|
233
|
-
* - list
|
|
233
|
+
* - list:每 item agent 用完整 ref(compact 用 basename 短名),并追加 sessionFile 路径行
|
|
234
|
+
* (renderListExpanded)
|
|
234
235
|
* - fork-from:源文件用完整路径(compact 用 basename 短标签)
|
|
235
236
|
*/
|
|
236
237
|
function buildExpandedLines(d: SubagentToolResult, theme: ThemeLike): string[] {
|
|
@@ -309,8 +310,14 @@ function getStringText(item: unknown): string | undefined {
|
|
|
309
310
|
// list 渲染 helper(action:"list" 分支)
|
|
310
311
|
// ============================================================
|
|
311
312
|
|
|
312
|
-
/**
|
|
313
|
-
|
|
313
|
+
/**
|
|
314
|
+
* list compact:标题行 + 每行一个 item 摘要(glyph + agent + slug + mode + status + duration)。
|
|
315
|
+
*
|
|
316
|
+
* agent 展示分层(同 fork-from 先例:compact 短标签 / expanded 完整路径——摘要行
|
|
317
|
+
* 单行宽度优先,详情行信息完整优先):`shortAgent: false`(expanded)输出完整 ref。
|
|
318
|
+
*/
|
|
319
|
+
function renderListCompact(resp: ListResponse, theme: ThemeLike, width: number, opts?: { shortAgent?: boolean }): string[] {
|
|
320
|
+
const shortAgent = opts?.shortAgent ?? true;
|
|
314
321
|
if (resp.items.length === 0) {
|
|
315
322
|
return [truncLine(theme.fg("dim", `No subagents (running: ${resp.running})`), width)];
|
|
316
323
|
}
|
|
@@ -323,16 +330,21 @@ function renderListCompact(resp: ListResponse, theme: ThemeLike, width: number):
|
|
|
323
330
|
const mode = "bg";
|
|
324
331
|
// slug 非空时在 agent 后展示(· 分隔),空串时省略。
|
|
325
332
|
const slugPart = it.slug ? `${theme.fg("dim", " · ")}${theme.fg("accent", it.slug)}` : "";
|
|
326
|
-
|
|
333
|
+
// compact 摘要:basename 短名(同 renderSubagentCall 标题行);expanded 详情:完整 ref
|
|
334
|
+
const agent = shortAgent ? displayAgentName(it.agent) : it.agent;
|
|
335
|
+
const line = `${theme.fg(glyph.color, icon)} ${theme.fg("accent", agent)}${slugPart}`
|
|
327
336
|
+ ` ${theme.fg("dim", `· ${mode} · ${it.status} · ${formatElapsedSeconds(it.duration)}`)}`;
|
|
328
337
|
lines.push(truncLine(`${STREAM_PREFIX}${line}`, width));
|
|
329
338
|
}
|
|
330
339
|
return lines;
|
|
331
340
|
}
|
|
332
341
|
|
|
333
|
-
/**
|
|
342
|
+
/**
|
|
343
|
+
* list expanded:compact 基础之上每 item agent 用完整 ref(详情层信息优先),并追加
|
|
344
|
+
* sessionFile 路径行。
|
|
345
|
+
*/
|
|
334
346
|
function renderListExpanded(resp: ListResponse, theme: ThemeLike, width: number): string[] {
|
|
335
|
-
const lines = renderListCompact(resp, theme, width);
|
|
347
|
+
const lines = renderListCompact(resp, theme, width, { shortAgent: false });
|
|
336
348
|
for (const it of resp.items) {
|
|
337
349
|
if (it.sessionFile) {
|
|
338
350
|
lines.push(truncLine(`${theme.fg("dim", `${FOOTER_PREFIX}session: `)}${it.sessionFile}`, width));
|
|
@@ -82,7 +82,7 @@ const SubagentsParams = Type.Object({
|
|
|
82
82
|
}),
|
|
83
83
|
agents: Type.Optional(Type.String({
|
|
84
84
|
description:
|
|
85
|
-
"Comma-separated absolute paths to agent .md files (
|
|
85
|
+
"Comma-separated absolute paths to agent .md files (usually <location> values from <available_subagents>; arbitrary absolute .md paths also work). " +
|
|
86
86
|
"One path applies to every member; N paths map one-to-one onto tasks in the same order. " +
|
|
87
87
|
"When more than one path is given it MUST equal the number of tasks — a mismatch fails the run fast (never a silent persona swap). " +
|
|
88
88
|
"Omit for the default (general-purpose) executor.",
|
|
@@ -30,6 +30,7 @@ import { type Static, Type } from "typebox";
|
|
|
30
30
|
|
|
31
31
|
import { SLUG_MAX_LENGTH } from "@zhushanwen/subagent-core";
|
|
32
32
|
import { THINKING_ORDER } from "@zhushanwen/subagent-core";
|
|
33
|
+
import { displayWorkflowName } from "@zhushanwen/subagent-core";
|
|
33
34
|
import type { LauncherDeps } from "@zhushanwen/subagent-core";
|
|
34
35
|
import { abortRun, resumeRun, runWorkflow } from "@zhushanwen/subagent-core";
|
|
35
36
|
import type { ResumeRunOptions } from "@zhushanwen/subagent-core";
|
|
@@ -86,7 +87,7 @@ const WORKFLOW_ACTIONS: readonly WorkflowAction[] = [
|
|
|
86
87
|
const WorkflowParams = Type.Object({
|
|
87
88
|
action: StringEnum(WORKFLOW_ACTIONS, { description: "Workflow action to execute" }),
|
|
88
89
|
name: Type.Optional(
|
|
89
|
-
Type.String({ description: "Workflow ref: absolute path to
|
|
90
|
+
Type.String({ description: "Workflow ref: absolute path to a .js script — normally the <location> of a workflow from <available_workflows>, but ANY absolute .js path works if the file declares `@pi-meta kind: \"workflow\"` (the list is not required). Relative paths and bare names are rejected (run action)" }),
|
|
90
91
|
),
|
|
91
92
|
slug: Type.Optional(
|
|
92
93
|
Type.String({
|
|
@@ -224,7 +225,7 @@ export function registerWorkflowTool(
|
|
|
224
225
|
"<available_workflows> (injected each turn). For parameter details, read the <location> " +
|
|
225
226
|
"script file (script header has @pi-meta parameters + usage + phases). Do NOT use " +
|
|
226
227
|
"workflow-script generate for patterns already covered by available workflows.",
|
|
227
|
-
"run: pass the workflow ref as name —
|
|
228
|
+
"run: pass the workflow ref as name — normally the <location> absolute .js path from <available_workflows>, but any absolute .js path with a valid @pi-meta also works (the list is not required). Bare names and relative paths are rejected with a not-found error listing locations.",
|
|
228
229
|
"DO NOT bash sleep or poll status after starting — results appear automatically via notifyDone.",
|
|
229
230
|
"Runs are one-shot: there is no pause — to stop a run early use abort; for a fresh result start a new run. " +
|
|
230
231
|
"resume is ONLY for runs whose status is interrupted (crash or session-switch during execution): it replays " +
|
|
@@ -241,7 +242,7 @@ export function registerWorkflowTool(
|
|
|
241
242
|
"Model/thinkingLevel: omit by default (inherit main agent's model). Only set model/thinkingLevel when the user explicitly requests a specific model or thinking depth for this run.",
|
|
242
243
|
"Anti-patterns: Flattening args sub-fields (task/items/...) to the top level — they belong inside args. Calling {\"action\":\"run\"} without name.",
|
|
243
244
|
"CRITICAL: For orchestration patterns, ALWAYS use action:run with the <location> absolute " +
|
|
244
|
-
"path of
|
|
245
|
+
"path of the matching listed workflow — NEVER use workflow-script action:generate to recreate patterns " +
|
|
245
246
|
"already covered by available workflows. workflow-script generate is ONLY for novel patterns.",
|
|
246
247
|
],
|
|
247
248
|
parameters: WorkflowParams,
|
|
@@ -296,7 +297,9 @@ export function registerWorkflowTool(
|
|
|
296
297
|
|
|
297
298
|
renderCall(args: Record<string, unknown>, theme: Theme, _context?: unknown) {
|
|
298
299
|
const action = String(args.action ?? "");
|
|
299
|
-
|
|
300
|
+
// workflow ref 是绝对路径,标题行显示取 basename 短名(displayWorkflowName,
|
|
301
|
+
// 与 subagent 标题行的 displayAgentName 对称);runId/slug 不受影响。
|
|
302
|
+
const name = args.name ? ` ${displayWorkflowName(String(args.name))}` : "";
|
|
300
303
|
// run action 可选 slug:在 name 后追加 · slug(accent 色)
|
|
301
304
|
const slug = optionSlugSuffix(args.slug, theme);
|
|
302
305
|
const runId = args.runId ? ` ${String(args.runId).slice(0, ID_PREVIEW_LENGTH)}` : "";
|
|
@@ -325,7 +328,7 @@ export async function actionRun(
|
|
|
325
328
|
const name = params.name;
|
|
326
329
|
if (!name) {
|
|
327
330
|
throw new Error(
|
|
328
|
-
"run requires 'name' parameter (absolute .js path
|
|
331
|
+
"run requires 'name' parameter (an absolute .js path, e.g. a <location> from <available_workflows>). Correct: {\"action\":\"run\",\"name\":\"<ref>\"}",
|
|
329
332
|
);
|
|
330
333
|
}
|
|
331
334
|
// 弱模型常见误用(P0 静默失败):把 task/items 等 args 子字段平铺到 workflow params
|