Compare commits

...

1 Commits

Author SHA1 Message Date
d911bd28cd Release v1.0.127: document executionProfile stream for HTML token streaming
All checks were successful
技能自动化发布 / release (push) Successful in 6s
2026-10-25 18:22:25 +08:00
14 changed files with 141 additions and 63 deletions

View File

@@ -9,6 +9,13 @@
- 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节
- 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段
## 1.0.127
- 沉淀第三种执行画像 `executionProfile: stream`(与 sync/async 并列):页内 LLM/长生成流式,不进任务中心主路径
- Schema / POLICY-SKILL-ACTION-002·004 / 正交测试改为 `sync|async|stream`
- 文档与示例:`ACTIONS.md` / `SKILL_ACTION_RUNTIME.md` / `SKILL.md` / `LOGGING.md`;新增 `assets/examples/stream-ask.action.example.json`
- 使用 stream 的业务技能须自提 `platform_kit_min_version` ≥ 1.2.14;模板默认 kit 下限仍为 1.2.11
## 1.0.126
- 沉淀 `replaces: import` 宿主契约:表头必填红星在前(`*显示名`),技能须同时兼容前/后缀 `*`;lookup 单元格可为 `{value} · {label}`

View File

@@ -1,7 +1,7 @@
---
name: 技能开发模板(通用业务版)
description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。"
version: 1.0.126
version: 1.0.127
author: 深圳匠厂科技有限公司
metadata:
openclaw:
@@ -41,11 +41,11 @@ allowed-tools:
- **`bind.selectionMode`(可选)**:表勾选如何驱动 Action(`required` / `none` / `optional`);省略由宿主推断。同一能力勿拆两个同义顶栏按钮;定时任务配置页按无勾选分支隐藏 `$selection.ids`,见 `references/ACTIONS.md`。
- **`bind.visibleWhen`(可选)**:`row` 菜单/按钮按当前行列值显隐(如仅已采用可入队);省略则始终显示。≠ 表单字段 `visibleWhen`,≠ Policy L3。见 `references/ACTIONS.md`「行菜单显隐」。
- **用户可见文案**:数据管理弹窗 / 任务中心文案须全中文产品用语;勿把 `pickCount`、`async`、`pending`、CLI 子命令写进弹窗(见 `references/ACTIONS.md`「用户可见文案」)。
- **`executionProfile`**:只决定执行方式——`sync` 当场返回结果,`async` 进**任务中心**。与 placements / 入口**无关**。心智上多数为 sync,仅长任务 / RPA 标 async;宿主省略该字段时按 sync。每个 Action **仍必须**显式写 `sync` 或 `async`。
- **`executionProfile`**:只决定执行方式——`sync` 当场返回;`async` 进**任务中心**;`stream` 建 Job 供**页内流式**(`jiangchang.stream`),**不**进任务中心主路径。与 placements / 入口**无关**。心智上多数为 sync;长任务 / RPA 标 async;HTML 内 LLM/长生成要 token 流式时标 stream。宿主省略该字段时按 sync。每个 Action **仍必须**显式写 `sync` / `async` / `stream`。
- **`policy`(可选)**:需要按用户/角色控制数据管理按钮或行列时,在 `actions.json` 顶层启用 Data Policy,由技能 `policy` CLI 输出 Snapshot;**未启用 = 与今日一致(L0)**。详见 `references/POLICY.md`。用户/角色数据源(飞书/库/API)属技能内部,宿主不托管。
- **`cellPresentation`(可选)**:需要数据管理「某格 / 整行 / 整列 / 区域」特殊标色时启用;技能按当前数据写入呈现(语义 tone),宿主映射主题样式;**未启用 = P0 无标色**。用途不限(超时、预警、驳回等)。详见 `references/CELL_PRESENTATION.md`。
- **业务行生命周期(有处理队列时)**:对某一行做生成/触达/私信等处理时,默认只操作「可操作」角色状态;开始抢占「进行中」;结束回写成功或失败(失败带原因);重做靠人工改回可操作。状态名由本技能自定。详见 `references/WORK_ITEM_STATUS.md`。
- **同步数据**写技能本地库、不生成导出文件;**导出当前表**由宿主数据管理负责;文件型特殊报告才另做 `export-*`(落 `output/`);**HTML**:壳子页放 `assets/html/pages/`,快照写 `html/` 并用固定 Action id `render-html`;页面用 `jiangchang.call` 调已声明命令(见 `references/ACTIONS.md`)。
- **同步数据**写技能本地库、不生成导出文件;**导出当前表**由宿主数据管理负责;文件型特殊报告才另做 `export-*`(落 `output/`);**HTML**:壳子页放 `assets/html/pages/`,快照写 `html/` 并用固定 Action id `render-html`;页面用 `jiangchang.call`(同步结果)或 `jiangchang.stream`(`executionProfile: stream`)调已声明命令(见 `references/ACTIONS.md`)。
- **准备 vs 执行(适用则拆)**:若技能存在「先写入/准备本地数据」与「再对外产生副作用」两段,Agent 必须能分开触发;禁止默认绑成不可拆的一步。一键无准备态的技能可跳过本条。
### 如何触发(能力类型)
@@ -55,6 +55,7 @@ allowed-tools:
| 短查询 / 只读 / 秒级(`health` / `version` / `stats` / `list` / `get` 等) | OpenClaw bash/exec + **共享 Python** 跑 CLI(见下方「共享 Python」) | 否(CLI 当场结束) |
| 已在 `actions.json` 声明且 `executionProfile: "sync"` 的能力 | 可用 `run_skill_action`,或同等效果的短 CLI(同一内核) | 否 |
| 长耗时 / RPA / 浏览器 / 需要进度或取消;或 `executionProfile: "async"` | **必须** `run_skill_action`(先 `list_skill_actions` / `get_skill_action`) | **是**(仅因为该 Action 是 async) |
| 页内 LLM / 长生成要 token 流式;或 `executionProfile: "stream"` | HTML 用 `jiangchang.stream`;Agent 仍可 `run_skill_action` | **否**(stream 静默 Job,不进任务中心主路径) |
| 用户只问用法 / 能做什么 | 读根目录 `README.md`;逐步操作再读 `TUTORIAL.md`;**不要**为答用法去列目录或默认打开 `references/` | — |
### 用户意图路由表(业务技能复制后必改)
@@ -76,7 +77,7 @@ allowed-tools:
3. **禁止** Agent 自行 `pip install`。
4. **入口按定位表挂载**:复制业务技能后须在 `development/REQUIREMENTS.md` 填写入口定位表(见 `development/PRODUCT_SURFACES.md`)。定位需要数据管理 / 定时 / Agent 结构化调用时,必须提供 `actions.json` 并声明对应 `placements`;定位确认无需任何宿主结构化入口时可不提供。有长任务 / RPA 时**必须**提供至少一条 `executionProfile: "async"` 且 `placements` 含 `agent` 的 Action。
5. **一个业务能力一个 Action**,用多个 `placements` 挂入口;底层仍是同一 `scripts/main.py` CLI。
6. 是否进任务中心**只**看该 Action 的 `executionProfile`,与数据管理 / 定时 / Agent 入口无关。
6. 是否进任务中心**只**看该 Action 的 `executionProfile`(仅 `async` 进;`sync` / `stream` 不进),与数据管理 / 定时 / Agent 入口无关。
7. **处理业务行**时遵守状态角色契约(可操作 / 进行中 / 终态);禁止对已成功/失败/跳过默认重复处理。详见 `references/WORK_ITEM_STATUS.md`。
Agent 执行契约细节:`references/CLI.md`、`references/ACTIONS.md`、`references/SCHEMA.md`、按需 `references/POLICY.md` / `references/WORK_ITEM_STATUS.md`(不要把 `development/` 当 Agent 主读路径)。

View File

@@ -1,14 +1,14 @@
# assets
- `actions.json`:Skill Action manifest 最小示例(按入口定位表决定是否保留/扩展;见 `development/PRODUCT_SURFACES.md`)。每个 action **必须**显式声明 `executionProfile`(`sync` \| `async`)。默认仅 `health` / `version` / `config-path`,不放 toolbar,故无 `bind` 示例。
- `actions.json`:Skill Action manifest 最小示例(按入口定位表决定是否保留/扩展;见 `development/PRODUCT_SURFACES.md`)。每个 action **必须**显式声明 `executionProfile`(`sync` \| `async` \| `stream`)。默认仅 `health` / `version` / `config-path`(均为 `sync`),不放 toolbar,故无 `bind` 示例。
- **业务技能注意:** 若主路径在数据管理,准备/载入/导入类 Action 应挂 `toolbar` + `bind.tables`(用户看得见),**不要**只挂 `skill-detail` 却在教程里写成「技能详情点按钮」。见 `development/TUTORIAL_SURFACES.md` §5、`references/ACTIONS.md`「用户可见性」。
- `html/example-report.html`:HTML 快照页最小壳(`{{TITLE}}` / `{{GENERATED_AT}}` / `{{BODY}}`);含数据管理 iframe 预览布局基线(流体铺满、禁横向滚动);渲染后须写入数据目录 `html/`。不要放进 `pages/`。
- `html/pages/*.html`:装完就出现在侧栏的壳子页(宿主直接读安装包,不经生成)。页面用 `jiangchang.call` 调本技能已声明的命令。见 `references/ACTIONS.md`「HTML 页面报告」。模板仓库不要在这里放示例页。
- `html/pages/*.html`:装完就出现在侧栏的壳子页(宿主直接读安装包,不经生成)。页面用 `jiangchang.call`(同步)或 `jiangchang.stream`(`executionProfile: stream`)调本技能已声明的命令。见 `references/ACTIONS.md`「HTML 页面报告」。模板仓库不要在这里放示例页。
- `data-sources.example.json`:声明式数据源默认示例(SQLite `{skillSlug}.db`);宿主安装时可复制到用户目录。MySQL 参考见 `examples/data-sources.mysql.example.json`。
- `examples/`:CLI 成功输出形状示例、Action 形状参考(含 `data-sources.mysql.example.json`、`path-root-file-action.example.json`、`selection-label-column.action.example.json`、`selection-mode-optional.action.example.json`、`render-html.action.example.json` 等;虚构路径与数据)。
- `examples/`:CLI 成功输出形状示例、Action 形状参考(含 `stream-ask.action.example.json`、`data-sources.mysql.example.json`、`path-root-file-action.example.json`、`selection-label-column.action.example.json`、`selection-mode-optional.action.example.json`、`render-html.action.example.json` 等;虚构路径与数据)。
- `schemas/`:轻量 JSON Schema(`skill-actions.schema.json` 为**新技能严格规范**)。
`skill-actions.schema.json` 使用 `additionalProperties: false`。正式支持 `bind.tables`(toolbar Action 必填)与可选 `bind.inputMapping` / `columnMapping` / **`labelColumn`**(选择摘要标签列)/ **`selectionMode`**(`required` \| `none` \| `optional`:表勾选如何驱动 Action;省略则宿主兼容推断)/ **`visibleWhen`**(行菜单/行按钮按当前行列值显隐)。Action Lookup 可选 **`presentation: "paged_table"`** + **`page_size`**(分页勾选小表,仍是 `display_type: lookup`)。placements 含 `toolbar` / `row` / `batch` / `cron` / `agent` / `skill-detail`(对齐匠厂宿主)。数据管理 confirm/form 弹层在会应用勾选时展示「已选择」摘要;**form + 有摘要时映射 `readOnly` 默认不占参数区(仍提交)**,可选 **`revealInForm`** 强制显示。弹窗 `description` / 确认黄条 / 参数说明须**用户可见中文**(勿写 `pickCount`/`async`/`pending`/CLI 子命令;见 ACTIONS.md「用户可见文案」)。可选 `hoverHint` 为数据管理工具栏悬停(省略不显示,不回退)。`concurrency`/`locks` 仍为预留。`placements` 与 `executionProfile` 正交——进任务中心只看 `async`,与入口无关。表级 Action 与 Agent 契约见 [`references/ACTIONS.md`](../references/ACTIONS.md);开发深度规范见 [`development/SKILL_ACTION_RUNTIME.md`](../development/SKILL_ACTION_RUNTIME.md)。
`skill-actions.schema.json` 使用 `additionalProperties: false`。正式支持 `bind.tables`(toolbar Action 必填)与可选 `bind.inputMapping` / `columnMapping` / **`labelColumn`**(选择摘要标签列)/ **`selectionMode`**(`required` \| `none` \| `optional`:表勾选如何驱动 Action;省略则宿主兼容推断)/ **`visibleWhen`**(行菜单/行按钮按当前行列值显隐)。Action Lookup 可选 **`presentation: "paged_table"`** + **`page_size`**(分页勾选小表,仍是 `display_type: lookup`)。placements 含 `toolbar` / `row` / `batch` / `cron` / `agent` / `skill-detail`(对齐匠厂宿主)。数据管理 confirm/form 弹层在会应用勾选时展示「已选择」摘要;**form + 有摘要时映射 `readOnly` 默认不占参数区(仍提交)**,可选 **`revealInForm`** 强制显示。弹窗 `description` / 确认黄条 / 参数说明须**用户可见中文**(勿写 `pickCount`/`async`/`pending`/CLI 子命令;见 ACTIONS.md「用户可见文案」)。可选 `hoverHint` 为数据管理工具栏悬停(省略不显示,不回退)。`concurrency`/`locks` 仍为预留。`placements` 与 `executionProfile` 正交——进任务中心只看 `async`;`stream` 为页内流式静默 Job;与入口无关。表级 Action 与 Agent 契约见 [`references/ACTIONS.md`](../references/ACTIONS.md);开发深度规范见 [`development/SKILL_ACTION_RUNTIME.md`](../development/SKILL_ACTION_RUNTIME.md)。
- 用户市场四 Tab 见根目录 [`README.md`](../README.md) / [`TUTORIAL.md`](../TUTORIAL.md) / [`DEMO.md`](../DEMO.md) / [`CHANGELOG.md`](../CHANGELOG.md);写法契约见 [`development/TUTORIAL_SURFACES.md`](../development/TUTORIAL_SURFACES.md)
- Agent 调用/编排参考见 [`references/`](../references/)(含 [`ACTIONS.md`](../references/ACTIONS.md))

View File

@@ -0,0 +1,30 @@
{
"_comment": "形状参考:页内 LLM/长生成流式。executionProfile=stream → 宿主建 Job 但不进任务中心主路径;HTML 用 jiangchang.stream;技能侧 platform-kit>=1.2.14 的 activity.stream_channel → Journal stream_delta;终态仍 finish/finish_success 打出完整 JSON。契约见 references/ACTIONS.md「executionProfile」与「页面调用」、development/SKILL_ACTION_RUNTIME.md §2。模板自带 health/version 仍用 sync,勿默认改成 stream。",
"schemaVersion": 1,
"skill": "your-skill-slug",
"actions": [
{
"id": "ask",
"label": "智能问答",
"description": "根据本地知识检索后回答问题;在数据管理 HTML 页内流式输出。",
"placements": ["skill-detail", "agent"],
"executionProfile": "stream",
"inputSchema": {
"type": "object",
"required": ["question"],
"properties": {
"question": {
"type": "string",
"title": "问题",
"description": "用户自然语言问题"
}
}
},
"entrypoint": {
"type": "cli",
"command": "ask",
"args": ["--question", "{{question}}"]
}
}
]
}

View File

@@ -530,8 +530,8 @@
"confirmation": { "$ref": "#/$defs/confirmation" },
"executionProfile": {
"type": "string",
"enum": ["sync", "async"],
"description": "sync=调用方等待结构化结果(不建 Job);async=宿主建后台 Job 并进任务中心。必须显式写出;长耗时建议 async,但不限制 placements。"
"enum": ["sync", "async", "stream"],
"description": "sync=调用方等待结构化结果(不建 Job);async=宿主建后台 Job 并进任务中心;stream=建 Job 供页内 token/增量流式(jiangchang.stream + stream_delta),不进任务中心主路径。必须显式写出;与 placements 正交。"
},
"interaction": {
"type": "string",

View File

@@ -937,7 +937,7 @@ uses: client-jiangchang/jiangchang-platform-kit/.github/workflows/reusable-relea
| **数据管理 · 行级** | `placements` 含 `row`(建议 `bind.tables` + `bind.inputMapping` 预填主键) | 操作列和/或行右键(及可选「⋯」)出现本技能行级 Action(需有主键列);点按后入参/行为与壳无关、均正确 |
| **数据管理 · 批量** | `placements` 含 `batch` | 勾选行后批量按钮可用;无勾选时禁用;批量行为正确 |
| **定时任务** | `placements` 含 `cron`(创建时选「技能直调」) | 能选到本技能 Action、保存并触发;到点或「立即运行」行为正确;`selectionMode: optional` 时参数区只见数量字段、不见空「编号列表」 |
| **任务中心** | 不是 placement;由 `executionProfile: "async"` 决定是否进 Job | 长任务出现进度 / 可取消;来源标签与触发入口一致 |
| **任务中心** | 不是 placement;仅 `executionProfile: "async"` 进入;`stream` 为静默 Job 不进侧栏 | 长任务出现进度 / 可取消;来源标签与触发入口一致 |
| **技能市场 → 技能详情** | `placements` 含 `skill-detail`(详情直调) | **必验**安装与四 Tab。详情页「技能直调」按钮随宿主版本上线;**已声明则须在具备该 UI 的宿主上完成按钮自测**(与其它入口同等对待,不要只靠 Agent) |
补充说明:

View File

@@ -75,6 +75,7 @@ OpenClaw 技能常在客户电脑上异步执行,且大量依赖 RPA、浏览
| API | 用途 |
|-----|------|
| `emit` | 推送用户可读进度(不写 stdout);进入步骤前自动步骤闸门(SRCP) |
| `stream_channel` | (platform-kit ≥1.2.14)页内流式通道:`ch.delta(text)` → Journal `stream_delta`,供 HTML `jiangchang.stream`;用于 `executionProfile: stream`,**不是**任务中心进度条替代品 |
| `step` | 结构化步骤 |
| `finish` | 任务结束;**唯一**写单行 result JSON 到 stdout 的场景 |
| `job_context` | 包裹 `cmd_run`;未捕获异常 / `JobStopped` 时自动 `finish` |

View File

@@ -41,9 +41,9 @@
| POLICY-CONTROL-003 | `scripts/service/*.py` 不得裸 `asyncio.sleep`(RPA 等待须 `interruptible_sleep`) | development/RPA.md §0.1 | hard | 扫描 service 层 Python | `tests/test_development_policy_guard.py::TestPolicyControl003` |
| POLICY-DATA-PATH-001 | 技能拥有路径须经 `resolve_data_path` / `get_*_dir`;`.env.example` 禁止 `./` CWD 相对路径;业务代码禁止 `abspath(config.get(...))` | development/DATA_PATHS.md | hard | 扫描 `.env.example` 与 `scripts/**/*.py` | `tests/test_development_policy_guard.py::TestPolicyDataPath001` |
| POLICY-SKILL-ACTION-001 | 存在 `development/SKILL_ACTION_RUNTIME.md`;schema 含 `executionProfile`;业务技能(非模板占位 slug)若 service 含 `RpaVideoSession` 则 `actions.json` 须有 `executionProfile=async` 且 placements 含 `agent` 的 action | development/SKILL_ACTION_RUNTIME.md;references/ACTIONS.md | hard | 文档/schema/manifest 检查 | `tests/test_development_policy_guard.py::TestPolicySkillAction001` |
| POLICY-SKILL-ACTION-002 | 每个 Action 必须显式声明 `executionProfile`,且只能是 `sync` 或 `async` | development/SKILL_ACTION_RUNTIME.md;references/ACTIONS.md;assets/schemas/skill-actions.schema.json | hard | schema required + manifest 扫描 | `tests/test_development_policy_guard.py::TestPolicySkillAction002`;`tests/test_actions_manifest.py` |
| POLICY-SKILL-ACTION-002 | 每个 Action 必须显式声明 `executionProfile`,且只能是 `sync` / `async` / `stream` | development/SKILL_ACTION_RUNTIME.md;references/ACTIONS.md;assets/schemas/skill-actions.schema.json | hard | schema required + manifest 扫描 | `tests/test_development_policy_guard.py::TestPolicySkillAction002`;`tests/test_actions_manifest.py` |
| POLICY-SKILL-ACTION-003 | toolbar Action 必须声明合法非空 `bind.tables`(snake_case、唯一) | references/ACTIONS.md;assets/schemas/skill-actions.schema.json | hard | schema if/then + manifest 扫描 | `tests/test_development_policy_guard.py::TestPolicySkillAction003`;`tests/test_actions_manifest.py` |
| POLICY-SKILL-ACTION-004 | `placements` 与 `executionProfile` 正交;文档不得把数据管理入口与异步执行方式硬绑定;Schema 须接受 toolbar/row/batch/cron/agent/skill-detail × sync/async 全部组合 | development/SKILL_ACTION_RUNTIME.md;references/ACTIONS.md;assets/schemas/skill-actions.schema.json | hard | Schema 6×2 矩阵校验 + 文档禁止短语扫描 | `tests/test_actions_manifest.py::test_schema_allows_all_placement_execution_profile_combinations`;`tests/test_development_policy_guard.py::TestPolicySkillAction004` |
| POLICY-SKILL-ACTION-004 | `placements` 与 `executionProfile` 正交;文档不得把数据管理入口与执行方式硬绑定;Schema 须接受 toolbar/row/batch/cron/agent/skill-detail × sync/async/stream 全部组合 | development/SKILL_ACTION_RUNTIME.md;references/ACTIONS.md;assets/schemas/skill-actions.schema.json | hard | Schema 6×3 矩阵校验 + 文档禁止短语扫描 | `tests/test_actions_manifest.py::test_schema_allows_all_placement_execution_profile_combinations`;`tests/test_development_policy_guard.py::TestPolicySkillAction004` |
| POLICY-TUTORIAL-001 | 存在 `development/TUTORIAL_SURFACES.md`;`TUTORIAL.md` 须含数据管理/对话/定时/对照表骨架;`README.md` 禁止「告诉 Agent」专章与入口专章;说明/教程正文禁止与页头重复的 `#` H1(主分节用 `##`);业务技能教程与 `actions.json` placements 对齐(无 cron 须声明不需要定时) | development/TUTORIAL_SURFACES.md;development/CHECKLIST.md A2-7~A2-12 | hard | 文档存在性 + 章节/反模式扫描 + placements 对齐 | `tests/test_tutorial_surfaces.py`;`tests/test_docs_standards.py::test_tutorial_surfaces_contract_doc_and_checklist` |
| POLICY-ARCH-CORE-001 | 同一业务能力的 Action、CLI、Agent、cron、数据管理入口复用同一业务内核(文档要求已声明) | development/DEVELOPMENT.md;development/SKILL_ACTION_RUNTIME.md | hard(文档标记) / soft(语义) | 文档关键表述存在性;真实复用靠 sample/review | `tests/test_development_policy_guard.py::TestPolicyArchCore001`;`tests/samples/test_action_core_contract.py.sample` |
| POLICY-DATA-SYNC-001 | 同步型业务须遵循幂等、事务、完整性与空结果保护(文档已声明) | references/SCHEMA.md;development/SKILL_ACTION_RUNTIME.md | hard(文档标记) / soft(语义) | 文档关键表述存在性;完整语义靠 sample/review | `tests/test_development_policy_guard.py::TestPolicyDataSync001`;`tests/samples/test_sync_contract.py.sample` |

View File

@@ -1,14 +1,14 @@
# Skill Action Runtime(宿主任务中心契约)
本文是匠厂宿主 **Skill Action / 任务中心** 的技能侧金标准。涉及浏览器 RPA、长耗时任务、数据管理按钮、定时任务或 Agent 直接调用时**必读**。
本文是匠厂宿主 **Skill Action / 任务中心** 的技能侧金标准。涉及浏览器 RPA、长耗时任务、数据管理按钮、定时任务、页内流式或 Agent 直接调用时**必读**。
**产品入口(平级关系、何时挂哪个)**先读 [`PRODUCT_SURFACES.md`](PRODUCT_SURFACES.md);本文管 sync/async、任务中心与编排。
**产品入口(平级关系、何时挂哪个)**先读 [`PRODUCT_SURFACES.md`](PRODUCT_SURFACES.md);本文管 sync/async/stream、任务中心与编排。
**边界:**
| 文档 | 职责 |
|------|------|
| 本文 | sync/async、任务中心、Agent 禁令、单内核编排、队列 pick、同步 vs 导出 |
| 本文 | sync/async/stream、任务中心、Agent 禁令、单内核编排、队列 pick、同步 vs 导出 |
| [`../references/ACTIONS.md`](../references/ACTIONS.md) | `actions.json` 字段、placements、`bind.tables`、`bind.selectionMode`、`bind.visibleWhen`(行菜单显隐)、**用户可见文案**、inputSchema、跨表 Lookup(含分页勾选 `presentation: paged_table`)、字段条件显隐 `visibleWhen`、本地路径 `file`/`directory` + 可选 **`pathRoot`**、选择摘要「已选择」与映射 **`readOnly` / `revealInForm`**、**按钮中文名 `label` 参考** |
| [`LOGGING.md`](LOGGING.md) | `job_context` / `emit` / `finish` / Run Journal |
| [`DATA_PATHS.md`](DATA_PATHS.md) | 下载/导入等落盘路径 |
@@ -22,26 +22,28 @@
1. **宿主管调度,技能管业务** — 不自行实现任务中心 / Job 轮询 / 后台守护进程。
2. **单业务内核、多入口** — 数据管理、定时任务、Agent、技能详情与直接 CLI 复用同一 domain service;禁止按入口复制业务逻辑。
3. **五职责正交** — `placements` / `bind.tables` / `executionProfile` / `entrypoint` / domain service 各管一事(见 ACTIONS.md)。可选第六轴 **policy**(见 `references/POLICY.md`):未启用 = L0 与今日一致。可选 **`hostEffects` / `steps`**(见 ACTIONS.md):数据管理交互前后的 UI/级联编排;省略 = 不额外改 UI(匠厂宿主 ≥2.8.5)。可选 **`hoverHint`**(见 ACTIONS.md):数据管理工具栏悬停;省略 = 不显示,不回退说明或确认黄条。可选 **表单 `visibleWhen`**(见 ACTIONS.md):Action 参数表单字段条件显隐;省略 = 字段始终平铺;**勿与** **`bind.visibleWhen`**(行菜单)或 Policy L3 混淆;**勿**用其藏映射主键。可选 **`pathRoot`**(见 ACTIONS.md):`file`/`directory` 原生选路对话框起始目录(相对技能数据根;省略默认 `{skill_data}/input`)。可选 **`bind.labelColumn`**(见 ACTIONS.md):选择摘要可读标签列;省略 = 宿主常见列名启发。可选 **`bind.selectionMode`**(见 ACTIONS.md):表勾选如何驱动 Action(`required` / `none` / `optional`);省略 = 宿主按 `$selection.ids` / `batch` 推断;**定时任务等无表勾选入口按无勾选分支**(隐藏 `$selection.ids`,保留 `pickCount`)。可选 **`bind.visibleWhen`**(见 ACTIONS.md「行菜单显隐」):`row` 菜单按当前行列值显隐;省略 = 始终显示。可选 **`revealInForm`**(见 ACTIONS.md):有「已选择」时仍强制显示某 `readOnly` 字段;省略 = 映射只读默认不占 form 参数区(仍提交)。可选 **`cellPresentation`**(见 `references/CELL_PRESENTATION.md`):单元格/整行/整列/区域语义标色;省略 = P0。**用户可见文案**(见 ACTIONS.md「用户可见文案」):弹窗 `description` / 确认黄条 / 参数说明 / 任务失败 `message` 须全中文产品用语;Agent 路由勿塞进弹窗。
4. **长耗时建议 async** — 开浏览器、RPA、HTTP 大文件下载等建议 `executionProfile: "async"`,进**任务中心**;这是对**方法**的选择,**不**限制可以出现在哪些 placement / 入口。
4. **长耗时建议 async;页内生成建议 stream** — 开浏览器、RPA、HTTP 大文件下载等建议 `executionProfile: "async"`,进**任务中心**;HTML 内 LLM/长生成要边出边看时用 `"stream"`(静默 Job)。这是对**方法**的选择,**不**限制可以出现在哪些 placement / 入口。
5. **Agent:短 CLI / 长 Action** — 短查询可用共享 Python bash CLI;长任务 / RPA **禁止** `exec` + `process poll`,必须 `run_skill_action`。
6. **进度走 Journal** — `emit` / `video.add_step` → Run Journal;UI / Skill Run Card / 任务中心展示;**不靠 stdout 流**。
7. **显式声明 `executionProfile`** — 心智默认 sync、例外才 async;每个 action **必须**写明 `sync` 或 `async`;宿主省略时按 sync,新技能仍禁止赌缺省;禁止第三种模糊模式(`auto` / `background` / `deferred` 等)。
6. **进度走 Journal** — `emit` / `video.add_step` → Run Journal(任务中心 / Run Card);`stream` 的增量走 Journal `stream_delta`(经 `stream_channel`),由 HTML `jiangchang.stream` 消费;**不要**靠 stdout 假流式。
7. **显式声明 `executionProfile`** — 心智默认 sync;例外才 `async` 或 `stream`;每个 action **必须**写明三者之一;宿主省略时按 sync,新技能仍禁止赌缺省;禁止模糊别名(`auto` / `background` / `deferred` 等)。
---
## 2. `executionProfile`:sync vs async
## 2. `executionProfile`:sync / async / stream
### 心智(先记住)
1. **默认按 sync 想**:多数 Action(诊断、统计、入队、秒级查询)是短调用,当场返回,**不进**任务中心。
2. **只有特殊情况标 async**:开浏览器 / RPA / 长下载 / 需要进度·暂停·停止时,才显式 `"executionProfile": "async"`,进入**任务中心**。
3. **匠厂宿主**:`actions.json` 若**省略** `executionProfile`,按 **`sync`** 处理(不再把漏写当成进任务中心)。
4. **新技能仍须显式写出** `sync` 或 `async`(模板 Schema / `POLICY-SKILL-ACTION-002` 硬门禁),禁止赌宿主缺省;显式写出也便于人和 AI 阅读。
2. **要任务中心进度·取消时标 async**:开浏览器 / RPA / 长下载等,显式 `"executionProfile": "async"`。
3. **要页内 token 流式时标 stream**:数据管理 HTML 问答 / 长文生成,显式 `"stream"`;**不**进任务中心主路径(角标不因 stream 涨)。
4. **匠厂宿主**:`actions.json` 若**省略** `executionProfile`,按 **`sync`** 处理。
5. **新技能仍须显式写出** `sync` / `async` / `stream`(模板 Schema / `POLICY-SKILL-ACTION-002` 硬门禁),禁止赌宿主缺省。
| 值 | 宿主行为 | 超时 | 任务中心 |
|----|----------|------|----------|
| **`sync`**(或省略时的宿主缺省) | 调用方等待 CLI 结束,直接返回结构化结果或结构化错误;**不**创建后台 Job | 默认约 **60s**(`SKILL_ACTION_SYNC_TIMEOUT_MS`) | ❌ 不进 |
| **`async`**(须显式声明) | 后台 spawn Job,立即返回 `jobId`;`emit` / `checkpoint` / `finish`;长任务应支持暂停/停止/取消检查 | Job 本身无默认墙钟上限 | ✅ 进入 |
| **`stream`**(须显式声明) | 建 Job + 页内桥接;技能 `stream_channel` → `stream_delta`;HTML `jiangchang.stream`;终态 `finish` / 结构化 JSON | 宿主对流式会话有墙钟上限(以宿主为准) | ❌ 不进(静默) |
直接执行 CLI:终端等待进程结束。宿主经 Action 调用同一 CLI:再按 `executionProfile` 分流。技能不得自建任务中心。
@@ -51,16 +53,27 @@
|------|------|
| 浏览器 / Playwright / RPA 主路径 | `async` + 建议 `confirmation` |
| 单次可能接近或超过约 60s 的网络/下载/同步 | `async` |
| HTML 页内 LLM / 长生成,要边生成边显示 | `stream`(需宿主 + kit≥1.2.14;见 ACTIONS.md「页面调用」) |
| `health` / `version` / `config-path` / 纯统计 / 秒级查询 | `sync` |
| 任意 placement(含 toolbar) | 允许 sync 或 async;以 manifest 为准 |
| 任意 placement(含 toolbar) | 允许 sync / async / stream;以 manifest 为准 |
合法:`sync|async` × `toolbar|row|batch|cron|agent|skill-detail`。
合法:`sync|async|stream` × `toolbar|row|batch|cron|agent|skill-detail`。
历史说明:旧文档曾错误地把数据管理按钮入口与异步执行方式绑死。该约束**已废止**;模板测试与 Schema **不得**再建立「入口位置决定 sync/async」一类硬耦合。旧版宿主曾「省略 → async」;**当前宿主为「省略 → sync」**。新技能仍须显式声明,不得依赖省略行为。
### stream 技能侧要点(最小)
1. `actions.json` 目标 Action 写 `"executionProfile": "stream"`(常见 `placements`:`skill-detail` + `agent`)。
2. CLI 中:`from jiangchang_skill_core.activity import stream_channel`,在生成循环里 `ch.delta(text)`;结束仍输出完整结果 JSON(供页面定稿与 Agent)。
3. HTML:`await jiangchang.stream(actionId, params, { onDelta, onEvent })`;缺 API 时提示升级宿主。
4. 将本技能 `platform_kit_min_version` 调到 **≥1.2.14**(模板默认仍可为更低;**用 stream 的技能必须自提**)。
5. 用户可见失败文案用中文;勿把 Job cancelled / Process exited 之类英文直接甩给页面(宿主侧会继续收敛,技能侧先保证自己的 `message`)。
形状参考:[`../assets/examples/stream-ask.action.example.json`](../assets/examples/stream-ask.action.example.json)。
历史说明:旧文档曾错误地把数据管理按钮入口与异步执行方式绑死。该约束**已废止**;模板测试与 Schema **不得**再建立「入口位置决定 sync/async/stream」一类硬耦合。旧版宿主曾「省略 → async」;**当前宿主为「省略 → sync」**。新技能仍须显式声明,不得依赖省略行为。
---
## 3. 多入口如何感知 sync / async
## 3. 多入口如何感知 sync / async / stream
多个入口统一走 `POST /api/skill-actions/run`,可能带 `source.kind`:
@@ -71,8 +84,8 @@
| **数据管理 · 行级** | `data-management` | `row`(建议 `bind.tables` + `inputMapping`) | 操作列和/或**行右键**(及可选「⋯」)可见并触发(需主键);技能配什么就显示什么 |
| **数据管理 · 批量** | `data-management` | `batch` | 勾选行后点批量按钮 |
| **定时任务** | `cron` | `placements` 含 `cron` | 侧栏「定时任务」→「技能直调」 |
| **任务中心** | (各入口触发的 Job) | `executionProfile: "async"` | 侧栏「任务中心」查看进度 / 取消;**不是** placement |
| **技能详情** | `skill-detail` | `placements` 含 `skill-detail` | 市场详情安装/四 Tab;详情直调按钮随宿主上线后**同等自测** |
| **任务中心** | (各入口触发的 Job) | `executionProfile: "async"` | 侧栏「任务中心」查看进度 / 取消;**不是** placement;`stream` Job **不**出现在此 |
| **技能详情 / HTML 预览** | `skill-detail` | `placements` 含 `skill-detail`;流式页用 `jiangchang.stream` | 市场详情安装/四 Tab;数据管理打开壳子页;详情直调按钮随宿主上线后**同等自测** |
技能 **不要** 在 Python 里按 `source` / placement 写业务分叉;宿主读 manifest 分流展示与等待策略。
**已声明的入口都要自测**,不要只测对话。操作清单见 [`DEVELOPMENT.md`](DEVELOPMENT.md) §15 第七步。
@@ -81,12 +94,13 @@
## 4. Action 分类与 placements(示例,不是限制)
下表只表示常见组合示例。**placements 以 manifest 为准;sync/async 不影响展示入口。**
下表只表示常见组合示例。**placements 以 manifest 为准;sync/async/stream 不影响展示入口。**
| Action 类型 | 常见 `executionProfile` | 常见 placements | `confirmation` |
|-------------|-------------------------|-----------------|----------------|
| 运行检查 / 版本 / 配置路径 | `sync` | `skill-detail`, `agent` | 无 |
| 库 / 队列统计 | `sync` | `skill-detail`, `agent` | 无 |
| HTML 页内问答 / 长生成 | `stream` | `skill-detail`, `agent` | 通常无 |
| 外源 → 本地库同步 | `sync` 或 `async` | 可含 `toolbar`(须 `bind.tables`)、`cron`、`agent`、`skill-detail` | 可选 |
| 单行 / 批量数据管理动作 | `sync` 或 `async` | `row` / `batch`(建议 `bind.tables`;行内常用 `inputMapping`) | 可选 |
| 数据导入 / 失败重置 | `sync` 或 `async` | `skill-detail`, `agent` | 可选 |
@@ -180,7 +194,7 @@ cmd_stats() # 只读 sync:JSON stdout,无 job_context
1. 短查询:共享 Python(PATH 上的 `python`)跑 CLI;**禁止** `uv run`。
2. 长任务 / RPA / async Action:必须通过宿主 **Skill Action**(`run_skill_action`);**禁止** `exec` + `process poll`。
3. 单条 vs 批量 action id 与参数(如 `url` / `pickCount`)。
4. `executionProfile: async` → 进度看**任务中心** / Skill Run Card;`sync` → 当场结果。与入口无关。
4. `executionProfile: async` → 进度看**任务中心** / Skill Run Card;`sync` → 当场结果;`stream` → 页内 `jiangchang.stream`(不进任务中心)。与入口无关。
5. 多入口复用同一业务内核。
模板源仓库的 `SKILL.md` / `references/ACTIONS.md` 含占位段落;scaffold 后按业务改写。
@@ -192,9 +206,9 @@ cmd_stats() # 只读 sync:JSON stdout,无 job_context
自动检测索引见 [`POLICY_MATRIX.md`](POLICY_MATRIX.md):
- **POLICY-SKILL-ACTION-001**:Runtime 文档存在;schema 含 `executionProfile`;有 RPA 的业务技能须声明 async agent action。
- **POLICY-SKILL-ACTION-002**:每个 Action 显式声明 `executionProfile`(sync/async)。
- **POLICY-SKILL-ACTION-002**:每个 Action 显式声明 `executionProfile`(sync/async/stream)。
- **POLICY-SKILL-ACTION-003**:toolbar Action 必须声明合法 `bind.tables`。
- **POLICY-SKILL-ACTION-004**:placements 与 executionProfile 正交(文档/测试不得写死耦合)。
- **POLICY-SKILL-ACTION-004**:placements 与 executionProfile 正交(文档/测试不得写死耦合;Schema 接受 6×3 组合)。
- **POLICY-ARCH-CORE-001** / **POLICY-DATA-SYNC-001**:见矩阵;完整语义多用 sample / code review。
---

View File

@@ -83,11 +83,11 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表**
| 字段 / 层 | 只决定什么 | 不决定什么 |
|-----------|------------|------------|
| **`placements`** | Action **出现在哪里**(toolbar / row / batch / cron / agent / skill-detail;`row`=行级入口,壳由宿主呈现) | 不决定 sync/async,不决定点了是否弹窗,不决定行级用操作列还是右键,不决定谁能点 |
| **`placements`** | Action **出现在哪里**(toolbar / row / batch / cron / agent / skill-detail;`row`=行级入口,壳由宿主呈现) | 不决定 sync/async/stream,不决定点了是否弹窗,不决定行级用操作列还是右键,不决定谁能点 |
| **`replaces`** | **可选**:接管数据管理默认「新增 / 编辑 / 删除 / **导入**」 | 不强制;无配置时宿主仍直写**已注册数据源**(默认本地 SQLite);**import 时 UI 仍用宿主「导入数据」面板** |
| **`bind.tables`** | 数据管理里出现在**哪些业务表** | 不决定业务实现 |
| **`executionProfile`** | 宿主是**同步等待**还是**异步 Job** | 不限制可展示入口,不决定是否弹窗 |
| **`interaction`** | 可点击入口(数据管理 toolbar/row/batch、默认 CRUD 接管、未来 skill-detail)**点了怎么交互** | 不决定 sync/async;**cron 触发与 agent 忽略** |
| **`interaction`** | 可点击入口(数据管理 toolbar/row/batch、默认 CRUD 接管、未来 skill-detail)**点了怎么交互** | 不决定 sync/async/stream;**cron 触发与 agent 忽略** |
| **`hoverHint`** | **可选**:数据管理**工具栏悬停**文案 | 不决定短按钮名;不决定点下去弹不弹窗;**不回退** `description` / `confirmation.message`;省略 = 不显示悬停。当前宿主实现为顶栏技能按钮 |
| **`inputSchema.visibleWhen`** | **可选**:Action **参数表单**字段条件显隐(先选模式再出对应参数) | **不是** Data Policy L3 行态 `visibleWhen`;不决定 placements;旧宿主忽略则字段平铺全显 |
| **`hostEffects` / `steps`** | **可选**:数据管理交互跑完前后,宿主要不要刷新表 / 清选区 / 级联删等 | 不决定业务写什么;**省略 = 宿主不额外改 UI**(≥2.8.5 起不再硬编码 mutation 后刷新);cron/agent 忽略 |
@@ -115,7 +115,7 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表**
| **hoverHint** | 可选字符串。数据管理**工具栏悬停**文案;与 `description` / `confirmation.message` **独立**。省略则不显示悬停,不回退。短名已够懂时不要写。需宿主已支持该字段;当前实现为顶栏技能按钮,不含宿主内置删除/刷新/新增/导入/导出 |
| **visibleWhen** | 可选。写在 **property** 上(非 action 顶层):依赖同表单其它字段值决定是否展示;隐藏字段不提交、不校验 `required`。**勿与** [`POLICY.md`](POLICY.md) L3 行态 `visibleWhen` 混淆;也**勿**用它藏「已选择」已表达的映射主键(见「选择摘要」) |
| **revealInForm** | 可选。写在 **property** 上:有「已选择」时仍强制画出该 `readOnly` 字段;省略 = 映射只读字段默认不占参数区(仍提交) |
| **executionProfile** | `"sync"` \| `"async"`(**必须显式写出**,禁止依赖缺省) |
| **executionProfile** | `"sync"` \| `"async"` \| `"stream"`(**必须显式写出**,禁止依赖缺省) |
| **interaction** | `"auto"` \| `"direct"` \| `"confirm"` \| `"form"`(可选;省略≈`auto`) |
| **interactionByPlacement** | 可选;按 placement 覆盖为 `direct`/`confirm`/`form`(不可写 `auto`) |
| **hostEffects** | 可选;`onSuccess` / `onFailure` → `reloadTable` \| `refreshCatalog` \| `clearSelection`(匠厂宿主 ≥2.8.5) |
@@ -734,20 +734,29 @@ python {skill_root}/scripts/main.py <command> <args...>
完整子命令定义见 [`scripts/cli/app.py`](../scripts/cli/app.py)。
### `executionProfile`(仅 sync / async)
### `executionProfile`(sync / async / stream)
**心智**:多数 Action 按 **sync**(短调用、不进任务中心);只有长耗时 / RPA / 需进度控制时才标 **async**。匠厂宿主若**省略**该字段,按 **sync** 处理;模板与新技能仍要求**每个 Action 显式写出** `sync` 或 `async`,禁止发明 `auto` / `background` / `deferred`,也禁止赌省略行为。
**心智**:多数 Action 按 **sync**(短调用、不进任务中心);长耗时 / RPA / 需任务中心进度·取消时标 **async**;HTML 页内 LLM / 长生成要 **token 流式**时标 **stream**(建 Job,但**不**进任务中心主路径)。匠厂宿主若**省略**该字段,按 **sync** 处理;模板与新技能仍要求**每个 Action 显式写出**三者之一,禁止发明 `auto` / `background` / `deferred`,也禁止赌省略行为。
| 值 | 宿主行为 | 任务中心 |
|----|----------|----------|
| `sync`(或省略时的宿主缺省) | 调用方等待结束,直接返回结构化结果或结构化错误;**不**创建后台 Job | 不进 |
| `async`(须显式声明) | 宿主创建后台 Job,立即返回 `jobId`;用 `emit` / `checkpoint` / `finish` 报告进度与终态 | **进入** |
| `stream`(须显式声明) | 宿主创建 Job 供页内增量;技能用 kit `stream_channel` → Journal `stream_delta`;HTML 用 `jiangchang.stream` 收 delta;终态仍走 `finish` / `finish_success` | **不进**(静默 Job;侧栏角标不因 stream 涨) |
合法组合(全部允许):`sync|async` × `toolbar|row|batch|cron|agent|skill-detail`。
合法组合(全部允许):`sync|async|stream` × `toolbar|row|batch|cron|agent|skill-detail`。
经验建议(**不是** placement 硬限制):耗时不可预测、需要暂停/停止/进度展示的任务,用 `async`;其余默认写 `sync`。
**选型(不是 placement 硬限制):**
直接执行短 CLI 时,终端自然等待进程结束。宿主通过 Action 调用同一 CLI 时,再按 `executionProfile` 决定同步等待还是建 Job。技能**不得**自行实现第二套任务中心或后台队列。
| 场景 | 建议 |
|------|------|
| `health` / `version` / 秒级查询 / 短写入 | `sync` |
| 浏览器 RPA / 长下载 / 要任务中心进度·暂停·停止 | `async` |
| 数据管理 HTML 内问答 / 长文生成,要边出边看 | `stream`(页面用 `jiangchang.stream`,勿用 `call` 假流式) |
`stream` 依赖:宿主已支持 `executionProfile: stream` + 预览桥 `jiangchang.stream`;技能侧 `jiangchang-platform-kit>=1.2.14`(`activity.stream_channel`)。未达版本时不要声明 `stream`。形状参考见 [`../assets/examples/stream-ask.action.example.json`](../assets/examples/stream-ask.action.example.json)。
直接执行短 CLI 时,终端自然等待进程结束。宿主通过 Action 调用同一 CLI 时,再按 `executionProfile` 分流。技能**不得**自行实现第二套任务中心或后台队列。
### CLI 输出契约
@@ -885,23 +894,34 @@ HINT: <next step>
### 页面调用
宿主在数据管理预览里注入 `jiangchang.call`。页面脚本把它当成普通函数调用;宿主只在**当前技能** `actions.json` 已声明的方法里查找,命中后走和表按钮同一条 Python CLI,把 CLI 打出的 JSON 交回。
宿主在数据管理预览里注入 `jiangchang.call`(以及可选的 `jiangchang.stream`)。页面脚本把它当成普通函数调用;宿主只在**当前技能** `actions.json` 已声明的方法里查找,命中后走和表按钮同一条 Python CLI。
**同步结果(`executionProfile: sync`,或只需最终 JSON):**
```javascript
const data = await jiangchang.call("方法名", { date: "2026-09-19" });
```
**页内流式(`executionProfile: stream`):** 用 `jiangchang.stream`,不要用 `call` 假装流式。技能侧在 CLI 里用 `stream_channel(...).delta(...)` 推增量;页面 `onDelta` 拼接并渲染;Promise resolve 为终态 JSON(与 `call` 相同形状)。
```javascript
const data = await jiangchang.stream("ask", { question: "..." }, {
onDelta: (piece) => { /* 追加文本并重绘 */ },
onEvent: (ev) => { /* 可选:检索中等中文提示 */ },
});
```
| 项 | 约定 |
|----|------|
| 方法名 | `actions.json` 里该条的 `id`。没声明则调用失败。不能传任意命令,也不能调别的技能 |
| 参数 | 一个 JSON 对象,字段必须落在该条 `inputSchema` 里 |
| 入口 | 可以不挂 `toolbar`。页面调用不要求表按钮。`placements` 仍不能为空(例如只挂 `agent`) |
| 成功 | CLI 退出码 0,并打印**一个** JSON 对象。页面拿到的就是这个对象 |
| 失败 | 退出码非 0,JSON 里带可读的 `message`(`type` 为 `result`、`status` 为 `failed` 时宿主会把 `message` 交给页面) |
| 范围 | 只在数据管理预览里有这个函数。用浏览器单独打开该文件时没有,页面应说明「请在数据管理中打开」 |
异步命令当场只返回任务号,不在这一节展开。写入和只读是同一种调用,差在技能声明了哪条命令。
| 入口 | 可以不挂 `toolbar`。页面调用不要求表按钮。`placements` 仍不能为空(例如只挂 `agent` / `skill-detail`) |
| 成功(call) | CLI 退出码 0,并打印**一个** JSON 对象。页面拿到的就是这个对象 |
| 成功(stream) | 过程中多次 `stream_delta`;结束后仍须结构化终态(含完整 `answer`/`message` 等),供页面定稿 |
| 失败 | 退出码非 0,JSON 里带可读的 **中文** `message`(`type` 为 `result`、`status` 为 `failed` 时宿主会把 `message` 交给页面) |
| 范围 | 只在数据管理预览里有这些函数。用浏览器单独打开该文件时没有,页面应说明「请在数据管理中打开」;缺 `jiangchang.stream` 时提示升级宿主 |
`async` 命令经 `call` 当场只返回任务号,进度看任务中心,不在这一节展开。写入和只读是同一种调用,差在技能声明了哪条命令。
### Action:`render-html`(固定 id)
宿主「重新生成」**硬编码**查找 Action id `render-html`(见 `service.html_page_service.HOST_HTML_RENDER_ACTION_ID`)。技能未提供该 id → 提示「该技能未提供生成 HTML」。

View File

@@ -1,6 +1,6 @@
"""技能标识、版本与平台公共库约束(复制后请修改 slug/version/logger)。"""
SKILL_SLUG = "your-skill-slug"
SKILL_VERSION = "1.0.126"
SKILL_VERSION = "1.0.127"
LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug"
PLATFORM_KIT_MIN_VERSION = "1.2.11"

View File

@@ -16,7 +16,7 @@ from util.constants import SKILL_SLUG
_TEMPLATE_PLACEHOLDER_SLUG = "your-skill-slug"
_ALLOWED_PLACEMENTS = frozenset({"toolbar", "row", "batch", "cron", "agent", "skill-detail"})
_RESERVED_ACTION_FIELDS = frozenset({"concurrency", "locks"})
_ALLOWED_EXECUTION_PROFILES = frozenset({"sync", "async"})
_ALLOWED_EXECUTION_PROFILES = frozenset({"sync", "async", "stream"})
_REQUIRED_MANIFEST_KEYS = frozenset({"schemaVersion", "skill", "actions"})
_REQUIRED_ACTION_KEYS = frozenset(
{"id", "label", "description", "placements", "entrypoint", "executionProfile"}
@@ -232,7 +232,7 @@ class TestActionsManifest(unittest.TestCase):
self.assertIn(
profile,
_ALLOWED_EXECUTION_PROFILES,
msg=f"{action['id']} executionProfile must be sync|async, got {profile!r}",
msg=f"{action['id']} executionProfile must be sync|async|stream, got {profile!r}",
)
def test_schema_requires_execution_profile_and_defines_bind(self) -> None:
@@ -240,7 +240,7 @@ class TestActionsManifest(unittest.TestCase):
action_def = schema["$defs"]["action"]
self.assertIn("executionProfile", action_def.get("required") or [])
profile = action_def["properties"]["executionProfile"]
self.assertEqual(set(profile.get("enum") or []), {"sync", "async"})
self.assertEqual(set(profile.get("enum") or []), {"sync", "async", "stream"})
self.assertIn("bind", schema["$defs"])
self.assertIn("bind", action_def["properties"])
bind_def = schema["$defs"]["bind"]
@@ -424,14 +424,14 @@ class TestActionsManifest(unittest.TestCase):
self.assertEqual(missing, [], msg="\n".join(missing))
def test_schema_allows_all_placement_execution_profile_combinations(self) -> None:
"""POLICY-SKILL-ACTION-004:6 placements × 2 profiles 必须全部通过 Schema。"""
"""POLICY-SKILL-ACTION-004:6 placements × 3 profiles 必须全部通过 Schema。"""
try:
import jsonschema
except ImportError:
self.fail("jsonschema is required to validate placement×executionProfile orthogonality")
schema = _load_actions_schema()
placements = ("toolbar", "row", "batch", "cron", "agent", "skill-detail")
profiles = ("sync", "async")
profiles = ("sync", "async", "stream")
for placement in placements:
for profile in profiles:
action: dict = {

View File

@@ -1382,12 +1382,12 @@ class TestPolicySkillAction001(unittest.TestCase):
"action.properties.executionProfile missing",
),
)
self.assertEqual(set(profile.get("enum") or []), {"sync", "async"})
self.assertEqual(set(profile.get("enum") or []), {"sync", "async", "stream"})
def test_actions_md_documents_execution_profile(self) -> None:
path = os.path.join(get_skill_root(), "references", "ACTIONS.md")
text = _read_text(path)
for marker in ("executionProfile", "async", "任务中心", "SKILL_ACTION_RUNTIME"):
for marker in ("executionProfile", "async", "stream", "任务中心", "SKILL_ACTION_RUNTIME"):
self.assertIn(
marker,
text,
@@ -1407,7 +1407,7 @@ class TestPolicySkillAction001(unittest.TestCase):
for action in manifest.get("actions") or []:
action_id = action.get("id", "?")
profile = action.get("executionProfile")
if profile not in ("sync", "async"):
if profile not in ("sync", "async", "stream"):
offenders.append(f"{action_id}: executionProfile={profile!r}")
self.assertEqual(
offenders,
@@ -1415,7 +1415,8 @@ class TestPolicySkillAction001(unittest.TestCase):
msg=_policy_msg(
"POLICY-SKILL-ACTION-001",
"assets/actions.json",
"every action must set executionProfile to sync|async:\n" + "\n".join(offenders),
"every action must set executionProfile to sync|async|stream:\n"
+ "\n".join(offenders),
),
)
@@ -1469,7 +1470,10 @@ class TestPolicySkillAction002(unittest.TestCase):
"executionProfile must be in action.required",
),
)
self.assertEqual(set(action["properties"]["executionProfile"].get("enum") or []), {"sync", "async"})
self.assertEqual(
set(action["properties"]["executionProfile"].get("enum") or []),
{"sync", "async", "stream"},
)
def test_manifest_actions_declare_execution_profile(self) -> None:
manifest = _load_actions_json(get_skill_root())
@@ -1478,7 +1482,7 @@ class TestPolicySkillAction002(unittest.TestCase):
offenders: list[str] = []
for action in manifest.get("actions") or []:
profile = action.get("executionProfile")
if profile not in ("sync", "async"):
if profile not in ("sync", "async", "stream"):
offenders.append(f"{action.get('id', '?')}: {profile!r}")
self.assertEqual(
offenders,
@@ -1486,7 +1490,8 @@ class TestPolicySkillAction002(unittest.TestCase):
msg=_policy_msg(
"POLICY-SKILL-ACTION-002",
"assets/actions.json",
"every action must set executionProfile to sync|async:\n" + "\n".join(offenders),
"every action must set executionProfile to sync|async|stream:\n"
+ "\n".join(offenders),
),
)
@@ -1588,7 +1593,7 @@ class TestPolicySkillAction004(unittest.TestCase):
def test_runtime_doc_states_orthogonality(self) -> None:
text = _read_text(os.path.join(get_skill_root(), "development", "SKILL_ACTION_RUNTIME.md"))
for marker in ("正交", "sync|async", "toolbar"):
for marker in ("正交", "sync|async|stream", "toolbar"):
self.assertIn(
marker,
text,

View File

@@ -24,7 +24,7 @@ def _platform_kit_meets_template_minimum() -> bool:
return version_ge(installed, PLATFORM_KIT_MIN_VERSION)
def _run_main(args: list[str], timeout: int = 15) -> subprocess.CompletedProcess[str]:
def _run_main(args: list[str], timeout: int = 60) -> subprocess.CompletedProcess[str]:
root = get_skill_root()
main_py = os.path.join(root, "scripts", "main.py")
env = os.environ.copy()