@migor/agentia 0.10.4 → 0.10.6
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/CHANGELOG.md +281 -1
- package/README.en.md +1 -1
- package/README.md +2 -2
- package/dist/AGENTS.md +93 -11
- package/dist/container/container.js +3 -0
- package/dist/container/container.js.map +1 -1
- package/dist/core/abort.js +6 -1
- package/dist/core/abort.js.map +1 -1
- package/dist/core/json.js +4 -0
- package/dist/core/json.js.map +1 -1
- package/dist/core/limits.d.ts +13 -5
- package/dist/core/limits.js +14 -5
- package/dist/core/limits.js.map +1 -1
- package/dist/core/sse.d.ts +0 -13
- package/dist/core/sse.js +28 -2
- package/dist/core/sse.js.map +1 -1
- package/dist/core/timeout.d.ts +8 -0
- package/dist/core/timeout.js +19 -1
- package/dist/core/timeout.js.map +1 -1
- package/dist/core/tool.d.ts +18 -0
- package/dist/core/trace.d.ts +9 -0
- package/dist/core/trace.js +21 -0
- package/dist/core/trace.js.map +1 -1
- package/dist/engine/forwarded.d.ts +3 -1
- package/dist/engine/forwarded.js +5 -0
- package/dist/engine/forwarded.js.map +1 -1
- package/dist/engine/loop.d.ts +5 -1
- package/dist/engine/loop.js +45 -12
- package/dist/engine/loop.js.map +1 -1
- package/dist/engine/mcp-server.d.ts +17 -1
- package/dist/engine/mcp-server.js +36 -2
- package/dist/engine/mcp-server.js.map +1 -1
- package/dist/engine/policy.js +5 -1
- package/dist/engine/policy.js.map +1 -1
- package/dist/engine/replay.js +63 -1
- package/dist/engine/replay.js.map +1 -1
- package/dist/engine/run-config.d.ts +6 -0
- package/dist/engine/run-config.js +18 -6
- package/dist/engine/run-config.js.map +1 -1
- package/dist/engine/spec.d.ts +11 -1
- package/dist/engine/spec.js.map +1 -1
- package/dist/engine/stop-reason.js +19 -1
- package/dist/engine/stop-reason.js.map +1 -1
- package/dist/engine/tool-context.d.ts +3 -1
- package/dist/engine/tool-context.js +3 -1
- package/dist/engine/tool-context.js.map +1 -1
- package/dist/engine/tool-events.d.ts +7 -2
- package/dist/engine/tool-events.js +24 -3
- package/dist/engine/tool-events.js.map +1 -1
- package/dist/engine/tracer.js +10 -3
- package/dist/engine/tracer.js.map +1 -1
- package/dist/engine/turn.d.ts +9 -13
- package/dist/engine/turn.js +96 -10
- package/dist/engine/turn.js.map +1 -1
- package/dist/engine/types.d.ts +19 -2
- package/dist/engine/types.js +1 -0
- package/dist/engine/types.js.map +1 -1
- package/dist/engine/usage.js +12 -4
- package/dist/engine/usage.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/anthropic.d.ts +9 -1
- package/dist/integrations/anthropic.js +42 -7
- package/dist/integrations/anthropic.js.map +1 -1
- package/dist/integrations/file-sink.d.ts +1 -0
- package/dist/integrations/file-sink.js +35 -32
- package/dist/integrations/file-sink.js.map +1 -1
- package/dist/integrations/mcp-http.js +33 -9
- package/dist/integrations/mcp-http.js.map +1 -1
- package/dist/integrations/mcp-protocol.js +33 -16
- package/dist/integrations/mcp-protocol.js.map +1 -1
- package/dist/integrations/metrics-otlp.js +4 -0
- package/dist/integrations/metrics-otlp.js.map +1 -1
- package/dist/integrations/metrics.js +17 -2
- package/dist/integrations/metrics.js.map +1 -1
- package/dist/integrations/openai.d.ts +6 -1
- package/dist/integrations/openai.js +45 -8
- package/dist/integrations/openai.js.map +1 -1
- package/dist/integrations/otlp.js +6 -0
- package/dist/integrations/otlp.js.map +1 -1
- package/dist/runtime/memory.d.ts +0 -8
- package/dist/runtime/memory.js +24 -2
- package/dist/runtime/memory.js.map +1 -1
- package/dist/runtime/run.js +34 -9
- package/dist/runtime/run.js.map +1 -1
- package/dist/store/fsStore.d.ts +2 -0
- package/dist/store/fsStore.js +38 -5
- package/dist/store/fsStore.js.map +1 -1
- package/dist/store/record.d.ts +40 -0
- package/dist/store/record.js +72 -0
- package/dist/store/record.js.map +1 -1
- package/dist/store/redisStore.js +15 -4
- package/dist/store/redisStore.js.map +1 -1
- package/dist/store/sqliteStore.js +39 -14
- package/dist/store/sqliteStore.js.map +1 -1
- package/dist/store/store.d.ts +6 -1
- package/dist/store/store.js +10 -4
- package/dist/store/store.js.map +1 -1
- package/dist/toolkit/capability-loop.d.ts +92 -0
- package/dist/toolkit/capability-loop.js +81 -0
- package/dist/toolkit/capability-loop.js.map +1 -0
- package/dist/toolkit/manifest.d.ts +21 -0
- package/dist/toolkit/manifest.js +38 -0
- package/dist/toolkit/manifest.js.map +1 -0
- package/dist/toolkit/module.d.ts +5 -0
- package/dist/toolkit/module.js +9 -0
- package/dist/toolkit/module.js.map +1 -1
- package/dist/toolkit/prompt.d.ts +3 -1
- package/dist/toolkit/prompt.js +22 -3
- package/dist/toolkit/prompt.js.map +1 -1
- package/dist/toolkit/skill.d.ts +3 -1
- package/dist/toolkit/skill.js +2 -0
- package/dist/toolkit/skill.js.map +1 -1
- package/dist/toolkit/subagent.d.ts +3 -1
- package/dist/toolkit/subagent.js +2 -0
- package/dist/toolkit/subagent.js.map +1 -1
- package/dist/toolkit/tool.d.ts +3 -1
- package/dist/toolkit/tool.js +2 -0
- package/dist/toolkit/tool.js.map +1 -1
- package/dist/transport/approval-policy.d.ts +1 -1
- package/dist/transport/approval-policy.js +18 -3
- package/dist/transport/approval-policy.js.map +1 -1
- package/dist/transport/approval-supervisor.d.ts +15 -3
- package/dist/transport/approval-supervisor.js +37 -11
- package/dist/transport/approval-supervisor.js.map +1 -1
- package/dist/transport/async.d.ts +3 -4
- package/dist/transport/async.js +157 -30
- package/dist/transport/async.js.map +1 -1
- package/dist/transport/drain-gate.js +13 -1
- package/dist/transport/drain-gate.js.map +1 -1
- package/dist/transport/http-endpoints.d.ts +13 -0
- package/dist/transport/http-endpoints.js +108 -28
- package/dist/transport/http-endpoints.js.map +1 -1
- package/dist/transport/http-shapes.d.ts +26 -3
- package/dist/transport/http-shapes.js +12 -2
- package/dist/transport/http-shapes.js.map +1 -1
- package/dist/transport/http.d.ts +46 -2
- package/dist/transport/http.js +43 -18
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/resume-scanner.d.ts +3 -0
- package/dist/transport/resume-scanner.js +10 -1
- package/dist/transport/resume-scanner.js.map +1 -1
- package/dist/transport/run-call-options.js +46 -1
- package/dist/transport/run-call-options.js.map +1 -1
- package/dist/transport/run-options.d.ts +98 -0
- package/dist/transport/run-options.js +121 -0
- package/dist/transport/run-options.js.map +1 -0
- package/dist/transport/run-outcome.js +21 -1
- package/dist/transport/run-outcome.js.map +1 -1
- package/dist/transport/serve.js +10 -0
- package/dist/transport/serve.js.map +1 -1
- package/dist/transport/signal-supervisor.d.ts +3 -0
- package/dist/transport/signal-supervisor.js +5 -1
- package/dist/transport/signal-supervisor.js.map +1 -1
- package/dist/transport/stream-janitor.d.ts +45 -0
- package/dist/transport/stream-janitor.js +137 -0
- package/dist/transport/stream-janitor.js.map +1 -0
- package/dist/transport/task-wake-gate.d.ts +40 -0
- package/dist/transport/task-wake-gate.js +69 -0
- package/dist/transport/task-wake-gate.js.map +1 -0
- package/package.json +2 -2
|
@@ -34,6 +34,23 @@ export async function readClaimed(store, rec) {
|
|
|
34
34
|
* `suspendedReason` / `wakeAt`,所以必须在本件**之后**调用 —— 顺序是承重的)。
|
|
35
35
|
*/
|
|
36
36
|
export function applySuspendedOutcome(rec, result, suspendedMessages, ctx) {
|
|
37
|
+
// 同一轮等待的判定(2026-11-07 深评 F2):事件投递会把 approval 挂起的任务叫醒,但
|
|
38
|
+
// 引擎发现决定仍不齐会**原样再挂起**(同一批 pending)。若无条件把 suspendedSince
|
|
39
|
+
// 刷成 now,每 < approvalTimeoutMs 投一条事件就能把审批超时永久推迟。
|
|
40
|
+
// 只有「新的一轮等待」(非续跑 / 待决集合变了 / 原因变了)才重置计时基准。
|
|
41
|
+
const previousPending = rec.pendingApprovals;
|
|
42
|
+
const samePendingSet = previousPending !== undefined &&
|
|
43
|
+
result.pendingApprovals !== undefined &&
|
|
44
|
+
previousPending.length === result.pendingApprovals.length &&
|
|
45
|
+
previousPending.every((id, i) => id === result.pendingApprovals[i]);
|
|
46
|
+
const sameWait = ctx.isResume &&
|
|
47
|
+
rec.suspendedReason === result.suspendedReason &&
|
|
48
|
+
(result.suspendedReason !== 'approval' ||
|
|
49
|
+
(samePendingSet &&
|
|
50
|
+
// 只要旧决定还在,就不能沿用 since:那条决定可能**已经被上一段消费**,
|
|
51
|
+
// 沿用会让这一次同名等待直接复用它(审批绕过)。F2 要保留的是「纯事件唤醒、
|
|
52
|
+
// 一个决定都还没做」的时钟。
|
|
53
|
+
previousPending?.every((id) => rec.approvals?.[id] === undefined) === true));
|
|
37
54
|
// HITL 挂起不是终态:槽位照常释放、onFinished 不触发、finishedAt 不置 —— 那三件事各自
|
|
38
55
|
// 在自己的出口判(`#execute` 的 finally),这里只负责把记录改成「在等」的样子。
|
|
39
56
|
rec.status = 'suspended';
|
|
@@ -44,11 +61,14 @@ export function applySuspendedOutcome(rec, result, suspendedMessages, ctx) {
|
|
|
44
61
|
const snapshot = !ctx.isResume && rec.spec.options?.sessionId !== undefined && ctx.hasSessionStore
|
|
45
62
|
? rec.spec.messages
|
|
46
63
|
: undefined;
|
|
64
|
+
// 快照同时进记录:内存 Map 只是本进程快路,重启后由它接着补写会话。
|
|
65
|
+
if (snapshot !== undefined)
|
|
66
|
+
rec.sessionInput = snapshot;
|
|
47
67
|
// 扩展后的消息历史(末尾是含未决 tool_use 的 assistant 消息)与待决清单、挂起时刻一起落库
|
|
48
68
|
// —— approve / 惰性超时 / 重启后都靠它们。
|
|
49
69
|
rec.spec = { ...rec.spec, messages: suspendedMessages };
|
|
50
70
|
rec.pendingApprovals = result.pendingApprovals;
|
|
51
|
-
rec.suspendedSince = ctx.now;
|
|
71
|
+
rec.suspendedSince = sameWait && rec.suspendedSince !== undefined ? rec.suspendedSince : ctx.now;
|
|
52
72
|
// 挂起原因:从结果形状里取,**不写死** —— 写死成 'approval' 会让将来新增的挂起原因在落库
|
|
53
73
|
// 这一步被悄悄改写成审批。
|
|
54
74
|
rec.suspendedReason = result.suspendedReason;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-outcome.js","sourceRoot":"","sources":["../../src/transport/run-outcome.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AA6CjD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAgB,EAChB,GAAe;IAEf,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAeD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAe,EACf,MAAsB,EACtB,iBAAiC,EACjC,GAA4B;IAE5B,4DAA4D;IAC5D,mDAAmD;IACnD,GAAG,CAAC,MAAM,GAAG,WAAW,CAAC;IACzB,kEAAkE;IAClE,yDAAyD;IACzD,oDAAoD;IACpD,qCAAqC;IACrC,MAAM,QAAQ,GACZ,CAAC,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS,IAAI,GAAG,CAAC,eAAe;QAC/E,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ;QACnB,CAAC,CAAC,SAAS,CAAC;IAChB,yDAAyD;IACzD,+BAA+B;IAC/B,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IACxD,GAAG,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAC/C,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"run-outcome.js","sourceRoot":"","sources":["../../src/transport/run-outcome.ts"],"names":[],"mappings":"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"}
|
package/dist/transport/serve.js
CHANGED
|
@@ -59,6 +59,16 @@ export function gracefulShutdown(opts) {
|
|
|
59
59
|
}
|
|
60
60
|
for (const s of signals)
|
|
61
61
|
process.off(s, onSignal);
|
|
62
|
+
// Node 的默认终止动作是在**信号投递那一刻**按「有没有监听者」决定的;在 handler
|
|
63
|
+
// 里替当前信号摘监听不会补做默认动作 ⇒ 第二次信号被吞掉,必须第三次才死
|
|
64
|
+
// (2026-11-07 深评 F10,实测第 2 次 SIGTERM 后进程仍存活)。重新投递一次。
|
|
65
|
+
//
|
|
66
|
+
// ⚠️ 前提收紧(2026-11-09 深评 P3-5):这里能走默认终止,是因为**本件的**监听已经
|
|
67
|
+
// 摘掉。宿主若为同一信号另注册了 handler,重新投递会交给它的 handler —— 它不退出
|
|
68
|
+
// 就还是不终止(`process.kill` 的返回值看不出这件事)。要在任何宿主下都保证「第二次
|
|
69
|
+
// 必死」,用 `onSecondSignal` 自己 `process.exit(128 + n)`;本件不替宿主决定这件事,
|
|
70
|
+
// 因为它可能还有自己的最后一跳要跑。
|
|
71
|
+
process.kill(process.pid, signal);
|
|
62
72
|
return;
|
|
63
73
|
}
|
|
64
74
|
first = false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/transport/serve.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/transport/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
import type { TaskEvent } from '../core/run.js';
|
|
28
28
|
import type { TaskRecord, TaskStore } from '../store/store.js';
|
|
29
29
|
import type { ApprovalSupervisor } from './approval-supervisor.js';
|
|
30
|
+
import { TaskWakeGate } from './task-wake-gate.js';
|
|
30
31
|
/**
|
|
31
32
|
* `signalTask` 的失败:带 HTTP 语义的状态码(404 = 任务不存在,409 = 任务当前不在
|
|
32
33
|
* `suspended` 状态 / 同 eventId 已投递)。形状与 `async.ts` 的 `TaskCancelError` 同款
|
|
@@ -45,6 +46,8 @@ export interface SignalSupervisorDeps {
|
|
|
45
46
|
approvals: ApprovalSupervisor;
|
|
46
47
|
/** 派发入口(`AsyncRunner.#dispatch`:停机闸 + 唯一派发点 + 停机窗口里的一次告警) */
|
|
47
48
|
dispatch(rec: TaskRecord): void;
|
|
49
|
+
/** 跨唤醒路径的 per-task 互斥闸(2026-11-07 深评 P1,与 approve / wakeDue 共用) */
|
|
50
|
+
wakeGate?: TaskWakeGate;
|
|
48
51
|
/**
|
|
49
52
|
* cancel 的 per-task 在飞闸读数(2026-10-13 ref-run-root-close):投递进入前等它收口再重判
|
|
50
53
|
* (否则事件落库与 cancelled 落库互相覆盖,两边都说了假话)。
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TaskWakeGate } from './task-wake-gate.js';
|
|
1
2
|
/**
|
|
2
3
|
* `signalTask` 的失败:带 HTTP 语义的状态码(404 = 任务不存在,409 = 任务当前不在
|
|
3
4
|
* `suspended` 状态 / 同 eventId 已投递)。形状与 `async.ts` 的 `TaskCancelError` 同款
|
|
@@ -39,8 +40,11 @@ export class SignalSupervisor {
|
|
|
39
40
|
/** 同一任务的在飞事件投递(`signalTask` 的重入闸,见下) */
|
|
40
41
|
#inflight = new Map();
|
|
41
42
|
#deps;
|
|
43
|
+
/** 共享闸;单测可省略(省略时本件自建一把,跨路径互斥只在 AsyncRunner 注入时生效) */
|
|
44
|
+
#wakeGate;
|
|
42
45
|
constructor(deps) {
|
|
43
46
|
this.#deps = deps;
|
|
47
|
+
this.#wakeGate = deps.wakeGate ?? new TaskWakeGate();
|
|
44
48
|
}
|
|
45
49
|
/**
|
|
46
50
|
* 投递一个事件给**挂起**的任务(2026-09-28 ref-run-event-input,run 事件投入口;
|
|
@@ -86,7 +90,7 @@ export class SignalSupervisor {
|
|
|
86
90
|
const prev = this.#inflight.get(taskId);
|
|
87
91
|
const run = Promise.resolve(prev)
|
|
88
92
|
.catch(() => undefined) // 前一次的 reject 已由它自己的调用方接走,这里只排队
|
|
89
|
-
.then(() => this.#signalInner(taskId, event))
|
|
93
|
+
.then(() => this.#wakeGate.run(taskId, () => this.#signalInner(taskId, event)))
|
|
90
94
|
.finally(() => {
|
|
91
95
|
// 只摘自己:链上可能已有更新的节点(无条件 delete 会摘掉别人的)
|
|
92
96
|
if (this.#inflight.get(taskId) === run)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal-supervisor.js","sourceRoot":"","sources":["../../src/transport/signal-supervisor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"signal-supervisor.js","sourceRoot":"","sources":["../../src/transport/signal-supervisor.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,MAAM,CAAY;IAC3B,YAAY,MAAiB,EAAE,OAAe;QAC5C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAEpC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAqB9B,MAAM,OAAO,gBAAgB;IAC3B,wCAAwC;IAC/B,SAAS,GAAG,IAAI,GAAG,EAA+B,CAAC;IAEnD,KAAK,CAAuB;IACrC,qDAAqD;IAC5C,SAAS,CAAe;IAEjC,YAAY,IAA0B;QACpC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,YAAY,EAAE,CAAC;IACvD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,UAAU,CAAC,MAAc,EAAE,KAAgB;QACzC,kEAAkE;QAClE,oDAAoD;QACpD,oBAAoB;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC;QACrD,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;iBAC7B,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;iBACtB,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAChD,CAAC;QACD,qDAAqD;QACrD,sDAAsD;QACtD,uCAAuC;QACvC,2CAA2C;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;aAC9B,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,gCAAgC;aACvD,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;aAC9E,OAAO,CAAC,GAAG,EAAE;YACZ,sCAAsC;YACtC,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG;gBAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QACL,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,OAAO,GAAG,CAAC;IACb,CAAC;IAED,gEAAgE;IAChE,WAAW,CAAC,MAAc;QACxB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,MAAc,EAAE,KAAgB;QACjD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,aAAa,MAAM,EAAE,CAAC,CAAC;QAC/D,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAC/B,MAAM,IAAI,cAAc,CACtB,GAAG,EACH,QAAQ,MAAM,UAAU,GAAG,CAAC,MAAM,eAAe,CAClD,CAAC;QACJ,CAAC;QACD,kDAAkD;QAClD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,GAAG,CAAC,iBAAiB,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAClF,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,OAAO,cAAc,MAAM,QAAQ,CAAC,CAAC;QACjF,CAAC;QACD,8CAA8C;QAC9C,oDAAoD;QACpD,qDAAqD;QACrD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,kBAAkB,EAAE,CAAC;YAC3D,MAAM,IAAI,cAAc,CACtB,GAAG,EACH,QAAQ,MAAM,eAAe,kBAAkB,SAC7C,GAAG,CAAC,eAAe,KAAK,UAAU;gBAChC,CAAC,CAAC,gEAAgE;gBAClE,CAAC,CAAC,oEACN,iBAAiB,CAClB,CAAC;QACJ,CAAC;QACD,GAAG,CAAC,aAAa,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1D,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAC9D,GAAG,CAAC,iBAAiB;gBACnB,GAAG,CAAC,MAAM,GAAG,uBAAuB;oBAClC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,uBAAuB,CAAC;oBACjD,CAAC,CAAC,GAAG,CAAC;QACZ,CAAC;QACD,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,yCAAyC;QACjE,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;QACjC,4CAA4C;QAC5C,6CAA6C;QAC7C,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjC,mDAAmD;QACnD,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC7C,yDAAyD;QACzD,6BAA6B;QAC7B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,EAAE,GAAG,GAAG,EAAE,CAAC;IACpB,CAAC;CACF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 跨进程 SSE 流的**兜底收口**(2026-11-07 深评 F5;2026-11-09 深评 P2-1 提到 runner 级)。
|
|
3
|
+
*
|
|
4
|
+
* 本进程的 `TaskEventStreams.markDone` 只覆盖「本进程把任务写到终态」;任务在**别的进程**
|
|
5
|
+
* 被 approve / 到期唤醒并跑完时,本进程那条流既没有 `push` 也没有 `markDone`,只剩心跳。
|
|
6
|
+
* 这个纯件做低频 store 轮询:终态 → 交给调用方 markDone(由 onDone 发 end);
|
|
7
|
+
* 记录消失 / 已换主 → 发流级 closed。
|
|
8
|
+
*
|
|
9
|
+
* **为什么是「一组」而不是「一条流一个 janitor」**(P2-1):此前每个 SSE 订阅各建一个
|
|
10
|
+
* `setInterval`、每拍各 `store.get` 一次 —— N 个并发连接就是 N 个定时器 + N/5 QPS(SQLite 下
|
|
11
|
+
* 是 N/5 次读锁),而 `unref()` 只保证不挂住进程、不减少读放大。同一个 taskId 的多个订阅
|
|
12
|
+
* 本来该看**同一份**记录,现在由 runner 持有一个扫描器:一拍里每个**任务**只读一次 store,
|
|
13
|
+
* 再把结论分发给该任务的各个订阅。单连接仍是「登记 / 注销」。
|
|
14
|
+
*
|
|
15
|
+
* 不碰 store、不认识 trace:poll / isTerminal / 收口都以回调注入,单测可直接驱动
|
|
16
|
+
* (喂一个假 poll + 假计时,不碰真 store)。
|
|
17
|
+
*/
|
|
18
|
+
/** 轮询只需要这两个字段:本进程归属 + 是否终态(由 `isTerminal` 判) */
|
|
19
|
+
export interface StreamJanitorRecord {
|
|
20
|
+
ownerId?: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
/** 一条流的收口回调(同一任务的多个订阅各有一份) */
|
|
23
|
+
export interface StreamJanitorTarget {
|
|
24
|
+
isTerminal: (rec: StreamJanitorRecord) => boolean;
|
|
25
|
+
/** 任务已终态:调用方负责 markDone(终态帧由既有 onDone 发) */
|
|
26
|
+
onTerminal: () => void;
|
|
27
|
+
/** 任务记录消失 / 后续段在别的进程:发流级 closed 并收口 */
|
|
28
|
+
onClosed: () => void;
|
|
29
|
+
}
|
|
30
|
+
export interface StreamJanitorGroupDeps {
|
|
31
|
+
/** 读一条记录;同步 store 抛错、异步 store reject 都按「这一拍读不动,下一拍再试」处理 */
|
|
32
|
+
poll: (taskId: string) => Promise<StreamJanitorRecord | undefined>;
|
|
33
|
+
/** 本进程 ownerId */
|
|
34
|
+
ownerId: string;
|
|
35
|
+
/** 扫描间隔(毫秒);<= 0 = 不启动定时器 */
|
|
36
|
+
pollMs: number;
|
|
37
|
+
}
|
|
38
|
+
export interface StreamJanitorGroup {
|
|
39
|
+
/** 登记一条流;返回注销函数(幂等) */
|
|
40
|
+
add(taskId: string, target: StreamJanitorTarget): () => void;
|
|
41
|
+
/** 停掉定时器并清空登记(runner 停机 / 测试收尾) */
|
|
42
|
+
close(): void;
|
|
43
|
+
}
|
|
44
|
+
/** 启动(或复用)扫描器。定时器**有登记才存在**:没有订阅时零成本。 */
|
|
45
|
+
export declare function createStreamJanitorGroup(deps: StreamJanitorGroupDeps): StreamJanitorGroup;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 跨进程 SSE 流的**兜底收口**(2026-11-07 深评 F5;2026-11-09 深评 P2-1 提到 runner 级)。
|
|
3
|
+
*
|
|
4
|
+
* 本进程的 `TaskEventStreams.markDone` 只覆盖「本进程把任务写到终态」;任务在**别的进程**
|
|
5
|
+
* 被 approve / 到期唤醒并跑完时,本进程那条流既没有 `push` 也没有 `markDone`,只剩心跳。
|
|
6
|
+
* 这个纯件做低频 store 轮询:终态 → 交给调用方 markDone(由 onDone 发 end);
|
|
7
|
+
* 记录消失 / 已换主 → 发流级 closed。
|
|
8
|
+
*
|
|
9
|
+
* **为什么是「一组」而不是「一条流一个 janitor」**(P2-1):此前每个 SSE 订阅各建一个
|
|
10
|
+
* `setInterval`、每拍各 `store.get` 一次 —— N 个并发连接就是 N 个定时器 + N/5 QPS(SQLite 下
|
|
11
|
+
* 是 N/5 次读锁),而 `unref()` 只保证不挂住进程、不减少读放大。同一个 taskId 的多个订阅
|
|
12
|
+
* 本来该看**同一份**记录,现在由 runner 持有一个扫描器:一拍里每个**任务**只读一次 store,
|
|
13
|
+
* 再把结论分发给该任务的各个订阅。单连接仍是「登记 / 注销」。
|
|
14
|
+
*
|
|
15
|
+
* 不碰 store、不认识 trace:poll / isTerminal / 收口都以回调注入,单测可直接驱动
|
|
16
|
+
* (喂一个假 poll + 假计时,不碰真 store)。
|
|
17
|
+
*/
|
|
18
|
+
/** 启动(或复用)扫描器。定时器**有登记才存在**:没有订阅时零成本。 */
|
|
19
|
+
export function createStreamJanitorGroup(deps) {
|
|
20
|
+
const targets = new Map();
|
|
21
|
+
let timer;
|
|
22
|
+
let closed = false;
|
|
23
|
+
let ticking = false;
|
|
24
|
+
const stopTimer = () => {
|
|
25
|
+
if (timer === undefined)
|
|
26
|
+
return;
|
|
27
|
+
clearInterval(timer);
|
|
28
|
+
timer = undefined;
|
|
29
|
+
};
|
|
30
|
+
const ensureTimer = () => {
|
|
31
|
+
if (closed || timer !== undefined || deps.pollMs <= 0)
|
|
32
|
+
return;
|
|
33
|
+
timer = setInterval(() => void tick(), deps.pollMs);
|
|
34
|
+
timer.unref?.();
|
|
35
|
+
};
|
|
36
|
+
const safe = (fn) => {
|
|
37
|
+
try {
|
|
38
|
+
fn();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
/* 收口回调自己的错不得变成 unhandled(观测不击穿业务) */
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/** 一拍只读一次 store:同任务的多个订阅共用这次结果 */
|
|
45
|
+
const checkOne = async (taskId) => {
|
|
46
|
+
const first = targets.get(taskId);
|
|
47
|
+
if (first === undefined)
|
|
48
|
+
return;
|
|
49
|
+
let rec;
|
|
50
|
+
try {
|
|
51
|
+
rec = await deps.poll(taskId);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// 同步 store(已 close)的 poll 可能同步抛、异步 store 可能 reject:
|
|
55
|
+
// 两种都按「这一拍读不动」处理 —— 定时器回调里逃出去就是 uncaughtException。
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (closed)
|
|
59
|
+
return;
|
|
60
|
+
// poll 期间流可能已经关掉 / 换主:重新取一次,别拿过期快照去调回调
|
|
61
|
+
const live = targets.get(taskId);
|
|
62
|
+
if (live === undefined)
|
|
63
|
+
return;
|
|
64
|
+
for (const target of [...live]) {
|
|
65
|
+
if (!live.has(target))
|
|
66
|
+
continue;
|
|
67
|
+
if (rec === undefined) {
|
|
68
|
+
live.delete(target);
|
|
69
|
+
safe(target.onClosed);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (target.isTerminal(rec)) {
|
|
73
|
+
live.delete(target);
|
|
74
|
+
safe(target.onTerminal);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (rec.ownerId !== undefined && rec.ownerId !== deps.ownerId) {
|
|
78
|
+
live.delete(target);
|
|
79
|
+
safe(target.onClosed);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (live.size === 0)
|
|
83
|
+
targets.delete(taskId);
|
|
84
|
+
};
|
|
85
|
+
/** 扫描一拍。上一拍还没跑完(poll 慢)⇒ 跳过,不叠请求 */
|
|
86
|
+
const tick = async () => {
|
|
87
|
+
if (closed || ticking || targets.size === 0)
|
|
88
|
+
return;
|
|
89
|
+
ticking = true;
|
|
90
|
+
try {
|
|
91
|
+
await Promise.all([...targets.keys()].map((taskId) => checkOne(taskId)));
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// 一拍里的意外不得外逃:`setInterval` 回调里的 rejection 就是 unhandledRejection
|
|
95
|
+
// (Node ≥15 终止宿主)—— 观测路径不允许有这种出口。下一拍照常继续。
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
ticking = false;
|
|
99
|
+
if (targets.size === 0)
|
|
100
|
+
stopTimer();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
return {
|
|
104
|
+
add(taskId, target) {
|
|
105
|
+
if (closed)
|
|
106
|
+
return () => { };
|
|
107
|
+
let set = targets.get(taskId);
|
|
108
|
+
if (set === undefined) {
|
|
109
|
+
set = new Set();
|
|
110
|
+
targets.set(taskId, set);
|
|
111
|
+
}
|
|
112
|
+
set.add(target);
|
|
113
|
+
ensureTimer();
|
|
114
|
+
let removed = false;
|
|
115
|
+
return () => {
|
|
116
|
+
if (removed)
|
|
117
|
+
return; // 幂等:调用方可能在连接关闭路径上重复调
|
|
118
|
+
removed = true;
|
|
119
|
+
const live = targets.get(taskId);
|
|
120
|
+
if (live === undefined)
|
|
121
|
+
return;
|
|
122
|
+
live.delete(target);
|
|
123
|
+
if (live.size === 0)
|
|
124
|
+
targets.delete(taskId);
|
|
125
|
+
// 最后一条流也走了 ⇒ 定时器不必再跑(下一个订阅到来时重新起)
|
|
126
|
+
if (targets.size === 0)
|
|
127
|
+
stopTimer();
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
close() {
|
|
131
|
+
closed = true;
|
|
132
|
+
targets.clear();
|
|
133
|
+
stopTimer();
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=stream-janitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-janitor.js","sourceRoot":"","sources":["../../src/transport/stream-janitor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 每条任务的**唤醒互斥闸**(approve / signalTask / 审批超时 / 到期唤醒共用)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么需要它(2026-11-07 深度评审 P1):四条唤醒路径各自有「在飞闸」,但**互不登记、互不查看**。
|
|
5
|
+
* `approve` 与 `wakeDue` 靠挂起原因天然互斥;而 `signalTask` 对两种挂起原因都放行 ⇒
|
|
6
|
+
* `approve × signalTask`、`wakeDue × signalTask` 可以并发。两边都先 `get` 到同一份 suspended
|
|
7
|
+
* 快照,再各自 `status='running'` + `save` + `dispatch` ⇒ 同一任务被 `#execute` 两次,
|
|
8
|
+
* 后写的那份记录还可能把已完成的段覆盖回 suspended、丢掉审批决定。
|
|
9
|
+
*
|
|
10
|
+
* 语义:**同一 taskId 上,进入本闸的唤醒路径串行**。最后一个登记的 tail 会在它及其之前
|
|
11
|
+
* 所有路径收口后才 resolve,所以 `cancel` 只要 `await gate.tail(taskId)` 就能等到
|
|
12
|
+
* 「此刻已登记的全部唤醒」—— 不需要知道它具体是 approve 还是 signal。
|
|
13
|
+
*
|
|
14
|
+
* 与各监督件自己的 `#inflight` 的关系:那些闸仍保留(approve 的「并发重入共享同一次」
|
|
15
|
+
* 语义依赖它;signal 的事件串行链也依赖它),本闸只负责**跨路径**互斥。两边在同一同步段
|
|
16
|
+
* 登记,先登记者赢。
|
|
17
|
+
*
|
|
18
|
+
* 纯件:不碰 store、不认识记录,只做「同一 key 的 Promise 串行」。单测见
|
|
19
|
+
* `tests/transport/task-wake-gate.test.ts`。
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ **隐含前提:`fn` 必须最终 settle**(2026-11-09 深评「待确认」)。等待链**没有超时** ——
|
|
22
|
+
* 若某条路径的 `fn` 永远挂着(典型:宿主 store 的 `save` 不返回),同任务后续所有唤醒会
|
|
23
|
+
* 永久排队,`cancel` 的 `await tail` 也会一起挂住。当前四个调用点的 `fn` 只碰 store,
|
|
24
|
+
* 属于「宿主必须把 store 实现成会 settle」的契约;要打破它得在闸上加超时,而超时语义
|
|
25
|
+
* (「超时后放行」= 互斥失效)比挂住更危险,所以这里选择写明前提而不是加计时器。
|
|
26
|
+
*/
|
|
27
|
+
export declare class TaskWakeGate {
|
|
28
|
+
#private;
|
|
29
|
+
/** 该任务上是否已有登记(排队中或执行中)——惰性路径据此跳过本轮 */
|
|
30
|
+
busy(taskId: string): boolean;
|
|
31
|
+
/** 该任务上全部已登记路径的收口 Promise;没有则为 undefined */
|
|
32
|
+
tail(taskId: string): Promise<unknown> | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* 串行执行:前一条(若存在)收口后才开始 `fn`。
|
|
35
|
+
*
|
|
36
|
+
* `fn` 的同步前段(第一个 await 之前)在**调用者的同步段**里执行 —— 与不加闸时
|
|
37
|
+
* 的时序一致,既有「进闸后立刻重读」的测试不会因为多了一个微任务而变味。
|
|
38
|
+
*/
|
|
39
|
+
run<T>(taskId: string, fn: () => Promise<T>): Promise<T>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 每条任务的**唤醒互斥闸**(approve / signalTask / 审批超时 / 到期唤醒共用)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么需要它(2026-11-07 深度评审 P1):四条唤醒路径各自有「在飞闸」,但**互不登记、互不查看**。
|
|
5
|
+
* `approve` 与 `wakeDue` 靠挂起原因天然互斥;而 `signalTask` 对两种挂起原因都放行 ⇒
|
|
6
|
+
* `approve × signalTask`、`wakeDue × signalTask` 可以并发。两边都先 `get` 到同一份 suspended
|
|
7
|
+
* 快照,再各自 `status='running'` + `save` + `dispatch` ⇒ 同一任务被 `#execute` 两次,
|
|
8
|
+
* 后写的那份记录还可能把已完成的段覆盖回 suspended、丢掉审批决定。
|
|
9
|
+
*
|
|
10
|
+
* 语义:**同一 taskId 上,进入本闸的唤醒路径串行**。最后一个登记的 tail 会在它及其之前
|
|
11
|
+
* 所有路径收口后才 resolve,所以 `cancel` 只要 `await gate.tail(taskId)` 就能等到
|
|
12
|
+
* 「此刻已登记的全部唤醒」—— 不需要知道它具体是 approve 还是 signal。
|
|
13
|
+
*
|
|
14
|
+
* 与各监督件自己的 `#inflight` 的关系:那些闸仍保留(approve 的「并发重入共享同一次」
|
|
15
|
+
* 语义依赖它;signal 的事件串行链也依赖它),本闸只负责**跨路径**互斥。两边在同一同步段
|
|
16
|
+
* 登记,先登记者赢。
|
|
17
|
+
*
|
|
18
|
+
* 纯件:不碰 store、不认识记录,只做「同一 key 的 Promise 串行」。单测见
|
|
19
|
+
* `tests/transport/task-wake-gate.test.ts`。
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ **隐含前提:`fn` 必须最终 settle**(2026-11-09 深评「待确认」)。等待链**没有超时** ——
|
|
22
|
+
* 若某条路径的 `fn` 永远挂着(典型:宿主 store 的 `save` 不返回),同任务后续所有唤醒会
|
|
23
|
+
* 永久排队,`cancel` 的 `await tail` 也会一起挂住。当前四个调用点的 `fn` 只碰 store,
|
|
24
|
+
* 属于「宿主必须把 store 实现成会 settle」的契约;要打破它得在闸上加超时,而超时语义
|
|
25
|
+
* (「超时后放行」= 互斥失效)比挂住更危险,所以这里选择写明前提而不是加计时器。
|
|
26
|
+
*/
|
|
27
|
+
export class TaskWakeGate {
|
|
28
|
+
/**
|
|
29
|
+
* 每个 taskId 的**队列尾**。它 resolve 的条件是「本路径之后才登记的路径也都已完成」,
|
|
30
|
+
* 因此它同时可当 `cancel` 的等待句柄。
|
|
31
|
+
*/
|
|
32
|
+
#tails = new Map();
|
|
33
|
+
/** 该任务上是否已有登记(排队中或执行中)——惰性路径据此跳过本轮 */
|
|
34
|
+
busy(taskId) {
|
|
35
|
+
return this.#tails.has(taskId);
|
|
36
|
+
}
|
|
37
|
+
/** 该任务上全部已登记路径的收口 Promise;没有则为 undefined */
|
|
38
|
+
tail(taskId) {
|
|
39
|
+
return this.#tails.get(taskId);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 串行执行:前一条(若存在)收口后才开始 `fn`。
|
|
43
|
+
*
|
|
44
|
+
* `fn` 的同步前段(第一个 await 之前)在**调用者的同步段**里执行 —— 与不加闸时
|
|
45
|
+
* 的时序一致,既有「进闸后立刻重读」的测试不会因为多了一个微任务而变味。
|
|
46
|
+
*/
|
|
47
|
+
async run(taskId, fn) {
|
|
48
|
+
const prev = this.#tails.get(taskId);
|
|
49
|
+
let release;
|
|
50
|
+
const gate = new Promise((resolve) => {
|
|
51
|
+
release = resolve;
|
|
52
|
+
});
|
|
53
|
+
// tail 必须把 gate 串在 prev 之后:等待者拿到 tail 时,等待的是「这条路径也结束」。
|
|
54
|
+
const tail = prev === undefined ? gate : prev.catch(() => undefined).then(() => gate);
|
|
55
|
+
this.#tails.set(taskId, tail);
|
|
56
|
+
if (prev !== undefined)
|
|
57
|
+
await prev.catch(() => undefined);
|
|
58
|
+
try {
|
|
59
|
+
return await fn();
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
release();
|
|
63
|
+
// 只摘自己:若后面又排了新的 tail,留给它自己摘(无条件 delete 会摘掉别人的)。
|
|
64
|
+
if (this.#tails.get(taskId) === tail)
|
|
65
|
+
this.#tails.delete(taskId);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=task-wake-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-wake-gate.js","sourceRoot":"","sources":["../../src/transport/task-wake-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,OAAO,YAAY;IACvB;;;OAGG;IACM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;IAEtD,sCAAsC;IACtC,IAAI,CAAC,MAAc;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,4CAA4C;IAC5C,IAAI,CAAC,MAAc;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAG,CAAI,MAAc,EAAE,EAAoB;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,OAAoB,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACzC,OAAO,GAAG,OAAO,CAAC;QACpB,CAAC,CAAC,CAAC;QACH,uDAAuD;QACvD,MAAM,IAAI,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACtF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC9B,IAAI,IAAI,KAAK,SAAS;YAAE,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC1D,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;gBAAS,CAAC;YACT,OAAO,EAAE,CAAC;YACV,gDAAgD;YAChD,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI;gBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@migor/agentia",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.6",
|
|
4
4
|
"description": "Declarative agent service framework for TypeScript: decorators + DI declare four capability kinds, durable runs, and per-run traces, cost and metrics. Zero runtime dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"bench:otlp": "tsx scripts/bench-otlp-snapshot.ts"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
|
-
"@anthropic-ai/sdk": "^
|
|
85
|
+
"@anthropic-ai/sdk": "^1.3.0",
|
|
86
86
|
"@biomejs/biome": "^2.5.14",
|
|
87
87
|
"@grpc/grpc-js": "^1.14.6",
|
|
88
88
|
"@grpc/proto-loader": "^0.8.1",
|