openxiangda 2.30.0 → 2.31.1

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.
Files changed (45) hide show
  1. package/dist/browser/ManagedCommand.d.ts +36 -0
  2. package/dist/browser/ManagedCommand.d.ts.map +1 -0
  3. package/dist/browser/ManagedCommand.js +41 -0
  4. package/dist/browser/ManagedCommand.js.map +1 -0
  5. package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
  6. package/dist/browser/managed-command.d.ts +72 -0
  7. package/dist/browser/managed-command.d.ts.map +1 -0
  8. package/dist/browser/managed-command.js +282 -0
  9. package/dist/browser/managed-command.js.map +1 -0
  10. package/dist/browser/platform-client.d.ts +3 -0
  11. package/dist/browser/platform-client.d.ts.map +1 -1
  12. package/dist/browser/platform-client.js +32 -0
  13. package/dist/browser/platform-client.js.map +1 -1
  14. package/dist/config.d.ts +1 -0
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/core.d.ts +3 -0
  17. package/dist/core.d.ts.map +1 -1
  18. package/dist/core.js +2 -0
  19. package/dist/core.js.map +1 -1
  20. package/dist/mobile.d.ts +3 -0
  21. package/dist/mobile.d.ts.map +1 -1
  22. package/dist/mobile.js +3 -0
  23. package/dist/mobile.js.map +1 -1
  24. package/dist/react.d.ts +4 -0
  25. package/dist/react.d.ts.map +1 -1
  26. package/dist/react.js +3 -0
  27. package/dist/react.js.map +1 -1
  28. package/documentation/backend.md +44 -0
  29. package/documentation/declarations-cheatsheet.md +2 -0
  30. package/documentation/frontend.md +6 -0
  31. package/documentation/getting-started.md +7 -7
  32. package/documentation/managed-concurrency-frontend.md +196 -0
  33. package/documentation/managed-concurrency.md +100 -0
  34. package/documentation/manifest.json +17 -5
  35. package/package.json +26 -22
  36. package/releases/2.31.0.json +39 -0
  37. package/releases/2.31.1.json +38 -0
  38. package/skills/manifest.json +1 -1
  39. package/skills/openxiangda-v2/SKILL.md +7 -4
  40. package/skills/openxiangda-v2/references/backend.md +44 -0
  41. package/skills/openxiangda-v2/references/declarations-cheatsheet.md +2 -0
  42. package/skills/openxiangda-v2/references/frontend.md +6 -0
  43. package/skills/openxiangda-v2/references/getting-started.md +7 -7
  44. package/skills/openxiangda-v2/references/managed-concurrency-frontend.md +196 -0
  45. package/skills/openxiangda-v2/references/managed-concurrency.md +100 -0
@@ -0,0 +1,196 @@
1
+ # 并发能力的前端接入
2
+
3
+ 适用于活动报名、限量申领、抢票和预约。本文使用 `openxiangda@2.31.0` 引入的公开 API;目标平台必须支持 `data.managed-concurrency@1.0.0`。先按[缓存、排队与整数配额](./managed-concurrency.md)声明读取、命令、配额和权限,再选择页面接入形式。
4
+
5
+ 推荐体验是正常浏览内容,在用户明确提交时按需等待,并持续核对同一次申请的结果。低负载时可以很快完成,不需要人为添加等待页。
6
+
7
+ ## 选择接入形式 {#patterns}
8
+
9
+ | 形式 | 适用场景 | 用户流程 | 当前接入方式 |
10
+ | --- | --- | --- | --- |
11
+ | 热点内容直接浏览 | 详情、场次、活动说明 | 浏览 → 按需刷新 | `client.read`,应用处理读取状态 |
12
+ | 按钮发起、原地等待 | 一键报名、限量申领 | 点击 → 排队 → 处理 → 结果 | `useManagedCommand` + `ManagedCommandStatus` |
13
+ | 先填写、再排队提交 | 有选项的预约和申请 | 填写校验 → 确认内容 → 排队提交 | 有界参数组合现有 hook;复杂资料需要额外冻结契约 |
14
+ | 放行后短确认 | 输入已确定、加载成本较高的确认步骤 | 进入 → 等待 → 短确认 → 提交 | `autoAccept: false` + `ManagedCommandGate` + `submit()` |
15
+ | 预占后确认 | 暂留名额、时段预约 | 预占 → 核实有效期 → 确认或释放 | 应用声明预占/确认/释放命令,配合 `client.allocation` |
16
+ | 返回后恢复原申请 | 刷新、断网、查看本人结果 | 恢复原请求 → 核对 → 结果 | hook 挂载恢复,或 `client.result` 查询已知操作 |
17
+
18
+ 常见报名页面可以组合前三种:详情走缓存,用户确认活动和选项后提交,按钮附近展开状态;成功后进入本人记录。PC 状态区、抽屉和移动端底部区域可以使用不同外观,共用同一个操作状态。
19
+
20
+ ## 页面与平台的分工 {#ownership}
21
+
22
+ 页面负责内容、选项、字段校验、业务文案和结果路由。平台负责当前用户身份、准入、幂等、事务、配额和权威结果。身份使用声明中的可信 `actor`,不使用用户填写的工号决定记录归属。
23
+
24
+ 平台身份就绪后创建 `createManagedConcurrencyClient()`,并保持实例稳定。它绑定当前应用、环境和主体;身份/环境变化时重新挂载对应页面子树,不能复用旧客户端或旧视图。
25
+
26
+ 一次操作只使用一个 `useManagedCommand`。按钮、状态条和弹层消费同一个快照,不要各自调用 hook 创建观察循环。当前 SDK 通过 `openxiangda/react` 和 `openxiangda/mobile` 导出相同的受管并发能力;普通字段仍复用[Field Kit](./field-components.md),界面遵循[前端组件与页面归属](./frontend.md)。
27
+
28
+ 不要把受管模型继续绑定到普通 CRUD 提交按钮,也不要传入任意 `async onSubmit` 并假设它会自动获得排队和原子保证。最终动作必须是已发布的受管命令。
29
+
30
+ ## 热点详情读取 {#cached-read}
31
+
32
+ 以下示例对应源码 `examples/managed-concurrency/declaration.ts` 中的 `offer` 命名读取。`offerId` 必须是该应用真实资源的 UUID:
33
+
34
+ ```ts
35
+ import { createManagedConcurrencyClient } from 'openxiangda/react';
36
+
37
+ type Client = ReturnType<typeof createManagedConcurrencyClient>;
38
+ type Offer = { id: string; title: string; closesAt: string };
39
+
40
+ export async function readOffer(
41
+ client: Client,
42
+ offerId: string,
43
+ signal?: AbortSignal,
44
+ ) {
45
+ return client.read<Offer>('offer', { id: offerId }, signal);
46
+ }
47
+ ```
48
+
49
+ 将读取接入应用已有的异步查询状态:初次加载显示局部骨架,空数组显示无可展示内容,失败提供局部重试。组件卸载或资源变化时中止旧请求,迟到响应不能覆盖新资源。同页多个观察者可复用应用已有查询层,查询键包含 `client.scope`、读取 code 和规范化参数,身份变化时清除旧主体视图;当前没有内置的 `useManagedRead` hook。
50
+
51
+ `freshness: 'stale'` 表示返回的是允许使用的旧内容,并不证明后台刷新已经成功。保留内容并提示“当前展示最近一次可用信息”;按新鲜期和有界退避刷新,超过 `staleUntil` 后不能继续无限展示。手动刷新仍调用同一命名读取,不绕回普通 CRUD。
52
+
53
+ 详情与余量使用不同读取声明。页面倒计时本地显示,不能每秒重查活动;资格、开放时间和扣减仍由服务端决定。缓存余量不保证获票;允许补量或释放的业务,也不能因旧余量为零永久阻止新的有效申请。等待期间不要重复读取详情或表单。
54
+
55
+ ## 一键申请与原结果恢复 {#one-click}
56
+
57
+ 示例对应官方声明中的 `claim` 命令。父级在身份就绪后挂载,用户、环境或 `offerId` 变化时重新挂载本组件:
58
+
59
+ ```tsx
60
+ import { useMemo } from 'react';
61
+ import {
62
+ createManagedConcurrencyClient,
63
+ useManagedCommand,
64
+ ManagedCommandStatus,
65
+ } from 'openxiangda/react';
66
+
67
+ export function ClaimAction({ offerId }: { offerId: string }) {
68
+ const client = useMemo(() => createManagedConcurrencyClient(), []);
69
+ const command = useManagedCommand({
70
+ client,
71
+ command: 'claim',
72
+ input: { id: offerId },
73
+ storageKey: `claim:${offerId}`,
74
+ });
75
+ // start / resume 的失败进入快照;保留原请求,按错误原因恢复。
76
+ const observe = (action: () => Promise<void>) => {
77
+ void action().catch(() => {});
78
+ };
79
+ return (
80
+ <section>
81
+ <button disabled={command.state !== 'idle'}
82
+ onClick={() => observe(() => command.start())}>
83
+ 申请
84
+ </button>
85
+ <ManagedCommandStatus snapshot={command} />
86
+ {command.state === 'error' && (
87
+ <div>
88
+ <p>暂时无法确认结果,请核对原申请。</p>
89
+ <button onClick={() => observe(() => command.resume())}>
90
+ 核对原申请
91
+ </button>
92
+ </div>
93
+ )}
94
+ {command.state === 'succeeded' && <p>可查看本次申请记录。</p>}
95
+ </section>
96
+ );
97
+ }
98
+ ```
99
+
100
+ 挂载只恢复已有意图;用户点击 `start()` 才创建新意图。默认放行后自动受理,适合点击已经明确表达提交意愿的动作。不要在 `useEffect` 中调用 `start()` 自动报名,不要手写循环换请求键重试。
101
+
102
+ 等待只轮询票据,受理后查询原命令结果;SDK 遵守建议间隔并退避。按钮禁用只是体验,服务端仍执行幂等和业务去重。成功时可从 `receipt.result.items` 定位已创建的业务记录;不要在成功回调里再次写订单、扣名额或发通知。导航和视图刷新应允许重复执行。
103
+
104
+ ## 填写后再提交 {#form-submit}
105
+
106
+ 先校验字段,向用户展示确认摘要,再将输入固定并启动命令。受管 hook 使用固定后的输入,不要直接绑定每次键入都变化的表单对象。提交后刷新页面,要从本次意图的稳定资料恢复相同输入,再挂载 hook;不能用空表单覆盖原请求。
107
+
108
+ 排队后需要修改资料时,先请求取消并核实结果,再允许编辑和创建新意图。取消与受理、执行、成功可能竞争;服务端返回已经成功时,应展示原结果,不能继续当作草稿编辑或假定取消成功。组件卸载或关闭面板仅停止观察,不取消命令。
109
+
110
+ 当前命令参数仅支持 UUID、枚举字符串和有限整数。场次、规格等可以按声明接入;配额 `units` 由命令声明固定,传入整数参数不会自动改变扣减数量。姓名、任意备注、附件及整份表单 JSON 不属于当前参数范围。
111
+
112
+ 复杂资料需要先定义并验证“草稿 → 不可变资料版本 → 命令引用版本 ID”的契约,核实作者、目标资源、版本和字段权限,并阻止冻结资料被普通 CRUD 改写。只传 `draftId` 而允许原稿继续变化不满足要求。该通用冻结机制和表单桥接尚未提供,不能把任意表单保存接口包进 hook 就当作已支持;草稿保存本身也需要独立预算。
113
+
114
+ ## 放行后的短确认 {#confirmation-gate}
115
+
116
+ 此形式要求排队前已确定命令参数,用户明确请求进入确认步骤。沿用前例的身份/client 生命周期,创建命令时设置 `autoAccept: false`;入口按钮调用 `start()`,获准后的确认按钮调用 `submit()`:
117
+
118
+ ```tsx
119
+ // command 已由 useManagedCommand 创建,并设置 autoAccept: false。
120
+ // Confirmation 是应用组件;它只核对已经固定的输入。
121
+ <ManagedCommandGate snapshot={command}>
122
+ {() => (
123
+ <Confirmation
124
+ onConfirm={() => void command.submit().catch(handleActionError)}
125
+ />
126
+ )}
127
+ </ManagedCommandGate>
128
+ ```
129
+
130
+ `ManagedCommandGate` 从与 hook 相同的公开入口导入。上例是前例中的组合片段,`Confirmation` 和 `handleActionError` 由应用定义;错误处理必须保留原请求,不能直接清除并重提。耗时请求和懒加载应在子树内发生,父组件提前发请求会失去保护。
131
+
132
+ `admitted` 是短期受理许可,不是名额预占,也不是长时间填写表单的许可证。当前 Gate 只按快照状态决定是否挂载,没有内建到期倒计时;应用按 `waiting.expiresAt` 提示并核实,最终提交时服务端校验许可,SDK 沿原请求恢复。
133
+
134
+ “先进入页面,再任意决定最终输入”需要与命令输入解耦的页面准入契约,当前 Gate 不提供这个语义。它在应用启动、身份就绪后工作,不能替代静态资源、登录、启动元数据和整个站点入口的保护。
135
+
136
+ ## 预占与最终确认 {#reservation}
137
+
138
+ 应用先声明 `quota.action: 'allocate'`、`mode: 'reserved'` 和 `reservationSeconds`,另声明确认/释放命令。三个动作分别使用稳定恢复键,并按[配额生命周期](./managed-concurrency.md#配额和发布边界)绑定原分配 ID。
139
+
140
+ 预占命令 `succeeded` 只证明预占事务曾成功。先读取 `receipt.result.allocation.id`,再调用 `client.allocation(allocationId)` 核实当前状态,界面显示“已暂留名额”。确认或释放后也要核对当前分配,不能用旧成功回执证明仍持有名额。
141
+
142
+ 进入页面、回到前台和临近到期时有界核实。`expiresAt` 用于展示估算倒计时;当前回执没有 `serverNow`,浏览器时间不能作为最终过期依据。归零显示“正在核实有效期”,确认和到期竞争以服务端结果为准。关闭窗口不自动释放。
143
+
144
+ 同池/主体永久去重;释放后不会自动允许同一主体重申同一池。需要下一轮申请时使用新的权威资源/池,不删除分配记录或换请求键绕过限制。
145
+
146
+ ## 状态文案与用户动作 {#states}
147
+
148
+ | 状态 | 界面含义 | 动作规则 |
149
+ | --- | --- | --- |
150
+ | `idle` | 可以申请 | 明确点击后 `start()` |
151
+ | `waiting` | 正在等待进入,尚未获得名额 | 等待;可申请退出排队 |
152
+ | `admitted` | 已轮到您,可以确认 | 手动确认形式调用 `submit()` |
153
+ | `accepted` / `executing` / `retry_wait` | 已收到申请,正在处理 | 查询原结果;按服务端规则申请取消 |
154
+ | `recovering` | 正在核对原申请 | 保留原键,继续退避观察 |
155
+ | `error` | 暂时无法确认或无法继续 | 按原因恢复登录、处理存储或 `resume()`;不统一显示报名失败 |
156
+ | `succeeded` | 权威事务已提交 | 按业务显示报名成功或暂留名额;预占另核实分配 |
157
+ | `rejected` | 明确未通过 | 区分售罄、资格、截止等业务原因 |
158
+ | `expired` / `cancelled` | 等待或命令到期、已取消 | 区分票据与命令终态,明确结束后才 `clear()` |
159
+
160
+ 排队位置只在 `waiting.position` 存在时显示,不换算成保证等待时长或保证名额;缺少位置时显示等待文案,不画虚假百分比。临时队列故障后位置可能变化。
161
+
162
+ `ManagedCommandStatus` 支持 `errorMessages` 映射回执错误。产品层同时检查 `snapshot.errorCode` 与 `receipt.errorCode`,补齐身份变化、存储不可用、恢复输入冲突等原因。用户文案不暴露数据库、锁或票据内部信息。
163
+
164
+ `cancel()` 的 Promise 失败也要明确展示“取消结果尚未确认”,保留原意图并恢复查询;不要吞掉取消错误后宣布取消成功。关闭状态面板、离开页面与取消申请使用不同按钮和文案。状态变化使用无障碍提示,抽屉/底部区域遵循现有组件的焦点管理。
165
+
166
+ ## 刷新、断网与我的申请 {#recovery}
167
+
168
+ 默认恢复信息保存在 `sessionStorage`,按应用、环境、主体、命令和 `storageKey` 隔离。同标签页刷新可以恢复;`storageKey` 必须稳定,不能在每次渲染时随机生成。存储不可用时明确停止或处理,不能悄悄改用易丢失的内存继续提交。
169
+
170
+ 已知 `operationId` 或原 `requestKey` 时,可以用 `client.result` 查询原结果。已成功业务记录可通过本人权限下的普通数据查询展示。当前没有“列出我的所有进行中命令”的接口;关闭标签后找回、跨设备继续和完整处理中列表需要额外的受管操作索引,不能把浏览器存储当作全平台事实。
171
+
172
+ 不要把超时解释为提交失败,也不要在结果未知时 `clear()`。重试、恢复与返回入口都应该指向原意图。配额永久去重与界面请求键是不同边界:清除一个已终结的界面状态不代表可以再次获得同一池名额。
173
+
174
+ ## 当前组件与后续封装边界 {#available-components}
175
+
176
+ 当前可用:`createManagedConcurrencyClient`、`useManagedCommand`、`ManagedCommandStatus`、`ManagedCommandGate`,以及客户端的读取、结果、取消和分配状态方法。
177
+
178
+ `useManagedRead`、`ManagedCommandButton`、`ManagedCommandPanel`、`useManagedFormSubmission`、`useManagedAllocation`、`AllocationStatus` 是建议的后续封装名称,当前不是公开导出。不要照这些名称编写 import。应用现在可以使用已有 hook 与平台普通 UI 组件组合界面,后续标准封装仍应复用同一状态机和传输。
179
+
180
+ 复杂资料冻结、跨设备命令索引、独立页面准入及精确倒计时需要相应的平台契约,不能仅通过前端外观补齐。SDK 版本、平台部署和实际业务容量需要分别确认。
181
+
182
+ ## 接入验收 {#acceptance}
183
+
184
+ | 操作 | 应观察到的结果 |
185
+ | --- | --- |
186
+ | 打开详情或重渲染 | 不自动报名;详情与余量按声明读取,不因排队反复查表 |
187
+ | 连点、刷新、提交响应丢失 | 沿用原意图,恢复原结果,不生成重复业务记录 |
188
+ | 排队时编辑资料 | 固定原输入,取消尚未核实前不切换到另一次提交 |
189
+ | 关闭状态区后返回 | 继续查看原操作,不宣称已取消 |
190
+ | 取消与受理/成功同时发生 | 显示服务端原结果;错误时保留待核实状态 |
191
+ | 身份切换、存储被禁用 | 不串用旧主体资料,不静默丢弃恢复能力 |
192
+ | 网络或依赖异常 | 退避观察,保留原键,不绕回数据库或快速换键重提 |
193
+ | 许可到期、预占到期 | 分别核实准入与分配,互不冒充 |
194
+ | 键盘、屏幕阅读器、窄屏 | 状态可读,焦点可控,关闭与取消语义清楚 |
195
+
196
+ 这些检查与目标环境的吞吐、P95/P99、授权查询成本和故障恢复验证一起完成;页面演示和 SDK 单元测试不能代替真实业务验收。
@@ -0,0 +1,100 @@
1
+ # 缓存、排队与整数配额
2
+
3
+ `data.concurrency` 是可选的平台声明,用于限量申领、抢票和预约。平台负责缓存回源、等待资格、持久受理、受控 Native 事务及整数配额;应用通过 `openxiangda/react` 或 `openxiangda/mobile` 调用。平台必须声明能力 `data.managed-concurrency@1.0.0`。旧平台会在编译/发布能力检查阶段拒绝此声明。
4
+
5
+ 页面开发参见[并发能力的前端接入](./managed-concurrency-frontend.md):按热点详情、一键申请、填完再提交、短确认入口、预占确认和原结果恢复选择交互方式,并核对当前 API 与尚未提供的封装边界。
6
+
7
+ ## 声明与权限
8
+
9
+ 完整且由双编译器测试验证的示例位于源码 `examples/managed-concurrency/declaration.ts`。调用 `defineOpenXiangdaApp(concurrencyExample())` 即可编译。把示例中的 `data.concurrency` 合入应用配置,并按实际业务配置模型和参与权限。
10
+
11
+ | 声明 | 作用 |
12
+ | --- | --- |
13
+ | `reads` | 命名读取、有限参数、字段、条件、权限范围、新鲜期、旧值期限、失效字段和回源预算 |
14
+ | `admission` | 本应用/环境所有命令共享的每秒速率、突发额度、在途上限 |
15
+ | `commands` | 当前用户的能力、按资源分队列、最长等待、处理截止、Native 守卫和原子写入 |
16
+ | `quotas` | 容量来源、业务记录上的分配 ID、可选的预占到期时间 |
17
+
18
+ 参数限于 UUID、有限字符串枚举和有限整数范围;绑定只接受 `input`、可信 `actor`、字面量和平台生成的 `allocation`。每个命令最多 8 个操作、8 个记录守卫。首版不执行应用任意 JS/SQL,也不在锁内调用外部系统。通知和外部副作用接已有事务后事件。
19
+
20
+ 配额来源模型的容量字段必须是整数;分配记录模型的引用字段必须是必填 UUID,模型设置 `mutationOwner: 'queued-command'`。角色仍须具备来源读取、分配模型创建和命令能力。普通 CRUD、导入、应用凭据均不能绕过命令所有权。用户身份由 `actor` 绑定,不能使用用户填写的人员编号决定名额归属。
21
+
22
+ `database-now` 条件使用数据库时间,并按“字段 操作符 当前时间”解释。例如截止前允许报名:`{ kind: 'database-now', field: 'closesAt', operator: 'gt' }`。资格和业务截止在执行时重验,因此等待或已受理不等于一定成功。
23
+
24
+ ## 展示读取
25
+
26
+ ```ts
27
+ const client = createManagedConcurrencyClient();
28
+ const snapshot = await client.read('offer', { id: selectedOfferId });
29
+ // snapshot.items、generatedAt、freshUntil、staleUntil、version、freshness
30
+ ```
31
+
32
+ 在平台身份初始化完成后创建并保持 `client` 实例稳定。它绑定应用、环境和当前用户;切换身份后旧客户端拒绝继续操作。组织内共享内容也要求登录与当前权限。`scope: 'application'` 不允许行策略或 actor 条件;个人/有行策略的数据使用 `scope: 'subject'`。
33
+
34
+ 每次访问仍通过平台现有身份、版本与字段权限链路。缓存减少内容查询;不能据此宣称整个请求零 SQL。活动内容和余量使用不同读取声明与依赖,避免每次报名使详情失效。`dependencies` 必须覆盖返回和过滤字段。Native 提交在同一事务写失效事实,恢复任务负责可靠推进;变更传播有延迟,要求即时资格或即时关闭的判断必须放在命令守卫中。
35
+
36
+ 冷缓存使用进程请求合并与跨副本刷新租约,短暂争用返回可重试响应。负缓存最长 5 秒,TTL 带随机抖动。可在应用版本激活后,用正常授权的 `read` 调用预热有限的热点 ID。首版提供认证后的 Redis/进程缓存,不提供公共 CDN 发布或自动扫描全量数据预热。
37
+
38
+ ## 提交与原结果恢复
39
+
40
+ ```tsx
41
+ import { useMemo } from 'react';
42
+ import { createManagedConcurrencyClient, useManagedCommand,
43
+ ManagedCommandStatus } from 'openxiangda/react';
44
+
45
+ export function Claim({ offerId }: { offerId: string }) {
46
+ // 父级在平台身份就绪后挂载;身份变化时重新挂载此子树。
47
+ const client = useMemo(() => createManagedConcurrencyClient(), []);
48
+ const command = useManagedCommand({ client, command: 'claim',
49
+ input: { id: offerId }, storageKey: offerId });
50
+ return <>
51
+ <button disabled={command.state !== 'idle'}
52
+ onClick={() => void command.start().catch(() => {})}>申请</button>
53
+ <ManagedCommandStatus snapshot={command} />
54
+ {command.state === 'error' && <button
55
+ onClick={() => void command.resume().catch(() => {})}>恢复原操作</button>}
56
+ </>;
57
+ }
58
+ ```
59
+
60
+ 挂载只恢复已有请求,用户调用 `start()` 才创建新意图。默认以 `sessionStorage` 保存原请求键及恢复资料,刷新后继续查原结果;浏览器禁止存储时明确失败,不自动改用易丢失的内存状态。可传入同接口的可靠存储。不要另写循环换请求键重试。
61
+
62
+ 等待阶段只轮询签名票据,不重复查业务数据。SDK 遵守建议间隔并加抖动,故障时逐步退避至约 30 秒。组件卸载或 `stop()` 只停止观察,不取消已受理命令。取消使用 `cancel()`,会核对原命令并处理与受理竞争的情况。只有已知终态才能 `clear()` 开始新意图。
63
+
64
+ 需要先排队再加载重型页面时,设置 `autoAccept: false`,用 `ManagedCommandGate` 的 `children={() => <HeavyContent />}` 延迟构建子树;用户确认后调用 `submit()`。命令输入在排队时固定。它不替代整站认证、应用启动元数据或未使用此能力的接口预算,也不能保护提前在父组件发起的请求。
65
+
66
+ | 状态 | 含义 |
67
+ | --- | --- |
68
+ | `waiting` / `admitted` | 等待/获准受理,尚未占名额 |
69
+ | `accepted` / `executing` / `retry_wait` | 已持久受理,平台继续处理 |
70
+ | `succeeded` | 权威事务已提交,原结果可恢复 |
71
+ | `rejected` / `expired` / `cancelled` | 明确终态 |
72
+ | `recovering` / `error` | 正在核对/暂时无法确认,保留原请求键 |
73
+
74
+ ## 配额和发布边界
75
+
76
+ 立即分配用 `quota.action: 'allocate', mode: 'committed'`;需要预占时声明 `reservationSeconds` 并用 `mode: 'reserved'`。另声明 `confirm`、`release` 命令,绑定原 `allocation`,该命令的 `operations` 为空。只有原主体可确认/释放;到期与确认竞争使用数据库时间及固定锁顺序。
77
+
78
+ 原命令回执不可变。预占之后到期或释放,应调用 `client.allocation(allocationId)` 获取当前分配状态,不能用旧成功回执证明仍持有名额。首版同一池/主体保留永久去重事实,释放后不会自动重新报名;应用若需要新一轮,应使用新的权威资源/配额池,不能删除历史分配。
79
+
80
+ 容量减少不能低于已预占加已确认数量。存在配额历史时,禁止删除来源、改写分配引用或发布移除配额映射的版本。应用激活先排空所有非终态命令;不会让旧任务在新 Head 上执行。连接开发消费已激活测试版本的并发声明,未激活的本地模型覆盖层不能作为持久命令契约。
81
+
82
+ ## 资源上限与故障处理
83
+
84
+ | 边界 | 首版硬上限 |
85
+ | --- | --- |
86
+ | 每应用/环境等待 | 128 个资源队列、合计 10000 人,同时受声明的更小上限约束 |
87
+ | 缓存 | 单值 256 KiB;应用/环境累计 32 MiB、2048 个有效键;进程 L1 最多 256 个条目 |
88
+ | 回源 | 每进程 2 个、每应用跨副本 2 个;应用合计 20 次/秒并受声明预算限制 |
89
+ | HTTP | 并发能力普通入口每进程 8 个,查原结果独立 4 个 |
90
+ | worker | 每进程最多 2 个,同一资源通过数据库事务锁协调 |
91
+
92
+ Redis 不可用会停止新准入和缓存回源,绝不把所有请求直接放到数据库。已持久受理命令依赖 PostgreSQL 和现有 RabbitMQ 恢复;原结果在 Redis 故障时每进程最多 4 次/秒、4 个并发可回查。Redis 丢失会重建在途事实并换票据世代,临时等待位置可能变化。
93
+
94
+ RabbitMQ 的消息只是唤醒提示;队列丢失或发布失败由数据库恢复,重复消息读取同一命令。锁等待或慢执行触发有界重试与应用短暂暂停放行。系统并不承诺跨队列严格公平,也不承诺一旦排队就必定获票。
95
+
96
+ 管理员可使用同源 POST `/openxiangda-api/v2/applications/:appCode/native/concurrency/status`,请求带 `environmentKey`,查看非终态数量/最早时间、最多 100 个配额池及当前进程缓存指标。`control` 接收 `{ environmentKey, paused: true }` 停止新受理;只允许应用超级管理员。停用或回滚前停止受理、排空命令、保留配额与原结果;不得通过删锁、删队列数据补偿业务。
97
+
98
+ 暂停/恢复按同一个环境锁串行传播;Redis 不可用时控制接口明确报错。控制值是绝对值,可以使用相同 `paused` 重试并核对状态。数据库提交确认丢失时不猜测控制结果;受理始终复查数据库的暂停状态。
99
+
100
+ 验证应包括内容 SQL 与授权 SQL 占比、每秒完成数、P95/P99、最老等待、连接数、锁等待和恢复时间。仓库故障测试证明协议边界,不代表任何客户环境的生产吞吐。部署者仍需使用目标硬件、真实权限和活动规模做容量验收。
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": "openxiangda.documentation/v1",
3
- "version": "2.30.0",
3
+ "version": "2.31.1",
4
4
  "topics": [
5
5
  {
6
6
  "id": "getting-started",
7
7
  "title": "安装与开始开发",
8
8
  "file": "getting-started.md",
9
- "sha256": "53c29676a178d4f9b0e656196cca8b6ac01461677ff9772e1c386fbaf67d2198"
9
+ "sha256": "108af379386c6802d4b775e2f3d0f08fe71b7109e8d365548299b2fb1cf55b9d"
10
10
  },
11
11
  {
12
12
  "id": "product-design",
@@ -36,7 +36,7 @@
36
36
  "id": "declarations-cheatsheet",
37
37
  "title": "声明速查:一次写对 config",
38
38
  "file": "declarations-cheatsheet.md",
39
- "sha256": "db20c125256d1491772b82d864c36662894a77aa64d0ccd2d54766a6bbc8e836"
39
+ "sha256": "17357e46cfea90ecb9752d33bc378e2c901df6260aca0981ea5710ec82dbbd4e"
40
40
  },
41
41
  {
42
42
  "id": "application-foundation",
@@ -60,7 +60,7 @@
60
60
  "id": "frontend",
61
61
  "title": "页面与标准组件扩展",
62
62
  "file": "frontend.md",
63
- "sha256": "a0318e91498471df2b12f894efca94fcc0f0f5e5d7e459b5deb7d029002d0b5d"
63
+ "sha256": "93db2bef516307475e86c70a4146055f0ada86673f88b70b19a588d8ea035c68"
64
64
  },
65
65
  {
66
66
  "id": "field-components",
@@ -90,7 +90,7 @@
90
90
  "id": "backend",
91
91
  "title": "按需后端与业务动作",
92
92
  "file": "backend.md",
93
- "sha256": "b4f99040c588e384f2b64e4ac74efb4a0728c900feda6170996f3fe5b863d92a"
93
+ "sha256": "12312dd666b8a7991282acf78a839c7af336aefa007a08645f5e30b18e67b8b4"
94
94
  },
95
95
  {
96
96
  "id": "decimal-reservations",
@@ -98,6 +98,18 @@
98
98
  "file": "decimal-reservations.md",
99
99
  "sha256": "cdbc0bfff79c9bf71b88f5f50a5b76e4de77bae92e590ce08f32662621c1ff7d"
100
100
  },
101
+ {
102
+ "id": "managed-concurrency",
103
+ "title": "缓存、排队与整数配额",
104
+ "file": "managed-concurrency.md",
105
+ "sha256": "21d7e1e6ce3f230f88013cd6522ce6195bd4079d55af6d0b74a525c209fd5601"
106
+ },
107
+ {
108
+ "id": "managed-concurrency-frontend",
109
+ "title": "并发能力的前端接入",
110
+ "file": "managed-concurrency-frontend.md",
111
+ "sha256": "294fa5655a9f0673ec871d204ad53a15bf8dfc8a9a91a2975d8eafe58f402469"
112
+ },
101
113
  {
102
114
  "id": "administration",
103
115
  "title": "应用管理与有效配置",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "2.30.0",
3
+ "version": "2.31.1",
4
4
  "description": "OpenXiangda 2.0 的统一命令、应用 SDK、MCP 与中文 AI 技能资料。",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,13 +60,13 @@
60
60
  "antd-mobile": "5.42.3",
61
61
  "dayjs": "1.11.18",
62
62
  "docx-preview": "0.3.7",
63
- "openxiangda-cli": "2.6.1",
64
- "openxiangda-contracts": "2.27.0",
65
- "openxiangda-devkit-core": "2.30.0",
63
+ "openxiangda-cli": "2.6.3",
64
+ "openxiangda-contracts": "2.29.0",
65
+ "openxiangda-devkit-core": "2.32.0",
66
66
  "openxiangda-legacy": "npm:openxiangda@1.0.269",
67
- "openxiangda-mcp": "2.0.51",
68
- "openxiangda-nest": "2.7.4",
69
- "openxiangda-skill-kit": "2.3.25",
67
+ "openxiangda-mcp": "2.0.53",
68
+ "openxiangda-nest": "2.7.6",
69
+ "openxiangda-skill-kit": "2.3.27",
70
70
  "xlsx": "https://github.com/1377385356/openxiangda/releases/download/vendor-mirror/xlsx-0.20.3.tgz"
71
71
  },
72
72
  "peerDependencies": {
@@ -132,37 +132,41 @@
132
132
  },
133
133
  "openxiangdaRelease": {
134
134
  "schemaVersion": "openxiangda.release-notes/v1",
135
- "version": "2.30.0",
135
+ "version": "2.31.1",
136
136
  "status": "reviewed",
137
- "title": "OpenXiangda 2.30.0:平台条件唯一键",
138
- "summary": "模型可声明条件唯一键,统一约束普通 CRUD、导入和业务事务;编译器在发布前检查规则并识别目标平台是否支持。",
137
+ "title": "OpenXiangda 2.31.1:应用业务 Agent 任务声明",
138
+ "summary": "应用可以在现有 AI 操作上显式声明面向普通用户的业务任务、常见问法、只读辅助能力和输入字段查询绑定。编译器把这些信息并入同一个 AppVersion AI Catalog,平台按当前用户权限发现和执行。",
139
139
  "newFeatures": [
140
- "模型与数据资源支持有界 uniqueKeys 声明,可选择精确比较、NFKC 空白折叠或 ASCII 大写,并按文本空值或单选集合限定参与记录。",
141
- "两端编译器、JSON Schema、声明诊断和能力清单使用一致契约,自动要求 data.unique-keys@1.0.0。未声明的模型不增加能力要求。"
140
+ "ai.agent.visibility 区分可独立办理的 task 与任务引用的只读 support;未声明的底层 CRUD 不自动成为普通用户任务。",
141
+ "任务可以声明 examples、aliases、supportOperations 和 inputLookups;编译器校验辅助操作只读、引用闭合和请求字段存在。",
142
+ "docs/backend.md、声明速查和 OpenXiangda 2.0 Skill 提供接入流程及访客预约示例。",
143
+ "并发能力前端指南覆盖热点读取、申请、排队、预占确认与原结果恢复,并标明已提供 API 和待实现封装。"
142
144
  ],
143
145
  "fixes": [],
144
146
  "affectedUsers": [
145
- "需要跨并发写入保障业务编号或复合字段唯一性的 OpenXiangda 2.0 应用开发者。"
147
+ "希望把业务操作接入平台助手或应用 Agent 的 OpenXiangda 2.0 应用开发者。",
148
+ "需要实现缓存、排队或限额场景前端的应用开发者。"
146
149
  ],
147
150
  "upgradeSteps": [
148
- "更新到 openxiangda@2.30.0 并更新锁文件;在需要唯一性的模型声明 uniqueKeys,使用当前版本的数据与权限文档。",
149
- "先确认目标平台实际提供 data.unique-keys@1.0.0,再激活测试版本。历史重复或超长数据会明确拒绝激活并保留原环境。",
150
- "重复写入处理 HTTP 409 和规则标识;长度超限处理 HTTP 422。避免盲目重试创建,事务失败后按原业务状态恢复。"
151
+ "升级到 openxiangda@2.31.1 并更新锁文件,先实现完整业务操作、授权和只读辅助查询,再按 docs/backend.md 声明 ai.agent。",
152
+ "在测试环境运行 check 与完整输入、缺失输入、同名对象、权限不足和重复提交场景;发布应用版本后核对 AI Catalog 摘要与当前环境 Head。",
153
+ "平台服务端须部署支持显式业务任务发现的版本;npm 包发布本身不会升级平台或自动公开旧应用操作。",
154
+ "并发前端接入按 docs/managed-concurrency-frontend.md 选择模式,先核对已导出的 API 与目标平台能力。"
151
155
  ],
152
156
  "knownLimitations": [
153
- "每资源最多 8 条、每条 1–4 个键字段和最多 4 个 AND 条件;参与比较字段归一化后最多 256 个 UTF-8 字节。",
154
- "已有规则的修改、删除和所用字段替换需要平台受管迁移;连接开发不能私自改变规则。",
155
- "平台升级与 SDK 发行分别验收;npm 发行不代表客户节点已经上线。跨 PostgreSQL 大版本变更须先安排归一化索引的受管重建。"
157
+ "本次包仅增加业务任务元数据与编译校验;交互卡片资源、持久 Interaction 和 MCP Apps Host 由后续平台与应用批次交付。",
158
+ "现有独立 MCP Facade 的写入预览与确认协议保持原样;平台自有 Agent 的自动写入由平台执行层和权限边界负责。",
159
+ "旧应用的底层 CRUD 不会自动转换为普通用户任务,需应用维护者完成业务语义与辅助能力接入。"
156
160
  ],
157
161
  "issues": [],
158
162
  "compatibility": {
159
163
  "node": ">=24",
160
164
  "workspaceGenerations": "v2",
161
- "platform": "声明规则时要求 data.unique-keys@1.0.0;普通应用沿用现有能力。",
165
+ "platform": "平台业务 Agent 任务发现需配套部署支持 ai.agent 元数据的服务端版本。",
162
166
  "v1": "独立 V1 引擎和工作区不受影响。"
163
167
  },
164
- "sha256": "cbe1a5766e0cc0d8948d54b6d2129320b5d2fbe1e8f7120874b9f56c774b1fcb",
165
- "url": "https://github.com/1377385356/openxiangda/releases/tag/v2.30.0"
168
+ "sha256": "37c61ac4c9b17a8ca688cc9e1f38f4ed106754a7342e1a6aa9b2095e954a5118",
169
+ "url": "https://github.com/1377385356/openxiangda/releases/tag/v2.31.1"
166
170
  },
167
171
  "scripts": {
168
172
  "build": "node ../../scripts/prune-package-dist.mjs && tsc -p tsconfig.json && node scripts/copy-assets.mjs",
@@ -0,0 +1,39 @@
1
+ {
2
+ "schemaVersion": "openxiangda.release-notes/v1",
3
+ "version": "2.31.0",
4
+ "status": "reviewed",
5
+ "title": "OpenXiangda 2.31.0:缓存、排队与原子配额",
6
+ "summary": "应用可声明热点内容缓存、全局准入、可恢复的 Native 命令和整数配额。平台承担回源保护、受理幂等与事务内分配,React 和移动端 SDK 提供等待、提交及原结果恢复。",
7
+ "newFeatures": [
8
+ "data.concurrency 由两端共享编译器验证,缓存读取声明包含权限范围、有限参数、失效依赖和回源预算。",
9
+ "等待准入按可信主体合并重复请求,持久命令返回原操作回执,响应丢失和页面刷新后可继续核对结果。",
10
+ "整数配额支持立即分配、预占、确认、释放与到期;配额、业务记录、事件和结果在同一 Native 事务提交,受管模型阻止普通 CRUD 绕过。",
11
+ "openxiangda/react 和 openxiangda/mobile 提供 createManagedConcurrencyClient、useManagedCommand、ManagedCommandStatus 与 ManagedCommandGate。"
12
+ ],
13
+ "fixes": [],
14
+ "affectedUsers": [
15
+ "需要缓存、排队、限量申领、预约或抢票的 OpenXiangda 2.0 应用开发者。"
16
+ ],
17
+ "upgradeSteps": [
18
+ "升级到 openxiangda@2.31.0 并更新锁文件,按 docs/managed-concurrency.md 及完整声明示例配置资源和权限。",
19
+ "先确认目标平台已部署 data.managed-concurrency@1.0.0 及对应 SQL migration,再激活测试版本。未启用此声明的应用沿用原路径。",
20
+ "上线前按目标硬件、真实权限和业务规模验证完成速率、P95/P99、回源 SQL、锁等待及恢复时间;部署和容量验收独立于 npm 发行。",
21
+ "停用或切换不兼容版本前停止新受理并排空非终态命令,保留历史命令、分配及回执。"
22
+ ],
23
+ "knownLimitations": [
24
+ "首版执行编译后的 Native 原子动作,不把任意 JS、SQL 或第三方调用纳入原子事务。",
25
+ "Redis 故障时暂停新准入和缓存回源;临时等待位置不承诺持久,已持久受理命令可恢复。",
26
+ "缓存减少内容 SQL,当前身份和权限链路仍有数据库访问;不提供公共 CDN 发布或自动全量预热。",
27
+ "同一配额池和主体永久去重,释放后不会自动重新报名;新一轮业务使用新的权威资源或配额池。",
28
+ "资源保护上限不代表生产吞吐保证;公开包升级不会自动升级客户平台。"
29
+ ],
30
+ "issues": [],
31
+ "compatibility": {
32
+ "node": ">=24",
33
+ "workspaceGenerations": "v2",
34
+ "platform": "声明并发能力时要求 data.managed-concurrency@1.0.0;同时声明业务唯一键时还要求 data.unique-keys@1.0.0。",
35
+ "v1": "独立 V1 引擎和工作区不受影响。"
36
+ },
37
+ "sha256": "6258bae64bad0db43a319ecb6886a037952e0c77410edd6a790d18b8cd470645",
38
+ "url": "https://github.com/1377385356/openxiangda/releases/tag/v2.31.0"
39
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "schemaVersion": "openxiangda.release-notes/v1",
3
+ "version": "2.31.1",
4
+ "status": "reviewed",
5
+ "title": "OpenXiangda 2.31.1:应用业务 Agent 任务声明",
6
+ "summary": "应用可以在现有 AI 操作上显式声明面向普通用户的业务任务、常见问法、只读辅助能力和输入字段查询绑定。编译器把这些信息并入同一个 AppVersion AI Catalog,平台按当前用户权限发现和执行。",
7
+ "newFeatures": [
8
+ "ai.agent.visibility 区分可独立办理的 task 与任务引用的只读 support;未声明的底层 CRUD 不自动成为普通用户任务。",
9
+ "任务可以声明 examples、aliases、supportOperations 和 inputLookups;编译器校验辅助操作只读、引用闭合和请求字段存在。",
10
+ "docs/backend.md、声明速查和 OpenXiangda 2.0 Skill 提供接入流程及访客预约示例。",
11
+ "并发能力前端指南覆盖热点读取、申请、排队、预占确认与原结果恢复,并标明已提供 API 和待实现封装。"
12
+ ],
13
+ "fixes": [],
14
+ "affectedUsers": [
15
+ "希望把业务操作接入平台助手或应用 Agent 的 OpenXiangda 2.0 应用开发者。",
16
+ "需要实现缓存、排队或限额场景前端的应用开发者。"
17
+ ],
18
+ "upgradeSteps": [
19
+ "升级到 openxiangda@2.31.1 并更新锁文件,先实现完整业务操作、授权和只读辅助查询,再按 docs/backend.md 声明 ai.agent。",
20
+ "在测试环境运行 check 与完整输入、缺失输入、同名对象、权限不足和重复提交场景;发布应用版本后核对 AI Catalog 摘要与当前环境 Head。",
21
+ "平台服务端须部署支持显式业务任务发现的版本;npm 包发布本身不会升级平台或自动公开旧应用操作。",
22
+ "并发前端接入按 docs/managed-concurrency-frontend.md 选择模式,先核对已导出的 API 与目标平台能力。"
23
+ ],
24
+ "knownLimitations": [
25
+ "本次包仅增加业务任务元数据与编译校验;交互卡片资源、持久 Interaction 和 MCP Apps Host 由后续平台与应用批次交付。",
26
+ "现有独立 MCP Facade 的写入预览与确认协议保持原样;平台自有 Agent 的自动写入由平台执行层和权限边界负责。",
27
+ "旧应用的底层 CRUD 不会自动转换为普通用户任务,需应用维护者完成业务语义与辅助能力接入。"
28
+ ],
29
+ "issues": [],
30
+ "compatibility": {
31
+ "node": ">=24",
32
+ "workspaceGenerations": "v2",
33
+ "platform": "平台业务 Agent 任务发现需配套部署支持 ai.agent 元数据的服务端版本。",
34
+ "v1": "独立 V1 引擎和工作区不受影响。"
35
+ },
36
+ "sha256": "37c61ac4c9b17a8ca688cc9e1f38f4ed106754a7342e1a6aa9b2095e954a5118",
37
+ "url": "https://github.com/1377385356/openxiangda/releases/tag/v2.31.1"
38
+ }
@@ -4,7 +4,7 @@
4
4
  {
5
5
  "name": "openxiangda-v2",
6
6
  "description": "使用 OpenXiangda 2.0 从模糊业务想法、已有资料或具体变更出发,通过对话发现模块、完成详细产品设计,由当前 AI Agent 按需用 Image 2.5 等图片能力形成视觉参考,直接实现真实页面并在浏览器修正,再检查和交付应用;维护 1.x 应用时使用对应的 1.x 技能。",
7
- "sha256": "43362f7f5e18dbfbaba0909a8c78d1ee83effa8ffbb1089131b150da408974ad"
7
+ "sha256": "a53fab63a4033b8dca5de456f17d7cc55542f95bfb14460e551377649d04ca18"
8
8
  }
9
9
  ]
10
10
  }
@@ -40,10 +40,10 @@ AI 接到新应用、页面或改版任务时,在同一个 OpenXiangda 工作
40
40
  未创建工作区时使用本 Skill 随根包发布的精确版本:
41
41
 
42
42
  ```bash
43
- pnpm dlx openxiangda@2.30.0 auth status --cwd <应用目录> --base-url <平台地址> --json
44
- pnpm dlx openxiangda@2.30.0 login --cwd <应用目录> --base-url <平台地址>
45
- pnpm dlx openxiangda@2.30.0 create <应用目录> --base-url <同一平台地址>
46
- pnpm dlx openxiangda@2.30.0 skill install --force
43
+ pnpm dlx openxiangda@2.31.1 auth status --cwd <应用目录> --base-url <平台地址> --json
44
+ pnpm dlx openxiangda@2.31.1 login --cwd <应用目录> --base-url <平台地址>
45
+ pnpm dlx openxiangda@2.31.1 create <应用目录> --base-url <同一平台地址>
46
+ pnpm dlx openxiangda@2.31.1 skill install --force
47
47
  ```
48
48
 
49
49
  创建前把产品要求的目标平台明确带入命令,不从旧登录态推断站点。已有工作区从原绑定恢复,平台不一致时先解决登录与目标,不改 link 文件跨站创建。
@@ -71,7 +71,10 @@ pnpm dlx openxiangda@2.30.0 skill install --force
71
71
  | 外部无账号表单、续填、上传、本人记录 | [匿名公开访问](references/public-access.md) |
72
72
  | 审批、待办、消息或事件 | [工作流与通知](references/workflow-events.md) |
73
73
  | 自定义事务、校验或外部集成 | [按需后端](references/backend.md) |
74
+ | 向平台业务 Agent 开放任务、辅助查询和字段绑定 | [Agent 任务接入](references/backend.md#面向普通用户的-agent-任务);先完成业务操作、权限和规则,再声明 `ai.agent` |
74
75
  | 主子额度、审批占用释放、撤回修订重提 | [精确金额占用](references/decimal-reservations.md) |
76
+ | 热点读取、抢票、排队、名额预占与原结果恢复 | [缓存、排队与配额](references/managed-concurrency.md) |
77
+ | 报名按钮、表单排队、短确认页、预占倒计时与前端恢复 | [并发前端接入](references/managed-concurrency-frontend.md):按六种形式选择;仅使用已导出的 API,区分待实现封装 |
75
78
  | 管理成员或查看有效流程参数 | [应用管理](references/administration.md) |
76
79
  | 检查、部署、生产发布与故障恢复 | [验收](references/testing.md)、[交付](references/delivery.md) |
77
80
  | 需求记录与版本升级 | [全流程记录](references/appspec.md)、[升级](references/upgrading.md) |
@@ -249,6 +249,50 @@ operations: [{
249
249
  引用 1–16 个已声明资源;`sideEffects` 最多 20 条——只读必须为零,写操作至少一条具体副作用;
250
250
  `concurrency` 可选 `none | revision`,`timeoutMs` 限 100–30000。
251
251
 
252
+ ### 面向普通用户的 Agent 任务
253
+
254
+ `ai` 使操作进入技术能力目录;只有显式声明 `ai.agent` 才把操作交给普通用户的业务 Agent 发现。`visibility: 'task'` 是可办理的任务,`visibility: 'support'` 是该任务需要的只读查询。平台仍按当前用户的应用权限过滤,声明本身不授予权限。生成的资源 CRUD、没有 `agent` 的操作和 `support` 操作都不是普通用户首页的独立任务。
255
+
256
+ 以下示例中,应用自己负责地点搜索与预约规则。给模型和用户的是“地点名称”,稳定地点引用只作为操作参数,用户无需填写地点编码。
257
+
258
+ ```ts
259
+ const locations = {
260
+ code: 'reservation.locations', method: 'GET', path: '/api/reservations/locations',
261
+ capability: 'app:visitor-app:reservation:locations',
262
+ requestSchema: { type: 'object', properties: { keyword: { type: 'string' } } },
263
+ responseSchema: { type: 'object' },
264
+ ai: {
265
+ name: '查找可预约地点', description: '按名称搜索当前用户可选择的地点',
266
+ risk: 'read', resources: ['reservations'], sideEffects: [],
267
+ agent: { visibility: 'support' },
268
+ },
269
+ };
270
+ const enroll = {
271
+ code: 'reservation.enroll', method: 'POST', path: '/api/reservations/enroll',
272
+ capability: 'app:visitor-app:reservation:enroll',
273
+ requestSchema: {
274
+ type: 'object', required: ['location'],
275
+ properties: { location: { type: 'string' } },
276
+ },
277
+ responseSchema: { type: 'object' },
278
+ ai: {
279
+ name: '发起访客预约', description: '校验访客信息、地点与时间后创建预约',
280
+ risk: 'write', resources: ['reservations'], sideEffects: ['创建访客预约'],
281
+ agent: {
282
+ visibility: 'task',
283
+ examples: ['帮我预约访客', '明天下午邀请张老师来访'],
284
+ aliases: ['发起邀约'],
285
+ supportOperations: ['reservation.locations'],
286
+ inputLookups: { location: 'reservation.locations' },
287
+ },
288
+ },
289
+ };
290
+ ```
291
+
292
+ 两个 `capability` 必须在 `authz.capabilities` 中声明为 `kind: 'backend'`,再授予相应角色。`supportOperations` 和 `inputLookups` 引用同一应用中的操作 code;被引用操作必须声明 `visibility: 'support'`、`risk: 'read'` 且使用 GET。`inputLookups` 的键必须存在于任务的 `requestSchema.properties`。示例问法最多 12 条、别名最多 20 条、辅助操作最多 12 个、字段绑定最多 32 个;运行时辅助查询仍应分页、有界,并返回业务标签与稳定引用。
293
+
294
+ 业务规则必须在后端操作中验证;提示词、卡片预填和前端校验不能取代权限与业务校验。完整输入、缺失输入、同名地点、权限不足、重复提交和结果未知都应有应用测试与回执核对。任务声明随 AppVersion 的 AI Catalog 一起发布,不能另建手工目录。交互卡片与自动执行仅在平台 Agent Host 支持后启用;本声明本身不改变现有 MCP Facade 的写入确认协议。
295
+
252
296
  宿主(平台 AI 网关)用该目录装配 MCP Facade,应用不自己实现协议:
253
297
 
254
298
  - **单应用 Facade**(`createApplicationAiMcpServer`):每个能力一个工具,只读能力直接以