@nowcrew/daemon 0.6.13 → 0.6.14
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.
- package/README.md +3 -6
- package/dist/config.js +2 -4
- package/dist/execution-runner.js +1 -4
- package/dist/host-execution-coordinator.js +3 -3
- package/dist/prompt.js +15 -8
- package/dist/serve.js +3 -9
- package/dist/shared-execution-slots.js +9 -17
- package/package.json +1 -1
- package/dist/remote/claude-bridge.js +0 -558
- package/dist/remote/claude-channel.js +0 -164
- package/dist/remote/codex-client.js +0 -451
- package/dist/remote/codex-runtime.js +0 -77
- package/dist/remote/config.js +0 -135
- package/dist/remote/gateway.js +0 -879
- package/dist/remote/identity.js +0 -39
- package/dist/remote/owner.js +0 -77
- package/dist/remote/protocol.js +0 -211
- package/dist/remote/remote-cli.js +0 -254
- package/dist/remote/runtime-probe.js +0 -182
- package/dist/remote/session-discovery.js +0 -249
- package/dist/remote/wrapper.js +0 -40
- package/dist/remote-web/assets/index-B_6VM_tw.js +0 -94
- package/dist/remote-web/assets/index-L6EiQbJn.css +0 -1
- package/dist/remote-web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/remote-web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/remote-web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/remote-web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/remote-web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/remote-web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/remote-web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/remote-web/icons/nowwork-192.png +0 -0
- package/dist/remote-web/icons/nowwork-512.png +0 -0
- package/dist/remote-web/icons/nowwork.svg +0 -7
- package/dist/remote-web/index.html +0 -20
- package/dist/remote-web/manifest.webmanifest +0 -13
- package/dist/remote-web/sw.js +0 -12
package/README.md
CHANGED
|
@@ -371,10 +371,9 @@ CREW_RUNTIME_SAFE=1
|
|
|
371
371
|
CREW_EXECUTION_MAX_PROMPT_BYTES=256000
|
|
372
372
|
CREW_EXECUTION_MAX_TIMEOUT_MS=10800000
|
|
373
373
|
CREW_EXECUTION_MAX_EVENT_BYTES=64000
|
|
374
|
-
CREW_MAX_PARALLEL=
|
|
375
|
-
CREW_SCHEDULED_MAX_PARALLEL=4
|
|
374
|
+
CREW_MAX_PARALLEL=4
|
|
376
375
|
CREW_EXECUTION_MAX_QUEUED_PER_AGENT=32
|
|
377
|
-
CREW_EXECUTION_MAX_PARALLEL_TOTAL=
|
|
376
|
+
CREW_EXECUTION_MAX_PARALLEL_TOTAL=4
|
|
378
377
|
CREW_EXECUTION_MAX_QUEUED_TOTAL=128
|
|
379
378
|
CREW_EXECUTION_MAX_STARTING_TOTAL=1
|
|
380
379
|
CREW_EXECUTION_MAX_STARTING_PER_RUNTIME=1
|
|
@@ -382,9 +381,7 @@ CREW_EXECUTION_START_GAP_MS=3000
|
|
|
382
381
|
CREW_EXECUTION_STARTUP_TIMEOUT_MS=120000
|
|
383
382
|
```
|
|
384
383
|
|
|
385
|
-
`
|
|
386
|
-
limits scheduled executions per Agent. `MAX_PARALLEL_TOTAL` is the machine-wide process cap shared by
|
|
387
|
-
protocol-v1 and legacy work.
|
|
384
|
+
`MAX_PARALLEL_TOTAL` is the machine-wide process cap shared by protocol-v1 and legacy work.
|
|
388
385
|
Runtime startup is a separate FIFO gate: by default only one Claude, Codex, or Kimi process may be
|
|
389
386
|
initializing at a time, and launches are spaced by three seconds. A process leaves the startup gate
|
|
390
387
|
only after its runtime-specific ready event; startup timeout cancels the owned process tree.
|
package/dist/config.js
CHANGED
|
@@ -10,10 +10,9 @@ export const DEFAULT_EXECUTION_LIMITS = Object.freeze({
|
|
|
10
10
|
maxPromptBytes: 256_000,
|
|
11
11
|
maxTimeoutMs: 3 * 60 * 60_000,
|
|
12
12
|
maxEventBytes: 64_000,
|
|
13
|
-
maxParallelPerAgent:
|
|
14
|
-
maxParallelScheduledPerAgent: 4,
|
|
13
|
+
maxParallelPerAgent: 4,
|
|
15
14
|
maxQueuedPerAgent: 32,
|
|
16
|
-
maxParallelTotal:
|
|
15
|
+
maxParallelTotal: 4,
|
|
17
16
|
maxQueuedTotal: 128,
|
|
18
17
|
maxStartingTotal: 1,
|
|
19
18
|
maxStartingPerRuntime: 1,
|
|
@@ -69,7 +68,6 @@ export function loadConfig(env = process.env) {
|
|
|
69
68
|
maxTimeoutMs: integerEnvAtMost(env, "CREW_EXECUTION_MAX_TIMEOUT_MS", DEFAULT_EXECUTION_LIMITS.maxTimeoutMs, MAX_EXECUTION_TIMEOUT_MS),
|
|
70
69
|
maxEventBytes: integerEnvAtLeast(env, "CREW_EXECUTION_MAX_EVENT_BYTES", DEFAULT_EXECUTION_LIMITS.maxEventBytes, MIN_EXECUTION_EVENT_BYTES),
|
|
71
70
|
maxParallelPerAgent: positiveIntegerEnv(env, "CREW_MAX_PARALLEL", DEFAULT_EXECUTION_LIMITS.maxParallelPerAgent),
|
|
72
|
-
maxParallelScheduledPerAgent: positiveIntegerEnv(env, "CREW_SCHEDULED_MAX_PARALLEL", DEFAULT_EXECUTION_LIMITS.maxParallelScheduledPerAgent),
|
|
73
71
|
maxQueuedPerAgent: positiveIntegerEnv(env, "CREW_EXECUTION_MAX_QUEUED_PER_AGENT", DEFAULT_EXECUTION_LIMITS.maxQueuedPerAgent),
|
|
74
72
|
maxParallelTotal: positiveIntegerEnv(env, "CREW_EXECUTION_MAX_PARALLEL_TOTAL", DEFAULT_EXECUTION_LIMITS.maxParallelTotal),
|
|
75
73
|
maxQueuedTotal: positiveIntegerEnv(env, "CREW_EXECUTION_MAX_QUEUED_TOTAL", DEFAULT_EXECUTION_LIMITS.maxQueuedTotal),
|
package/dist/execution-runner.js
CHANGED
|
@@ -231,10 +231,7 @@ function admission(spec, config, dependencies, at) {
|
|
|
231
231
|
|| dependencies.facts.queuedTotal < 0) {
|
|
232
232
|
return { rejected: rejection(spec.executionId, "invalid_spec", "Invalid local resource facts", at) };
|
|
233
233
|
}
|
|
234
|
-
const
|
|
235
|
-
? config.executionLimits.maxParallelPerAgent
|
|
236
|
-
: config.executionLimits.maxParallelScheduledPerAgent;
|
|
237
|
-
const agentAtCapacity = dependencies.facts.activeForAgent >= maxParallelForAgent;
|
|
234
|
+
const agentAtCapacity = dependencies.facts.activeForAgent >= config.executionLimits.maxParallelPerAgent;
|
|
238
235
|
const machineAtCapacity = dependencies.facts.activeTotal >= config.executionLimits.maxParallelTotal;
|
|
239
236
|
const slotInvalid = dependencies.slot?.state === "ready"
|
|
240
237
|
? agentAtCapacity || machineAtCapacity
|
|
@@ -4,7 +4,7 @@ import { mkdir, open, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { defaultProcessController } from "./execution-journal.js";
|
|
6
6
|
import { createJournalLease, createJournalLeaseRegistry, JournalLockedError, } from "./execution-journal-lock.js";
|
|
7
|
-
const HOST_EXECUTION_SLOT_COUNT =
|
|
7
|
+
const HOST_EXECUTION_SLOT_COUNT = 4;
|
|
8
8
|
const HOST_STARTUP_GAP_MS = 3_000;
|
|
9
9
|
const RETRY_MS = 50;
|
|
10
10
|
export function defaultHostExecutionCoordinatorRoot(userHome = homedir()) {
|
|
@@ -215,8 +215,8 @@ export function createHostExecutionCoordinator(options = {}) {
|
|
|
215
215
|
}
|
|
216
216
|
export function hostCoordinatedSlotManager(local, host) {
|
|
217
217
|
return {
|
|
218
|
-
reserve: (handle, kind
|
|
219
|
-
const localReservation = local.reserve(handle, kind
|
|
218
|
+
reserve: (handle, kind) => {
|
|
219
|
+
const localReservation = local.reserve(handle, kind);
|
|
220
220
|
if (!localReservation.accepted)
|
|
221
221
|
return localReservation;
|
|
222
222
|
const hostReservation = host.reserveExecution(localReservation.ready);
|
package/dist/prompt.js
CHANGED
|
@@ -82,7 +82,7 @@ export function buildSystemPrompt(ctx) {
|
|
|
82
82
|
// 消息、没有 task,这条规则字面上无法执行,省略(2026-07-13 复审 Minor 5)。
|
|
83
83
|
const claimRule = scheduled
|
|
84
84
|
? ""
|
|
85
|
-
: "\n-
|
|
85
|
+
: "\n- **除定时任务创建与管理和 memory_prune 外,动手干活前必须先 `crew task claim`**;claim 失败就转做别的任务。";
|
|
86
86
|
// "一线程一 task 且必须带 --thread"与"进度/产出回本线程,严禁发到顶层"都建立在"本轮有一条
|
|
87
87
|
// 触发消息、线程根即为该消息 id"这个前提上——scheduled run 没有触发消息、没有 thread 锚点,
|
|
88
88
|
// 唯一输出路径就是频道顶层 `--send-draft`,与这两条正面矛盾且 --thread 参数根本填不出来,
|
|
@@ -114,14 +114,14 @@ export function buildSystemPrompt(ctx) {
|
|
|
114
114
|
: "- **始终只通过 crew CLI 发声。在 crew 命令之外产生的任何文字都不会送达任何人。**";
|
|
115
115
|
const actionRule = scheduled
|
|
116
116
|
? `- **判定规则**:直接执行本轮定时指令,执行前不需要 claim。${threadTaskRule}${progressRule}`
|
|
117
|
-
: `-
|
|
117
|
+
: `- **判定规则**:定时任务创建与管理和 memory_prune 无需 task claim,只遵守各自唤醒指令;其它来信若需要你"回复之外的动作"(跑工具/改代码/做变更),先 claim;若只是回答问题或闲聊,无需 claim。${threadTaskRule}${progressRule}`;
|
|
118
118
|
const taskAndScheduleCommands = alwaysReport
|
|
119
119
|
? `4. **\`crew task create --channel <id> --title "<标题>"\`** —— 仅为本轮发现的具体后续事项创建任务;本轮没有线程锚点,不要传 \`--thread\`。`
|
|
120
120
|
: scheduled
|
|
121
121
|
? `5. **\`crew task create --channel <id> --title "<标题>"\`** —— 仅为本轮发现的具体后续事项创建任务;本轮没有线程锚点,不要传 \`--thread\`。`
|
|
122
122
|
: `5. **\`crew task list --channel <id>\`** —— 看任务板。支持 \`--status <s>\` / \`--mine\`。
|
|
123
123
|
6. **\`crew task create --channel <id> --title "<标题>" --thread <当前线程根msgId>\`** —— 新建任务并**绑定到当前线程**。\`--thread\` 传你读到的那条**触发消息 id**(线程根),任务就和讨论同处一个线程。省略 \`--thread\` 会另起新线程,**几乎总是错的——务必带上**。
|
|
124
|
-
7. **\`crew task claim <taskId>\`** ——
|
|
124
|
+
7. **\`crew task claim <taskId>\`** —— 除定时任务创建与管理和 memory_prune 外,执行需要动作的工作前先认领任务。
|
|
125
125
|
8. **\`crew task update <taskId> --status <in_progress|in_review|done>\`** —— 推进任务状态。
|
|
126
126
|
9. **\`crew task unclaim <taskId>\`** —— 释放认领,把任务让给别人。
|
|
127
127
|
10. **\`crew task assign <taskId> --to <handle>\`** —— 把任务指派/交接给另一个 agent(用于交接,见下)。
|
|
@@ -176,7 +176,7 @@ ${taskAndScheduleCommands}`;
|
|
|
176
176
|
## 协作礼仪
|
|
177
177
|
- **尊重正在进行的对话**:人类正与他人一来一回时,除非明确 @你或显然在叫你,否则不要插话。
|
|
178
178
|
- **只有真正干活的人来汇报**:别替别人总结或冒领他们的工作。
|
|
179
|
-
-
|
|
179
|
+
- **除定时任务创建与管理和 memory_prune 外,claim 后再动手**:claim 失败立即停手,换一个任务。
|
|
180
180
|
- **停止前检查你欠的具体阻塞项**:若你还欠某人一个 handoff/review/决定/回复且正卡着对方,先发一条最小可行动消息再停。
|
|
181
181
|
- **少发废话**:只在有可行动内容时发消息,不要播报"我在等/我空闲"。`;
|
|
182
182
|
// 对人说话:约束的是"表达方式",不是"执行规则"——claim/thread/freshness 等操作协议照常执行,
|
|
@@ -234,8 +234,15 @@ ${taskAndScheduleCommands}`;
|
|
|
234
234
|
|
|
235
235
|
## 并行与记忆(CRITICAL — 防冲突 + 防上下文膨胀)
|
|
236
236
|
你**可能同时在处理多个任务**(你自己的多个并行进程)。为避免互相覆盖、并保持上下文精简,严格遵守:
|
|
237
|
-
-
|
|
238
|
-
-
|
|
237
|
+
- **普通任务默认只写两处**:① 本任务的隔离 cwd(代码/草稿);② 本任务的工作日志 **\`$CREW_TASK_LOG\`**(每任务一份,别人不会碰)。默认不要修改共享 \`$CREW_HOME/MEMORY.md\` 或 notes/;只有当前用户明确要求更新共享记忆时,才按下方条件写入相关共享文件。在工作日志里记:当前阶段 / 下一步 / 卡在谁 / 关键结论(每条尽量 ≤3 行)。
|
|
238
|
+
- **当前请求明确要求更新记忆,或当前消息是对本线程紧邻记忆方案的无歧义确认时**,可以按最小范围直接写共享记忆;先读取相关索引和 note,语义已存在时不重复追加,不要把 Agent 自己判断值得记住视为用户授权。
|
|
239
|
+
- 本节的直接更新共享记忆和相关 task 流转仅适用于用户消息触发的普通交互执行。memory_prune 和 scheduled 执行只遵守各自唤醒指令,不得依据本节认领、创建、重开或推进 task。
|
|
240
|
+
- 普通交互执行将写共享记忆时,使用当前线程唯一的普通 task:已有非终态 task 时先按归属规则确保当前 Agent 成功认领;当前线程没有 task 时先创建并认领一个普通 task;已有 task 为 done/closed 时不得由 Agent 重开,done 报告当前轻量方案无法安全复用终态 task,closed 保持人工终态。遇到终态或归属问题时报告冲突且不得创建第二个 task 或新的任务类型。完成写入后,只有任务本身的全部工作已完成时才推进终态以触发 prune。
|
|
241
|
+
- 提出共享记忆修改方案并等待用户确认时,该 task 仍有剩余工作,保持 in_review 或其它非终态;收到无歧义确认后再执行写入,并仅在全部工作完成后推进终态。
|
|
242
|
+
- 普通执行直接更新后,仍然保留任务结束后的专门收尾执行,不因已经写过而跳过 prune。
|
|
243
|
+
- 新建 note 必须说明现有分类为何不适用;MEMORY.md 默认只更新 Knowledge Index,不得默认修改 Role、Core Goals 或 Active Context;只有用户明确要求或任务本身就是档案/状态维护时例外。
|
|
244
|
+
- 未明确要求、记忆意图含糊或仅由 Agent 自己判断值得记住时,不直接写共享记忆,也不把含糊内容写成长期记忆候选。含糊表达本身不作为长期记忆候选,但不得因此跳过整个 task 的 memory_prune;任务按正常状态流程完成。收尾只能处理该任务其它符合现有收尾规则的稳定内容,不允许让收尾代替用户补充记忆授权。
|
|
245
|
+
- 若用户明确要求本次不要修改共享记忆,同样不写共享记忆或长期记忆候选。任务尚未完成时,把该禁止要求作为运行状态写入 $CREW_TASK_LOG;该运行状态不是长期记忆候选,后续执行必须保留该状态。若用户之后在同一任务中明确撤销禁止,移除该运行状态并按最新明确授权处理。完成 task 前检查该状态:Agent 可自行改状态时,使用 crew task update <taskId> --status done --skip-memory-prune。CLI 返回状态由 Server 管理时沿用上述 Server 管理任务的安全回退:不重试、不绕过权限;收尾会读取该运行状态并禁止共享记忆写入,但本路径不提供机器级 skip 保证。不要只在回复里声明不修改。
|
|
239
246
|
- **跨任务协调看实时清单**:用 \`crew task list --mine\` 查你当前所有任务及其状态(谁在排队、谁在评审)。例:你一次只能测一个页面时,据此判断"正在测 #1、#2 排队",在 #2 的工作日志里标注"等 #1 完成"。
|
|
240
247
|
- 需要更多背景时再 \`Read $CREW_HOME/notes/<topic>.md\`;不要一次把所有笔记读进上下文。
|
|
241
248
|
|
|
@@ -261,7 +268,7 @@ ${interactiveTaskRules}${freshnessRule}${externalReplyRule}${collaborationEtique
|
|
|
261
268
|
## Workspace 与分层记忆(CRITICAL — 索引+按需,配合上面的并行规则)
|
|
262
269
|
你的持久记忆在 \`$CREW_HOME\`(跨你所有任务共享),按“常驻索引 + 功能明细 + 当前工作”分层:
|
|
263
270
|
1. **MEMORY.md = 常驻索引 + 角色**:系统每次已把它(截断索引版)注入本提示词末尾。索引条目包含 \`file/type/scope/summary/triggers/priority/updated\`,其中 file 是相对于 \`$CREW_HOME\` 的路径(例如 \`notes/testing.md\`),先用这些字段判断当前问题是否相关;通常不必再读整份 MEMORY.md。
|
|
264
|
-
2. **notes/<topic>.md = 功能明细**:按用户偏好、项目规则、测试、调试、Git、事故复盘等主题拆分。优先读取与当前任务匹配的文件;\`notes/lessons.md\`
|
|
271
|
+
2. **notes/<topic>.md = 功能明细**:按用户偏好、项目规则、测试、调试、Git、事故复盘等主题拆分。优先读取与当前任务匹配的文件;\`notes/lessons.md\` 只保留未分类经验和历史兼容内容,不是所有新经验的默认目的地。普通任务默认只读不写,但当前用户明确要求时可以按最小范围更新;任务结束后的专门收尾执行仍按收尾指令复核。
|
|
265
272
|
- 对旧版 MEMORY.md,如果条目没有完整结构化字段但明确指向 \`notes/<file>.md\`,把它视为 legacy 索引;仍需结合条目标题/链接和当前请求判断相关性,不要因为缺少 summary/triggers 就假定正文已注入。
|
|
266
273
|
3. **本任务 work-log = \`$CREW_TASK_LOG\`**:每任务一份(别的运行不会碰),记当前阶段/下一步/卡点/关键结论。系统已把它的当前内容注入末尾让你续上;有进展就更新它——这是你**唯一**该在任务中持续写的记忆文件。**work-log 是现状快照,不是流水账**:保持 ≤100 行,更新时重写全文替换过时内容,不要无限追加(过长会被注入截断)。
|
|
267
274
|
|
|
@@ -269,7 +276,7 @@ ${interactiveTaskRules}${freshnessRule}${externalReplyRule}${collaborationEtique
|
|
|
269
276
|
- 当前请求命中 MEMORY.md 某条目的 \`summary\` 或 \`triggers\`,或用户明确询问历史经验/偏好时,在最终回答或执行动作前用 \`Read $CREW_HOME/<file>\` 读取对应明细;不要再次拼接 \`notes/\`。
|
|
270
277
|
- 未命中时不要为了“保险”读取全部 notes;只读取能直接影响当前任务的文件。MEMORY.md 注入的是索引,不会自动让 note 正文可用。
|
|
271
278
|
|
|
272
|
-
|
|
279
|
+
**沉淀与瘦身由系统触发,不要平时自行扩展范围**:任务被改为 done/closed 时,系统会单独唤醒你收尾——那时按功能分类把可复用经验提炼进合适的 \`notes/<topic>.md\`,更新 MEMORY.md 索引。普通执行已经按用户明确要求写入时,收尾先检查、后修改,只在明确缺漏时做最小必要修正,不得重复追加或只为优化措辞而改写。最终结论必须且只能归类为:无变化、已修正、冲突、失败;无变化或已修正时必须满足对应后置条件并清空 work-log,冲突或失败时保留 work-log,任一记忆写入失败则保留 work-log。不要默认全部写入 \`notes/lessons.md\`。默认不要改 MEMORY.md/notes,但不能覆盖当前用户明确授权的最小范围更新。
|
|
273
280
|
|
|
274
281
|
**为什么(CRITICAL)**:上下文会被周期压缩;你的恢复力来自——注入的 MEMORY.md 索引(始终精简)+ 本任务 work-log + 按需读相关 notes,而**不是**把一切堆进 MEMORY.md 或一次性加载所有明细。
|
|
275
282
|
${ctx.memory ? `\n## [注入] 你的 MEMORY.md(索引,只读参考)\n${ctx.memory}` : ""}${ctx.workLog ? `\n## [注入] 本任务 work-log(续上进度;更新写到 $CREW_TASK_LOG)\n${ctx.workLog}` : ""}`;
|
package/dist/serve.js
CHANGED
|
@@ -71,8 +71,7 @@ export function serve(config, opts = {}) {
|
|
|
71
71
|
let detectedExecutionRuntimes = [];
|
|
72
72
|
let runtimeFacts = null;
|
|
73
73
|
const runtimeProbe = createRuntimeProbeCoordinator();
|
|
74
|
-
const hostCoordinator = opts.execution?.hostCoordinator
|
|
75
|
-
?? createHostExecutionCoordinator({ executionSlots: config.executionLimits.maxParallelTotal });
|
|
74
|
+
const hostCoordinator = opts.execution?.hostCoordinator ?? createHostExecutionCoordinator();
|
|
76
75
|
const localSlots = createSharedSlotManager(config.executionLimits);
|
|
77
76
|
const sharedSlots = hostCoordinatedSlotManager(localSlots, hostCoordinator);
|
|
78
77
|
const runtimeStartupGate = hostCoordinatedStartupGate(createRuntimeStartupGate(config.executionLimits), hostCoordinator);
|
|
@@ -497,14 +496,12 @@ export function serve(config, opts = {}) {
|
|
|
497
496
|
task_key: spec.workspace.taskKey,
|
|
498
497
|
});
|
|
499
498
|
}
|
|
500
|
-
const
|
|
501
|
-
const reservation = sharedSlots.reserve(spec.agent.handle, "execution", executionClass);
|
|
499
|
+
const reservation = sharedSlots.reserve(spec.agent.handle, "execution");
|
|
502
500
|
if (!reservation.accepted) {
|
|
503
501
|
dslog("execution.machine_queue_rejected", "机器执行队列已满", {
|
|
504
502
|
level: "WARN",
|
|
505
503
|
execution_id: spec.executionId,
|
|
506
504
|
agent_handle: spec.agent.handle,
|
|
507
|
-
execution_class: executionClass,
|
|
508
505
|
...reservation.facts,
|
|
509
506
|
});
|
|
510
507
|
safeExecutionSend(ExecutionRejectedSchema.parse({
|
|
@@ -520,7 +517,6 @@ export function serve(config, opts = {}) {
|
|
|
520
517
|
dslog("execution.machine_queued", "execution 已进入机器队列", {
|
|
521
518
|
execution_id: spec.executionId,
|
|
522
519
|
agent_handle: spec.agent.handle,
|
|
523
|
-
execution_class: executionClass,
|
|
524
520
|
...reservation.facts,
|
|
525
521
|
});
|
|
526
522
|
}
|
|
@@ -538,7 +534,6 @@ export function serve(config, opts = {}) {
|
|
|
538
534
|
...(activeSameTask > 1 ? { level: "WARN" } : {}),
|
|
539
535
|
execution_id: spec.executionId,
|
|
540
536
|
agent_handle: spec.agent.handle,
|
|
541
|
-
execution_class: executionClass,
|
|
542
537
|
task_key: spec.workspace.taskKey,
|
|
543
538
|
active_same_task: activeSameTask,
|
|
544
539
|
...reservation.facts,
|
|
@@ -607,7 +602,6 @@ export function serve(config, opts = {}) {
|
|
|
607
602
|
dslog("execution.machine_slot_ready", "execution 获得机器执行名额", {
|
|
608
603
|
execution_id: spec.executionId,
|
|
609
604
|
agent_handle: spec.agent.handle,
|
|
610
|
-
execution_class: executionClass,
|
|
611
605
|
queue_ms: Date.now() - machineQueueEnteredAt,
|
|
612
606
|
active_total: snapshot.activeTotal,
|
|
613
607
|
queued_total: snapshot.queuedTotal,
|
|
@@ -830,7 +824,7 @@ export function serve(config, opts = {}) {
|
|
|
830
824
|
}
|
|
831
825
|
}
|
|
832
826
|
running.add(key);
|
|
833
|
-
legacyReservation = sharedSlots.reserve(msg.agentHandle, "legacy"
|
|
827
|
+
legacyReservation = sharedSlots.reserve(msg.agentHandle, "legacy");
|
|
834
828
|
if (legacyReservation.isQueued()) {
|
|
835
829
|
const snapshot = sharedSlots.snapshot();
|
|
836
830
|
dslog("run.machine_queued", "legacy run 已进入机器队列", {
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
export function createSharedSlotManager(limits) {
|
|
2
|
-
const
|
|
2
|
+
const activeByHandle = new Map();
|
|
3
3
|
const queue = [];
|
|
4
4
|
let activeTotal = 0;
|
|
5
5
|
let exclusive = false;
|
|
6
6
|
const queuedFor = (handle) => queue.filter((entry) => !entry.released && entry.handle === handle).length;
|
|
7
|
-
const activeKey = (handle, executionClass) => `${handle}:${executionClass}`;
|
|
8
|
-
const maxParallelFor = (executionClass) => executionClass === "scheduled"
|
|
9
|
-
? limits.maxParallelScheduledPerAgent
|
|
10
|
-
: limits.maxParallelPerAgent;
|
|
11
7
|
const promote = () => {
|
|
12
8
|
while (activeTotal < limits.maxParallelTotal) {
|
|
13
9
|
const index = queue.findIndex((entry) => !entry.released
|
|
14
|
-
&& (
|
|
15
|
-
< maxParallelFor(entry.executionClass));
|
|
10
|
+
&& (activeByHandle.get(entry.handle) ?? 0) < limits.maxParallelPerAgent);
|
|
16
11
|
if (index < 0)
|
|
17
12
|
return;
|
|
18
13
|
const [next] = queue.splice(index, 1);
|
|
@@ -20,15 +15,13 @@ export function createSharedSlotManager(limits) {
|
|
|
20
15
|
continue;
|
|
21
16
|
next.promoted = true;
|
|
22
17
|
activeTotal += 1;
|
|
23
|
-
|
|
24
|
-
activeByHandleAndClass.set(key, (activeByHandleAndClass.get(key) ?? 0) + 1);
|
|
18
|
+
activeByHandle.set(next.handle, (activeByHandle.get(next.handle) ?? 0) + 1);
|
|
25
19
|
next.resolve();
|
|
26
20
|
}
|
|
27
21
|
};
|
|
28
22
|
return {
|
|
29
|
-
reserve: (handle, kind
|
|
30
|
-
const
|
|
31
|
-
const activeForAgent = activeByHandleAndClass.get(key) ?? 0;
|
|
23
|
+
reserve: (handle, kind) => {
|
|
24
|
+
const activeForAgent = activeByHandle.get(handle) ?? 0;
|
|
32
25
|
const queuedForAgent = queuedFor(handle);
|
|
33
26
|
const facts = {
|
|
34
27
|
activeForAgent,
|
|
@@ -46,7 +39,7 @@ export function createSharedSlotManager(limits) {
|
|
|
46
39
|
};
|
|
47
40
|
}
|
|
48
41
|
const canStartImmediately = activeTotal < limits.maxParallelTotal
|
|
49
|
-
&& activeForAgent <
|
|
42
|
+
&& activeForAgent < limits.maxParallelPerAgent
|
|
50
43
|
&& queue.length === 0;
|
|
51
44
|
if (kind === "execution" && !canStartImmediately && (queuedForAgent >= limits.maxQueuedPerAgent
|
|
52
45
|
|| queue.length >= limits.maxQueuedTotal)) {
|
|
@@ -62,7 +55,6 @@ export function createSharedSlotManager(limits) {
|
|
|
62
55
|
const ready = new Promise((resolve) => { resolveReady = resolve; });
|
|
63
56
|
const entry = {
|
|
64
57
|
handle,
|
|
65
|
-
executionClass,
|
|
66
58
|
released: false,
|
|
67
59
|
promoted: false,
|
|
68
60
|
resolve: resolveReady,
|
|
@@ -81,11 +73,11 @@ export function createSharedSlotManager(limits) {
|
|
|
81
73
|
entry.released = true;
|
|
82
74
|
if (entry.promoted) {
|
|
83
75
|
activeTotal = Math.max(0, activeTotal - 1);
|
|
84
|
-
const nextForAgent = Math.max(0, (
|
|
76
|
+
const nextForAgent = Math.max(0, (activeByHandle.get(handle) ?? 1) - 1);
|
|
85
77
|
if (nextForAgent === 0)
|
|
86
|
-
|
|
78
|
+
activeByHandle.delete(handle);
|
|
87
79
|
else
|
|
88
|
-
|
|
80
|
+
activeByHandle.set(handle, nextForAgent);
|
|
89
81
|
}
|
|
90
82
|
else {
|
|
91
83
|
const index = queue.indexOf(entry);
|