@mebular/fleet 0.0.0-stage → 0.1.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 (178) hide show
  1. package/dist/agentdir.d.ts +43 -0
  2. package/dist/agentdir.d.ts.map +1 -0
  3. package/dist/agentdir.js +107 -0
  4. package/dist/agentdir.js.map +1 -0
  5. package/dist/cli.d.ts +3 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +1135 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/collab/chatter.d.ts +54 -0
  10. package/dist/collab/chatter.d.ts.map +1 -0
  11. package/dist/collab/chatter.js +94 -0
  12. package/dist/collab/chatter.js.map +1 -0
  13. package/dist/collab/dag.d.ts +27 -0
  14. package/dist/collab/dag.d.ts.map +1 -0
  15. package/dist/collab/dag.js +107 -0
  16. package/dist/collab/dag.js.map +1 -0
  17. package/dist/collab/negotiation.d.ts +60 -0
  18. package/dist/collab/negotiation.d.ts.map +1 -0
  19. package/dist/collab/negotiation.js +90 -0
  20. package/dist/collab/negotiation.js.map +1 -0
  21. package/dist/collab/tree.d.ts +39 -0
  22. package/dist/collab/tree.d.ts.map +1 -0
  23. package/dist/collab/tree.js +177 -0
  24. package/dist/collab/tree.js.map +1 -0
  25. package/dist/config.d.ts +123 -0
  26. package/dist/config.d.ts.map +1 -0
  27. package/dist/config.js +206 -0
  28. package/dist/config.js.map +1 -0
  29. package/dist/index.d.ts +33 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +40 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/join.d.ts +50 -0
  34. package/dist/join.d.ts.map +1 -0
  35. package/dist/join.js +167 -0
  36. package/dist/join.js.map +1 -0
  37. package/dist/jointoken.d.ts +144 -0
  38. package/dist/jointoken.d.ts.map +1 -0
  39. package/dist/jointoken.js +472 -0
  40. package/dist/jointoken.js.map +1 -0
  41. package/dist/model.d.ts +64 -0
  42. package/dist/model.d.ts.map +1 -0
  43. package/dist/model.js +158 -0
  44. package/dist/model.js.map +1 -0
  45. package/dist/onboard.d.ts +90 -0
  46. package/dist/onboard.d.ts.map +1 -0
  47. package/dist/onboard.js +510 -0
  48. package/dist/onboard.js.map +1 -0
  49. package/dist/protocol/envelope.d.ts +94 -0
  50. package/dist/protocol/envelope.d.ts.map +1 -0
  51. package/dist/protocol/envelope.js +169 -0
  52. package/dist/protocol/envelope.js.map +1 -0
  53. package/dist/protocol/events.d.ts +51 -0
  54. package/dist/protocol/events.d.ts.map +1 -0
  55. package/dist/protocol/events.js +87 -0
  56. package/dist/protocol/events.js.map +1 -0
  57. package/dist/qr.d.ts +15 -0
  58. package/dist/qr.d.ts.map +1 -0
  59. package/dist/qr.js +52 -0
  60. package/dist/qr.js.map +1 -0
  61. package/dist/quickstart.d.ts +167 -0
  62. package/dist/quickstart.d.ts.map +1 -0
  63. package/dist/quickstart.js +482 -0
  64. package/dist/quickstart.js.map +1 -0
  65. package/dist/quota.d.ts +41 -0
  66. package/dist/quota.d.ts.map +1 -0
  67. package/dist/quota.js +84 -0
  68. package/dist/quota.js.map +1 -0
  69. package/dist/runtime/agent.d.ts +100 -0
  70. package/dist/runtime/agent.d.ts.map +1 -0
  71. package/dist/runtime/agent.js +260 -0
  72. package/dist/runtime/agent.js.map +1 -0
  73. package/dist/runtime/executor.d.ts +42 -0
  74. package/dist/runtime/executor.d.ts.map +1 -0
  75. package/dist/runtime/executor.js +87 -0
  76. package/dist/runtime/executor.js.map +1 -0
  77. package/dist/runtime/fairness.d.ts +25 -0
  78. package/dist/runtime/fairness.d.ts.map +1 -0
  79. package/dist/runtime/fairness.js +79 -0
  80. package/dist/runtime/fairness.js.map +1 -0
  81. package/dist/runtime/node.d.ts +91 -0
  82. package/dist/runtime/node.d.ts.map +1 -0
  83. package/dist/runtime/node.js +203 -0
  84. package/dist/runtime/node.js.map +1 -0
  85. package/dist/runtime/openchamber-http.d.ts +27 -0
  86. package/dist/runtime/openchamber-http.d.ts.map +1 -0
  87. package/dist/runtime/openchamber-http.js +118 -0
  88. package/dist/runtime/openchamber-http.js.map +1 -0
  89. package/dist/runtime/openchamber.d.ts +38 -0
  90. package/dist/runtime/openchamber.d.ts.map +1 -0
  91. package/dist/runtime/openchamber.js +43 -0
  92. package/dist/runtime/openchamber.js.map +1 -0
  93. package/dist/runtime/planner.d.ts +23 -0
  94. package/dist/runtime/planner.d.ts.map +1 -0
  95. package/dist/runtime/planner.js +17 -0
  96. package/dist/runtime/planner.js.map +1 -0
  97. package/dist/runtime/worker.d.ts +96 -0
  98. package/dist/runtime/worker.d.ts.map +1 -0
  99. package/dist/runtime/worker.js +296 -0
  100. package/dist/runtime/worker.js.map +1 -0
  101. package/dist/security.d.ts +462 -0
  102. package/dist/security.d.ts.map +1 -0
  103. package/dist/security.js +1197 -0
  104. package/dist/security.js.map +1 -0
  105. package/dist/store/daemon-store.d.ts +33 -0
  106. package/dist/store/daemon-store.d.ts.map +1 -0
  107. package/dist/store/daemon-store.js +115 -0
  108. package/dist/store/daemon-store.js.map +1 -0
  109. package/dist/store/file-store.d.ts +22 -0
  110. package/dist/store/file-store.d.ts.map +1 -0
  111. package/dist/store/file-store.js +63 -0
  112. package/dist/store/file-store.js.map +1 -0
  113. package/dist/store/mebular-store.d.ts +22 -0
  114. package/dist/store/mebular-store.d.ts.map +1 -0
  115. package/dist/store/mebular-store.js +53 -0
  116. package/dist/store/mebular-store.js.map +1 -0
  117. package/dist/store/message-store.d.ts +26 -0
  118. package/dist/store/message-store.d.ts.map +1 -0
  119. package/dist/store/message-store.js +51 -0
  120. package/dist/store/message-store.js.map +1 -0
  121. package/dist/surface.d.ts +43 -0
  122. package/dist/surface.d.ts.map +1 -0
  123. package/dist/surface.js +515 -0
  124. package/dist/surface.js.map +1 -0
  125. package/dist/transport/null.d.ts +7 -0
  126. package/dist/transport/null.d.ts.map +1 -0
  127. package/dist/transport/null.js +16 -0
  128. package/dist/transport/null.js.map +1 -0
  129. package/dist/transport/spool.d.ts +12 -0
  130. package/dist/transport/spool.d.ts.map +1 -0
  131. package/dist/transport/spool.js +54 -0
  132. package/dist/transport/spool.js.map +1 -0
  133. package/dist/transport/types.d.ts +21 -0
  134. package/dist/transport/types.d.ts.map +1 -0
  135. package/dist/transport/types.js +6 -0
  136. package/dist/transport/types.js.map +1 -0
  137. package/dist/version.json +6 -0
  138. package/package.json +37 -4
  139. package/protocol/collab.example.json +23 -0
  140. package/protocol/daemon-home.example.json +66 -0
  141. package/protocol/envelope.example.json +12 -0
  142. package/protocol/state-machine.json +14 -0
  143. package/protocol/task-tree.example.json +72 -0
  144. package/src/agentdir.ts +138 -0
  145. package/src/cli.ts +1192 -0
  146. package/src/collab/chatter.ts +116 -0
  147. package/src/collab/dag.ts +124 -0
  148. package/src/collab/negotiation.ts +130 -0
  149. package/src/collab/tree.ts +184 -0
  150. package/src/config.ts +286 -0
  151. package/src/index.ts +39 -0
  152. package/src/join.ts +227 -0
  153. package/src/jointoken.ts +609 -0
  154. package/src/model.ts +203 -0
  155. package/src/onboard.ts +601 -0
  156. package/src/protocol/envelope.ts +228 -0
  157. package/src/protocol/events.ts +135 -0
  158. package/src/qr.ts +68 -0
  159. package/src/quickstart.ts +632 -0
  160. package/src/quota.ts +112 -0
  161. package/src/runtime/agent.ts +313 -0
  162. package/src/runtime/executor.ts +114 -0
  163. package/src/runtime/fairness.ts +90 -0
  164. package/src/runtime/node.ts +250 -0
  165. package/src/runtime/openchamber-http.ts +137 -0
  166. package/src/runtime/openchamber.ts +66 -0
  167. package/src/runtime/planner.ts +36 -0
  168. package/src/runtime/worker.ts +354 -0
  169. package/src/security.ts +1660 -0
  170. package/src/store/daemon-store.ts +144 -0
  171. package/src/store/file-store.ts +75 -0
  172. package/src/store/mebular-store.ts +66 -0
  173. package/src/store/message-store.ts +62 -0
  174. package/src/surface.ts +535 -0
  175. package/src/transport/null.ts +20 -0
  176. package/src/transport/spool.ts +62 -0
  177. package/src/transport/types.ts +27 -0
  178. package/README.md +0 -3
@@ -0,0 +1,100 @@
1
+ import type { TaskState } from '../model.js';
2
+ import type { ExecutionOutcome, TaskExecutor } from './executor.js';
3
+ /** 按 agent 名字选择执行器;**未知 agent 返回 null**(调用方须显式失败,不得静默回退)。 */
4
+ export declare class ExecutorRegistry {
5
+ private readonly exact;
6
+ private wildcard;
7
+ /** 注册某 agent 名;`'*'` 为通配(仅匹配 `to.agent === '*'`)。 */
8
+ register(agent: string, executor: TaskExecutor): this;
9
+ resolve(agent: string): TaskExecutor | null;
10
+ has(agent: string): boolean;
11
+ names(): string[];
12
+ }
13
+ /** UTF-8 安全截断(按字节),附截断标记。 */
14
+ export declare function truncateOutput(text: string, maxBytes: number): {
15
+ text: string;
16
+ truncated: boolean;
17
+ totalBytes: number;
18
+ };
19
+ export interface SpawnResult {
20
+ code: number | null;
21
+ /** 已捕获的 stdout(至多 `maxOutputBytes` 字节) */
22
+ stdout: string;
23
+ /** stdout 的**总字节数**(含未捕获部分) */
24
+ stdoutBytes: number;
25
+ stderrTail: string;
26
+ timedOut: boolean;
27
+ }
28
+ /**
29
+ * 允许透传给被派发 Agent 的环境变量**白名单**(非私密运行所需)。
30
+ * 其余(尤其凭据/令牌)**一律不继承**——daemon 的 env 不外流。
31
+ */
32
+ export declare const ENV_ALLOWLIST: readonly string[];
33
+ /** 按白名单构建子进程 env;`extra`(调用方显式给出)覆盖白名单同名项。 */
34
+ export declare function buildChildEnv(extra?: Readonly<Record<string, string>>, source?: NodeJS.ProcessEnv): Record<string, string>;
35
+ /** 以参数数组执行命令;捕获 stdout/stderr(stderr 保留尾部);超时 kill。 */
36
+ export declare function runOnce(options: {
37
+ command: string;
38
+ args: readonly string[];
39
+ cwd?: string;
40
+ env?: Readonly<Record<string, string>>;
41
+ timeoutMs: number;
42
+ maxOutputBytes: number;
43
+ }): Promise<SpawnResult>;
44
+ export interface CommandAgentOptions {
45
+ /** 可执行文件(不经 shell) */
46
+ command: string;
47
+ /** 命令后、prompt 参数前的固定参数 */
48
+ baseArgs?: readonly string[];
49
+ /** 由任务生成 prompt 相关参数(默认 `['-z', task.intent]`) */
50
+ promptArgs?: (task: TaskState) => string[];
51
+ cwd?: string;
52
+ /** 额外环境变量(值不会被打印/落盘) */
53
+ env?: Readonly<Record<string, string>>;
54
+ /** 超时(默认 60000ms) */
55
+ timeoutMs?: number;
56
+ /** stdout 截断上限字节(默认 65536) */
57
+ maxOutputBytes?: number;
58
+ /** 并发上限(默认 1) */
59
+ concurrency?: number;
60
+ /** 结果引用生成(默认:截断后的 stdout) */
61
+ resultRef?: (stdout: string, task: TaskState) => string;
62
+ }
63
+ /** 通用「命令行 Agent」执行器。 */
64
+ export declare class CommandAgent implements TaskExecutor {
65
+ private readonly options;
66
+ private readonly sem;
67
+ constructor(options: CommandAgentOptions);
68
+ execute(task: TaskState): Promise<ExecutionOutcome>;
69
+ }
70
+ export interface HermesAgentOptions {
71
+ /** hermes 可执行文件(默认 `hermes`) */
72
+ hermesPath?: string;
73
+ /** 命令后、hermes 参数前的固定参数(测试注入用;生产留空) */
74
+ commandArgs?: readonly string[];
75
+ /** Hermes profile(`-p`) */
76
+ profile?: string;
77
+ /** toolsets(`-t`) */
78
+ toolsets?: string;
79
+ /** 模型(`-m`) */
80
+ model?: string;
81
+ /** 工作目录(`--in`) */
82
+ cwd?: string;
83
+ timeoutMs?: number;
84
+ maxOutputBytes?: number;
85
+ concurrency?: number;
86
+ /** `--usage-file` 临时目录 */
87
+ usageDir?: string;
88
+ }
89
+ /**
90
+ * Hermes 一次性非交互适配器:`hermes [-p P] [-t T] [-m M] [--in DIR] -z <prompt> --usage-file <tmp>`。
91
+ * 成功 → 结果 = stdout(截断);超时/非零退出 → `failed` 带原因与 stderr 尾巴。
92
+ */
93
+ export declare class HermesAgent implements TaskExecutor {
94
+ private readonly options;
95
+ private readonly sem;
96
+ constructor(options?: HermesAgentOptions);
97
+ private argv;
98
+ execute(task: TaskState): Promise<ExecutionOutcome>;
99
+ }
100
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/runtime/agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,260 @@
1
+ // Fleet Agent 适配器(M4):按 `to.agent` 名字路由到可插拔执行器。
2
+ //
3
+ // 安全与工程约束:
4
+ // - **参数数组传参**(绝不 shell 拼接,防注入);
5
+ // - 超时 / 非零退出 / 输出超限 各自语义明确(超时与非零 → 任务 `failed` 带原因);
6
+ // - **不继承 daemon 的全量 env**:只透传 `ENV_ALLOWLIST`(PATH/HOME/… 运行所需)+ 调用方显式
7
+ // 给出的 `options.env`;凭据/令牌不外流。同时不打印/不落盘任何 env 值;
8
+ // - 工作目录与并发上限可配。
9
+ import { spawn } from 'node:child_process';
10
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
11
+ import { tmpdir } from 'node:os';
12
+ import { join } from 'node:path';
13
+ /** 按 agent 名字选择执行器;**未知 agent 返回 null**(调用方须显式失败,不得静默回退)。 */
14
+ export class ExecutorRegistry {
15
+ exact = new Map();
16
+ wildcard = null;
17
+ /** 注册某 agent 名;`'*'` 为通配(仅匹配 `to.agent === '*'`)。 */
18
+ register(agent, executor) {
19
+ if (agent === '*')
20
+ this.wildcard = executor;
21
+ else
22
+ this.exact.set(agent, executor);
23
+ return this;
24
+ }
25
+ resolve(agent) {
26
+ if (agent === '*')
27
+ return this.wildcard;
28
+ return this.exact.get(agent) ?? null; // 显式:不 fallback 到通配
29
+ }
30
+ has(agent) {
31
+ return agent === '*' ? this.wildcard !== null : this.exact.has(agent);
32
+ }
33
+ names() {
34
+ return [...this.exact.keys()].sort();
35
+ }
36
+ }
37
+ /** UTF-8 安全截断(按字节),附截断标记。 */
38
+ export function truncateOutput(text, maxBytes) {
39
+ const buf = Buffer.from(text, 'utf-8');
40
+ if (buf.byteLength <= maxBytes)
41
+ return { text, truncated: false, totalBytes: buf.byteLength };
42
+ const head = buf.subarray(0, maxBytes).toString('utf-8');
43
+ return { text: `${head}\n…[truncated ${buf.byteLength - maxBytes} bytes]`, truncated: true, totalBytes: buf.byteLength };
44
+ }
45
+ /** 简单并发闸(确定性:FIFO)。 */
46
+ class Semaphore {
47
+ limit;
48
+ active = 0;
49
+ waiters = [];
50
+ constructor(limit) {
51
+ this.limit = limit;
52
+ }
53
+ async acquire() {
54
+ if (this.active < this.limit) {
55
+ this.active += 1;
56
+ return;
57
+ }
58
+ await new Promise((resolve) => this.waiters.push(resolve));
59
+ this.active += 1;
60
+ }
61
+ release() {
62
+ this.active -= 1;
63
+ const next = this.waiters.shift();
64
+ if (next)
65
+ next();
66
+ }
67
+ }
68
+ /**
69
+ * 允许透传给被派发 Agent 的环境变量**白名单**(非私密运行所需)。
70
+ * 其余(尤其凭据/令牌)**一律不继承**——daemon 的 env 不外流。
71
+ */
72
+ export const ENV_ALLOWLIST = [
73
+ 'PATH',
74
+ 'HOME',
75
+ 'LANG',
76
+ 'LC_ALL',
77
+ 'LC_CTYPE',
78
+ 'TZ',
79
+ 'SHELL',
80
+ 'USER',
81
+ 'LOGNAME',
82
+ 'TMPDIR',
83
+ 'TEMP',
84
+ 'TMP',
85
+ 'HERMES_HOME', // Hermes profile 解析(非密)
86
+ // Windows 运行所需(存在才带)
87
+ 'SystemRoot',
88
+ 'PATHEXT',
89
+ 'COMSPEC',
90
+ 'USERPROFILE',
91
+ 'APPDATA',
92
+ 'LOCALAPPDATA',
93
+ 'ProgramData',
94
+ ];
95
+ /** 按白名单构建子进程 env;`extra`(调用方显式给出)覆盖白名单同名项。 */
96
+ export function buildChildEnv(extra, source = process.env) {
97
+ const env = {};
98
+ for (const key of ENV_ALLOWLIST) {
99
+ const value = source[key];
100
+ if (value !== undefined)
101
+ env[key] = value;
102
+ }
103
+ for (const [key, value] of Object.entries(extra ?? {}))
104
+ env[key] = value;
105
+ return env;
106
+ }
107
+ /** 以参数数组执行命令;捕获 stdout/stderr(stderr 保留尾部);超时 kill。 */
108
+ export async function runOnce(options) {
109
+ const limit = options.maxOutputBytes;
110
+ return new Promise((resolve) => {
111
+ let child;
112
+ try {
113
+ child = spawn(options.command, [...options.args], {
114
+ ...(options.cwd !== undefined ? { cwd: options.cwd } : {}),
115
+ // 只继承白名单 + 显式 env;不把 daemon 的全量环境(含凭据)传给被派发 Agent。
116
+ env: buildChildEnv(options.env),
117
+ stdio: ['ignore', 'pipe', 'pipe'],
118
+ });
119
+ }
120
+ catch (error) {
121
+ resolve({ code: null, stdout: '', stdoutBytes: 0, stderrTail: error.message, timedOut: false });
122
+ return;
123
+ }
124
+ const outChunks = [];
125
+ const errChunks = [];
126
+ let outBytes = 0;
127
+ let totalOutBytes = 0;
128
+ let timedOut = false;
129
+ const timer = setTimeout(() => {
130
+ timedOut = true;
131
+ child.kill('SIGKILL');
132
+ }, Math.max(1, options.timeoutMs));
133
+ child.stdout?.on('data', (chunk) => {
134
+ totalOutBytes += chunk.length;
135
+ if (outBytes < limit) {
136
+ // 按**字节**精确封顶:大 chunk 只取剩余额度,避免截断结果超过 maxOutputBytes。
137
+ const remaining = limit - outBytes;
138
+ const piece = chunk.length <= remaining ? chunk : chunk.subarray(0, remaining);
139
+ outChunks.push(piece);
140
+ outBytes += piece.length;
141
+ }
142
+ });
143
+ child.stderr?.on('data', (chunk) => {
144
+ errChunks.push(chunk);
145
+ while (errChunks.length > 0 && Buffer.concat(errChunks).length > 8192)
146
+ errChunks.shift();
147
+ });
148
+ const finish = (code) => {
149
+ clearTimeout(timer);
150
+ const stdout = Buffer.concat(outChunks).toString('utf-8');
151
+ const stderrTail = Buffer.concat(errChunks).toString('utf-8').trim().slice(-800);
152
+ resolve({ code, stdout, stdoutBytes: totalOutBytes, stderrTail, timedOut });
153
+ };
154
+ child.on('error', () => finish(null));
155
+ child.on('close', (code) => finish(code));
156
+ });
157
+ }
158
+ /** 通用「命令行 Agent」执行器。 */
159
+ export class CommandAgent {
160
+ options;
161
+ sem;
162
+ constructor(options) {
163
+ this.options = options;
164
+ this.sem = new Semaphore(Math.max(1, options.concurrency ?? 1));
165
+ }
166
+ async execute(task) {
167
+ await this.sem.acquire();
168
+ try {
169
+ const args = [
170
+ ...(this.options.baseArgs ?? []),
171
+ ...(this.options.promptArgs ? this.options.promptArgs(task) : ['-z', task.intent]),
172
+ ];
173
+ const timeoutMs = this.options.timeoutMs ?? 60_000;
174
+ const maxOutputBytes = this.options.maxOutputBytes ?? 65_536;
175
+ const result = await runOnce({
176
+ command: this.options.command,
177
+ args,
178
+ ...(this.options.cwd !== undefined ? { cwd: this.options.cwd } : {}),
179
+ ...(this.options.env !== undefined ? { env: this.options.env } : {}),
180
+ timeoutMs,
181
+ maxOutputBytes,
182
+ });
183
+ if (result.timedOut)
184
+ return { ok: false, reason: `TIMEOUT after ${timeoutMs}ms` };
185
+ if (result.code !== 0) {
186
+ return { ok: false, reason: `EXIT_${result.code}${result.stderrTail ? `: ${result.stderrTail}` : ''}` };
187
+ }
188
+ const stored = result.stdout.trim();
189
+ const text = result.stdoutBytes > maxOutputBytes ? `${stored}\n…[truncated ${result.stdoutBytes - maxOutputBytes} bytes]` : stored;
190
+ const resultRef = this.options.resultRef ? this.options.resultRef(text, task) : text;
191
+ return { ok: true, resultRef };
192
+ }
193
+ finally {
194
+ this.sem.release();
195
+ }
196
+ }
197
+ }
198
+ /**
199
+ * Hermes 一次性非交互适配器:`hermes [-p P] [-t T] [-m M] [--in DIR] -z <prompt> --usage-file <tmp>`。
200
+ * 成功 → 结果 = stdout(截断);超时/非零退出 → `failed` 带原因与 stderr 尾巴。
201
+ */
202
+ export class HermesAgent {
203
+ options;
204
+ sem;
205
+ constructor(options = {}) {
206
+ this.options = options;
207
+ this.sem = new Semaphore(Math.max(1, options.concurrency ?? 2));
208
+ }
209
+ argv(prompt, usagePath) {
210
+ const args = [...(this.options.commandArgs ?? [])];
211
+ if (this.options.profile)
212
+ args.push('-p', this.options.profile);
213
+ if (this.options.toolsets)
214
+ args.push('-t', this.options.toolsets);
215
+ if (this.options.model)
216
+ args.push('-m', this.options.model);
217
+ if (this.options.cwd)
218
+ args.push('--in', this.options.cwd);
219
+ args.push('-z', prompt, '--usage-file', usagePath);
220
+ return args;
221
+ }
222
+ async execute(task) {
223
+ await this.sem.acquire();
224
+ const dir = await mkdtemp(join(this.options.usageDir ?? tmpdir(), 'fleet-hermes-'));
225
+ const usagePath = join(dir, 'usage.json');
226
+ try {
227
+ const timeoutMs = this.options.timeoutMs ?? 600_000;
228
+ const maxOutputBytes = this.options.maxOutputBytes ?? 65_536;
229
+ const result = await runOnce({
230
+ command: this.options.hermesPath ?? 'hermes',
231
+ args: this.argv(task.intent, usagePath),
232
+ ...(this.options.cwd !== undefined ? { cwd: this.options.cwd } : {}),
233
+ timeoutMs,
234
+ maxOutputBytes,
235
+ });
236
+ if (result.timedOut)
237
+ return { ok: false, reason: `TIMEOUT after ${timeoutMs}ms` };
238
+ if (result.code !== 0) {
239
+ return { ok: false, reason: `EXIT_${result.code}${result.stderrTail ? `: ${result.stderrTail}` : ''}` };
240
+ }
241
+ const stored = result.stdout.trim();
242
+ const text = result.stdoutBytes > maxOutputBytes ? `${stored}\n…[truncated ${result.stdoutBytes - maxOutputBytes} bytes]` : stored;
243
+ // usage 仅用于观测 sessionId;解析失败不影响结果。
244
+ let sessionId;
245
+ try {
246
+ const usage = JSON.parse(await readFile(usagePath, 'utf-8'));
247
+ sessionId = usage.session_id;
248
+ }
249
+ catch {
250
+ sessionId = undefined;
251
+ }
252
+ return { ok: true, ...(sessionId !== undefined ? { reason: `session:${sessionId}` } : {}), resultRef: text };
253
+ }
254
+ finally {
255
+ await rm(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 20 });
256
+ this.sem.release();
257
+ }
258
+ }
259
+ }
260
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/runtime/agent.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,WAAW;AACX,iCAAiC;AACjC,sDAAsD;AACtD,wEAAwE;AACxE,kDAAkD;AAClD,iBAAiB;AAEjB,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,6DAA6D;AAC7D,MAAM,OAAO,gBAAgB;IACV,KAAK,GAAG,IAAI,GAAG,EAAwB,CAAC;IACjD,QAAQ,GAAwB,IAAI,CAAC;IAE7C,qDAAqD;IACrD,QAAQ,CAAC,KAAa,EAAE,QAAsB;QAC5C,IAAI,KAAK,KAAK,GAAG;YAAE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;;YACvC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CAAC,KAAa;QACnB,IAAI,KAAK,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;QACxC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,oBAAoB;IAC5D,CAAC;IAED,GAAG,CAAC,KAAa;QACf,OAAO,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACxE,CAAC;IAED,KAAK;QACH,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvC,CAAC;CACF;AAED,6BAA6B;AAC7B,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,QAAgB;IAC3D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACvC,IAAI,GAAG,CAAC,UAAU,IAAI,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC;IAC9F,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzD,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,iBAAiB,GAAG,CAAC,UAAU,GAAG,QAAQ,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC;AAC3H,CAAC;AAED,uBAAuB;AACvB,MAAM,SAAS;IAGgB;IAFrB,MAAM,GAAG,CAAC,CAAC;IACF,OAAO,GAAsB,EAAE,CAAC;IACjD,YAA6B,KAAa;QAAb,UAAK,GAAL,KAAK,CAAQ;IAAG,CAAC;IAC9C,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;YACjB,OAAO;QACT,CAAC;QACD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO;QACL,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,IAAI;YAAE,IAAI,EAAE,CAAC;IACnB,CAAC;CACF;AAYD;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAsB;IAC9C,MAAM;IACN,MAAM;IACN,MAAM;IACN,QAAQ;IACR,UAAU;IACV,IAAI;IACJ,OAAO;IACP,MAAM;IACN,SAAS;IACT,QAAQ;IACR,MAAM;IACN,KAAK;IACL,aAAa,EAAE,wBAAwB;IACvC,qBAAqB;IACrB,YAAY;IACZ,SAAS;IACT,SAAS;IACT,aAAa;IACb,SAAS;IACT,cAAc;IACd,aAAa;CACd,CAAC;AAEF,8CAA8C;AAC9C,MAAM,UAAU,aAAa,CAC3B,KAAwC,EACxC,SAA4B,OAAO,CAAC,GAAG;IAEvC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,SAAS;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC5C,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACzE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,uDAAuD;AACvD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAO7B;IACC,MAAM,KAAK,GAAG,OAAO,CAAC,cAAc,CAAC;IACrC,OAAO,IAAI,OAAO,CAAc,CAAC,OAAO,EAAE,EAAE;QAC1C,IAAI,KAAmB,CAAC;QACxB,IAAI,CAAC;YACH,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE;gBAChD,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1D,mDAAmD;gBACnD,GAAG,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC;gBAC/B,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;aAClC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,UAAU,EAAG,KAAe,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;YAC3G,OAAO;QACT,CAAC;QACD,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,QAAQ,GAAG,IAAI,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;QAEnC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,aAAa,IAAI,KAAK,CAAC,MAAM,CAAC;YAC9B,IAAI,QAAQ,GAAG,KAAK,EAAE,CAAC;gBACrB,sDAAsD;gBACtD,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,CAAC;gBACnC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;gBAC/E,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACtB,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;YAC3B,CAAC;QACH,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,IAAI;gBAAE,SAAS,CAAC,KAAK,EAAE,CAAC;QAC3F,CAAC,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,CAAC,IAAmB,EAAQ,EAAE;YAC3C,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC1D,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;YACjF,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9E,CAAC,CAAC;QACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACtC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC;AAsBD,wBAAwB;AACxB,MAAM,OAAO,YAAY;IAEM;IADZ,GAAG,CAAY;IAChC,YAA6B,OAA4B;QAA5B,YAAO,GAAP,OAAO,CAAqB;QACvD,IAAI,CAAC,GAAG,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAe;QAC3B,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG;gBACX,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;gBAChC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;aACnF,CAAC;YACF,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;YACnD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,IAAI,MAAM,CAAC;YAC7D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;gBAC3B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;gBAC7B,IAAI;gBACJ,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpE,SAAS;gBACT,cAAc;aACf,CAAC,CAAC;YACH,IAAI,MAAM,CAAC,QAAQ;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,SAAS,IAAI,EAAE,CAAC;YAClF,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC;YAC1G,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,iBAAiB,MAAM,CAAC,WAAW,GAAG,cAAc,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;YACnI,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACrF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QACjC,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;CACF;AAsBD;;;GAGG;AACH,MAAM,OAAO,WAAW;IAEO;IADZ,GAAG,CAAY;IAChC,YAA6B,UAA8B,EAAE;QAAhC,YAAO,GAAP,OAAO,CAAyB;QAC3D,IAAI,CAAC,GAAG,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IAEO,IAAI,CAAC,MAAc,EAAE,SAAiB;QAC5C,MAAM,IAAI,GAAa,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC;QAC7D,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAClE,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC5D,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAe;QAC3B,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;QACpF,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC;YACpD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,IAAI,MAAM,CAAC;YAC7D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;gBAC3B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,QAAQ;gBAC5C,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC;gBACvC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpE,SAAS;gBACT,cAAc;aACf,CAAC,CAAC;YACH,IAAI,MAAM,CAAC,QAAQ;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,SAAS,IAAI,EAAE,CAAC;YAClF,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC;YAC1G,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,iBAAiB,MAAM,CAAC,WAAW,GAAG,cAAc,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;YACnI,mCAAmC;YACnC,IAAI,SAA6B,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAA4B,CAAC;gBACxF,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,GAAG,SAAS,CAAC;YACxB,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC/G,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;YAC/E,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,42 @@
1
+ import type { TaskState } from '../model.js';
2
+ export interface ExecutionOutcome {
3
+ ok: boolean;
4
+ resultRef?: string;
5
+ reason?: string;
6
+ }
7
+ /** 执行器契约:**按 taskId 幂等**(同一任务重复执行必须得到同一结果且无副作用)。 */
8
+ export interface TaskExecutor {
9
+ execute(task: TaskState): Promise<ExecutionOutcome>;
10
+ }
11
+ /** 内置 echo 执行器:结果与意图确定性绑定(幂等、无副作用)。 */
12
+ export declare class EchoExecutor implements TaskExecutor {
13
+ execute(task: TaskState): Promise<ExecutionOutcome>;
14
+ }
15
+ /** 期望结果(供校验):与 `EchoExecutor` 对齐。 */
16
+ export declare function echoResultFor(intent: string): string;
17
+ interface LogEntry {
18
+ taskId: string;
19
+ resultRef: string;
20
+ }
21
+ /** 执行记录:持久化「已执行的 taskId → 结果」,跨重启/重复投递去重执行。 */
22
+ export declare class ExecutionLog {
23
+ private readonly path;
24
+ private readonly entries;
25
+ private readonly index;
26
+ private constructor();
27
+ static open(path: string): Promise<ExecutionLog>;
28
+ has(taskId: string): boolean;
29
+ resultOf(taskId: string): string | undefined;
30
+ /** 记录一次真实执行(若已存在则不重复追加)。 */
31
+ record(taskId: string, resultRef: string): Promise<void>;
32
+ all(): LogEntry[];
33
+ size(): number;
34
+ close(): Promise<void>;
35
+ }
36
+ /**
37
+ * 按 taskId 至多执行一次:已记录则直接返回记录结果(不再次调用执行器)。
38
+ * 这保证「重复投递不重复执行」与「重启不重复执行」。
39
+ */
40
+ export declare function executeOnce(task: TaskState, executor: TaskExecutor, log: ExecutionLog): Promise<ExecutionOutcome>;
41
+ export {};
42
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/runtime/executor.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACrD;AAED,uCAAuC;AACvC,qBAAa,YAAa,YAAW,YAAY;IACzC,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAG1D;AAED,qCAAqC;AACrC,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,UAAU,QAAQ;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,+CAA+C;AAC/C,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IAEnD,OAAO;WAIM,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAyBtD,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAI5B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5C,4BAA4B;IACtB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9D,GAAG,IAAI,QAAQ,EAAE;IAIjB,IAAI,IAAI,MAAM;IAIR,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,YAAY,EACtB,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAM3B"}
@@ -0,0 +1,87 @@
1
+ // 可插拔执行器(M2):内置 `echo`;执行记录(ExecutionLog)保证**按 taskId 至多执行一次**。
2
+ import { appendFile, mkdir, readFile } from 'node:fs/promises';
3
+ import { dirname } from 'node:path';
4
+ /** 内置 echo 执行器:结果与意图确定性绑定(幂等、无副作用)。 */
5
+ export class EchoExecutor {
6
+ async execute(task) {
7
+ return { ok: true, resultRef: `echo:${task.intent}` };
8
+ }
9
+ }
10
+ /** 期望结果(供校验):与 `EchoExecutor` 对齐。 */
11
+ export function echoResultFor(intent) {
12
+ return `echo:${intent}`;
13
+ }
14
+ /** 执行记录:持久化「已执行的 taskId → 结果」,跨重启/重复投递去重执行。 */
15
+ export class ExecutionLog {
16
+ path;
17
+ entries = [];
18
+ index = new Map();
19
+ constructor(path) {
20
+ this.path = path;
21
+ }
22
+ static async open(path) {
23
+ const log = new ExecutionLog(path);
24
+ await mkdir(dirname(path), { recursive: true });
25
+ let raw = '';
26
+ try {
27
+ raw = await readFile(path, 'utf-8');
28
+ }
29
+ catch {
30
+ raw = '';
31
+ }
32
+ for (const line of raw.split('\n')) {
33
+ const trimmed = line.trim();
34
+ if (trimmed === '')
35
+ continue;
36
+ try {
37
+ const entry = JSON.parse(trimmed);
38
+ if (!log.index.has(entry.taskId)) {
39
+ log.index.set(entry.taskId, entry.resultRef);
40
+ log.entries.push(entry);
41
+ }
42
+ }
43
+ catch {
44
+ continue; // 半行/损坏行跳过
45
+ }
46
+ }
47
+ return log;
48
+ }
49
+ has(taskId) {
50
+ return this.index.has(taskId);
51
+ }
52
+ resultOf(taskId) {
53
+ return this.index.get(taskId);
54
+ }
55
+ /** 记录一次真实执行(若已存在则不重复追加)。 */
56
+ async record(taskId, resultRef) {
57
+ if (this.index.has(taskId))
58
+ return;
59
+ const entry = { taskId, resultRef };
60
+ await appendFile(this.path, `${JSON.stringify(entry)}\n`, 'utf-8');
61
+ this.index.set(taskId, resultRef);
62
+ this.entries.push(entry);
63
+ }
64
+ all() {
65
+ return [...this.entries];
66
+ }
67
+ size() {
68
+ return this.entries.length;
69
+ }
70
+ async close() {
71
+ // 追加即时落盘;无长连接。
72
+ }
73
+ }
74
+ /**
75
+ * 按 taskId 至多执行一次:已记录则直接返回记录结果(不再次调用执行器)。
76
+ * 这保证「重复投递不重复执行」与「重启不重复执行」。
77
+ */
78
+ export async function executeOnce(task, executor, log) {
79
+ const existing = log.resultOf(task.taskId);
80
+ if (existing !== undefined)
81
+ return { ok: true, resultRef: existing };
82
+ const outcome = await executor.execute(task);
83
+ if (outcome.ok)
84
+ await log.record(task.taskId, outcome.resultRef ?? '');
85
+ return outcome;
86
+ }
87
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/runtime/executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { TaskState } from '../model.js';
2
+ export interface FairOrderOptions {
3
+ /** 单一发送方份额上限(有其它待处理发送方时);默认 0.5 */
4
+ shareCap?: number;
5
+ }
6
+ /**
7
+ * 轮转序:每一步选「已服务次数最少」的发送方(tie:剩余最多 → device 字典序),
8
+ * 组内按 `taskId` 逻辑序。返回 taskId 的确定序。
9
+ */
10
+ export declare function fairOrder(pending: readonly TaskState[], options?: FairOrderOptions): string[];
11
+ export interface AgentConcurrencyOptions {
12
+ /** 每个目标 Agent 的并发上限(默认 2;仅本地调度门,无全局账本) */
13
+ perAgent?: number;
14
+ /** `(来源设备, 目标 Agent)` 准入配额(默认 Infinity = 不限) */
15
+ perPair?: number;
16
+ }
17
+ /**
18
+ * 在某轮内按「目标 Agent 并发 + (来源设备,目标 Agent) 配额」过滤可执行 taskId(保序)。
19
+ * `inFlight` 为已在途/已执行的计数(本地记账;调用方维护)。
20
+ */
21
+ export declare function admitByConcurrency(ordered: readonly TaskState[], inFlight: {
22
+ agent: ReadonlyMap<string, number>;
23
+ pair: ReadonlyMap<string, number>;
24
+ }, options?: AgentConcurrencyOptions): string[];
25
+ //# sourceMappingURL=fairness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fairness.d.ts","sourceRoot":"","sources":["../../src/runtime/fairness.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,EAAE,CAyCjG;AAED,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,QAAQ,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EACnF,OAAO,GAAE,uBAA4B,GACpC,MAAM,EAAE,CAgBV"}
@@ -0,0 +1,79 @@
1
+ // W1 公平准入(本地、确定性、无墙钟):收件按 `(from.device, 逻辑序)` 轮转;
2
+ // 有其它待处理发送方时,单一发送方**份额 ≤ 50%**;`(来源设备, 目标 Agent)` 准入配额;
3
+ // 每 Agent 并发默认 2。全部为纯函数,便于判别性测试与全端一致。
4
+ /**
5
+ * 轮转序:每一步选「已服务次数最少」的发送方(tie:剩余最多 → device 字典序),
6
+ * 组内按 `taskId` 逻辑序。返回 taskId 的确定序。
7
+ */
8
+ export function fairOrder(pending, options = {}) {
9
+ const shareCap = options.shareCap ?? 0.5;
10
+ const groups = new Map();
11
+ for (const state of pending) {
12
+ const list = groups.get(state.from.device);
13
+ if (list)
14
+ list.push(state);
15
+ else
16
+ groups.set(state.from.device, [state]);
17
+ }
18
+ for (const list of groups.values())
19
+ list.sort((a, b) => (a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0));
20
+ const served = new Map();
21
+ for (const device of groups.keys())
22
+ served.set(device, 0);
23
+ const order = [];
24
+ const total = pending.length;
25
+ while (order.length < total) {
26
+ const active = [...groups.keys()]
27
+ .filter((d) => (groups.get(d)?.length ?? 0) > 0)
28
+ .sort((a, b) => {
29
+ const sa = served.get(a);
30
+ const sb = served.get(b);
31
+ if (sa !== sb)
32
+ return sa - sb;
33
+ const ra = groups.get(a).length;
34
+ const rb = groups.get(b).length;
35
+ if (ra !== rb)
36
+ return rb - ra;
37
+ return a < b ? -1 : a > b ? 1 : 0;
38
+ });
39
+ if (active.length === 0)
40
+ break;
41
+ let pick = active[0];
42
+ // 份额上限:若选它会让其份额 > cap 且还有尚未达到同等优先级的其它活跃发送方 → 换下一个
43
+ const cap = shareCap * (order.length + 1);
44
+ if (served.get(pick) + 1 > cap) {
45
+ const alternative = active.find((d) => served.get(d) + 1 <= cap);
46
+ if (alternative !== undefined)
47
+ pick = alternative;
48
+ }
49
+ const list = groups.get(pick);
50
+ const next = list.shift();
51
+ order.push(next.taskId);
52
+ served.set(pick, served.get(pick) + 1);
53
+ }
54
+ return order;
55
+ }
56
+ /**
57
+ * 在某轮内按「目标 Agent 并发 + (来源设备,目标 Agent) 配额」过滤可执行 taskId(保序)。
58
+ * `inFlight` 为已在途/已执行的计数(本地记账;调用方维护)。
59
+ */
60
+ export function admitByConcurrency(ordered, inFlight, options = {}) {
61
+ const perAgent = options.perAgent ?? 2;
62
+ const perPair = options.perPair ?? Number.POSITIVE_INFINITY;
63
+ const agentCount = new Map(inFlight.agent);
64
+ const pairCount = new Map(inFlight.pair);
65
+ const admitted = [];
66
+ for (const state of ordered) {
67
+ const agent = state.to.agent;
68
+ const pair = `${state.from.device}->${agent}`;
69
+ if ((agentCount.get(agent) ?? 0) >= perAgent)
70
+ continue;
71
+ if ((pairCount.get(pair) ?? 0) >= perPair)
72
+ continue;
73
+ agentCount.set(agent, (agentCount.get(agent) ?? 0) + 1);
74
+ pairCount.set(pair, (pairCount.get(pair) ?? 0) + 1);
75
+ admitted.push(state.taskId);
76
+ }
77
+ return admitted;
78
+ }
79
+ //# sourceMappingURL=fairness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fairness.js","sourceRoot":"","sources":["../../src/runtime/fairness.ts"],"names":[],"mappings":"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"}