draftgo-cli 4.0.23 → 4.0.25

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 (38) hide show
  1. package/README.md +3 -1
  2. package/bin/draftgo.js +8 -8
  3. package/package.json +72 -72
  4. package/resources/custom-service-sdk/auth_test.go +1 -1
  5. package/resources/custom-service-sdk/manifest.json +11 -11
  6. package/resources/custom-service-sdk/platform.go +10 -3
  7. package/resources/custom-service-sdk/resources.go +1 -0
  8. package/resources/custom-service-sdk/resources_scope_test.go +10 -5
  9. package/resources/custom-service-sdk/sdk.go +4 -3
  10. package/resources/skill/SKILL.md +1 -1
  11. package/resources/skill/manifest.json +1 -1
  12. package/resources/skill/references/aihub.md +74 -74
  13. package/resources/skill/references/app-api.md +78 -78
  14. package/resources/skill/references/architecture.md +40 -40
  15. package/resources/skill/references/checkout.md +105 -105
  16. package/resources/skill/references/custom-services.md +4 -4
  17. package/resources/skill/references/data.md +168 -168
  18. package/resources/skill/references/methods.md +3 -0
  19. package/resources/skill/references/modules.md +47 -47
  20. package/resources/skill/references/runtime.md +95 -96
  21. package/resources/skill/story/SKILL.md +264 -264
  22. package/src/commands/help.js +72 -71
  23. package/src/commands/listTargets.js +12 -12
  24. package/src/commands/status.js +2 -2
  25. package/src/commands/uninstall.js +45 -45
  26. package/src/commands/update.js +24 -30
  27. package/src/customServices.js +5 -4
  28. package/src/detect.js +14 -14
  29. package/src/fsx.js +67 -67
  30. package/src/index.js +25 -25
  31. package/src/localRuntime/detect.js +76 -76
  32. package/src/localRuntime/mysqlClient.js +138 -138
  33. package/src/logger.js +37 -37
  34. package/src/mcp/client.js +586 -595
  35. package/src/mcp/hosts.js +520 -520
  36. package/src/mcp/protocol.js +167 -167
  37. package/src/prompt.js +94 -94
  38. package/src/updateCheck.js +16 -16
package/README.md CHANGED
@@ -75,6 +75,8 @@ draftgo connect [target...]
75
75
 
76
76
  `draftgo status` 会通过只读 MCP/API 诊断显示服务连接健康状态、服务版本和当前用户 API Key 的 `platform`/`space` 上下文;`--output json` 适合读取 `connection.health`、`workspace_id` 与 `space_id`。
77
77
 
78
+ `draftgo update` 和 `draftgo update all` 只刷新项目中已经安装的 DraftGo Skill,不会因为检测到 `.cursor`、`AGENTS.md` 等工具痕迹而新增配置。显式指定 target 时(例如 `draftgo update cursor`),如果该 Skill 尚未安装,会按你的明确请求直接安装并更新它;首次接入也可以使用 `draftgo init <target>`。
79
+
78
80
  ### 发现与正文
79
81
 
80
82
  ```text
@@ -172,7 +174,7 @@ draftgo data-range list
172
174
  `role` 管理无作用域的 Role 模板;授权使用 `grant` 创建带 `platform` 或 `space` 范围的 AccessGrant。工作区成员关系不直接授予权限。资源归属由服务端持久化 ResourceOwnership 决定,DB 的 DataRange
173
175
  只在该范围内应用 `none`、`owner` 或 `all` 记录策略。交互式 CLI、MCP 和普通 HTTP API 均使用当前用户的
174
176
  API Key,并始终按该用户的 AccessGrant 授权;API Key 不能创建或伪造服务身份。CI、定时任务和共享服务等
175
- 无人值守自动化使用平台提供的独立服务凭据通道及服务主体 AccessGrant,不复用个人 API Key。
177
+ 无人值守自动化由运行时使用 system 身份;定时和事件处理器没有调用者,必须在代码中显式使用 `ctx.Admin.*`,不创建服务主体 AccessGrant,也不复用个人 API Key。
176
178
 
177
179
  ### 交付与记录
178
180
 
package/bin/draftgo.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
- Promise.resolve(require('../src/index.js').run(process.argv.slice(2)))
5
- .then((code) => { process.exitCode = Number(code) || 0; })
6
- .catch((err) => {
7
- console.error(err && err.stack ? err.stack : String(err));
8
- process.exitCode = 1;
9
- });
2
+ 'use strict';
3
+
4
+ Promise.resolve(require('../src/index.js').run(process.argv.slice(2)))
5
+ .then((code) => { process.exitCode = Number(code) || 0; })
6
+ .catch((err) => {
7
+ console.error(err && err.stack ? err.stack : String(err));
8
+ process.exitCode = 1;
9
+ });
package/package.json CHANGED
@@ -1,72 +1,72 @@
1
- {
2
- "name": "draftgo-cli",
3
- "version": "4.0.23",
4
- "description": "Install and manage the DraftGo skill across AI coding agents (Claude Code, Codex, Cursor, Windsurf, Antigravity, Copilot, Gemini, Kiro, Pi).",
5
- "bin": {
6
- "draftgo": "bin/draftgo.js"
7
- },
8
- "main": "src/index.js",
9
- "engines": {
10
- "node": ">=20.19"
11
- },
12
- "files": [
13
- "bin/",
14
- "src/",
15
- "resources/",
16
- "!resources/**/__pycache__/**",
17
- "!resources/**/*.py[cod]",
18
- "LICENSE",
19
- "README.md"
20
- ],
21
- "keywords": [
22
- "draftgo",
23
- "skill",
24
- "cli",
25
- "claude-code",
26
- "codex",
27
- "cursor",
28
- "windsurf",
29
- "antigravity",
30
- "copilot",
31
- "gemini",
32
- "kiro",
33
- "pi"
34
- ],
35
- "author": "draftgo <cabinai@163.com> (https://github.com/draftgo)",
36
- "license": "MIT",
37
- "homepage": "https://github.com/draftgo/draftgo-cli#readme",
38
- "bugs": {
39
- "url": "https://github.com/draftgo/draftgo-cli/issues"
40
- },
41
- "repository": {
42
- "type": "git",
43
- "url": "git+https://github.com/draftgo/draftgo-cli.git"
44
- },
45
- "scripts": {
46
- "lint": "node scripts/check-syntax.js",
47
- "validate:skill": "node scripts/validate-skill.js",
48
- "sync:custom-service-sdk": "node scripts/sync-custom-service-sdk.js",
49
- "build:release": "node scripts/build-release.js",
50
- "verify:package": "node scripts/verify-package.js",
51
- "test": "npm run lint && npm run validate:skill && npm run verify:package && npm run test:unit && npm run test:components && npm run test:capabilities && npm run test:workflow && npm run test:worklog && npm run test:mcp && npm run test:worktree && npm run test:integration && npm run test:local && npm run test:e2e",
52
- "test:unit": "node tests/unit.js",
53
- "test:components": "node --test tests/components.test.js",
54
- "test:capabilities": "node --test tests/capabilities.test.js",
55
- "test:workflow": "node tests/workflow.test.js",
56
- "test:worklog": "node tests/worklog.test.js",
57
- "test:mcp": "node tests/mcp.test.js",
58
- "test:worktree": "node tests/worktree.test.js",
59
- "test:integration": "node tests/integration.test.js",
60
- "test:local": "node tests/local-runtime.js",
61
- "test:e2e": "node tests/e2e.js"
62
- },
63
- "dependencies": {
64
- "@grpc/grpc-js": "1.14.1",
65
- "adm-zip": "0.5.16",
66
- "parse5": "6.0.1",
67
- "playwright-core": "1.61.1"
68
- },
69
- "directories": {
70
- "test": "tests"
71
- }
72
- }
1
+ {
2
+ "name": "draftgo-cli",
3
+ "version": "4.0.25",
4
+ "description": "Install and manage the DraftGo skill across AI coding agents (Claude Code, Codex, Cursor, Windsurf, Antigravity, Copilot, Gemini, Kiro, Pi).",
5
+ "bin": {
6
+ "draftgo": "bin/draftgo.js"
7
+ },
8
+ "main": "src/index.js",
9
+ "engines": {
10
+ "node": ">=20.19"
11
+ },
12
+ "files": [
13
+ "bin/",
14
+ "src/",
15
+ "resources/",
16
+ "!resources/**/__pycache__/**",
17
+ "!resources/**/*.py[cod]",
18
+ "LICENSE",
19
+ "README.md"
20
+ ],
21
+ "keywords": [
22
+ "draftgo",
23
+ "skill",
24
+ "cli",
25
+ "claude-code",
26
+ "codex",
27
+ "cursor",
28
+ "windsurf",
29
+ "antigravity",
30
+ "copilot",
31
+ "gemini",
32
+ "kiro",
33
+ "pi"
34
+ ],
35
+ "author": "draftgo <cabinai@163.com> (https://github.com/draftgo)",
36
+ "license": "MIT",
37
+ "homepage": "https://github.com/draftgo/draftgo-cli#readme",
38
+ "bugs": {
39
+ "url": "https://github.com/draftgo/draftgo-cli/issues"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/draftgo/draftgo-cli.git"
44
+ },
45
+ "scripts": {
46
+ "lint": "node scripts/check-syntax.js",
47
+ "validate:skill": "node scripts/validate-skill.js",
48
+ "sync:custom-service-sdk": "node scripts/sync-custom-service-sdk.js",
49
+ "build:release": "node scripts/build-release.js",
50
+ "verify:package": "node scripts/verify-package.js",
51
+ "test": "npm run lint && npm run validate:skill && npm run verify:package && npm run test:unit && npm run test:components && npm run test:capabilities && npm run test:workflow && npm run test:worklog && npm run test:mcp && npm run test:worktree && npm run test:integration && npm run test:local && npm run test:e2e",
52
+ "test:unit": "node tests/unit.js",
53
+ "test:components": "node --test tests/components.test.js",
54
+ "test:capabilities": "node --test tests/capabilities.test.js",
55
+ "test:workflow": "node tests/workflow.test.js",
56
+ "test:worklog": "node tests/worklog.test.js",
57
+ "test:mcp": "node tests/mcp.test.js",
58
+ "test:worktree": "node tests/worktree.test.js",
59
+ "test:integration": "node tests/integration.test.js",
60
+ "test:local": "node tests/local-runtime.js",
61
+ "test:e2e": "node tests/e2e.js"
62
+ },
63
+ "dependencies": {
64
+ "@grpc/grpc-js": "1.14.1",
65
+ "adm-zip": "0.5.16",
66
+ "parse5": "6.0.1",
67
+ "playwright-core": "1.61.1"
68
+ },
69
+ "directories": {
70
+ "test": "tests"
71
+ }
72
+ }
@@ -38,7 +38,7 @@ func TestAuthAssertionsUseHostAuthorization(t *testing.T) {
38
38
  }
39
39
 
40
40
  client.err = errors.New("denied by host")
41
- if err := draftgo.Auth.RequireAdmin(); err == nil || err.Error() != "denied by host" {
41
+ if err := draftgo.Auth.RequireAdmin(); err == nil || err.Error() != "require_admin failed: denied by host" {
42
42
  t.Fatalf("host denial was not returned: %v", err)
43
43
  }
44
44
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "module": "draftgo/sdk",
4
- "fingerprint": "94d0b5064c3d5c3d742ddf2856ed6e37196ff81bd16696aa88cd8267c3f57889",
4
+ "fingerprint": "4d0fc5d9b9dd8e0f3332fc1f61c544579d1d0bd1c96b39bc1a25006bb172bcc3",
5
5
  "files": [
6
6
  {
7
7
  "path": "ai.go",
@@ -15,8 +15,8 @@
15
15
  },
16
16
  {
17
17
  "path": "auth_test.go",
18
- "sha256": "f0a02db739c8c5fa9d2856c59bd7ec823194cae50c1445f7b1340ce3dc0a616f",
19
- "bytes": 1604
18
+ "sha256": "2905126322a678df82289ebf13c15f6595fb8def2f98cbd5bb53c406bf5ac31c",
19
+ "bytes": 1626
20
20
  },
21
21
  {
22
22
  "path": "billing.go",
@@ -35,8 +35,8 @@
35
35
  },
36
36
  {
37
37
  "path": "platform.go",
38
- "sha256": "deb0ab4a91dbb9a1f07974982ffb218d89f719b3691cf5e9a955abcc86a6d2ba",
39
- "bytes": 11923
38
+ "sha256": "cc696f69c3a8efe093968ea4f6bfea1f257944ebcd83591b5ffd75ae85a8b583",
39
+ "bytes": 12182
40
40
  },
41
41
  {
42
42
  "path": "platform_logger_test.go",
@@ -50,8 +50,8 @@
50
50
  },
51
51
  {
52
52
  "path": "resources.go",
53
- "sha256": "e633aa1431beddd25b2c999370a770f77c3de7a2d7123687eca75c9ed0d3838f",
54
- "bytes": 9708
53
+ "sha256": "78f465a598ace6dc2858fea4a448488d195a5d1a8dd3452465d73ce9727b6a72",
54
+ "bytes": 9761
55
55
  },
56
56
  {
57
57
  "path": "resources_billing_test.go",
@@ -65,13 +65,13 @@
65
65
  },
66
66
  {
67
67
  "path": "resources_scope_test.go",
68
- "sha256": "bba3243f348e9418888f37ee2d5b487fc26a31e4a646e75bef46d5d807938cee",
69
- "bytes": 3592
68
+ "sha256": "f9b19b592c0b2ddc0564715828fd840c974216f8e99e9690af43f2cf4a0d7fb5",
69
+ "bytes": 3870
70
70
  },
71
71
  {
72
72
  "path": "sdk.go",
73
- "sha256": "b67db621642c389a2000e52b1640bacccf23a6d94f1fdcc1989e6a4244821924",
74
- "bytes": 8224
73
+ "sha256": "18872d1f8535cdba53ddb3f487792cf74f10035f22a3023ec5cff3c3e597a8c0",
74
+ "bytes": 8342
75
75
  }
76
76
  ]
77
77
  }
@@ -329,11 +329,11 @@ type authClient struct {
329
329
  func (a authClient) CurrentUser() map[string]any { return a.user }
330
330
  func (a authClient) RequireLogin() error {
331
331
  _, err := a.call(a.context, "auth.require_login", map[string]any{})
332
- return err
332
+ return authAssertionError("require_login", err)
333
333
  }
334
334
  func (a authClient) RequireAdmin() error {
335
335
  _, err := a.call(a.context, "auth.require_admin", map[string]any{})
336
- return err
336
+ return authAssertionError("require_admin", err)
337
337
  }
338
338
  func (a authClient) RequireRole(role string) error {
339
339
  role = strings.TrimSpace(role)
@@ -341,5 +341,12 @@ func (a authClient) RequireRole(role string) error {
341
341
  return errors.New("require_role failed: role is required")
342
342
  }
343
343
  _, err := a.call(a.context, "auth.require_role", map[string]any{"role": role})
344
- return err
344
+ return authAssertionError("require_role", err)
345
+ }
346
+
347
+ func authAssertionError(assertion string, err error) error {
348
+ if err == nil {
349
+ return nil
350
+ }
351
+ return fmt.Errorf("%s failed: %w", assertion, err)
345
352
  }
@@ -94,6 +94,7 @@ type ResourceContext struct {
94
94
  ScopeType string `json:"scope_type"`
95
95
  WorkspaceID int64 `json:"workspace_id,omitempty"`
96
96
  SpaceID int64 `json:"space_id,omitempty"`
97
+ OwnerUserID int64 `json:"owner_user_id,omitempty"`
97
98
  }
98
99
 
99
100
  // ResourceOwnership identifies the persisted authorization boundary of a
@@ -16,7 +16,7 @@ func (client *scopeRecordingClient) Call(_ context.Context, operation string, ar
16
16
  case "resource.context.current":
17
17
  return map[string]any{"scope_type": ScopeSpace, "workspace_id": float64(2), "space_id": float64(8)}, nil
18
18
  case "scope.principal.current":
19
- return map[string]any{"subject_type": "service", "subject_id": "service:42", "username": "publisher", "is_admin": true}, nil
19
+ return map[string]any{"subject_type": "system", "subject_id": "0", "username": "System", "is_admin": true}, nil
20
20
  default:
21
21
  return map[string]any{"ok": true}, nil
22
22
  }
@@ -38,7 +38,7 @@ func TestScopeClientUsesResourceAndPermissionOperations(t *testing.T) {
38
38
  if err != nil || recorder.operation != "scope.principal.current" {
39
39
  t.Fatalf("principal operation=%q err=%v", recorder.operation, err)
40
40
  }
41
- if principal.SubjectType != "service" || principal.SubjectID != "service:42" || principal.Username != "publisher" || !principal.IsAdmin {
41
+ if principal.SubjectType != "system" || principal.SubjectID != "0" || principal.Username != "System" || !principal.IsAdmin {
42
42
  t.Fatalf("principal=%#v", principal)
43
43
  }
44
44
 
@@ -75,13 +75,18 @@ func TestContextCarriesPersistedResourceAndPrincipal(t *testing.T) {
75
75
  recorder := &scopeRecordingClient{}
76
76
  ctx := NewContextWithResource(context.Background(), nil, nil, nil, recorder,
77
77
  ResourceContext{ScopeType: ScopeSpace, WorkspaceID: 11, SpaceID: 13},
78
- ScopePrincipal{SubjectType: "service", SubjectID: "custom-service:7", Username: "Service-Script"})
79
- if ctx.Platform || ctx.WorkspaceID != 11 || ctx.SpaceID != 13 || ctx.Principal.SubjectID != "custom-service:7" {
78
+ ScopePrincipal{SubjectType: "system", SubjectID: "0", Username: "System"})
79
+ if ctx.Platform || ctx.WorkspaceID != 11 || ctx.SpaceID != 13 || ctx.Principal.SubjectID != "0" {
80
80
  t.Fatalf("runtime context = %+v", ctx)
81
81
  }
82
82
  platform := NewContextWithResource(context.Background(), nil, nil, nil, recorder,
83
- ResourceContext{ScopeType: ScopePlatform}, ScopePrincipal{SubjectType: "service", SubjectID: "custom-service:8"})
83
+ ResourceContext{ScopeType: ScopePlatform}, ScopePrincipal{SubjectType: "system", SubjectID: "0"})
84
84
  if !platform.Platform || platform.WorkspaceID != 0 || platform.SpaceID != 0 {
85
85
  t.Fatalf("platform runtime context = %+v", platform)
86
86
  }
87
+ personal := NewContextWithResource(context.Background(), nil, nil, nil, recorder,
88
+ ResourceContext{ScopeType: "user", OwnerUserID: 17}, ScopePrincipal{SubjectType: "system", SubjectID: "0"})
89
+ if personal.Platform || personal.OwnerUserID != 17 || personal.WorkspaceID != 0 || personal.SpaceID != 0 {
90
+ t.Fatalf("personal runtime context = %+v", personal)
91
+ }
87
92
  }
@@ -106,6 +106,7 @@ type Context struct {
106
106
  Platform bool `json:"platform"`
107
107
  WorkspaceID int64 `json:"workspace_id,omitempty"`
108
108
  SpaceID int64 `json:"space_id,omitempty"`
109
+ OwnerUserID int64 `json:"owner_user_id,omitempty"`
109
110
  Principal ScopePrincipal `json:"principal"`
110
111
 
111
112
  DB Database `json:"-"`
@@ -137,7 +138,7 @@ func NewContext(draftgo context.Context, input, user map[string]any, headers htt
137
138
  }
138
139
 
139
140
  // NewContextWithResource constructs a handler context with persisted resource
140
- // ownership and the service principal supplied by DraftGo. Custom services
141
+ // ownership and the verified caller/system principal supplied by DraftGo. Custom services
141
142
  // cannot override these values through Input or request headers.
142
143
  func NewContextWithResource(draftgo context.Context, input, user map[string]any, headers http.Header, client Client, resource ResourceContext, principal ScopePrincipal) *Context {
143
144
  if draftgo == nil {
@@ -150,7 +151,7 @@ func NewContextWithResource(draftgo context.Context, input, user map[string]any,
150
151
  adminPlatform := platformClient{client: client, context: draftgo, admin: true}
151
152
  return &Context{
152
153
  Input: input, User: user, Headers: headers, context: draftgo,
153
- Platform: resource.ScopeType == ScopePlatform, WorkspaceID: resource.WorkspaceID, SpaceID: resource.SpaceID, Principal: principal,
154
+ Platform: resource.ScopeType == ScopePlatform, WorkspaceID: resource.WorkspaceID, SpaceID: resource.SpaceID, OwnerUserID: resource.OwnerUserID, Principal: principal,
154
155
  DB: dbClient{platform}, Users: usersClient{platform}, Auth: authClient{platformClient: platform, user: user}, Notify: notifierClient{platform},
155
156
  HTTP: httpClient{platform}, Cache: cacheClient{platform}, Config: configClient{platform}, AIHub: aiHubClient{platform},
156
157
  Knowledge: knowledgeClient{platform}, Memory: memoryClient{platform}, Files: fileStoreClient{platform}, Scope: scopeClient{platform}, Billing: billingClient{platform},
@@ -165,7 +166,7 @@ func NewContextWithResource(draftgo context.Context, input, user map[string]any,
165
166
  }
166
167
 
167
168
  // Admin exposes explicitly elevated platform capabilities to trusted custom
168
- // services. The runtime preserves the service subject, scopes elevation to one
169
+ // services. The runtime uses a system principal (actor_id=0), scopes elevation to one
169
170
  // audited RPC call, and never exposes platform credentials.
170
171
  type Admin struct {
171
172
  DB Database
@@ -47,7 +47,7 @@ description: Use this skill to inspect, develop, debug, or deliver a DraftGo app
47
47
  - 同一文件或 DraftGo 资源全程只能由一个 Agent 修改;不同资源、operation 和 owner 不冲突的单元可以并发,不设置客户端并发上限;同一资源的依赖步骤保持串行。主 Agent 负责 owner 分配、汇总、验证、交付和 worklog 状态。
48
48
  - 用户 API Key 只保存在 `.draftgo/config.json`,不得进入宿主配置、命令参数、Skill、日志、manifest 或错误消息。宿主 MCP 配置只运行 `draftgo mcp serve`。
49
49
  - 页面优先使用可信本地资源;禁止境外 CDN。净化不可信 HTML,关键状态不能只靠颜色或动效表达。页面通过 `window.parent.App` 使用认证、权限、反馈和主题;详细规则见前端与运行时 Reference。
50
- - 普通自定义服务 SDK 调用继承调用者、服务归属和 AccessGrant;`ctx.Admin.*` 是仅供自定义服务显式选择的单次系统内部授权,按 `*:*:all` 全权限执行并进入审计,不会泄漏到后续调用。高风险 MCP operation 仅在用户意图明确且影响范围已核对时设置 `confirm=true`。
50
+ - Route 普通自定义服务 SDK 调用继承真实调用者和服务归属;Event/Scheduled 没有调用者,普通 `ctx.xxx` 返回 403,必须显式使用 `ctx.Admin.*`。Admin RPC 使用 system actor_id=0,按 `*:*:all` 单次执行并进入审计,不创建服务主体 AccessGrant,也不会泄漏到后续调用。
51
51
  - `.draftgo/tmp/` 可清理;先运行 `draftgo clean --dry-run`,再在需要时使用 `--yes`。用户可查验的证据放入已注册的 `.draftgo/artifacts/`。
52
52
 
53
53
  ## 完成条件
@@ -2,7 +2,7 @@
2
2
  "schema_version": "1.0",
3
3
  "id": "draftgo",
4
4
  "name": "DraftGo 开发助手",
5
- "version": "4.0.23",
5
+ "version": "4.0.25",
6
6
  "entry": "SKILL.md",
7
7
  "description": "以 Skill/reference 任务路由、MCP 实时发现、长正文 checkout/commit、统一验证和完成日志为边界的 DraftGo 工作流。",
8
8
  "license": "MIT",
@@ -1,7 +1,7 @@
1
- ---
2
- read_when: 创建或调优 AI Agent 时 · 需要工具/子智能体/记忆/多轮/结构化输出/多模态时
3
- ---
4
-
1
+ ---
2
+ read_when: 创建或调优 AI Agent 时 · 需要工具/子智能体/记忆/多轮/结构化输出/多模态时
3
+ ---
4
+
5
5
  # AIHub / Agent 资源
6
6
 
7
7
  ## 最短管理流程
@@ -24,86 +24,86 @@ AIHub 与知识库是独立权限域:Agent、模型和 AI 运行使用 `aihub:
24
24
 
25
25
  AIHub 是结构化远端资源,不 checkout,也不生成本地镜像。先使用 MCP `draftgo_resource_search`/`draftgo_resource_list`
26
26
  定位资产;未知 operation 才用 `draftgo_api_search`,首次使用或 registry revision 变化时 `draftgo_api_describe`,然后通过 `draftgo_api_call` 读写。
27
- 更新 AIHub 资产时只发送实时契约允许的字段;常见字段包括
28
- `type, name, data, priority, version, tags, describe, permission, status`。供应商和模型使用各自的实时接口,
29
- 不要经旧 AIHub 资产接口写入。任何输出、工作区文件或调用参数都不得保存 API Key 或 header 值;只可处理
30
- “是否已配置”以及 header 名称等非秘密元数据。
31
- **Agent 的全部行为都在 `data`(尤其 `data.spec`)里**——本页就是 `data.spec` 的字段地图。
32
-
33
- ## 条目骨架
34
-
35
- ```jsonc
36
- {
37
- "type": "agent", // AIHub 资产类型
38
- "name": "产品顾问",
39
- "describe": "面向用户的产品答疑助手",
40
- "status": "active",
41
- "data": {
42
- "mode": "chat", // chat | image_generation
43
- "spec": { /* 见下表 */ }
44
- }
45
- }
46
- ```
47
-
48
- 页面对话 UI 使用 `<dg-chat protocol="draftgo-agent" agent-id="AGENT_ID">` 或 `DraftGoChat.create()`;
49
- 旧代码/无 UI 文本调用可用 `DraftGoAI.chat(...)`,图片模式使用 `DraftGoAI.images(...)`。调用前都必须加载
27
+ 更新 AIHub 资产时只发送实时契约允许的字段;常见字段包括
28
+ `type, name, data, priority, version, tags, describe, permission, status`。供应商和模型使用各自的实时接口,
29
+ 不要经旧 AIHub 资产接口写入。任何输出、工作区文件或调用参数都不得保存 API Key 或 header 值;只可处理
30
+ “是否已配置”以及 header 名称等非秘密元数据。
31
+ **Agent 的全部行为都在 `data`(尤其 `data.spec`)里**——本页就是 `data.spec` 的字段地图。
32
+
33
+ ## 条目骨架
34
+
35
+ ```jsonc
36
+ {
37
+ "type": "agent", // AIHub 资产类型
38
+ "name": "产品顾问",
39
+ "describe": "面向用户的产品答疑助手",
40
+ "status": "active",
41
+ "data": {
42
+ "mode": "chat", // chat | image_generation
43
+ "spec": { /* 见下表 */ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ 页面对话 UI 使用 `<dg-chat protocol="draftgo-agent" agent-id="AGENT_ID">` 或 `DraftGoChat.create()`;
49
+ 旧代码/无 UI 文本调用可用 `DraftGoAI.chat(...)`,图片模式使用 `DraftGoAI.images(...)`。调用前都必须加载
50
50
  `/assets/draftgo-chat.js`,完整用法见 `references/chat-sdk.md`。后端 operation 与可调用 Agent 列表通过 MCP 实时确认;已知契约可复用项目缓存。
51
-
52
- ## `data.spec` 字段地图
53
-
54
- 留空即维持默认/旧行为;除标注外都是可选。运行时统一在 `parseOrchestrationConfig` + 就地读取时带默认值与 clamp。
55
-
56
- | 字段 | 类型 / 取值 | 说明 |
57
- |---|---|---|
51
+
52
+ ## `data.spec` 字段地图
53
+
54
+ 留空即维持默认/旧行为;除标注外都是可选。运行时统一在 `parseOrchestrationConfig` + 就地读取时带默认值与 clamp。
55
+
56
+ | 字段 | 类型 / 取值 | 说明 |
57
+ |---|---|---|
58
58
  | `mode` | `chat` / `image_generation` | 决定主要交互形态;具体 Responses、Embedding、Rerank、TTS、ASR、Video operation 以 MCP 和模型 capability 为准 |
59
- | `model` | string | 主模型(逻辑模型名,映射到供应商路由) |
60
- | `fallback_models` | string[] | 主模型失败后按序回退(跨模型 failover) |
61
- | `model_selection.user_selectable` | bool | 是否允许调用方在请求里覆盖 `model`(配合 `selectable-models`) |
62
- | `ttft_timeout` | number(秒,1–600,空=不启用) | 首字超时 failover:首个 SSE data 事件超时即跨模型+跨供应商切换,推理模型不误杀 |
59
+ | `model` | string | 主模型(逻辑模型名,映射到供应商路由) |
60
+ | `fallback_models` | string[] | 主模型失败后按序回退(跨模型 failover) |
61
+ | `model_selection.user_selectable` | bool | 是否允许调用方在请求里覆盖 `model`(配合 `selectable-models`) |
62
+ | `ttft_timeout` | number(秒,1–600,空=不启用) | 首字超时 failover:首个 SSE data 事件超时即跨模型+跨供应商切换,推理模型不误杀 |
63
63
  | `sync_request_timeout` | number(秒,1–600,默认 100) | 非流式请求上限 |
64
64
  | `stream_ttl` | number(秒,1–3600,默认 600) | 流式请求上限 |
65
65
  | `max_tokens` | number / 空 | 最大输出 token;留空时不写入请求,即不由 Agent 额外限制 |
66
66
  | `reasoning_effort` | `off`/`minimal`/`low`/`medium`/`high` | `off`/留空均不透传;其它值仅 OpenAI 系模型生效 |
67
- | `system_prompt_template` | string | 系统提示模板 |
68
- | `context.max_history` | number(默认 20) | 工作窗口:保留最近 N 条;关闭持续对话时即滑动窗口硬上限 |
69
- | `output_format.mode` | `text` / `json` | JSON 时按 `output_format.json.{schema,schema_name,strategy}` 约束/校验/降级 |
70
- | `capabilities.vision.{enabled,input,max_mb}` | 见值 | 图片/视觉输入(`image_url` part),`input`⊂{base64,url},默认 5MB |
71
- | `capabilities.files.{enabled,allowed_ext,max_mb}` | 见值 | 文件附件抽取成文本注入;白名单 `.txt .md .docx .pdf .xlsx .json .csv`(pptx 不支持),默认 8MB |
72
- | `tools.max_iterations` | 1–50(默认 10) | ReAct 工具循环步数上限 |
73
- | `tools.sources[].{type,id}` | `mcp` / `custom_script` | 绑定 MCP 与「自定义服务作为工具」 |
74
- | `knowledge_base_ids` | int[] | 绑定知识库,生成检索工具 |
75
- | `skills` | 见运行时 | 绑定 Skill |
76
- | `sub_agent_ids` | int[] | 子智能体:为每个 id 生成 `agent_{id}` 委派工具(用法同 `knowledge_base_ids`) |
67
+ | `system_prompt_template` | string | 系统提示模板 |
68
+ | `context.max_history` | number(默认 20) | 工作窗口:保留最近 N 条;关闭持续对话时即滑动窗口硬上限 |
69
+ | `output_format.mode` | `text` / `json` | JSON 时按 `output_format.json.{schema,schema_name,strategy}` 约束/校验/降级 |
70
+ | `capabilities.vision.{enabled,input,max_mb}` | 见值 | 图片/视觉输入(`image_url` part),`input`⊂{base64,url},默认 5MB |
71
+ | `capabilities.files.{enabled,allowed_ext,max_mb}` | 见值 | 文件附件抽取成文本注入;白名单 `.txt .md .docx .pdf .xlsx .json .csv`(pptx 不支持),默认 8MB |
72
+ | `tools.max_iterations` | 1–50(默认 10) | ReAct 工具循环步数上限 |
73
+ | `tools.sources[].{type,id}` | `mcp` / `custom_script` | 绑定 MCP 与「自定义服务作为工具」 |
74
+ | `knowledge_base_ids` | int[] | 绑定知识库,生成检索工具 |
75
+ | `skills` | 见运行时 | 绑定 Skill |
76
+ | `sub_agent_ids` | int[] | 子智能体:为每个 id 生成 `agent_{id}` 委派工具(用法同 `knowledge_base_ids`) |
77
77
  | `call_permissions` | 角色配置 | 哪些角色可以调用此 Agent;调用接口与筛选条件以 MCP 实时契约为准 |
78
78
  | `billing_mode` | `disabled` / `inherit_model` / `per_call` / `usage` | Agent 零售计费;公开 Agent 强制 `disabled`,订阅/会员额度由业务服务管理 |
79
-
80
- ### `orchestration.*`(编排开关)
81
-
82
- | 字段 | 默认 | 说明 |
83
- |---|---|---|
84
- | `tool_concurrency` | 8(1–32) | 单步内并发执行工具数 |
85
- | `on_max_steps` | `error` | 达步数上限:`error` 报错 / `stop` 返回最后一条 |
86
- | `tool_disclosure.{mode,threshold_tools}` | `off` | 工具渐进披露:`off`/`auto`/`always`,首轮只给目录+`load_tools` |
87
- | `planning.{enabled,prompt}` | false | 规划层:执行前先让模型列步骤,提示折叠进 system |
88
- | `memory.{enabled,scope}` | false / `user_agent` | 长期记忆:对话后自动提炼、下轮召回注入;作用域 `agent`/`user`/`user_agent` |
89
- | `compaction.{enabled,keep_recent,trigger_messages,trigger_tokens,preset}` | 关闭 | **持续对话/上下文闭环**:见下 |
90
- | `checkpoint_input_mode` / `checkpoint_max_messages` / `checkpoint_ttl_seconds` | 全量 / 100 / 86400 | 会话历史持久化(配合请求 `session_id`) |
79
+
80
+ ### `orchestration.*`(编排开关)
81
+
82
+ | 字段 | 默认 | 说明 |
83
+ |---|---|---|
84
+ | `tool_concurrency` | 8(1–32) | 单步内并发执行工具数 |
85
+ | `on_max_steps` | `error` | 达步数上限:`error` 报错 / `stop` 返回最后一条 |
86
+ | `tool_disclosure.{mode,threshold_tools}` | `off` | 工具渐进披露:`off`/`auto`/`always`,首轮只给目录+`load_tools` |
87
+ | `planning.{enabled,prompt}` | false | 规划层:执行前先让模型列步骤,提示折叠进 system |
88
+ | `memory.{enabled,scope}` | false / `user_agent` | 长期记忆:对话后自动提炼、下轮召回注入;作用域 `agent`/`user`/`user_agent` |
89
+ | `compaction.{enabled,keep_recent,trigger_messages,trigger_tokens,preset}` | 关闭 | **持续对话/上下文闭环**:见下 |
90
+ | `checkpoint_input_mode` / `checkpoint_max_messages` / `checkpoint_ttl_seconds` | 全量 / 100 / 86400 | 会话历史持久化(配合请求 `session_id`) |
91
91
  | `delegation.{max_depth,max_total_calls}` | 2 / 8 | 最大嵌套层数与整棵调用树共享的子 Agent 调用次数;并行分支也从同一预算扣减 |
92
92
 
93
93
  同一模型步骤返回多个子 Agent 工具调用时,运行时立即按 `tool_concurrency` 并发执行;不同步骤自然串行,由模型自行决定编排方式。委派成功后,子 Agent 的每个模型回合 token 会汇总到入口 run,总量也写入对应委派 span。一次入口请求只创建一条主 run,Agent 列归属入口 Agent,委派链路从 span 查看。
94
-
95
- ### 持续对话(上下文闭环)
96
-
97
- - `compaction.enabled=true` = 闭环:填满工作窗口后把溢出旧消息**摘要成一条滚动 summary** 续接,
98
- 而非直接丢弃。此时后端**跳过 `context.max_history` 硬砍**,让完整 checkpoint 历史进入压缩器蒸馏。
99
- - 触发为双通道任一命中:`trigger_messages`(条数)或 `trigger_tokens`(估算 token,256–2000000)。
100
- - `keep_recent`:保留最近 N 条不压缩。`preset`(`aggressive`/`balanced`/`conservative`)是管理台档位回显,
101
- 后端只认 `keep_recent`/`trigger_messages`/`trigger_tokens` 三个底层字段。
102
- - 配合请求体 `session_id` 才会加载/续写会话历史;`<dg-chat>` 为每个 UI thread 自动维护该值,兼容门面可通过 `DraftGoAI.chat(..., {sessionId})` 显式传入。不传即无状态单轮。
103
- - 关闭时逐字回退为 `max_history` 滑动窗口(旧行为,零影响)。
104
-
105
- ## 观测
106
-
94
+
95
+ ### 持续对话(上下文闭环)
96
+
97
+ - `compaction.enabled=true` = 闭环:填满工作窗口后把溢出旧消息**摘要成一条滚动 summary** 续接,
98
+ 而非直接丢弃。此时后端**跳过 `context.max_history` 硬砍**,让完整 checkpoint 历史进入压缩器蒸馏。
99
+ - 触发为双通道任一命中:`trigger_messages`(条数)或 `trigger_tokens`(估算 token,256–2000000)。
100
+ - `keep_recent`:保留最近 N 条不压缩。`preset`(`aggressive`/`balanced`/`conservative`)是管理台档位回显,
101
+ 后端只认 `keep_recent`/`trigger_messages`/`trigger_tokens` 三个底层字段。
102
+ - 配合请求体 `session_id` 才会加载/续写会话历史;`<dg-chat>` 为每个 UI thread 自动维护该值,兼容门面可通过 `DraftGoAI.chat(..., {sessionId})` 显式传入。不传即无状态单轮。
103
+ - 关闭时逐字回退为 `max_history` 滑动窗口(旧行为,零影响)。
104
+
105
+ ## 观测
106
+
107
107
  每次调用都开一条 AI run,管理台 `/admin/ai-runs` 展示状态、tokens、延迟、`ttft_ms` 与 span 链路。
108
108
  运行记录接口通过 MCP 实时发现,不维护静态路径表;已知 operation 复用 registry revision 未变化的契约缓存。
109
109
 
@@ -112,5 +112,5 @@ profile、transport readiness 和实际 route;不要只根据供应商名称
112
112
  能力名大小写敏感,使用实时 describe 返回的 canonical 值,不使用历史别名。
113
113
 
114
114
  Agent 对外响应(`/api/agents/{id}/chat`、图片接口、Go/Script SDK Agent 调用)不返回内部 `model`、`fallback_models` 或 `upstream_model`;错误也不暴露内部模型名、供应商名称、服务地址或运行时实现名。只有显式启用 `model_selection.user_selectable` 后,`/selectable-models` 才作为授权的模型选择目录返回可选逻辑模型名。运行日志仍在服务端保留真实模型、供应商与链路信息用于定位。
115
-
115
+
116
116
  > 权威细节以 DraftGo `docs/modules/ai-platform/agent-runtime.md` 和实时 MCP schema 为准;本页是基座开发者视角的字段速查。