create-windy 0.3.16 → 0.3.18

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 (65) hide show
  1. package/README.md +14 -36
  2. package/dist/cli.js +1831 -3178
  3. package/package.json +1 -1
  4. package/template/.windy-template.json +2 -2
  5. package/template/AGENTS.md +7 -1
  6. package/template/README.md +8 -11
  7. package/template/apps/agent-server/Dockerfile +2 -1
  8. package/template/apps/agent-server/README.md +13 -5
  9. package/template/apps/agent-server/adk_web_agents/canonical_debug/__init__.py +1 -0
  10. package/template/apps/agent-server/adk_web_agents/canonical_debug/agent.py +125 -0
  11. package/template/apps/agent-server/project/adk-web-debug.json +4 -0
  12. package/template/apps/agent-server/src/southwind_agent_server/adk_debug.py +315 -0
  13. package/template/apps/agent-server/tests/test_adk_web_canonical_debug.py +235 -0
  14. package/template/apps/server/Dockerfile +1 -1
  15. package/template/apps/server/src/agent/run-debug-authorization.ts +27 -0
  16. package/template/apps/server/src/agent/run-facade-adk-debug.test.ts +56 -0
  17. package/template/apps/server/src/agent/run-facade.test-support.ts +235 -0
  18. package/template/apps/server/src/agent/run-facade.test.ts +8 -244
  19. package/template/apps/server/src/agent/run-facade.ts +23 -48
  20. package/template/apps/server/src/agent/run-operation-registry.ts +51 -0
  21. package/template/apps/server/src/agent/runtime-bootstrap.ts +5 -2
  22. package/template/apps/server/src/agent/tool-audit.ts +3 -0
  23. package/template/apps/server/src/agent/tool-registry.test.ts +29 -0
  24. package/template/apps/server/src/application-services.ts +83 -15
  25. package/template/apps/server/src/audit/contracts.ts +8 -0
  26. package/template/apps/server/src/audit/recovery-queue.test.ts +48 -1
  27. package/template/apps/server/src/audit/recovery-queue.ts +73 -4
  28. package/template/apps/server/src/audit/reliable-writer.test.ts +120 -2
  29. package/template/apps/server/src/audit/reliable-writer.ts +98 -34
  30. package/template/apps/server/src/guards.ts +16 -0
  31. package/template/apps/server/src/index.ts +23 -20
  32. package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +0 -1
  33. package/template/apps/server/src/jobs/legacy-adapter.test.ts +6 -0
  34. package/template/apps/server/src/jobs/legacy-service-runtime.ts +18 -0
  35. package/template/apps/server/src/module-host/durable-context-unavailable.test.ts +84 -0
  36. package/template/apps/server/src/module-host/durable-context.test.ts +14 -0
  37. package/template/apps/server/src/module-host/durable-context.ts +17 -15
  38. package/template/apps/server/src/module-host/request-context.test.ts +30 -1
  39. package/template/apps/server/src/module-host/request-context.ts +15 -2
  40. package/template/apps/server/src/module-host/storage-port.ts +1 -1
  41. package/template/apps/server/src/platform-bootstrap.ts +16 -0
  42. package/template/apps/server/src/runtime-adk-debug.test.ts +246 -0
  43. package/template/apps/server/src/runtime-adk-debug.ts +158 -0
  44. package/template/apps/server/src/runtime-request-actor.test.ts +55 -0
  45. package/template/apps/server/src/runtime-request-actor.ts +22 -0
  46. package/template/apps/server/src/runtime.test.ts +3 -0
  47. package/template/apps/server/src/runtime.ts +36 -3
  48. package/template/apps/server/src/scheduler/definitions.test.ts +16 -0
  49. package/template/apps/web/Dockerfile +1 -1
  50. package/template/docker-compose.agent-debug.yml +56 -0
  51. package/template/docker-compose.yml +1 -39
  52. package/template/docs/architecture/ai-runtime.md +14 -5
  53. package/template/docs/architecture/module-manifest-composition.md +14 -0
  54. package/template/docs/development/custom-login-page.md +2 -16
  55. package/template/docs/platform/agent-runtime.md +57 -16
  56. package/template/docs/platform/agent-tool-sandbox.md +4 -3
  57. package/template/docs/platform/audit-reliability.md +33 -5
  58. package/template/docs/platform/platform-authentication.md +9 -1
  59. package/template/package.json +4 -3
  60. package/template/packages/database/drizzle/0043_activate_agent_dependencies.sql +196 -0
  61. package/template/packages/database/drizzle/meta/0043_snapshot.json +8335 -0
  62. package/template/packages/database/drizzle/meta/_journal.json +7 -0
  63. package/template/packages/database/src/schema-notification-delivery.test.ts +15 -6
  64. package/template/packages/database/src/schema-workflow-notification.test.ts +44 -29
  65. package/template/scripts/dev-adk-web.ts +107 -0
@@ -8,12 +8,22 @@ describe("平台任务 Manifest Adapter", () => {
8
8
  const definitions = createPlatformTaskDefinitions(
9
9
  createFoundationSnapshot().features,
10
10
  {
11
+ // @windy-module system.bulk-data begin
11
12
  bulkDataDispatch: () => ({ claimed: false }),
12
13
  bulkDataCleanup: () => ({ deleted: 0 }),
14
+ // @windy-module system.bulk-data end
15
+ // @windy-module system.storage begin
13
16
  storageCleanup: () => ({ deleted: 0 }),
17
+ // @windy-module system.storage end
18
+ // @windy-module system.workflow begin
14
19
  workflowTimeoutDispatch: () => ({ claimed: false }),
20
+ // @windy-module system.workflow end
21
+ // @windy-module system.data-governance begin
15
22
  governedExportDispatch: () => ({ claimed: false }),
23
+ // @windy-module system.data-governance end
24
+ // @windy-module system.notification begin
16
25
  notificationDispatch: () => ({ claimed: false }),
26
+ // @windy-module system.notification end
17
27
  },
18
28
  );
19
29
 
@@ -22,20 +32,26 @@ describe("平台任务 Manifest Adapter", () => {
22
32
  .tasks.map(({ key }) => key)
23
33
  .sort(),
24
34
  );
35
+ // @windy-module system.bulk-data begin
25
36
  expect(
26
37
  definitions
27
38
  .find(({ key }) => key === "system.bulk-data.dispatch")
28
39
  ?.isIdleResult?.({ claimed: false }),
29
40
  ).toBe(true);
41
+ // @windy-module system.bulk-data end
42
+ // @windy-module system.data-governance begin
30
43
  expect(
31
44
  definitions
32
45
  .find(({ key }) => key === "system.governed-export.dispatch")
33
46
  ?.isIdleResult?.({ claimed: false }),
34
47
  ).toBe(true);
48
+ // @windy-module system.data-governance end
49
+ // @windy-module system.notification begin
35
50
  expect(
36
51
  definitions
37
52
  .find(({ key }) => key === "system.notification.dispatch")
38
53
  ?.isIdleResult?.({ claimed: false }),
39
54
  ).toBe(true);
55
+ // @windy-module system.notification end
40
56
  });
41
57
  });
@@ -22,7 +22,7 @@ COPY packages/server-sdk/package.json packages/server-sdk/package.json
22
22
  COPY packages/shared/package.json packages/shared/package.json
23
23
  COPY packages/storage/package.json packages/storage/package.json
24
24
  # windy-copy:deps end
25
- RUN bun install --frozen-lockfile
25
+ RUN --mount=type=cache,id=windy-bun-1.3.14,target=/root/.bun/install/cache,sharing=shared bun -e 'const child=Bun.spawn(["bun","install","--frozen-lockfile","--no-progress"],{stdin:"inherit",stdout:"inherit",stderr:"inherit"});const heartbeat=setInterval(()=>console.log("[windy] Bun 依赖安装仍在进行"),15000);const exitCode=await child.exited;clearInterval(heartbeat);process.exit(exitCode)'
26
26
 
27
27
  FROM deps AS build
28
28
  # windy-copy:build begin
@@ -0,0 +1,56 @@
1
+ services:
2
+ server:
3
+ environment:
4
+ PLATFORM_ENABLE_AI: "true"
5
+ WINDY_ADK_DEBUG_TOKEN: ${ADK_DEBUG_PLATFORM_TOKEN:?请先由 create-windy doctor 修复本地调试 Token}
6
+ WINDY_ADK_DEBUG_OPERATION_KEY: ${WINDY_ADK_DEBUG_OPERATION_KEY:?项目尚未选择 ADK Web 调试 Operation}
7
+ WINDY_ADK_DEBUG_DATA_ACTOR_ID: ${WINDY_ADK_DEBUG_DATA_ACTOR_ID:?项目尚未选择 ADK Web 真实开发数据用户}
8
+
9
+ adk-web-debug:
10
+ profiles: ["agent-debug"]
11
+ build:
12
+ context: .
13
+ dockerfile: apps/agent-server/Dockerfile
14
+ environment:
15
+ ADK_DEBUG_PLATFORM_URL: http://server:9747
16
+ ADK_DEBUG_PLATFORM_TOKEN: ${ADK_DEBUG_PLATFORM_TOKEN:?请先由 create-windy doctor 修复本地调试 Token}
17
+ ADK_DEBUG_OPERATION_KEY: ${WINDY_ADK_DEBUG_OPERATION_KEY:?项目尚未选择 ADK Web 调试 Operation}
18
+ command:
19
+ [
20
+ "adk",
21
+ "web",
22
+ "--host",
23
+ "0.0.0.0",
24
+ "--port",
25
+ "8000",
26
+ "--no-reload",
27
+ "--session_service_uri",
28
+ "memory://",
29
+ "--artifact_service_uri",
30
+ "memory://",
31
+ "--logo-text",
32
+ "Windy ADK 真实 Run 调试",
33
+ "--logo-image-url",
34
+ "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%2F%3E",
35
+ "/app/apps/agent-server/adk_web_agents",
36
+ ]
37
+ ports:
38
+ - "127.0.0.1:8000:8000"
39
+ healthcheck:
40
+ test:
41
+ [
42
+ "CMD",
43
+ "python",
44
+ "-c",
45
+ "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/', timeout=3)",
46
+ ]
47
+ interval: 10s
48
+ timeout: 5s
49
+ retries: 5
50
+ depends_on:
51
+ server:
52
+ condition: service_started
53
+ agent-server:
54
+ condition: service_healthy
55
+ tool-sandbox:
56
+ condition: service_started
@@ -100,7 +100,7 @@ services:
100
100
  [
101
101
  "sh",
102
102
  "-c",
103
- "chmod 0700 /run/windy-tool-sandbox && chown 1000:1000 /run/windy-tool-sandbox",
103
+ "chown 0:0 /run/windy-tool-sandbox && chmod 0700 /run/windy-tool-sandbox && chown 1000:1000 /run/windy-tool-sandbox",
104
104
  ]
105
105
  network_mode: none
106
106
  read_only: true
@@ -230,44 +230,6 @@ services:
230
230
  server:
231
231
  condition: service_started
232
232
 
233
- adk-web-debug:
234
- profiles: ["agent-debug"]
235
- build:
236
- context: .
237
- dockerfile: apps/agent-server/Dockerfile
238
- command:
239
- [
240
- "adk",
241
- "web",
242
- "--host",
243
- "0.0.0.0",
244
- "--port",
245
- "8000",
246
- "--no-reload",
247
- "--session_service_uri",
248
- "memory://",
249
- "--artifact_service_uri",
250
- "memory://",
251
- "--logo-text",
252
- "Windy ADK 开发调试(无业务工具)",
253
- "--logo-image-url",
254
- "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%2F%3E",
255
- "/app/apps/agent-server/adk_web_agents",
256
- ]
257
- ports:
258
- - "127.0.0.1:8000:8000"
259
- healthcheck:
260
- test:
261
- [
262
- "CMD",
263
- "python",
264
- "-c",
265
- "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/', timeout=3)",
266
- ]
267
- interval: 10s
268
- timeout: 5s
269
- retries: 5
270
-
271
233
  web:
272
234
  build:
273
235
  context: .
@@ -654,13 +654,22 @@ type DictationEvent =
654
654
 
655
655
  ## Prompt、Debug 与 Trace
656
656
 
657
- ADK Web 只在 developer profile 启动,绑定 localhost 或受控开发网络。它不是生产 Prompt 管理或生产 Debug 控制面。
657
+ ADK Web 只在 developer profile 启动,绑定 localhost 或受控开发网络。它不是生产
658
+ Prompt 管理或生产 Debug 控制面。
658
659
 
659
660
  当前 Compose 使用独立 `agent-debug` profile,宿主机只发布
660
- `127.0.0.1:8000`。由于原生 ADK Web 没有 Southwind AI Server 签发的 Actor 与
661
- Execution Grant,它只加载一个无 Provider、无业务 Tool 的确定性调试 Agent。真实业务
662
- Operation 只能走 canonical HTTP/SSE → ADK Adapter → 受治理 Provider/Tool Host 链路;
663
- 不得把 ADK Web 的调试会话解释为生产授权或业务执行证据。
661
+ `127.0.0.1:8000`。Windy 的 `canonical_debug` 是确定性 Adapter:它把用户文本交给
662
+ project-owned 配置选择的真实文本 Operation,并通过 canonical HTTP/SSE 读取 Run、
663
+ Tool 和终态摘要。业务 Permission、Scope、Execution Grant、Provider 与 Tool Host
664
+ 继续由正式 Server/Agent Server 管理,ADK Web 不获得 Repository、Provider Key 或内部
665
+ Token。项目配置只包含 Operation Key 与当前启用的真实开发数据用户 ID,不能重复或扩大
666
+ Manifest 的权限、Scope 与 Tool 列表。数据用户用于复用其成员关系和当前 Scope。开发 Wrapper
667
+ 创建的核心主体仍是 `service_adk_debug`,权限只取该用户已有权限与 Operation/allowed
668
+ Tools 闭包的精确交集,并且只能调用 canonical Run API;审计不会把它记录成普通用户会话。
669
+
670
+ 该 Adapter 证明真实业务执行链,不等于把另一进程内的完整 ADK Agent/Workflow 图装入
671
+ 原生 ADK Web。内部节点、Prompt 版本和 replay 若要可视化,必须通过另一个版本化协议
672
+ 显式暴露。不得把开发调试会话解释为生产授权或业务执行证据。
664
673
 
665
674
  Prompt 和 Workflow 使用:
666
675
 
@@ -28,6 +28,20 @@ Manifest 当前覆盖:
28
28
 
29
29
  同一个稳定能力基础 Key 仍可分别出现在 Feature、Permission 前缀、菜单和页面入口中;“重复”按能力类别判断,避免破坏项目既定 Key 语义。
30
30
 
31
+ ## 功能注册门禁
32
+
33
+ 所有面向用户、业务或系统开放的能力必须有 Manifest 功能注册;这是一项可执行门禁,
34
+ 不是只靠文档约定的建议。每个能力注册一个 Feature 基础 Key,并声明所需动作;Feature
35
+ 用于功能目录、开关与 License,动作派生或声明同一基础 Key 下的 RBAC Permission。
36
+ 例如 `personnel.profile` 对应 `personnel.profile.read`、`personnel.profile.create`、
37
+ `personnel.profile.update` 与 `personnel.profile.delete`。
38
+
39
+ 菜单、Admin 路由、按钮、Server API、Agent Tool、后台任务、Provider、审计动作和
40
+ License 配置必须引用同一 Manifest 中已注册的 Feature/Permission。未知引用、重复 Key、
41
+ 缺少 Feature、缺少动作权限或未声明实现均在三处以同一规则 fail closed:模块安装前、
42
+ 项目构建时和服务监听前。这样权限目录与 License/Feature 目录不会依赖第二份手写 Key
43
+ 列表,也不会出现只注册页面或 API 的“幽灵功能”。
44
+
31
45
  ## 模块与 Adapter
32
46
 
33
47
  - `@southwind-ai/modules` 是深 Module:拥有声明类型、依赖排序、引用规则和组合验证,不依赖 Elysia 或 Vue。
@@ -74,19 +74,5 @@ const developmentAuth = developmentAuthStatus();
74
74
  布局和内容,但不应绕过 Router 的 `guestOnly` Guard、首次改密跳转或平台认证
75
75
  composable。
76
76
 
77
- ## 从 0.2.32 升级
78
-
79
- 可直接在现有业务改动上执行:更新不会检查无关工作区改动;若平台变更与同一共享文件
80
- 重叠,计划会报告三方合并冲突:
81
-
82
- ```bash
83
- bunx --bun create-windy@latest update --check --cwd .
84
- bunx --bun create-windy@latest update --dry-run --cwd .
85
- bunx --bun create-windy@latest update --cwd .
86
- bun install
87
- ```
88
-
89
- 计划中应看到 selector 以 `project-owned` 新增,Router 通过三方合并接入 selector。
90
- 若旧业务登录页曾放在 `apps/web/src/pages/auth/LoginPage.vue` 或直接改 Router,应将
91
- 业务 SFC 移到 `apps/web/src/app/auth/`,改 selector 指向它,再恢复受管文件。后续升级
92
- 会保留 selector 和业务 SFC。
77
+ 该 selector 与业务 SFC 属于 project-owned seam。后续 Starter 更新必须保留它们,
78
+ Router 只通过稳定 selector 组合,不把业务登录页重新纳入受管文件。
@@ -124,6 +124,8 @@ Manifest 的 Operation limits 同时包含输入、执行时间和输出上限
124
124
  ```dotenv
125
125
  PLATFORM_ENABLE_AI=true
126
126
  AGENT_SERVER_INTERNAL_TOKEN=<至少 32 字符的随机值>
127
+ ADK_DEBUG_PLATFORM_TOKEN=<create-windy 自动生成的独立随机值>
128
+ AGENT_RUN_ENCRYPTION_SECRET=<至少 32 字符的独立随机值>
127
129
  AI_ARTIFACT_ENCRYPTION_SECRET=<至少 32 字符的随机值>
128
130
  QWEN_API_URL=https://dashscope-intl.aliyuncs.com/compatible-mode/v1
129
131
  QWEN_API_KEY=<服务端密钥>
@@ -145,9 +147,13 @@ AI_PROVIDER_AUDIT_RETENTION_DAYS=90
145
147
  bun run dev:agent
146
148
  ```
147
149
 
148
- `create-windy` 会为新的本地项目同时生成内部 Token 与 Artifact 加密 Secret。已有项目
149
- 升级后必须补齐 `AI_ARTIFACT_ENCRYPTION_SECRET`;它只用于平台签发短时 opaque
150
- ArtifactRef,不得复用 Provider Key。Vision Deployment 需把能力与输入模态显式扩展为
150
+ `create-windy` 会为新的本地项目分别生成内部 Token、ADK 调试 Token、Run Store 加密
151
+ Secret 与 Artifact 加密 Secret,四者不得互相复用。已有项目升级时 updater 只会为
152
+ 普通 `.env` 原子补齐缺失或空的受管 Secret,不覆盖任何非空值;符号链接、重复定义或
153
+ 缺失的 `.env` 会停止自动修复并由 `windy doctor` 报告,避免覆盖外部 Secret Store 或
154
+ 已有部署配置。
155
+ `AI_ARTIFACT_ENCRYPTION_SECRET` 只用于平台签发短时 opaque ArtifactRef,不得复用
156
+ Provider Key。Vision Deployment 需把能力与输入模态显式扩展为
151
157
  `text-generation,vision` 和 `text,image`,详见
152
158
  [Vision AI Runtime](./vision-ai-runtime.md)。Document profile 的配置与调用链见
153
159
  [Document AI Runtime](./document-ai-runtime.md)。
@@ -187,25 +193,60 @@ Agent profile 还必须挂载 `AI_PROVIDER_AUDIT_STORE_PATH`。create-windy Star
187
193
 
188
194
  ## Developer-only ADK Web
189
195
 
190
- ADK Web 使用独立 profile 启动:
196
+ ADK Web 使用独立 profile 调试真实 canonical Run。项目先在 project-owned 的
197
+ `apps/agent-server/project/adk-web-debug.json` 选择一个文本 Operation:
198
+
199
+ ```json
200
+ {
201
+ "schemaVersion": 1,
202
+ "textOperation": {
203
+ "key": "your-module.copilot.global",
204
+ "dataActorId": "user_dev_admin"
205
+ }
206
+ }
207
+ ```
208
+
209
+ 该 v1 接缝只承诺 `{ "question": string } -> { "text": string }`。Operation、Permission、
210
+ Feature、Tool、Scope、Provider Policy 和输入输出 Schema 仍由业务 Module Manifest
211
+ 声明;`dataActorId` 必须是项目当前存在且启用的开发用户,用于复用真实案件成员关系与
212
+ 当前 Scope。Windy 不接受第二份权限、Scope 或 Tool 列表。用户不存在、权限闭包不足、
213
+ Operation 未知或不符合文本契约时明确拒绝,不回退成伪造回答。
214
+
215
+ 一条命令会同时启动 Server、Tool Sandbox、Agent Server 与 ADK Web:
191
216
 
192
217
  ```bash
193
218
  bun run dev:adk-web
194
219
  ```
195
220
 
196
221
  访问地址为 `http://127.0.0.1:8000`。Compose 服务在容器内监听 `0.0.0.0:8000`,
197
- 但端口只发布到宿主机 `127.0.0.1`。普通 `dev`、`agent` profile 和生产 Compose 均不
198
- 包含该服务。
199
-
200
- 该界面明确显示“Windy ADK 开发调试(无业务工具)”,并只加载一个确定性 Agent:
201
-
202
- - 不读取 `QWEN_*`、Provider URL/Secret 或 `AGENT_SERVER_INTERNAL_TOKEN`;
203
- - 不注册业务 Tool,不连接 Tool Host 或业务 Repository;
204
- - 不接收 Actor、Scope 或 Execution Grant;
205
- - 只用于观察 ADK Web、Session 和 Event,不代表真实业务 Operation。
206
-
207
- 真实业务调试必须继续从 canonical Run API 发起并提供 Southwind AI Server 签发的
208
- Execution Grant;不能通过 ADK Web 补造或绕过这条授权链。
222
+ 但端口只发布到宿主机 `127.0.0.1`。启动脚本读取 project-owned JSON,将唯一
223
+ Operation Key 与真实开发数据用户注入 Windy-owned `docker-compose.agent-debug.yml`;
224
+ 普通 `dev`、`agent` profile 和生产 Compose 均不包含该服务或调试身份。
225
+
226
+ 选择 `canonical_debug` 应用后,每条问题由确定性 dispatcher 交给正式
227
+ `POST /api/agent/runs`,再读取 SSE 与最终结果:
228
+
229
+ - `create-windy` 自动生成独立随机 `ADK_DEBUG_PLATFORM_TOKEN`,无需复制登录 Token;
230
+ - Server 只在 `NODE_ENV=development` 且加载调试 Compose Wrapper 时创建专用
231
+ `service_adk_debug` 身份;该 Token 只在 canonical Run 路径生效,不能访问 Foundation、
232
+ Admin 或其它 Server API;即使与普通会话 Token 误碰撞也被视为保留调试凭据,不会
233
+ 回退普通认证;
234
+ - Server 每次从用户目录解析 `dataActorId`,复用该用户当前 Scope 和业务成员 ID;只在
235
+ 用户本身拥有完整权限闭包时,向 Module 投影该真实用户。核心 Run、Token 和审计主体仍
236
+ 是 `service_adk_debug`,不会冒充普通用户会话;
237
+ - 专用身份的权限精确收窄到所选 Operation Permission 与其 `allowedToolKeys` 的
238
+ Permission 闭包,并在 Tool Host 重新解析用户和复核 Scope;不能切换到其它 Operation;
239
+ - ADK Web 容器不读取 `QWEN_*`、Provider Secret、`AGENT_SERVER_INTERNAL_TOKEN`,
240
+ 不连接业务 Repository 或 Tool Host;
241
+ - 正式 Server 仍签发 Actor、Scope 和短时 Execution Grant,Agent Server 仍按
242
+ `allowedToolKeys` 执行并审计;
243
+ - 事件摘要最多 64 条,单个 SSE frame 最大 64 KiB;流式 `output.delta` 只显示计数,
244
+ 客户端边读边压缩,不先把完整事件流保存在内存。
245
+
246
+ `safe_debug` 仍保留为不接业务的纯 ADK UI smoke。`canonical_debug` 能验证真实数据、
247
+ 模型与 Tool 链,但 ADK Web 显示的是“dispatcher → canonical Run”,不是另一进程内部
248
+ 完整的 Prompt/Agent/Workflow 节点图。完整 workflow replay 需要另行定义版本化调试
249
+ 协议,不能从当前事件摘要推断。
209
250
 
210
251
  ## 客户端协议
211
252
 
@@ -41,9 +41,10 @@ Worker 重启会轮换密钥。Client 在连接中断或密钥轮换竞态时用
41
41
  读取宿主路径或依赖导入时初始化的外部连接。
42
42
 
43
43
  命名 Volume 初始由 root 持有。一次性的 `tool-sandbox-init` 仅挂载该 IPC Volume,
44
- 使用 `CHOWN` capability 将目录交给镜像内固定的 `bun` 用户后退出;它同样无网络、
45
- 无 Secret、只读 RootFS。常驻 Worker 与 Server 随后以相同非 root UID 访问权限为
46
- `0700` 的目录,HMAC 密钥文件权限为 `0600`。
44
+ 使用 `CHOWN` capability 先临时取回目录所有权,收紧为 `0700`,再交给镜像内固定的
45
+ `bun` 用户后退出;这个顺序对首次创建和已由 `bun` 持有的残留 Volume 都是幂等的,
46
+ 无需扩大为 `FOWNER` capability。它同样无网络、无 Secret、只读 RootFS。常驻 Worker
47
+ 与 Server 随后以相同非 root UID 访问该目录,HMAC 密钥文件权限为 `0600`。
47
48
 
48
49
  ## 失败与审计
49
50
 
@@ -79,9 +79,19 @@ License 上传仍只记录候选摘要、可识别的 License ID、公钥 ID 和
79
79
  2. Server 启动、以及后续任一审计写入恢复成功时,会顺序重放队列。
80
80
  3. PostgreSQL 以事件 ID 幂等插入,连接超时后即使数据库实际已提交,重放也不会生成重复事件。
81
81
  4. 只有成功持久化的前缀会从队列移除;首次失败事件及其后续事件继续保留。
82
+ 5. 启动读取队列遇到权限、JSON 格式或其它文件 I/O 错误时,Server 仍以审计
83
+ `degraded` 状态启动并告警,不让审计恢复文件阻断全部无关业务。
84
+ 未知 `formatVersion`、缺失或非数组 `events`、损坏的事件结构都属于格式错误;
85
+ 平台不会把它们静默解释为空队列。
86
+ 6. 恢复故障只有在队列重新成功读取、且非空队列成功原子改写后才清除;单次普通审计
87
+ 成功写入 PostgreSQL 不能把仍不可读的恢复队列误报为健康。
82
88
 
83
89
  恢复文件所在目录必须挂载到持久卷,并只允许 Server 运行账号访问。不要把它放到容器临时层、共享静态目录或备份公开目录。
84
90
 
91
+ 启动降级不改变两类写入策略:普通请求继续按 best-effort 审计语义执行;关键动作优先
92
+ 写 PostgreSQL,主库持续失败时再写恢复队列。只有主库与恢复队列同时不可用时,关键
93
+ 动作仍然 fail closed,不会因为 Server 已监听端口而降级放行。
94
+
85
95
  ## 可观测状态
86
96
 
87
97
  `GET /api/health` 的 `audit` 字段返回非敏感运行状态:
@@ -97,7 +107,9 @@ License 上传仍只记录候选摘要、可识别的 License ID、公钥 ID 和
97
107
  "dropped": 1,
98
108
  "lastFailureAt": "2026-07-10T08:00:00.000Z",
99
109
  "lastSuccessAt": "2026-07-10T07:59:00.000Z",
100
- "alert": "关键审计已进入可恢复队列"
110
+ "recoveryFailure": "permission-denied",
111
+ "lastRecoveryFailureAt": "2026-07-10T08:00:01.000Z",
112
+ "alert": "关键审计恢复队列不可用:文件权限不足"
101
113
  }
102
114
  ```
103
115
 
@@ -105,6 +117,10 @@ License 上传仍只记录候选摘要、可识别的 License ID、公钥 ID 和
105
117
  - `retries` 是已经执行的重试次数。
106
118
  - `queued` 是当前仍待恢复的关键事件数。
107
119
  - `dropped` 是 PostgreSQL 持续失败后未进入持久队列的普通事件数。
120
+ - `recoveryFailure` 是不包含路径或事件正文的稳定故障分类:
121
+ `permission-denied`、`invalid-format`、`io-error` 或 `unknown`。
122
+ - `lastRecoveryFailureAt` 是最近一次恢复队列操作失败的时间;队列不可读时
123
+ `queued=0` 只代表没有已知计数,不能解释为文件内没有待恢复事件。
108
124
  - `state=degraded` 或 `queued/dropped > 0` 应触发运维告警。
109
125
 
110
126
  进入降级状态时 Server 同时写入结构化 error 日志,日志只包含上述状态,不包含事件正文。
@@ -130,10 +146,22 @@ License 上传仍只记录候选摘要、可识别的 License ID、公钥 ID 和
130
146
 
131
147
  1. 检查 `/api/health` 的 `audit` 状态和 Server 的“审计持久化进入降级状态”日志。
132
148
  2. 检查 PostgreSQL 连接、连接池容量、`audit_logs` 表和磁盘容量。
133
- 3. 确认恢复文件所在持久卷可写、权限为 Server 账号私有,且队列未达到上限。
134
- 4. 修复 PostgreSQL 后执行一个普通的已审计请求;写入成功会触发队列恢复。也可以重启 Server,在启动阶段恢复。
135
- 5. 只有 `queued=0` 且状态恢复为 `healthy` 后才解除告警。`dropped` 是累计计数,不会自动归零,应结合故障窗口内的进程内事件和业务日志进行人工补查。
149
+ 3. 对恢复文件或持久卷先做只读备份或卷快照,再进行权限和内容处置。平台不会自动
150
+ 丢弃、清空或覆盖不可读、格式损坏的队列,因为其中可能包含尚未落库的关键事件。
151
+ 4. `permission-denied` 时把目录和文件所有权恢复为 Server 运行账号,保持文件
152
+ `0600`、目录不对其它账号开放;不要用全局可写权限绕过问题。
153
+ 5. `invalid-format` 时保留原始备份,把损坏文件人工隔离到 Server 不会继续写入的位置,
154
+ 离线核对可恢复事件后再修复或重建有效队列。未经审阅不要直接截断文件或创建空文件
155
+ 覆盖原内容。
156
+ 6. `io-error` 时检查挂载、只读文件系统、磁盘容量和底层存储健康;修复后执行一个普通
157
+ 的已审计请求触发恢复,也可以重启 Server 在启动阶段恢复。
158
+ 7. 只有 `recoveryFailure` 已清除、`queued=0` 且状态恢复为 `healthy` 后才解除告警。
159
+ `dropped` 是累计计数,不会自动归零,应结合故障窗口内的进程内事件和业务日志进行
160
+ 人工补查。
136
161
 
137
162
  ## 验证覆盖
138
163
 
139
- 自动测试覆盖瞬时故障后成功、退避上限、普通事件持续失败、关键事件入队、数据库恢复重放、数据库与队列同时失败、队列内容深层脱敏、License 损坏文件恢复上传入队,以及 PostgreSQL 事件 ID 幂等重放。真实 PostgreSQL 集成测试使用 `AUDIT_TEST_DATABASE_URL` 显式启用。
164
+ 自动测试覆盖瞬时故障后成功、退避上限、普通事件持续失败、关键事件入队、数据库恢复
165
+ 重放、启动读取权限错误和损坏 JSON 后降级、普通写入不得误清恢复故障、数据库与队列
166
+ 同时失败、队列内容深层脱敏、License 损坏文件恢复上传入队,以及 PostgreSQL 事件 ID
167
+ 幂等重放。真实 PostgreSQL 集成测试使用 `AUDIT_TEST_DATABASE_URL` 显式启用。
@@ -97,7 +97,15 @@ Server 会幂等创建默认开发管理员和 `developer` 角色;仅当默认
97
97
  - `WINDY_DB_USER_TOKEN_PREFIX` 只能修改开发直通前缀。
98
98
  - `NODE_ENV=production` 时,上述 Token 与 `user:` 直通无条件关闭,即使环境变量误配置也不会生效。
99
99
 
100
- Web 不提供默认开发 Token。只有 Vite 开发模式同时设置 `VITE_DEV_AUTH_BYPASS=1` 和非空 `VITE_DEV_ADMIN_TOKEN` 时,前端才会携带显式 Bearer Token,并在登录页和应用壳持续显示警告;生产构建无条件忽略这两个变量。完成一次真实密码登录后,当前页面生命周期内会停用开发 Token,后续请求只使用 Cookie。Docker Compose 不再注入默认 `dev-admin-token`。
100
+ Web 不提供默认开发 Token。只有 Vite 开发模式同时设置 `VITE_DEV_AUTH_BYPASS=1` 和非空 `VITE_DEV_ADMIN_TOKEN` 时,前端才会携带显式 Bearer Token,并在登录页和应用壳持续显示警告;生产构建无条件忽略这两个变量。完成一次真实密码登录后,当前页面生命周期内会停用开发 Token,后续请求只使用 Cookie。
101
+
102
+ 选择 Agent 模块的新项目会在本机 `.env` 自动生成独立随机
103
+ `ADK_DEBUG_PLATFORM_TOKEN`。只有 `bun run dev:adk-web` 加载的开发 Compose Wrapper
104
+ 会把它作为 `WINDY_ADK_DEBUG_TOKEN` 注入 Server,并注入 localhost-only ADK Web;它
105
+ 不会复用 `WINDY_DEV_ADMIN_TOKEN`,也不会启用固定 `dev-admin-token` 或 `user:` 直通。
106
+ Server 仅在 canonical Agent Run 路径把它解析成 `service_adk_debug`,权限收敛为项目
107
+ 选择的 Operation 与 allowed Tools 权限闭包。该 Token 不进入 Web、Agent Server、
108
+ Provider 或生产 Compose;非 development Server 即使误配也会拒绝。
101
109
 
102
110
  未配置 `DATABASE_URL` 时不会注册真实登录 API;只允许显式的非生产开发直通,不存在内存生产账号或默认生产密码。
103
111
 
@@ -19,7 +19,7 @@
19
19
  "dev:document": "PLATFORM_ENABLE_AI=true DOCUMENT_SERVER_URL=http://document-server:8090 docker compose --profile agent --profile document up --build",
20
20
  "dev:speech": "PLATFORM_ENABLE_AI=true SPEECH_SERVER_URL=http://speech-server:8091 docker compose --profile agent --profile speech up --build",
21
21
  "dev:embedding": "PLATFORM_ENABLE_AI=true EMBEDDING_SERVER_URL=http://embedding-server:8092 docker compose --profile agent --profile embedding up --build",
22
- "dev:adk-web": "docker compose --profile agent-debug up --build adk-web-debug",
22
+ "dev:adk-web": "bun run scripts/dev-adk-web.ts",
23
23
  "dev:search": "OPENSEARCH_URL=http://opensearch:9200 docker compose --profile search up --build",
24
24
  "dev:license": "bun run --cwd apps/license dev",
25
25
  "dev:license:docker": "docker compose --profile license up license",
@@ -50,7 +50,7 @@
50
50
  "ops:db": "bun run scripts/production-database.ts",
51
51
  "ops:release": "bun run scripts/production-release.ts",
52
52
  "test:merge": "bun run scripts/merge-gate.ts",
53
- "release:verify": "bun run scripts/release-verify.ts",
53
+ "release:verify": "bun run scripts/release-verify.ts --full",
54
54
  "release:candidate": "bun run scripts/release-candidate.ts",
55
55
  "release:verify:full": "bun run scripts/release-verify.ts --full",
56
56
  "test:packages:pack": "bun run scripts/platform-package-release.ts",
@@ -70,7 +70,8 @@
70
70
  "test": "bun test --path-ignore-patterns='packages/create-windy/template/**' && bun run --cwd apps/web test"
71
71
  },
72
72
  "lint-staged": {
73
- "*.{cjs,css,cts,html,js,json,jsonc,jsx,less,md,mdx,mjs,mts,scss,ts,tsx,vue,yaml,yml}": [
73
+ "*.{css,html,json,jsonc,less,md,mdx,scss,yaml,yml}": "oxfmt --write",
74
+ "*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}": [
74
75
  "oxfmt --write",
75
76
  "oxlint"
76
77
  ]