@ai-zen/agents-host 0.0.0-stage → 0.1.0-alpha.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 (146) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/LICENSE +21 -0
  3. package/README.md +759 -2
  4. package/dist/app.d.ts +384 -0
  5. package/dist/app.js +1148 -0
  6. package/dist/cli.d.ts +21 -0
  7. package/dist/cli.js +353 -0
  8. package/dist/config.d.ts +97 -0
  9. package/dist/config.js +171 -0
  10. package/dist/handlers/agentHandlers.d.ts +18 -0
  11. package/dist/handlers/agentHandlers.js +20 -0
  12. package/dist/handlers/assetHandlers.d.ts +29 -0
  13. package/dist/handlers/assetHandlers.js +35 -0
  14. package/dist/handlers/capabilities.d.ts +26 -0
  15. package/dist/handlers/capabilities.js +138 -0
  16. package/dist/handlers/completionHandlers.d.ts +18 -0
  17. package/dist/handlers/completionHandlers.js +13 -0
  18. package/dist/handlers/configHandlers.d.ts +31 -0
  19. package/dist/handlers/configHandlers.js +59 -0
  20. package/dist/handlers/conversationHandlers.d.ts +118 -0
  21. package/dist/handlers/conversationHandlers.js +314 -0
  22. package/dist/handlers/handoffHandlers.d.ts +17 -0
  23. package/dist/handlers/handoffHandlers.js +14 -0
  24. package/dist/handlers/hostHandlers.d.ts +39 -0
  25. package/dist/handlers/hostHandlers.js +56 -0
  26. package/dist/handlers/index.d.ts +128 -0
  27. package/dist/handlers/index.js +92 -0
  28. package/dist/handlers/messageHandlers.d.ts +11 -0
  29. package/dist/handlers/messageHandlers.js +10 -0
  30. package/dist/handlers/modelHandlers.d.ts +17 -0
  31. package/dist/handlers/modelHandlers.js +19 -0
  32. package/dist/handlers/noticeHandlers.d.ts +24 -0
  33. package/dist/handlers/noticeHandlers.js +24 -0
  34. package/dist/handlers/questionHandlers.d.ts +16 -0
  35. package/dist/handlers/questionHandlers.js +16 -0
  36. package/dist/handlers/searchHandlers.d.ts +12 -0
  37. package/dist/handlers/searchHandlers.js +12 -0
  38. package/dist/handlers/workspaceHandlers.d.ts +29 -0
  39. package/dist/handlers/workspaceHandlers.js +47 -0
  40. package/dist/i18n/en.d.ts +243 -0
  41. package/dist/i18n/en.js +270 -0
  42. package/dist/i18n/index.d.ts +43 -0
  43. package/dist/i18n/index.js +72 -0
  44. package/dist/i18n/zh.d.ts +240 -0
  45. package/dist/i18n/zh.js +257 -0
  46. package/dist/index.d.ts +17 -0
  47. package/dist/index.js +15 -0
  48. package/dist/net/UploadReceiver.d.ts +186 -0
  49. package/dist/net/UploadReceiver.js +494 -0
  50. package/dist/net/WsHub.d.ts +231 -0
  51. package/dist/net/WsHub.js +689 -0
  52. package/dist/net/auth.d.ts +73 -0
  53. package/dist/net/auth.js +74 -0
  54. package/dist/net/discovery.d.ts +72 -0
  55. package/dist/net/discovery.js +171 -0
  56. package/dist/net/index.d.ts +14 -0
  57. package/dist/net/index.js +10 -0
  58. package/dist/runtime/ChatRunner.d.ts +374 -0
  59. package/dist/runtime/ChatRunner.js +1204 -0
  60. package/dist/runtime/ConversationDrafts.d.ts +43 -0
  61. package/dist/runtime/ConversationDrafts.js +47 -0
  62. package/dist/runtime/RunRegistry.d.ts +79 -0
  63. package/dist/runtime/RunRegistry.js +108 -0
  64. package/dist/runtime/ScopeRegistry.d.ts +153 -0
  65. package/dist/runtime/ScopeRegistry.js +259 -0
  66. package/dist/runtime/TransientRunner.d.ts +90 -0
  67. package/dist/runtime/TransientRunner.js +179 -0
  68. package/dist/runtime/index.d.ts +14 -0
  69. package/dist/runtime/index.js +10 -0
  70. package/dist/services/AgentService.d.ts +150 -0
  71. package/dist/services/AgentService.js +718 -0
  72. package/dist/services/AssetService.d.ts +338 -0
  73. package/dist/services/AssetService.js +739 -0
  74. package/dist/services/AutoNaming.d.ts +92 -0
  75. package/dist/services/AutoNaming.js +235 -0
  76. package/dist/services/CompletionService.d.ts +122 -0
  77. package/dist/services/CompletionService.js +442 -0
  78. package/dist/services/ConfigService.d.ts +171 -0
  79. package/dist/services/ConfigService.js +695 -0
  80. package/dist/services/ConversationService.d.ts +274 -0
  81. package/dist/services/ConversationService.js +902 -0
  82. package/dist/services/CredentialService.d.ts +33 -0
  83. package/dist/services/CredentialService.js +86 -0
  84. package/dist/services/DiscoveryService.d.ts +182 -0
  85. package/dist/services/DiscoveryService.js +998 -0
  86. package/dist/services/HandoffService.d.ts +189 -0
  87. package/dist/services/HandoffService.js +414 -0
  88. package/dist/services/Logger.d.ts +130 -0
  89. package/dist/services/Logger.js +256 -0
  90. package/dist/services/MessageService.d.ts +88 -0
  91. package/dist/services/MessageService.js +280 -0
  92. package/dist/services/ModelService.d.ts +89 -0
  93. package/dist/services/ModelService.js +489 -0
  94. package/dist/services/NoticeService.d.ts +93 -0
  95. package/dist/services/NoticeService.js +217 -0
  96. package/dist/services/QuestionService.d.ts +184 -0
  97. package/dist/services/QuestionService.js +457 -0
  98. package/dist/services/SearchService.d.ts +54 -0
  99. package/dist/services/SearchService.js +117 -0
  100. package/dist/services/WorkspaceService.d.ts +96 -0
  101. package/dist/services/WorkspaceService.js +213 -0
  102. package/dist/services/imageSize.d.ts +41 -0
  103. package/dist/services/imageSize.js +146 -0
  104. package/dist/services/index.d.ts +20 -0
  105. package/dist/services/index.js +15 -0
  106. package/dist/services/secretMask.d.ts +24 -0
  107. package/dist/services/secretMask.js +34 -0
  108. package/dist/storage/AssetRepository.d.ts +87 -0
  109. package/dist/storage/AssetRepository.js +180 -0
  110. package/dist/storage/ConversationRepository.d.ts +55 -0
  111. package/dist/storage/ConversationRepository.js +128 -0
  112. package/dist/storage/CredentialRepository.d.ts +28 -0
  113. package/dist/storage/CredentialRepository.js +47 -0
  114. package/dist/storage/Database.d.ts +95 -0
  115. package/dist/storage/Database.js +180 -0
  116. package/dist/storage/MessageRepository.d.ts +104 -0
  117. package/dist/storage/MessageRepository.js +323 -0
  118. package/dist/storage/MetaRepository.d.ts +21 -0
  119. package/dist/storage/MetaRepository.js +33 -0
  120. package/dist/storage/NoticeRepository.d.ts +31 -0
  121. package/dist/storage/NoticeRepository.js +89 -0
  122. package/dist/storage/QuestionRepository.d.ts +34 -0
  123. package/dist/storage/QuestionRepository.js +96 -0
  124. package/dist/storage/WorkspaceRepository.d.ts +25 -0
  125. package/dist/storage/WorkspaceRepository.js +44 -0
  126. package/dist/storage/dbWorker.d.ts +10 -0
  127. package/dist/storage/dbWorker.js +132 -0
  128. package/dist/storage/index.d.ts +20 -0
  129. package/dist/storage/index.js +16 -0
  130. package/dist/storage/migrations.d.ts +26 -0
  131. package/dist/storage/migrations.js +207 -0
  132. package/dist/storage/types.d.ts +298 -0
  133. package/dist/storage/types.js +14 -0
  134. package/dist/wire/errors.d.ts +105 -0
  135. package/dist/wire/errors.js +143 -0
  136. package/dist/wire/events.d.ts +102 -0
  137. package/dist/wire/events.js +13 -0
  138. package/dist/wire/frames.d.ts +106 -0
  139. package/dist/wire/frames.js +156 -0
  140. package/dist/wire/index.d.ts +31 -0
  141. package/dist/wire/index.js +31 -0
  142. package/dist/wire/methods.d.ts +1896 -0
  143. package/dist/wire/methods.js +99 -0
  144. package/dist/wire/states.d.ts +487 -0
  145. package/dist/wire/states.js +61 -0
  146. package/package.json +63 -3
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 会话草稿:`conversation.create` 发出去的那段对话,在第一条用户消息之前只活在这里(R56)。
3
+ *
4
+ * PRD F-C2 的原话是「没有用户消息就不写」,所以创建时不落 `conversations` 行:这里只记住
5
+ * **id 与已经定死的身份**(项目 / 助手,PRD L7;模型与思考档位也在这份行里,只是它们可以改)。
6
+ * 首次 `conversation.send` 时由对话服务把这一份原样写进库,之后这段对话就与「创建即落库」的对话
7
+ * 没有区别。
8
+ *
9
+ * 两处刻意的选择:
10
+ *
11
+ * 1. **存的是存储层的行形状**(`Conversation`),不是另造一份 DTO:草稿与真行在服务层走同一批
12
+ * 代码路径(视图拼装、换模型、写思考档位),差别只在「有没有写进库」。
13
+ * 2. **不持久化、不共享**:进程没了草稿就没了,这是预期结果(没发过消息的会话重连后消失)。
14
+ * 所以这里没有计时器,也没有落盘。
15
+ */
16
+ import type { Conversation } from "../storage/types.js";
17
+ /**
18
+ * 对话服务要用到的草稿读写口(结构上 {@link ConversationDrafts} 满足,用例可给替身)。
19
+ *
20
+ * 只暴露服务层真正需要的三个动作:取出、放进、扔掉;「换成一个真行」是服务层自己做的事。
21
+ */
22
+ export interface ConversationDraftStore {
23
+ get(conversationId: string): Conversation | undefined;
24
+ /** 取出并摘掉(落库那一刻用:先摘再写,并发的第二次就抢不到同一份草稿) */
25
+ take(conversationId: string): Conversation | undefined;
26
+ put(draft: Conversation): void;
27
+ forget(conversationId: string): void;
28
+ }
29
+ export declare class ConversationDrafts implements ConversationDraftStore {
30
+ private readonly drafts;
31
+ get(conversationId: string): Conversation | undefined;
32
+ put(draft: Conversation): void;
33
+ /**
34
+ * 取出并摘掉(落库那一刻用)。
35
+ *
36
+ * 摘除与落库分开是故意的:先同步摘掉,两次并发的 `send` 里后到的那一次就看不到草稿,
37
+ * 不会两次插同一行。
38
+ */
39
+ take(conversationId: string): Conversation | undefined;
40
+ forget(conversationId: string): void;
41
+ /** 现在挂着几份草稿(诊断与用例用;不参与业务判断) */
42
+ size(): number;
43
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 会话草稿:`conversation.create` 发出去的那段对话,在第一条用户消息之前只活在这里(R56)。
3
+ *
4
+ * PRD F-C2 的原话是「没有用户消息就不写」,所以创建时不落 `conversations` 行:这里只记住
5
+ * **id 与已经定死的身份**(项目 / 助手,PRD L7;模型与思考档位也在这份行里,只是它们可以改)。
6
+ * 首次 `conversation.send` 时由对话服务把这一份原样写进库,之后这段对话就与「创建即落库」的对话
7
+ * 没有区别。
8
+ *
9
+ * 两处刻意的选择:
10
+ *
11
+ * 1. **存的是存储层的行形状**(`Conversation`),不是另造一份 DTO:草稿与真行在服务层走同一批
12
+ * 代码路径(视图拼装、换模型、写思考档位),差别只在「有没有写进库」。
13
+ * 2. **不持久化、不共享**:进程没了草稿就没了,这是预期结果(没发过消息的会话重连后消失)。
14
+ * 所以这里没有计时器,也没有落盘。
15
+ */
16
+ export class ConversationDrafts {
17
+ drafts = new Map();
18
+ get(conversationId) {
19
+ return this.drafts.get(conversationId);
20
+ }
21
+ put(draft) {
22
+ const id = typeof draft.id === "string" ? draft.id.trim() : "";
23
+ if (id === "")
24
+ return;
25
+ this.drafts.set(id, { ...draft, id });
26
+ }
27
+ /**
28
+ * 取出并摘掉(落库那一刻用)。
29
+ *
30
+ * 摘除与落库分开是故意的:先同步摘掉,两次并发的 `send` 里后到的那一次就看不到草稿,
31
+ * 不会两次插同一行。
32
+ */
33
+ take(conversationId) {
34
+ const draft = this.drafts.get(conversationId);
35
+ if (!draft)
36
+ return undefined;
37
+ this.drafts.delete(conversationId);
38
+ return draft;
39
+ }
40
+ forget(conversationId) {
41
+ this.drafts.delete(conversationId);
42
+ }
43
+ /** 现在挂着几份草稿(诊断与用例用;不参与业务判断) */
44
+ size() {
45
+ return this.drafts.size;
46
+ }
47
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * `RunRegistry` —— 「这段对话正在跑哪一轮」的唯一一处(architecture §4 / PRD D14)。
3
+ *
4
+ * 为什么要有它:一段对话同时跑一轮(PRD D14)。再来请求时**直接拒**并说明「正忙」,
5
+ * 不排队也不自动打断 —— 中止是用户的显式动作,只有用户能按。
6
+ *
7
+ * 为什么它是注册表而不是散在服务里的布尔:服务层要问「现在忙不忙」,接线层的
8
+ * `conversation.abort` 要找到那一轮去中止;两处问的必须是同一份事实,所以只留一处
9
+ * (`isRunning` 只有这里一个实现,见 PRD D14)。服务层只需要窄接口 `RunsView`,
10
+ * 本类结构上就满足它,不必包一层。
11
+ *
12
+ * 环的内存真相在 `ChatRunner` 那一轮自己手里(消息、生成态),本文件只记「谁在跑」——
13
+ * 跑的内容不往这里存第二份。
14
+ */
15
+ import type { Clock } from "./ScopeRegistry.js";
16
+ /** 一轮的开始时刻与中止状态:`list()` 与重连快照要用的最小事实 */
17
+ export interface RunSummary {
18
+ conversationId: string;
19
+ /** ISO 8601 */
20
+ startedAt: string;
21
+ /** 用户是否已经要求中止(中止请求已送达,那一轮还没结算) */
22
+ aborted: boolean;
23
+ }
24
+ /**
25
+ * 一轮的句柄:拿到它的人可以中止这一轮。
26
+ *
27
+ * 中止是注册表转达给执行者的(`onAbort`):注册表不认识 Agent,执行者才认识;
28
+ * 这样「谁在跑」与「怎么停」各自只有一处。
29
+ */
30
+ export interface RunHandle extends RunSummary {
31
+ /** 用户是否已经要求中止 */
32
+ readonly aborted: boolean;
33
+ /**
34
+ * 要求中止。
35
+ *
36
+ * 重复调用无副作用;中止之后仍然等执行者写库、结算才算真正结束(`finish` 由执行者叫)。
37
+ */
38
+ abort(reason?: string): void;
39
+ /**
40
+ * 登记中止时要做什么(执行者在这里挂 `agent.abort()`)。返回取消登记的函数。
41
+ *
42
+ * 允许在 `abort()` 之后才登记:那样会立刻执行一次,免得「中止来得太早」被丢掉。
43
+ */
44
+ onAbort(listener: (reason: string | undefined) => void): () => void;
45
+ }
46
+ /** 服务层要用窄接口:只问忙不忙(`RunRegistry` 直接满足,不必适配) */
47
+ export interface RunsView {
48
+ isRunning(conversationId: string): boolean;
49
+ }
50
+ export interface RunRegistryOptions {
51
+ /** 计时入口,缺省真实时钟(`startedAt` 与测试里的假时间用它) */
52
+ clock?: Clock;
53
+ }
54
+ export declare class RunRegistry implements RunsView {
55
+ private readonly runs;
56
+ private readonly clock;
57
+ constructor(options?: RunRegistryOptions);
58
+ /**
59
+ * 占位:这段对话开始跑一轮。
60
+ *
61
+ * 已经在跑就抛 `HostError("busy")` —— PRD D14:直接拒并说明「正忙」,不排队、不自动打断。
62
+ * `detail` 带上「上一次是什么时候开始的」,让界面能说清「它在忙什么」而不是干巴巴一句忙。
63
+ */
64
+ start(conversationId: string): RunHandle;
65
+ /** 结算:这一轮结束(正常、出错、被中止都走这里)。返回是否真的有这一轮 */
66
+ finish(conversationId: string): boolean;
67
+ /** 这段对话现在忙不忙 —— 服务层注入的就是这个方法 */
68
+ isRunning(conversationId: string): boolean;
69
+ /** 当前在跑的全部轮次(根对象判「能不能退出」时用) */
70
+ list(): RunSummary[];
71
+ /**
72
+ * 按对话中止(接线层的 `conversation.abort` 用):返回是否真的有在跑的一轮。
73
+ *
74
+ * `false` 表示本来就没在跑 —— 那不是错误,客户端只是按了一下没东西可停的按钮。
75
+ */
76
+ abort(conversationId: string, reason?: string): boolean;
77
+ /** 正在跑的轮次数量 */
78
+ size(): number;
79
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * `RunRegistry` —— 「这段对话正在跑哪一轮」的唯一一处(architecture §4 / PRD D14)。
3
+ *
4
+ * 为什么要有它:一段对话同时跑一轮(PRD D14)。再来请求时**直接拒**并说明「正忙」,
5
+ * 不排队也不自动打断 —— 中止是用户的显式动作,只有用户能按。
6
+ *
7
+ * 为什么它是注册表而不是散在服务里的布尔:服务层要问「现在忙不忙」,接线层的
8
+ * `conversation.abort` 要找到那一轮去中止;两处问的必须是同一份事实,所以只留一处
9
+ * (`isRunning` 只有这里一个实现,见 PRD D14)。服务层只需要窄接口 `RunsView`,
10
+ * 本类结构上就满足它,不必包一层。
11
+ *
12
+ * 环的内存真相在 `ChatRunner` 那一轮自己手里(消息、生成态),本文件只记「谁在跑」——
13
+ * 跑的内容不往这里存第二份。
14
+ */
15
+ import { HostError } from "../wire/errors.js";
16
+ import { systemClock } from "./ScopeRegistry.js";
17
+ /** 一轮的内部记录 */
18
+ class Run {
19
+ conversationId;
20
+ startedAt;
21
+ /** 中止原因(用户没给就是 undefined) */
22
+ abortReason;
23
+ abortRequested = false;
24
+ abortListeners = new Set();
25
+ constructor(conversationId, clock) {
26
+ this.conversationId = conversationId;
27
+ this.startedAt = new Date(clock.now()).toISOString();
28
+ }
29
+ get aborted() {
30
+ return this.abortRequested;
31
+ }
32
+ abort(reason) {
33
+ if (this.abortRequested)
34
+ return;
35
+ this.abortRequested = true;
36
+ this.abortReason = reason;
37
+ for (const listener of [...this.abortListeners])
38
+ listener(reason);
39
+ }
40
+ onAbort(listener) {
41
+ this.abortListeners.add(listener);
42
+ // 中止已经发生过了:登记得太晚也要收到,否则执行者会一直以为没人叫它停
43
+ if (this.abortRequested)
44
+ listener(this.abortReason);
45
+ return () => {
46
+ this.abortListeners.delete(listener);
47
+ };
48
+ }
49
+ summary() {
50
+ return {
51
+ conversationId: this.conversationId,
52
+ startedAt: this.startedAt,
53
+ aborted: this.abortRequested,
54
+ };
55
+ }
56
+ }
57
+ export class RunRegistry {
58
+ runs = new Map();
59
+ clock;
60
+ constructor(options = {}) {
61
+ this.clock = options.clock ?? systemClock;
62
+ }
63
+ /**
64
+ * 占位:这段对话开始跑一轮。
65
+ *
66
+ * 已经在跑就抛 `HostError("busy")` —— PRD D14:直接拒并说明「正忙」,不排队、不自动打断。
67
+ * `detail` 带上「上一次是什么时候开始的」,让界面能说清「它在忙什么」而不是干巴巴一句忙。
68
+ */
69
+ start(conversationId) {
70
+ const existing = this.runs.get(conversationId);
71
+ if (existing) {
72
+ throw new HostError("busy", {
73
+ detail: { conversationId, startedAt: existing.startedAt, aborted: existing.aborted },
74
+ });
75
+ }
76
+ const run = new Run(conversationId, this.clock);
77
+ this.runs.set(conversationId, run);
78
+ return run;
79
+ }
80
+ /** 结算:这一轮结束(正常、出错、被中止都走这里)。返回是否真的有这一轮 */
81
+ finish(conversationId) {
82
+ return this.runs.delete(conversationId);
83
+ }
84
+ /** 这段对话现在忙不忙 —— 服务层注入的就是这个方法 */
85
+ isRunning(conversationId) {
86
+ return this.runs.has(conversationId);
87
+ }
88
+ /** 当前在跑的全部轮次(根对象判「能不能退出」时用) */
89
+ list() {
90
+ return [...this.runs.values()].map((run) => run.summary());
91
+ }
92
+ /**
93
+ * 按对话中止(接线层的 `conversation.abort` 用):返回是否真的有在跑的一轮。
94
+ *
95
+ * `false` 表示本来就没在跑 —— 那不是错误,客户端只是按了一下没东西可停的按钮。
96
+ */
97
+ abort(conversationId, reason) {
98
+ const run = this.runs.get(conversationId);
99
+ if (!run)
100
+ return false;
101
+ run.abort(reason);
102
+ return true;
103
+ }
104
+ /** 正在跑的轮次数量 */
105
+ size() {
106
+ return this.runs.size;
107
+ }
108
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * `ScopeRegistry` —— workspaceId → `Scope` 的持有者、依赖计数与空闲回收(architecture §4 / PRD D28)。
3
+ *
4
+ * 一个 Workspace 一个 Scope(PRD D9):同一 Workspace 下的多段对话复用同一个 Scope,
5
+ * 所以这里记的是**依赖计数**而不是「谁在打开它」。计数归零之后不立刻拆,而是起一个
6
+ * 10 分钟的空闲计时;计时到点才真正销毁 —— 销毁就是 `Scope.dispose()`,它会逆序调用各插件的
7
+ * `dispose()`,MCP 插件在那里断开它自持的全部连接,于是「Scope 与 MCP 连接同生共死」
8
+ * 不用第二套代码来表达。
9
+ *
10
+ * 计时器只有这一处(architecture §4 A10):L4 说的「10 分钟无活动」与 D28 说的
11
+ * 「Scope 空闲 10 分钟」是同一个计时器的两个说法,别处不再另起一份。
12
+ *
13
+ * Scope 会被销毁的三种场合(architecture §4):空闲到点、Workspace 被删、配置变更要重连 MCP。
14
+ * 三者都收敛到本文件的 `dispose`,区别只在「谁来叫」。
15
+ *
16
+ * 时间与 Scope 的创建方式都从构造参数进来:测试用假时钟推进十分钟,接线层提供真实的
17
+ * SDK 装配(见本文件末尾的 `createWorkspaceScopeFactory`)。
18
+ */
19
+ import type { AppConfig, ToolEnvDeps } from "@ai-zen/agents-sdk";
20
+ import { Scope } from "@ai-zen/agents-sdk";
21
+ /** 空闲回收的缺省时长:10 分钟(PRD D28) */
22
+ export declare const DEFAULT_SCOPE_IDLE_TIMEOUT_MS: number;
23
+ /**
24
+ * 计时入口。
25
+ *
26
+ * `setTimeout` 返回「取消函数」而不是句柄:假时钟与真定时器都能实现它,调用方不必知道
27
+ * 句柄长什么样,也就不需要 `clearTimeout` 的第二个入口。
28
+ */
29
+ export interface Clock {
30
+ /** 当前时刻(毫秒,`Date.now()` 口径) */
31
+ now(): number;
32
+ /** 到点后执行 `handler`,返回取消函数 */
33
+ setTimeout(handler: () => void, ms: number): () => void;
34
+ }
35
+ /**
36
+ * 真实时钟。无状态,可以放心共享;需要可控时间的地方(测试、或将来把时钟挂到根对象上)
37
+ * 通过构造参数换成别的实现。
38
+ */
39
+ export declare const systemClock: Clock;
40
+ /**
41
+ * 造一个 Scope 的方式(按 workspaceId)。
42
+ *
43
+ * 之所以是外部注入而不是本文件自己 `new Scope(...)`:装配 Scope 需要配置快照、能力路径、
44
+ * MCP 配置的 5 层链(PRD L9)—— 那些都是配置层的财产;本文件只管「什么时候有、什么时候拆」。
45
+ * 接线层若不想自己写,用本文件末尾的 `createWorkspaceScopeFactory`。
46
+ */
47
+ export type ScopeFactory = (workspaceId: string) => Promise<Scope>;
48
+ export interface ScopeRegistryOptions {
49
+ /** 懒创建:只有 `acquire` 到不存在的 workspaceId 时才叫它 */
50
+ createScope: ScopeFactory;
51
+ /** 空闲回收时长,缺省 10 分钟 */
52
+ idleTimeoutMs?: number;
53
+ /** 计时入口,缺省真实时钟 */
54
+ clock?: Clock;
55
+ /** 销毁 Scope 时抛出的错误往哪里报(缺省吞掉:回收失败不该让调用方以为业务失败了) */
56
+ onError?: (error: unknown, workspaceId: string) => void;
57
+ }
58
+ export declare class ScopeRegistry {
59
+ private readonly entries;
60
+ private readonly createScope;
61
+ private readonly idleTimeoutMs;
62
+ private readonly clock;
63
+ private readonly onError?;
64
+ private readonly allRecycledListeners;
65
+ constructor(options: ScopeRegistryOptions);
66
+ /**
67
+ * 借一个 Scope:没有就懒创建,依赖计数 +1,并取消空闲计时。
68
+ *
69
+ * 并发的两次 `acquire` 只会创建一次(两个调用方等同一个 Promise)。
70
+ */
71
+ acquire(workspaceId: string): Promise<Scope>;
72
+ /** 创建失败时不留下坏条目:下一次 acquire 重新尝试 */
73
+ private create;
74
+ /**
75
+ * 归还:依赖计数 −1。归零后按两种场合分叉 ——
76
+ * 平时起 10 分钟空闲计时;若期间发生过配置变更(`recycleAll`)要求它重建,就立刻销毁。
77
+ */
78
+ release(workspaceId: string): void;
79
+ /** 看而不借:已经建好就返回它,没有或还在建就返回 `undefined` */
80
+ peek(workspaceId: string): Scope | undefined;
81
+ /** 当前持有几个 Scope(含正在创建的) */
82
+ size(): number;
83
+ /** 当前持有哪几个(配置改动后要报告「回收了哪些项目的 Scope」) */
84
+ workspaceIds(): string[];
85
+ /**
86
+ * 全部回收 —— 配置改完要重连 MCP 时用(PRD F-E2:改完即生效,靠重连而不是重启)。
87
+ *
88
+ * 没人用的立刻销毁;正被持有的打上标记,等最后一次 `release` 再销毁。这样正在跑的那一轮
89
+ * 不会在写到一半时被抽掉能力,而它结束后下一段对话拿到的已经是按新配置建的 Scope。
90
+ */
91
+ recycleAll(): Promise<void>;
92
+ /**
93
+ * 销毁某一个 Workspace 的 Scope —— Workspace 被删时用。
94
+ *
95
+ * 与 `recycleAll` 的区别:这里不等引用归零,因为 Workspace 已经不在了,
96
+ * 再留着这个 Scope 也没有下一个调用方。
97
+ */
98
+ disposeWorkspace(workspaceId: string): Promise<void>;
99
+ /**
100
+ * 全部回收之后通知根对象(architecture §4:根对象据此判断进程能不能退出)。
101
+ *
102
+ * 返回取消订阅的函数。
103
+ */
104
+ onAllRecycled(listener: () => void): () => void;
105
+ private scheduleIdle;
106
+ private cancelIdle;
107
+ /**
108
+ * 销毁:先把条目从表里摘掉(之后 acquire 会重建),再等创建完成并 `dispose()`。
109
+ *
110
+ * 创建还没完成就得销毁时也要等它 —— 否则刚建出来的 Scope 没人拆,MCP 连接会漏。
111
+ */
112
+ private dispose;
113
+ private doDispose;
114
+ private notifyAllRecycled;
115
+ }
116
+ /**
117
+ * 按 SDK 口径装配 Scope 的现成工厂。
118
+ *
119
+ * 装配内容:`new Scope({ config, cwd, agentsDir })` + `allInOne(...)`(内置工具、用户工具、
120
+ * Skill、MCP、子 Agent 五类插件),然后 `init()` 做首次能力发现。MCP 插件自带连接管理器,
121
+ * 于是「Scope 建好 = MCP 配置已发现」,`dispose()` 时连接一起断。
122
+ *
123
+ * 工作目录、配置、各能力路径都由接线层给:本文件不去问进程当前目录,也不猜数据根。
124
+ */
125
+ export interface WorkspaceScopeFactoryOptions {
126
+ /** 按 workspaceId 取工作目录(Workspace 的 cwd 不可变,PRD D9) */
127
+ resolveWorkspace(workspaceId: string): Promise<{
128
+ cwd: string;
129
+ } | undefined>;
130
+ /** 取当前配置:配置改过之后再 acquire 会拿到新的那一份 */
131
+ getConfig(): AppConfig | Promise<AppConfig>;
132
+ /** Agent 定义目录(SDK 的 `createAgent` 从这里读定义) */
133
+ agentsDir: string;
134
+ /** Skill 目录(可多层,越靠前优先级越高);也能给一个「按 cwd 算」的函数 */
135
+ skillsPaths?: AbilityPathOption;
136
+ /** 用户工具目录 */
137
+ toolsPaths?: AbilityPathOption;
138
+ /** 子 Agent 目录 */
139
+ subAgentsPaths?: AbilityPathOption;
140
+ /** MCP 配置文件的 5 层链(PRD L9),按 cwd 算好 */
141
+ mcpPaths?: (cwd: string) => string[];
142
+ /** 出网/外部依赖的注入点(测试替身) */
143
+ deps?: ToolEnvDeps;
144
+ }
145
+ /**
146
+ * 一组能力目录:可以是固定的一份,也可以是「按 cwd 算」的函数。
147
+ *
148
+ * Skill / 工具 / 子 Agent 的目录跟项目走(L9:离项目越近越优先),而 Scope 是按 cwd 装配的,
149
+ * 所以装配处需要把「按 cwd 算」这件事交进来,而不是在构造时先算一份死的。
150
+ */
151
+ export type AbilityPathOption = string[] | ((cwd: string) => string[]);
152
+ /** 造一个「按 SDK 装能力」的工厂;返回值直接喂给 `ScopeRegistry` 的 `createScope` */
153
+ export declare function createWorkspaceScopeFactory(options: WorkspaceScopeFactoryOptions): ScopeFactory;