team-anya 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +38 -0
  2. package/apps/server/dist/broker/cc-broker.js +267 -0
  3. package/apps/server/dist/cli.js +296 -0
  4. package/apps/server/dist/config.js +78 -0
  5. package/apps/server/dist/daemon.js +51 -0
  6. package/apps/server/dist/franky/context-builder.js +161 -0
  7. package/apps/server/dist/franky/franky-mcp-server.js +110 -0
  8. package/apps/server/dist/franky/franky-orchestrator.js +629 -0
  9. package/apps/server/dist/franky/index.js +5 -0
  10. package/apps/server/dist/franky/topic-router.js +16 -0
  11. package/apps/server/dist/gateway/chat-sync.js +135 -0
  12. package/apps/server/dist/gateway/command-router.js +116 -0
  13. package/apps/server/dist/gateway/commands/cancel.js +32 -0
  14. package/apps/server/dist/gateway/commands/help.js +16 -0
  15. package/apps/server/dist/gateway/commands/index.js +26 -0
  16. package/apps/server/dist/gateway/commands/restart.js +43 -0
  17. package/apps/server/dist/gateway/commands/status.js +34 -0
  18. package/apps/server/dist/gateway/commands/tasks.js +33 -0
  19. package/apps/server/dist/gateway/feishu-sender.js +508 -0
  20. package/apps/server/dist/gateway/feishu-ws.js +353 -0
  21. package/apps/server/dist/gateway/health-monitor.js +154 -0
  22. package/apps/server/dist/gateway/http.js +1064 -0
  23. package/apps/server/dist/gateway/media-downloader.js +182 -0
  24. package/apps/server/dist/gateway/message-events.js +10 -0
  25. package/apps/server/dist/gateway/message-intake.js +72 -0
  26. package/apps/server/dist/gateway/message-queue.js +118 -0
  27. package/apps/server/dist/gateway/session-reader.js +142 -0
  28. package/apps/server/dist/gateway/ws-push.js +115 -0
  29. package/apps/server/dist/loid/brain.js +121 -0
  30. package/apps/server/dist/loid/clarifier.js +162 -0
  31. package/apps/server/dist/loid/context-builder.js +462 -0
  32. package/apps/server/dist/loid/mcp-server.js +119 -0
  33. package/apps/server/dist/loid/memory-settler.js +189 -0
  34. package/apps/server/dist/loid/opportunity-manager.js +148 -0
  35. package/apps/server/dist/loid/profile-updater.js +179 -0
  36. package/apps/server/dist/loid/project-registry.js +192 -0
  37. package/apps/server/dist/loid/reporter.js +148 -0
  38. package/apps/server/dist/loid/schemas.js +117 -0
  39. package/apps/server/dist/loid/self-calibrator.js +314 -0
  40. package/apps/server/dist/loid/session-manager.js +472 -0
  41. package/apps/server/dist/loid/session.js +276 -0
  42. package/apps/server/dist/main.js +528 -0
  43. package/apps/server/dist/tracing/index.js +2 -0
  44. package/apps/server/dist/tracing/trace-context.js +92 -0
  45. package/apps/server/dist/types/message.js +2 -0
  46. package/apps/server/dist/yor/yor-mcp-server.js +107 -0
  47. package/apps/server/dist/yor/yor-orchestrator.js +248 -0
  48. package/apps/web/dist/assets/index-BiiEB0qZ.css +1 -0
  49. package/apps/web/dist/assets/index-Dnb9LGZd.js +798 -0
  50. package/apps/web/dist/index.html +13 -0
  51. package/package.json +42 -0
  52. package/packages/cc-client/dist/claude-code-backend.js +792 -0
  53. package/packages/cc-client/dist/index.js +2 -0
  54. package/packages/cc-client/package.json +11 -0
  55. package/packages/core/dist/constants.js +60 -0
  56. package/packages/core/dist/errors.js +35 -0
  57. package/packages/core/dist/index.js +9 -0
  58. package/packages/core/dist/office-init.js +190 -0
  59. package/packages/core/dist/repo-cache.js +70 -0
  60. package/packages/core/dist/scope/checker.js +114 -0
  61. package/packages/core/dist/scope/defaults.js +55 -0
  62. package/packages/core/dist/scope/index.js +3 -0
  63. package/packages/core/dist/state-machine.js +86 -0
  64. package/packages/core/dist/types/audit.js +12 -0
  65. package/packages/core/dist/types/backend.js +2 -0
  66. package/packages/core/dist/types/commitment.js +17 -0
  67. package/packages/core/dist/types/communication.js +18 -0
  68. package/packages/core/dist/types/index.js +9 -0
  69. package/packages/core/dist/types/opportunity.js +27 -0
  70. package/packages/core/dist/types/org.js +26 -0
  71. package/packages/core/dist/types/task.js +46 -0
  72. package/packages/core/dist/types/workspace.js +39 -0
  73. package/packages/core/dist/workspace-manager.js +314 -0
  74. package/packages/core/package.json +10 -0
  75. package/packages/db/dist/client.js +69 -0
  76. package/packages/db/dist/index.js +756 -0
  77. package/packages/db/dist/schema/audit-events.js +13 -0
  78. package/packages/db/dist/schema/cc-sessions.js +14 -0
  79. package/packages/db/dist/schema/chats.js +35 -0
  80. package/packages/db/dist/schema/commitments.js +18 -0
  81. package/packages/db/dist/schema/communication-events.js +14 -0
  82. package/packages/db/dist/schema/index.js +14 -0
  83. package/packages/db/dist/schema/message-log.js +20 -0
  84. package/packages/db/dist/schema/opportunities.js +23 -0
  85. package/packages/db/dist/schema/org.js +36 -0
  86. package/packages/db/dist/schema/projects.js +23 -0
  87. package/packages/db/dist/schema/tasks.js +51 -0
  88. package/packages/db/dist/schema/topics.js +22 -0
  89. package/packages/db/dist/schema/trace-spans.js +19 -0
  90. package/packages/db/dist/schema/workspaces.js +15 -0
  91. package/packages/db/package.json +12 -0
  92. package/packages/db/src/migrations/0000_baseline.sql +251 -0
  93. package/packages/db/src/migrations/0001_workspaces.sql +19 -0
  94. package/packages/db/src/migrations/0002_workspace_parent.sql +1 -0
  95. package/packages/db/src/migrations/0003_chat_context.sql +3 -0
  96. package/packages/db/src/migrations/meta/_journal.json +34 -0
  97. package/packages/mcp-tools/dist/index.js +41 -0
  98. package/packages/mcp-tools/dist/layer1/audit-append.js +38 -0
  99. package/packages/mcp-tools/dist/layer1/audit-query.js +51 -0
  100. package/packages/mcp-tools/dist/layer1/memory-brief.js +168 -0
  101. package/packages/mcp-tools/dist/layer1/memory-context.js +124 -0
  102. package/packages/mcp-tools/dist/layer1/memory-digest.js +126 -0
  103. package/packages/mcp-tools/dist/layer1/memory-forget.js +108 -0
  104. package/packages/mcp-tools/dist/layer1/memory-learn.js +63 -0
  105. package/packages/mcp-tools/dist/layer1/memory-recall.js +287 -0
  106. package/packages/mcp-tools/dist/layer1/memory-reflect.js +80 -0
  107. package/packages/mcp-tools/dist/layer1/memory-remember.js +119 -0
  108. package/packages/mcp-tools/dist/layer1/memory-search.js +263 -0
  109. package/packages/mcp-tools/dist/layer1/memory-write.js +21 -0
  110. package/packages/mcp-tools/dist/layer1/org-lookup.js +47 -0
  111. package/packages/mcp-tools/dist/layer1/project-get.js +28 -0
  112. package/packages/mcp-tools/dist/layer1/project-list.js +20 -0
  113. package/packages/mcp-tools/dist/layer1/report-daily.js +68 -0
  114. package/packages/mcp-tools/dist/layer1/task-get.js +29 -0
  115. package/packages/mcp-tools/dist/layer1/task-update.js +34 -0
  116. package/packages/mcp-tools/dist/layer2/franky/topic-checkpoint.js +43 -0
  117. package/packages/mcp-tools/dist/layer2/franky/topic-escalate.js +19 -0
  118. package/packages/mcp-tools/dist/layer2/loid/decision-log.js +15 -0
  119. package/packages/mcp-tools/dist/layer2/loid/decision-no-action.js +15 -0
  120. package/packages/mcp-tools/dist/layer2/loid/delivery-create-pr.js +30 -0
  121. package/packages/mcp-tools/dist/layer2/loid/delivery-share.js +12 -0
  122. package/packages/mcp-tools/dist/layer2/loid/delivery-submit.js +77 -0
  123. package/packages/mcp-tools/dist/layer2/loid/delivery-upload.js +18 -0
  124. package/packages/mcp-tools/dist/layer2/loid/project-remove.js +16 -0
  125. package/packages/mcp-tools/dist/layer2/loid/project-upsert.js +33 -0
  126. package/packages/mcp-tools/dist/layer2/loid/task-dispatch.js +206 -0
  127. package/packages/mcp-tools/dist/layer2/loid/task-escalate-to-topic.js +170 -0
  128. package/packages/mcp-tools/dist/layer2/loid/task-lookup.js +45 -0
  129. package/packages/mcp-tools/dist/layer2/loid/topic-close.js +22 -0
  130. package/packages/mcp-tools/dist/layer2/loid/topic-create.js +60 -0
  131. package/packages/mcp-tools/dist/layer2/loid/yor-approve.js +8 -0
  132. package/packages/mcp-tools/dist/layer2/loid/yor-kill.js +7 -0
  133. package/packages/mcp-tools/dist/layer2/loid/yor-rework.js +7 -0
  134. package/packages/mcp-tools/dist/layer2/loid/yor-spawn.js +28 -0
  135. package/packages/mcp-tools/dist/layer2/loid/yor-status.js +8 -0
  136. package/packages/mcp-tools/dist/layer2/yor/task-block.js +11 -0
  137. package/packages/mcp-tools/dist/layer2/yor/task-deliver.js +35 -0
  138. package/packages/mcp-tools/dist/layer2/yor/task-progress.js +21 -0
  139. package/packages/mcp-tools/dist/layer3/adapters/feishu-adapter.js +203 -0
  140. package/packages/mcp-tools/dist/layer3/adapters/types.js +28 -0
  141. package/packages/mcp-tools/dist/layer3/channel-receive.js +11 -0
  142. package/packages/mcp-tools/dist/layer3/channel-send.js +75 -0
  143. package/packages/mcp-tools/dist/layer3/file-upload.js +44 -0
  144. package/packages/mcp-tools/dist/registry.js +911 -0
  145. package/packages/mcp-tools/package.json +13 -0
@@ -0,0 +1,2 @@
1
+ export { ClaudeCodeBackend } from './claude-code-backend.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@team-anya/cc-client",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "dependencies": {
8
+ "@team-anya/core": "file:../core",
9
+ "ws": "^8.18.0"
10
+ }
11
+ }
@@ -0,0 +1,60 @@
1
+ // 任务状态枚举
2
+ export const TaskStatus = {
3
+ NEW: 'NEW',
4
+ NEED_CLARIFICATION: 'NEED_CLARIFICATION',
5
+ READY: 'READY',
6
+ IN_PROGRESS: 'IN_PROGRESS',
7
+ DELIVERING: 'DELIVERING',
8
+ DONE: 'DONE',
9
+ BLOCKED: 'BLOCKED',
10
+ CANCELLED: 'CANCELLED',
11
+ ESCALATED_TO_TOPIC: 'ESCALATED_TO_TOPIC',
12
+ };
13
+ /**
14
+ * @deprecated MCP v3 不再使用 Triage L0-L3 分级。
15
+ * Loid 改为 SOUL 驱动 + 硬约束模式。
16
+ * 保留此常量仅为向后兼容 intent-classifier 和 opportunity-manager。
17
+ */
18
+ export const IntentLevel = {
19
+ L0_NOISE: 0,
20
+ L1_CONTEXT: 1,
21
+ L2_OPPORTUNITY: 2,
22
+ L3_COMMITMENT: 3,
23
+ };
24
+ // 承诺状态
25
+ export const CommitmentStatus = {
26
+ ACTIVE: 'active',
27
+ FULFILLED: 'fulfilled',
28
+ BROKEN: 'broken',
29
+ CANCELLED: 'cancelled',
30
+ };
31
+ // 机会状态
32
+ export const OpportunityStatus = {
33
+ DETECTED: 'detected',
34
+ CONFIRMED: 'confirmed',
35
+ SKIPPED: 'skipped',
36
+ REJECTED: 'rejected',
37
+ CONVERTED: 'converted',
38
+ };
39
+ // 连接状态
40
+ export const ConnectionState = {
41
+ DISCONNECTED: 'disconnected',
42
+ CONNECTING: 'connecting',
43
+ CONNECTED: 'connected',
44
+ EXECUTING: 'executing',
45
+ ERROR: 'error',
46
+ };
47
+ // 任务 ID 前缀
48
+ export const TASK_ID_PREFIX = 'ANYA';
49
+ // 默认配置
50
+ export const DEFAULTS = {
51
+ MAX_RETRIES: 3,
52
+ MAX_YOR_CONCURRENCY: 3,
53
+ HEARTBEAT_INTERVAL_MS: 15_000,
54
+ RECONNECT_BASE_DELAY_MS: 1_000,
55
+ RECONNECT_MAX_RETRIES: 3,
56
+ CONNECT_TIMEOUT_MS: 30_000,
57
+ IDLE_TIMEOUT_MS: 300_000,
58
+ TOTAL_TIMEOUT_MS: 3_600_000,
59
+ };
60
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1,35 @@
1
+ export class AnyaError extends Error {
2
+ code;
3
+ details;
4
+ constructor(message, code, details) {
5
+ super(message);
6
+ this.code = code;
7
+ this.details = details;
8
+ this.name = 'AnyaError';
9
+ }
10
+ }
11
+ export class InvalidTransitionError extends AnyaError {
12
+ constructor(from, to) {
13
+ super(`非法状态转换: ${from} → ${to}`, 'INVALID_TRANSITION', { from, to });
14
+ this.name = 'InvalidTransitionError';
15
+ }
16
+ }
17
+ export class TaskNotFoundError extends AnyaError {
18
+ constructor(taskId) {
19
+ super(`任务不存在: ${taskId}`, 'TASK_NOT_FOUND', { taskId });
20
+ this.name = 'TaskNotFoundError';
21
+ }
22
+ }
23
+ export class StatusConflictError extends AnyaError {
24
+ constructor(taskId, expected, actual) {
25
+ super(`状态冲突: 任务 ${taskId} 期望 ${expected},实际 ${actual}`, 'STATUS_CONFLICT', { taskId, expected, actual });
26
+ this.name = 'StatusConflictError';
27
+ }
28
+ }
29
+ export class ScopeDeniedError extends AnyaError {
30
+ constructor(tool, reason) {
31
+ super(`SCOPE 拒绝: ${tool} - ${reason}`, 'SCOPE_DENIED', { tool, reason });
32
+ this.name = 'ScopeDeniedError';
33
+ }
34
+ }
35
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,9 @@
1
+ export * from './types/index.js';
2
+ export * from './constants.js';
3
+ export * from './state-machine.js';
4
+ export * from './errors.js';
5
+ export * from './office-init.js';
6
+ export * from './repo-cache.js';
7
+ export * from './workspace-manager.js';
8
+ export * from './scope/index.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,190 @@
1
+ import { mkdir, copyFile, stat, symlink, readlink } from 'node:fs/promises';
2
+ import { join, dirname } from 'node:path';
3
+ /** 每次启动强制覆盖的文件(提示词/协议,必须与 blueprint 保持同步) */
4
+ const ALWAYS_OVERWRITE_FILES = [
5
+ 'CLAUDE.md',
6
+ 'CHARTER.md',
7
+ 'PROTOCOL.md',
8
+ 'TOOLS.md',
9
+ 'loid/PROFILE.md',
10
+ 'loid/PLAYBOOK.md',
11
+ 'yor/CLAUDE.md',
12
+ 'yor/PROFILE.md',
13
+ 'yor/PLAYBOOK.md',
14
+ 'yor/SELF-HEAL.md',
15
+ 'franky/CLAUDE.md',
16
+ 'franky/PROFILE.md',
17
+ 'franky/PLAYBOOK.md',
18
+ ];
19
+ /** 仅当目标不存在时才复制的文件(配置/数据,运行时可能被修改) */
20
+ const COPY_IF_MISSING_FILES = [
21
+ '.mcp.json',
22
+ '.claude/settings.local.json',
23
+ 'reference/org/ownership.yaml',
24
+ 'reference/org/escalation.yaml',
25
+ ];
26
+ /** 需要确保存在的空目录 */
27
+ const REQUIRED_DIRS = [
28
+ 'memory/people',
29
+ 'memory/projects',
30
+ 'memory/execution',
31
+ 'memory/commitments',
32
+ 'memory/self',
33
+ 'reference/org',
34
+ 'reference/identity',
35
+ 'audit',
36
+ 'reports',
37
+ 'yor',
38
+ 'loid',
39
+ 'franky',
40
+ '.claude',
41
+ ];
42
+ async function exists(path) {
43
+ try {
44
+ await stat(path);
45
+ return true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ /** Loid 工作区每次启动强制覆盖的文件(提示词/协议)
52
+ * source: 相对于 templateDir 的路径
53
+ * dest: 相对于 targetDir 的路径(扁平化:角色文件直接放根下)
54
+ */
55
+ const LOID_WORKSPACE_OVERWRITE_FILES = [
56
+ { source: 'CLAUDE.md', dest: 'CLAUDE.md' },
57
+ { source: 'CHARTER.md', dest: 'CHARTER.md' },
58
+ { source: 'PROTOCOL.md', dest: 'PROTOCOL.md' },
59
+ { source: 'TOOLS.md', dest: 'TOOLS.md' },
60
+ { source: 'loid/PROFILE.md', dest: 'PROFILE.md' },
61
+ { source: 'loid/PLAYBOOK.md', dest: 'PLAYBOOK.md' },
62
+ ];
63
+ /** Loid 工作区仅补缺的配置文件(运行时由 CCBroker/writeMcpJson 动态维护) */
64
+ const LOID_WORKSPACE_COPY_IF_MISSING_FILES = [
65
+ '.mcp.json',
66
+ '.claude/settings.local.json',
67
+ ];
68
+ /**
69
+ * 确保 workspaces 目录存在($ANYA_HOME/workspaces/,与 office 同级)
70
+ * Loid per-chatId 工作区由 SessionManager 按需调用 ensureLoidWorkspace() 创建
71
+ */
72
+ export async function ensureWorkspacesDir(anyaHome, _templateDir, logger) {
73
+ const wsDir = join(anyaHome, 'workspaces');
74
+ await mkdir(wsDir, { recursive: true });
75
+ await mkdir(join(wsDir, 'loid'), { recursive: true });
76
+ logger?.info(`Workspaces 目录已确认: ${wsDir}`);
77
+ }
78
+ /** 需要从共享 office 目录 symlink 到 per-chatId 工作区的目录 */
79
+ const LOID_SHARED_SYMLINK_DIRS = [
80
+ 'memory',
81
+ 'reference',
82
+ 'audit',
83
+ 'reports',
84
+ ];
85
+ /**
86
+ * 确保单个 Loid per-chatId 工作区就绪:
87
+ * - 同步模板文件(CLAUDE.md 等)
88
+ * - symlink 共享目录(memory/reference/audit/reports → officePath)
89
+ */
90
+ export async function ensureLoidWorkspace(targetDir, templateDir, officePath, logger) {
91
+ await mkdir(targetDir, { recursive: true });
92
+ // 同步 Loid 模板文件(强制覆盖)
93
+ let overwritten = 0;
94
+ let created = 0;
95
+ // 强制覆盖提示词/协议文件(角色文件扁平化到根下)
96
+ for (const { source, dest } of LOID_WORKSPACE_OVERWRITE_FILES) {
97
+ const srcPath = join(templateDir, source);
98
+ const destPath = join(targetDir, dest);
99
+ if (!(await exists(srcPath)))
100
+ continue;
101
+ await mkdir(dirname(destPath), { recursive: true });
102
+ await copyFile(srcPath, destPath);
103
+ overwritten++;
104
+ }
105
+ // 仅补缺的配置文件(运行时由 writeMcpJson 等动态维护,不可覆盖)
106
+ for (const file of LOID_WORKSPACE_COPY_IF_MISSING_FILES) {
107
+ const src = join(templateDir, file);
108
+ const dest = join(targetDir, file);
109
+ if (await exists(dest))
110
+ continue;
111
+ if (!(await exists(src)))
112
+ continue;
113
+ await mkdir(dirname(dest), { recursive: true });
114
+ await copyFile(src, dest);
115
+ created++;
116
+ }
117
+ // symlink 共享目录
118
+ for (const dir of LOID_SHARED_SYMLINK_DIRS) {
119
+ const linkPath = join(targetDir, dir);
120
+ const sharedPath = join(officePath, dir);
121
+ // 确保共享目录存在
122
+ await mkdir(sharedPath, { recursive: true });
123
+ // 如果已经是正确的 symlink,跳过
124
+ try {
125
+ const target = await readlink(linkPath);
126
+ if (target === sharedPath)
127
+ continue;
128
+ }
129
+ catch {
130
+ // 不是 symlink 或不存在,继续创建
131
+ }
132
+ // 如果目标路径不存在,创建 symlink
133
+ if (!(await exists(linkPath))) {
134
+ await symlink(sharedPath, linkPath, 'dir');
135
+ }
136
+ }
137
+ // 确保 .claude 目录存在
138
+ await mkdir(join(targetDir, '.claude'), { recursive: true });
139
+ logger?.info(`Loid 工作区已就绪: ${targetDir} (${overwritten} 覆盖, ${created} 补缺)`);
140
+ }
141
+ /**
142
+ * 确保 office 目录存在并包含所有必需的模板文件和子目录。
143
+ * - 提示词/协议文件(.md):每次启动强制覆盖,保证与 blueprint 同步
144
+ * - 配置/数据文件:仅补缺,不覆盖运行时修改
145
+ */
146
+ export async function ensureOffice(officePath, templateDir, logger) {
147
+ const isNew = !(await exists(officePath));
148
+ // 创建所有必需目录
149
+ for (const dir of REQUIRED_DIRS) {
150
+ await mkdir(join(officePath, dir), { recursive: true });
151
+ }
152
+ let overwritten = 0;
153
+ let created = 0;
154
+ let skipped = 0;
155
+ // 强制覆盖提示词/协议文件
156
+ for (const file of ALWAYS_OVERWRITE_FILES) {
157
+ const src = join(templateDir, file);
158
+ const dest = join(officePath, file);
159
+ if (!(await exists(src))) {
160
+ logger?.info(`模板文件不存在,跳过: ${file}`);
161
+ continue;
162
+ }
163
+ await mkdir(dirname(dest), { recursive: true });
164
+ await copyFile(src, dest);
165
+ overwritten++;
166
+ }
167
+ // 仅补缺的配置/数据文件
168
+ for (const file of COPY_IF_MISSING_FILES) {
169
+ const src = join(templateDir, file);
170
+ const dest = join(officePath, file);
171
+ if (await exists(dest)) {
172
+ skipped++;
173
+ continue;
174
+ }
175
+ if (!(await exists(src))) {
176
+ logger?.info(`模板文件不存在,跳过: ${file}`);
177
+ continue;
178
+ }
179
+ await mkdir(dirname(dest), { recursive: true });
180
+ await copyFile(src, dest);
181
+ created++;
182
+ }
183
+ if (isNew) {
184
+ logger?.info(`Office 目录已创建: ${officePath} (${overwritten + created} 个文件)`);
185
+ }
186
+ else {
187
+ logger?.info(`Office 已同步: ${overwritten} 个提示词覆盖, ${created} 个配置补全, ${skipped} 个配置跳过`);
188
+ }
189
+ }
190
+ //# sourceMappingURL=office-init.js.map
@@ -0,0 +1,70 @@
1
+ import { execFile as execFileCb } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { stat } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ const execFile = promisify(execFileCb);
6
+ /** fetch cooldown: 同一仓库 5 分钟内不重复 fetch */
7
+ const FETCH_COOLDOWN_MS = 5 * 60 * 1000;
8
+ /**
9
+ * 仓库缓存层 — repos/ 是本地缓存,用于加速 clone --local。
10
+ *
11
+ * 心智模型:repos/ 是武器库的库存,workspace 是领出去用的装备。
12
+ */
13
+ export class RepoCache {
14
+ config;
15
+ constructor(config) {
16
+ this.config = config;
17
+ }
18
+ /** 返回 repos/ 下指定仓库的本地路径 */
19
+ getRepoPath(repoName) {
20
+ return join(this.config.reposPath, repoName);
21
+ }
22
+ /**
23
+ * 确保 repos/ 下存在指定仓库且代码较新。
24
+ * - 不存在 → git clone
25
+ * - 已存在 → git fetch origin(带 cooldown)
26
+ */
27
+ async ensureRepo(repoName, gitUrl) {
28
+ const repoPath = this.getRepoPath(repoName);
29
+ const exists = await this.dirExists(repoPath);
30
+ if (!exists) {
31
+ this.config.logger?.info(`[RepoCache] 首次克隆: ${repoName} ← ${gitUrl}`);
32
+ await execFile('git', ['clone', gitUrl, repoPath]);
33
+ }
34
+ else {
35
+ await this.fetchIfStale(repoPath, repoName);
36
+ }
37
+ return repoPath;
38
+ }
39
+ /**
40
+ * 检查上次 fetch 时间,超过 cooldown 才执行 fetch。
41
+ * 通过 FETCH_HEAD 文件的 mtime 判断。
42
+ */
43
+ async fetchIfStale(repoPath, repoName) {
44
+ const fetchHeadPath = join(repoPath, '.git', 'FETCH_HEAD');
45
+ let lastFetchMs = 0;
46
+ try {
47
+ const s = await stat(fetchHeadPath);
48
+ lastFetchMs = s.mtimeMs;
49
+ }
50
+ catch {
51
+ // FETCH_HEAD 不存在 → 从未 fetch 过,强制执行
52
+ }
53
+ if (Date.now() - lastFetchMs < FETCH_COOLDOWN_MS) {
54
+ this.config.logger?.info(`[RepoCache] ${repoName} 在 cooldown 内,跳过 fetch`);
55
+ return;
56
+ }
57
+ this.config.logger?.info(`[RepoCache] fetch: ${repoName}`);
58
+ await execFile('git', ['-C', repoPath, 'fetch', 'origin']);
59
+ }
60
+ async dirExists(path) {
61
+ try {
62
+ const s = await stat(path);
63
+ return s.isDirectory();
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ }
70
+ //# sourceMappingURL=repo-cache.js.map
@@ -0,0 +1,114 @@
1
+ import { DEFAULT_FORBIDDEN_COMMANDS, DEFAULT_FORBIDDEN_PATHS } from './defaults.js';
2
+ /**
3
+ * 默认 scope(无特定限制,仅启用默认安全守卫)
4
+ */
5
+ export const DEFAULT_SCOPE = {
6
+ allowedPaths: ['**'],
7
+ forbiddenPaths: [],
8
+ allowedCommands: ['**'],
9
+ forbiddenCommands: [],
10
+ };
11
+ /**
12
+ * SCOPE 安全检查器
13
+ *
14
+ * 在协议层拦截工具请求,不依赖提示词约束。
15
+ * Claude Code 每次调用工具前都会发送 control_request,
16
+ * ScopeChecker 在此处做硬拦截。
17
+ */
18
+ export class ScopeChecker {
19
+ scope;
20
+ constructor(scope = DEFAULT_SCOPE) {
21
+ this.scope = scope;
22
+ }
23
+ /**
24
+ * 检查工具请求是否被允许
25
+ */
26
+ check(req) {
27
+ // 文件写入检查
28
+ if (this.isFileWriteTool(req.toolName)) {
29
+ const filePath = (req.input.file_path ?? req.input.path ?? '');
30
+ if (!filePath) {
31
+ return this.allow(req.requestId);
32
+ }
33
+ // 先检查默认禁止路径
34
+ if (this.matchesAnyGlob(filePath, DEFAULT_FORBIDDEN_PATHS)) {
35
+ return this.deny(req.requestId, `默认禁止路径: ${filePath}`);
36
+ }
37
+ // 再检查 task scope 的禁止路径
38
+ if (this.matchesAnyGlob(filePath, this.scope.forbiddenPaths)) {
39
+ return this.deny(req.requestId, `任务禁止路径: ${filePath}`);
40
+ }
41
+ // 检查是否在允许路径中
42
+ if (this.scope.allowedPaths.length > 0 &&
43
+ !this.scope.allowedPaths.includes('**') &&
44
+ !this.matchesAnyGlob(filePath, this.scope.allowedPaths)) {
45
+ return this.deny(req.requestId, `路径不在允许范围: ${filePath}`);
46
+ }
47
+ }
48
+ // 命令执行检查
49
+ if (req.toolName === 'Bash') {
50
+ const command = (req.input.command ?? '');
51
+ if (!command) {
52
+ return this.allow(req.requestId);
53
+ }
54
+ // 先检查默认禁止命令
55
+ if (this.matchesAnyRegex(command, DEFAULT_FORBIDDEN_COMMANDS)) {
56
+ return this.deny(req.requestId, `默认禁止命令: ${command}`);
57
+ }
58
+ // 再检查 task scope 的禁止命令
59
+ const customForbidden = this.scope.forbiddenCommands.map(p => new RegExp(p));
60
+ if (this.matchesAnyRegex(command, customForbidden)) {
61
+ return this.deny(req.requestId, `任务禁止命令: ${command}`);
62
+ }
63
+ }
64
+ return this.allow(req.requestId);
65
+ }
66
+ /**
67
+ * 创建一个绑定了当前 scope 的 onToolRequest handler
68
+ */
69
+ createHandler() {
70
+ return async (req) => this.check(req);
71
+ }
72
+ // ── 私有方法 ──
73
+ isFileWriteTool(toolName) {
74
+ return ['Write', 'Edit', 'NotebookEdit'].includes(toolName);
75
+ }
76
+ matchesAnyGlob(path, patterns) {
77
+ for (const pattern of patterns) {
78
+ if (this.globMatch(path, pattern))
79
+ return true;
80
+ }
81
+ return false;
82
+ }
83
+ matchesAnyRegex(text, patterns) {
84
+ for (const pattern of patterns) {
85
+ if (pattern.test(text))
86
+ return true;
87
+ }
88
+ return false;
89
+ }
90
+ /**
91
+ * 简单 glob 匹配
92
+ * 支持: * (匹配非/字符), ** (匹配任意字符含/), ? (匹配单字符)
93
+ */
94
+ globMatch(text, pattern) {
95
+ // 将 glob 转为正则
96
+ let regex = pattern
97
+ .replace(/\*\*/g, '<<DOUBLESTAR>>')
98
+ .replace(/\*/g, '[^/]*')
99
+ .replace(/<<DOUBLESTAR>>/g, '.*')
100
+ .replace(/\?/g, '.');
101
+ // 如果 pattern 不以 / 开头,允许匹配任意前缀
102
+ if (!pattern.startsWith('/') && !pattern.startsWith('*')) {
103
+ regex = '(.*/)?' + regex;
104
+ }
105
+ return new RegExp(`^${regex}$`).test(text);
106
+ }
107
+ allow(requestId) {
108
+ return { requestId, behavior: 'allow' };
109
+ }
110
+ deny(requestId, reason) {
111
+ return { requestId, behavior: 'deny', reason };
112
+ }
113
+ }
114
+ //# sourceMappingURL=checker.js.map
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 默认禁止命令正则列表
3
+ * 即使 TaskScope 未配置 forbiddenCommands,这些命令也会被拦截
4
+ */
5
+ export const DEFAULT_FORBIDDEN_COMMANDS = [
6
+ /\brm\s+-[rf]{1,2}\b/, // rm -rf
7
+ /\brm\s+-[rf]{1,2}\s+\//, // rm -rf /
8
+ /\bgit\s+push\s+--force\b/, // force push
9
+ /\bgit\s+push\b.*\b(main|master)\b/, // 直接推主分支
10
+ /\bgit\s+reset\s+--hard\b/, // hard reset
11
+ /\bgit\s+clean\s+-[fd]{1,2}\b/, // git clean
12
+ /\b(DROP|TRUNCATE)\s+/i, // 危险 SQL DDL
13
+ /\bDELETE\s+FROM\b/i, // 危险 SQL DML(无 WHERE)
14
+ /\b(shutdown|reboot|poweroff|halt)\b/, // 系统命令
15
+ /\bdd\s+if=/, // 磁盘操作
16
+ /:\(\)\s*\{.*\};\s*:/, // fork bomb
17
+ /\bcurl\b.*\|\s*\b(bash|sh)\b/, // curl | bash
18
+ /\bchmod\s+777\b/, // 过度开放权限
19
+ /\bsudo\b/, // sudo
20
+ ];
21
+ /**
22
+ * 默认禁止写入路径
23
+ * 保护系统和敏感文件
24
+ */
25
+ export const DEFAULT_FORBIDDEN_PATHS = [
26
+ '**/.env',
27
+ '**/.env.*',
28
+ '**/credentials*',
29
+ '**/secrets*',
30
+ '**/*.pem',
31
+ '**/*.key',
32
+ '**/id_rsa*',
33
+ '/etc/**',
34
+ '/usr/**',
35
+ '/var/**',
36
+ '**/node_modules/**',
37
+ '**/.git/objects/**',
38
+ '**/.git/refs/**',
39
+ ];
40
+ /**
41
+ * Franky 话题模式的默认 scope
42
+ * 比 Yor 更宽松:允许 git push 到 feature 分支,允许创建 PR
43
+ * 但仍禁止 push main/master 和 force push
44
+ */
45
+ export const FRANKY_DEFAULT_SCOPE = {
46
+ allowedPaths: ['**'],
47
+ forbiddenPaths: DEFAULT_FORBIDDEN_PATHS,
48
+ allowedCommands: ['**'],
49
+ forbiddenCommands: [
50
+ 'git push\\s+--force',
51
+ 'git push\\b.*--force',
52
+ 'git push\\b.*\\b(main|master)\\b',
53
+ ],
54
+ };
55
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1,3 @@
1
+ export { ScopeChecker, DEFAULT_SCOPE } from './checker.js';
2
+ export { DEFAULT_FORBIDDEN_COMMANDS, DEFAULT_FORBIDDEN_PATHS, FRANKY_DEFAULT_SCOPE } from './defaults.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,86 @@
1
+ import { TaskStatus, TASK_ID_PREFIX } from './constants.js';
2
+ import { InvalidTransitionError } from './errors.js';
3
+ /**
4
+ * 合法状态转换表
5
+ * 定义了每个状态可以转换到哪些目标状态
6
+ */
7
+ export const VALID_TRANSITIONS = {
8
+ [TaskStatus.NEW]: [
9
+ TaskStatus.NEED_CLARIFICATION,
10
+ TaskStatus.READY,
11
+ TaskStatus.BLOCKED,
12
+ TaskStatus.CANCELLED,
13
+ ],
14
+ [TaskStatus.NEED_CLARIFICATION]: [
15
+ TaskStatus.READY,
16
+ TaskStatus.CANCELLED,
17
+ ],
18
+ [TaskStatus.READY]: [
19
+ TaskStatus.IN_PROGRESS,
20
+ TaskStatus.BLOCKED,
21
+ TaskStatus.CANCELLED,
22
+ ],
23
+ [TaskStatus.IN_PROGRESS]: [
24
+ TaskStatus.DELIVERING,
25
+ TaskStatus.BLOCKED,
26
+ TaskStatus.CANCELLED,
27
+ ],
28
+ [TaskStatus.DELIVERING]: [
29
+ TaskStatus.DONE,
30
+ TaskStatus.IN_PROGRESS,
31
+ TaskStatus.CANCELLED,
32
+ ],
33
+ [TaskStatus.BLOCKED]: [
34
+ TaskStatus.READY,
35
+ TaskStatus.CANCELLED,
36
+ ],
37
+ [TaskStatus.DONE]: [TaskStatus.ESCALATED_TO_TOPIC],
38
+ [TaskStatus.CANCELLED]: [],
39
+ [TaskStatus.ESCALATED_TO_TOPIC]: [],
40
+ };
41
+ /**
42
+ * 校验状态转换是否合法
43
+ */
44
+ export function validateTransition(from, to) {
45
+ const allowed = VALID_TRANSITIONS[from];
46
+ if (!allowed)
47
+ return false;
48
+ return allowed.includes(to);
49
+ }
50
+ /**
51
+ * 校验状态转换,非法则抛异常
52
+ */
53
+ export function assertTransition(from, to) {
54
+ if (!validateTransition(from, to)) {
55
+ throw new InvalidTransitionError(from, to);
56
+ }
57
+ }
58
+ /**
59
+ * 判断是否为终态(不可再转换)
60
+ */
61
+ export function isTerminalStatus(status) {
62
+ return VALID_TRANSITIONS[status]?.length === 0;
63
+ }
64
+ /**
65
+ * 生成任务 ID
66
+ * 格式: ANYA-YYYYMMDD-NNN
67
+ */
68
+ export function generateTaskId(sequence) {
69
+ const now = new Date();
70
+ const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');
71
+ const seqStr = String(sequence).padStart(3, '0');
72
+ return `${TASK_ID_PREFIX}-${dateStr}-${seqStr}`;
73
+ }
74
+ /**
75
+ * 解析任务 ID 中的日期和序号
76
+ */
77
+ export function parseTaskId(taskId) {
78
+ const match = taskId.match(/^ANYA-(\d{8})-(\d{3})$/);
79
+ if (!match)
80
+ return null;
81
+ return {
82
+ date: match[1],
83
+ sequence: parseInt(match[2], 10),
84
+ };
85
+ }
86
+ //# sourceMappingURL=state-machine.js.map
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ export const auditEventSchema = z.object({
3
+ id: z.number().optional(),
4
+ event_type: z.string(),
5
+ actor: z.string(),
6
+ task_id: z.string().nullable().optional(),
7
+ summary: z.string(),
8
+ detail: z.record(z.unknown()).nullable().optional(),
9
+ file_ref: z.string().nullable().optional(),
10
+ created_at: z.string().datetime().optional(),
11
+ });
12
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=backend.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @deprecated 此模块已废弃,改为 Agent 自主判断/使用 memory 系统。
3
+ * 保留此文件仅为兼容历史数据,新代码请勿使用。
4
+ */
5
+ import { z } from 'zod';
6
+ import { CommitmentStatus } from '../constants.js';
7
+ export const commitmentSchema = z.object({
8
+ id: z.number().optional(),
9
+ task_id: z.string(),
10
+ promised_to: z.string(),
11
+ promise: z.string(),
12
+ deadline: z.string().datetime().nullable().optional(),
13
+ status: z.nativeEnum(CommitmentStatus).default('active'),
14
+ break_reason: z.string().nullable().optional(),
15
+ promised_at: z.string().datetime().optional(),
16
+ });
17
+ //# sourceMappingURL=commitment.js.map