@migor/agentia 0.9.3 → 0.9.5
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 +779 -1
- package/README.md +1 -1
- package/dist/AGENTS.md +320 -37
- package/dist/container/container.js +15 -0
- package/dist/container/container.js.map +1 -1
- package/dist/core/limits.d.ts +121 -17
- package/dist/core/limits.js +133 -16
- package/dist/core/limits.js.map +1 -1
- package/dist/core/line-framing.d.ts +36 -0
- package/dist/core/line-framing.js +39 -0
- package/dist/core/line-framing.js.map +1 -0
- package/dist/core/message.d.ts +11 -0
- package/dist/core/run.d.ts +68 -4
- package/dist/core/run.js +27 -1
- package/dist/core/run.js.map +1 -1
- package/dist/core/schema.js +59 -8
- package/dist/core/schema.js.map +1 -1
- package/dist/core/timeout.d.ts +43 -1
- package/dist/core/timeout.js +76 -3
- package/dist/core/timeout.js.map +1 -1
- package/dist/core/tool.d.ts +39 -1
- package/dist/core/trace.d.ts +40 -1
- package/dist/core/trace.js +26 -0
- package/dist/core/trace.js.map +1 -1
- package/dist/engine/defer.d.ts +49 -0
- package/dist/engine/defer.js +72 -0
- package/dist/engine/defer.js.map +1 -0
- package/dist/engine/forwarded.d.ts +14 -5
- package/dist/engine/forwarded.js +4 -1
- package/dist/engine/forwarded.js.map +1 -1
- package/dist/engine/loop-result.d.ts +65 -7
- package/dist/engine/loop-result.js +24 -5
- package/dist/engine/loop-result.js.map +1 -1
- package/dist/engine/loop.d.ts +2 -0
- package/dist/engine/loop.js +157 -17
- package/dist/engine/loop.js.map +1 -1
- package/dist/engine/mcp-server-http.d.ts +2 -0
- package/dist/engine/mcp-server-http.js +215 -0
- package/dist/engine/mcp-server-http.js.map +1 -0
- package/dist/engine/mcp-server-stdio.d.ts +15 -0
- package/dist/engine/mcp-server-stdio.js +106 -0
- package/dist/engine/mcp-server-stdio.js.map +1 -0
- package/dist/engine/mcp-server.d.ts +134 -0
- package/dist/engine/mcp-server.js +232 -0
- package/dist/engine/mcp-server.js.map +1 -0
- package/dist/engine/menu-drift.d.ts +34 -0
- package/dist/engine/menu-drift.js +81 -0
- package/dist/engine/menu-drift.js.map +1 -0
- package/dist/engine/policy.d.ts +2 -1
- package/dist/engine/policy.js +7 -1
- package/dist/engine/policy.js.map +1 -1
- package/dist/engine/replay.d.ts +2 -1
- package/dist/engine/replay.js +3 -2
- package/dist/engine/replay.js.map +1 -1
- package/dist/engine/resume-input.d.ts +10 -0
- package/dist/engine/resume-input.js +11 -0
- package/dist/engine/resume-input.js.map +1 -1
- package/dist/engine/run-config.d.ts +26 -2
- package/dist/engine/run-config.js +60 -0
- package/dist/engine/run-config.js.map +1 -1
- package/dist/engine/spec.d.ts +33 -4
- package/dist/engine/spec.js.map +1 -1
- package/dist/engine/tool-context.d.ts +10 -5
- package/dist/engine/tool-context.js +5 -1
- package/dist/engine/tool-context.js.map +1 -1
- package/dist/engine/tracer.d.ts +43 -5
- package/dist/engine/tracer.js +73 -10
- package/dist/engine/tracer.js.map +1 -1
- package/dist/engine/trimming.js +151 -17
- package/dist/engine/trimming.js.map +1 -1
- package/dist/engine/turn.d.ts +60 -3
- package/dist/engine/turn.js +180 -71
- package/dist/engine/turn.js.map +1 -1
- package/dist/engine/types.d.ts +89 -3
- package/dist/engine/types.js +38 -2
- package/dist/engine/types.js.map +1 -1
- package/dist/engine/usage.d.ts +22 -3
- package/dist/engine/usage.js +47 -6
- package/dist/engine/usage.js.map +1 -1
- package/dist/eval/export.d.ts +48 -0
- package/dist/eval/export.js +146 -0
- package/dist/eval/export.js.map +1 -0
- package/dist/eval/harvest.js +7 -2
- package/dist/eval/harvest.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapter-options.d.ts +22 -0
- package/dist/integrations/adapter-options.js +51 -0
- package/dist/integrations/adapter-options.js.map +1 -1
- package/dist/integrations/anthropic.d.ts +4 -2
- package/dist/integrations/anthropic.js +27 -27
- package/dist/integrations/anthropic.js.map +1 -1
- package/dist/integrations/file-sink.d.ts +20 -0
- package/dist/integrations/file-sink.js +27 -0
- package/dist/integrations/file-sink.js.map +1 -0
- package/dist/integrations/mcp-http.d.ts +3 -3
- package/dist/integrations/mcp-http.js +1 -1
- package/dist/integrations/mcp-http.js.map +1 -1
- package/dist/integrations/mcp-protocol.d.ts +115 -0
- package/dist/integrations/mcp-protocol.js +69 -0
- package/dist/integrations/mcp-protocol.js.map +1 -0
- package/dist/integrations/mcp-stdio.d.ts +3 -3
- package/dist/integrations/mcp-stdio.js +17 -3
- package/dist/integrations/mcp-stdio.js.map +1 -1
- package/dist/integrations/mcp.d.ts +6 -95
- package/dist/integrations/mcp.js +24 -66
- package/dist/integrations/mcp.js.map +1 -1
- package/dist/integrations/metrics-otlp.js +52 -7
- package/dist/integrations/metrics-otlp.js.map +1 -1
- package/dist/integrations/metrics-render.d.ts +15 -0
- package/dist/integrations/metrics-render.js +79 -2
- package/dist/integrations/metrics-render.js.map +1 -1
- package/dist/integrations/metrics-state.d.ts +99 -1
- package/dist/integrations/metrics-state.js +128 -4
- package/dist/integrations/metrics-state.js.map +1 -1
- package/dist/integrations/metrics.d.ts +62 -6
- package/dist/integrations/metrics.js +49 -8
- package/dist/integrations/metrics.js.map +1 -1
- package/dist/integrations/openai.js +9 -31
- package/dist/integrations/openai.js.map +1 -1
- package/dist/integrations/otlp.d.ts +3 -3
- package/dist/integrations/report.d.ts +7 -0
- package/dist/integrations/report.js +20 -1
- package/dist/integrations/report.js.map +1 -1
- package/dist/runtime/memory.d.ts +61 -1
- package/dist/runtime/memory.js +47 -7
- package/dist/runtime/memory.js.map +1 -1
- package/dist/runtime/run.d.ts +12 -2
- package/dist/runtime/run.js +112 -15
- package/dist/runtime/run.js.map +1 -1
- package/dist/store/fsStore.js +10 -4
- package/dist/store/fsStore.js.map +1 -1
- package/dist/store/record.d.ts +41 -0
- package/dist/store/record.js +54 -0
- package/dist/store/record.js.map +1 -0
- package/dist/store/redisStore.d.ts +3 -1
- package/dist/store/redisStore.js +21 -7
- package/dist/store/redisStore.js.map +1 -1
- package/dist/store/sqliteStore.d.ts +23 -0
- package/dist/store/sqliteStore.js +103 -8
- package/dist/store/sqliteStore.js.map +1 -1
- package/dist/store/store.d.ts +65 -2
- package/dist/store/store.js +5 -2
- package/dist/store/store.js.map +1 -1
- package/dist/toolkit/capability-cycles.d.ts +50 -0
- package/dist/toolkit/capability-cycles.js +88 -0
- package/dist/toolkit/capability-cycles.js.map +1 -0
- package/dist/toolkit/capability-slice.d.ts +86 -0
- package/dist/toolkit/capability-slice.js +98 -0
- package/dist/toolkit/capability-slice.js.map +1 -0
- package/dist/toolkit/module.d.ts +7 -1
- package/dist/toolkit/module.js +65 -32
- package/dist/toolkit/module.js.map +1 -1
- package/dist/transport/approval-policy.d.ts +1 -1
- package/dist/transport/approval-policy.js +8 -4
- package/dist/transport/approval-policy.js.map +1 -1
- package/dist/transport/async.d.ts +197 -18
- package/dist/transport/async.js +871 -85
- package/dist/transport/async.js.map +1 -1
- package/dist/transport/drain-gate.d.ts +6 -2
- package/dist/transport/drain-gate.js +14 -2
- package/dist/transport/drain-gate.js.map +1 -1
- package/dist/transport/http-endpoints.d.ts +151 -0
- package/dist/transport/http-endpoints.js +480 -0
- package/dist/transport/http-endpoints.js.map +1 -0
- package/dist/transport/http-io.d.ts +66 -0
- package/dist/transport/http-io.js +145 -0
- package/dist/transport/http-io.js.map +1 -0
- package/dist/transport/http-route.d.ts +10 -3
- package/dist/transport/http-route.js +18 -3
- package/dist/transport/http-route.js.map +1 -1
- package/dist/transport/http-shapes.d.ts +11 -0
- package/dist/transport/http-shapes.js +31 -1
- package/dist/transport/http-shapes.js.map +1 -1
- package/dist/transport/http.d.ts +22 -35
- package/dist/transport/http.js +110 -467
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/owner-id.d.ts +29 -0
- package/dist/transport/owner-id.js +46 -0
- package/dist/transport/owner-id.js.map +1 -0
- package/dist/transport/owner-liveness.d.ts +8 -0
- package/dist/transport/owner-liveness.js +56 -0
- package/dist/transport/owner-liveness.js.map +1 -0
- package/dist/transport/resume-policy.d.ts +48 -10
- package/dist/transport/resume-policy.js +25 -10
- package/dist/transport/resume-policy.js.map +1 -1
- package/dist/transport/scheduler.js +23 -24
- package/dist/transport/scheduler.js.map +1 -1
- package/dist/transport/slot-pool.d.ts +9 -1
- package/dist/transport/slot-pool.js +9 -1
- package/dist/transport/slot-pool.js.map +1 -1
- package/dist/transport/sse.js +8 -0
- package/dist/transport/sse.js.map +1 -1
- package/dist/transport/task-events.d.ts +34 -3
- package/dist/transport/task-events.js +86 -5
- package/dist/transport/task-events.js.map +1 -1
- package/dist/transport/wake-policy.d.ts +50 -0
- package/dist/transport/wake-policy.js +43 -0
- package/dist/transport/wake-policy.js.map +1 -0
- package/package.json +6 -4
package/dist/runtime/run.d.ts
CHANGED
|
@@ -30,9 +30,19 @@ export declare class Run {
|
|
|
30
30
|
/** meta(供运行记录/持久化使用) */
|
|
31
31
|
toMeta(): RunMeta;
|
|
32
32
|
start(): void;
|
|
33
|
+
/**
|
|
34
|
+
* 收尾(进终态)。守卫与 `start()` / `suspend()` **同款**(外部深评 K3)。
|
|
35
|
+
*
|
|
36
|
+
* ⚠️ 以前它没有守卫:对一条**挂起中**的 run 调 `finish()` 会**静默把它翻成终态** ——
|
|
37
|
+
* 而挂起不是终态(它带着 `suspendedMessages` 等审批/唤醒,宿主的恢复路径正是靠它接回来的),
|
|
38
|
+
* 翻掉之后那条 run 在 store 里看起来「已结束」,恢复路径再也接不上,且**没有任何信号**。
|
|
39
|
+
* `Run` 是公开导出(`src/index.ts`)⇒ `finish` 是**对外承诺**,不设防等于让使用者自己踩。
|
|
40
|
+
*
|
|
41
|
+
* 判据只认 `running`:`queued` 的 run 还没开跑(`start()` 未调),拿结果去收尾同样是调用方用错。
|
|
42
|
+
*/
|
|
33
43
|
finish(result: AgentRunResult): void;
|
|
34
44
|
/**
|
|
35
|
-
* 挂起(HITL):从 running 进
|
|
45
|
+
* 挂起(HITL):从 running 进 suspended。**不是终态** —— finishedAt 不置、
|
|
36
46
|
* 不算成功也不算失败;扩展后的消息历史在 `result.suspendedMessages` 里,
|
|
37
47
|
* 由异步宿主落库等待审批,决定到齐后带着它重进引擎循环。
|
|
38
48
|
*/
|
|
@@ -74,7 +84,7 @@ export interface ExecuteRunOptions<S extends JsonSchema = JsonSchema> extends Ru
|
|
|
74
84
|
* 意外抛出等);rethrow:false 时这些同样收成 result 返回。
|
|
75
85
|
*/
|
|
76
86
|
rethrow?: boolean;
|
|
77
|
-
/** trace 出口(观测):run 收尾后逐个投递;sink
|
|
87
|
+
/** trace 出口(观测):run 收尾后逐个投递;sink 抛错被吞(吞之前落一条 console.warn 告警),不影响 run */
|
|
78
88
|
sinks?: TraceSink[];
|
|
79
89
|
/**
|
|
80
90
|
* trace 交给 sinks **之前**的最后一笔账(`run.finish` 之后、`flushSinks` 之前调一次,
|
package/dist/runtime/run.js
CHANGED
|
@@ -40,6 +40,8 @@ export class Run {
|
|
|
40
40
|
startedAt: this.startedAt,
|
|
41
41
|
finishedAt: this.finishedAt,
|
|
42
42
|
error: this._result?.error,
|
|
43
|
+
suspendedReason: this._result?.suspendedReason,
|
|
44
|
+
wakeAt: this._result?.wakeAt,
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
start() {
|
|
@@ -48,13 +50,26 @@ export class Run {
|
|
|
48
50
|
this._status = 'running';
|
|
49
51
|
this.startedAt = Date.now();
|
|
50
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* 收尾(进终态)。守卫与 `start()` / `suspend()` **同款**(外部深评 K3)。
|
|
55
|
+
*
|
|
56
|
+
* ⚠️ 以前它没有守卫:对一条**挂起中**的 run 调 `finish()` 会**静默把它翻成终态** ——
|
|
57
|
+
* 而挂起不是终态(它带着 `suspendedMessages` 等审批/唤醒,宿主的恢复路径正是靠它接回来的),
|
|
58
|
+
* 翻掉之后那条 run 在 store 里看起来「已结束」,恢复路径再也接不上,且**没有任何信号**。
|
|
59
|
+
* `Run` 是公开导出(`src/index.ts`)⇒ `finish` 是**对外承诺**,不设防等于让使用者自己踩。
|
|
60
|
+
*
|
|
61
|
+
* 判据只认 `running`:`queued` 的 run 还没开跑(`start()` 未调),拿结果去收尾同样是调用方用错。
|
|
62
|
+
*/
|
|
51
63
|
finish(result) {
|
|
64
|
+
if (this._status !== 'running') {
|
|
65
|
+
throw new Error(`cannot finish a run in status ${this._status}`);
|
|
66
|
+
}
|
|
52
67
|
this._result = result;
|
|
53
68
|
this.finishedAt = Date.now();
|
|
54
69
|
this._status = isSuccessStopReason(result.stopReason) ? 'succeeded' : 'failed';
|
|
55
70
|
}
|
|
56
71
|
/**
|
|
57
|
-
* 挂起(HITL):从 running 进
|
|
72
|
+
* 挂起(HITL):从 running 进 suspended。**不是终态** —— finishedAt 不置、
|
|
58
73
|
* 不算成功也不算失败;扩展后的消息历史在 `result.suspendedMessages` 里,
|
|
59
74
|
* 由异步宿主落库等待审批,决定到齐后带着它重进引擎循环。
|
|
60
75
|
*/
|
|
@@ -63,7 +78,7 @@ export class Run {
|
|
|
63
78
|
throw new Error(`cannot suspend a run in status ${this._status}`);
|
|
64
79
|
}
|
|
65
80
|
this._result = result;
|
|
66
|
-
this._status = '
|
|
81
|
+
this._status = 'suspended';
|
|
67
82
|
}
|
|
68
83
|
fail(error) {
|
|
69
84
|
this._status = 'failed';
|
|
@@ -84,6 +99,9 @@ export class Run {
|
|
|
84
99
|
typed: undefined,
|
|
85
100
|
suspendedMessages: undefined,
|
|
86
101
|
pendingApprovals: undefined,
|
|
102
|
+
suspendedReason: undefined,
|
|
103
|
+
wakeAt: undefined,
|
|
104
|
+
eventsDelivered: false,
|
|
87
105
|
};
|
|
88
106
|
return;
|
|
89
107
|
}
|
|
@@ -111,6 +129,15 @@ export async function executeRun(options) {
|
|
|
111
129
|
const ctx = new RunContext(run);
|
|
112
130
|
const memory = options.memory;
|
|
113
131
|
const session = options.session;
|
|
132
|
+
// 记忆 store 的形状检查走**入口**(不在下面那个 try 里):半个 CAS 是装配错误,
|
|
133
|
+
// 要与「store 故障」(那类辅助动作失败只降级不击穿)分开 —— 前者当场抛,后者照旧吞。
|
|
134
|
+
if (memory) {
|
|
135
|
+
assertMemoryStoreShape(memory.store);
|
|
136
|
+
if (!hasMemoryCas(memory.store))
|
|
137
|
+
noticeMissingMemoryCasOnce(memory.store);
|
|
138
|
+
}
|
|
139
|
+
/** 水合时读到的版本句柄:回写照它做 CAS(没 CAS 的 store 恒 undefined) */
|
|
140
|
+
let memoryRev;
|
|
114
141
|
return withRunContext(ctx, async () => {
|
|
115
142
|
try {
|
|
116
143
|
options.contextInit?.(ctx);
|
|
@@ -118,7 +145,7 @@ export async function executeRun(options) {
|
|
|
118
145
|
// 水合是辅助动作:store 故障(Redis 挂掉等)不得杀死本次 run ——
|
|
119
146
|
// 与下面 flushMemory 对称(那里已有同款防护)。失败即当「无记忆」继续跑。
|
|
120
147
|
try {
|
|
121
|
-
await hydrateMemory(memory, ctx);
|
|
148
|
+
memoryRev = await hydrateMemory(memory, ctx);
|
|
122
149
|
}
|
|
123
150
|
catch {
|
|
124
151
|
/* ignore:辅助动作失败不影响 run */
|
|
@@ -132,7 +159,7 @@ export async function executeRun(options) {
|
|
|
132
159
|
// 会话标识落 run 根 attribute(`session.id`)—— 多轮 run 按会话聚合的锚点
|
|
133
160
|
...(session ? { sessionId: session.id } : {}),
|
|
134
161
|
});
|
|
135
|
-
if (result.stopReason === '
|
|
162
|
+
if (result.stopReason === 'suspended') {
|
|
136
163
|
// HITL 挂起:不是终态 —— 记忆回写/会话追加维持「只成功才写」(挂起不写),
|
|
137
164
|
// beforeFlush 也只在正常收尾路径调(见该选项注释)。
|
|
138
165
|
// 但**照常 flushSinks**:挂起段的 trace 段落必须可观测(每段执行一棵树,
|
|
@@ -146,7 +173,7 @@ export async function executeRun(options) {
|
|
|
146
173
|
// 回写是辅助动作:失败不得把已成功的 run 翻成 failed(会丢结果与 trace),
|
|
147
174
|
// 与下面失败路径的 flushMemory 同款防护。
|
|
148
175
|
try {
|
|
149
|
-
await flushMemory(memory, ctx);
|
|
176
|
+
await flushMemory(memory, ctx, memoryRev);
|
|
150
177
|
}
|
|
151
178
|
catch {
|
|
152
179
|
/* ignore */
|
|
@@ -169,7 +196,7 @@ export async function executeRun(options) {
|
|
|
169
196
|
if (memory) {
|
|
170
197
|
// 失败路径也回写;save 自身出错不掩盖原始错误
|
|
171
198
|
try {
|
|
172
|
-
await flushMemory(memory, ctx);
|
|
199
|
+
await flushMemory(memory, ctx, memoryRev);
|
|
173
200
|
}
|
|
174
201
|
catch {
|
|
175
202
|
/* ignore */
|
|
@@ -241,7 +268,11 @@ async function runBeforeFlush(hook, trace, result) {
|
|
|
241
268
|
/* ignore:收尾动作失败不影响 run */
|
|
242
269
|
}
|
|
243
270
|
}
|
|
244
|
-
/**
|
|
271
|
+
/**
|
|
272
|
+
* 投递 trace 给所有 sink:观测失败(sink 抛错)不得影响 run 结果(同 memory 回写防护),
|
|
273
|
+
* 但**不再零信号**:吞之前落一条 console.warn(文案含「trace sink」,可 grep / 接日志采集)——
|
|
274
|
+
* 否则 sink 天天挂、面板一切如常,「trace 根本没落盘」要等下游消费时才发现。
|
|
275
|
+
*/
|
|
245
276
|
async function flushSinks(sinks, trace) {
|
|
246
277
|
if (!sinks || sinks.length === 0)
|
|
247
278
|
return;
|
|
@@ -249,23 +280,77 @@ async function flushSinks(sinks, trace) {
|
|
|
249
280
|
try {
|
|
250
281
|
await sink.export(trace);
|
|
251
282
|
}
|
|
252
|
-
catch {
|
|
253
|
-
|
|
283
|
+
catch (e) {
|
|
284
|
+
console.warn('[agentia] trace sink 投递失败:', e instanceof Error ? e.message : e);
|
|
254
285
|
}
|
|
255
286
|
}
|
|
256
287
|
}
|
|
257
|
-
/** 水合:store 值注入 blackboard;contextInit 已写的同名 key
|
|
288
|
+
/** 水合:store 值注入 blackboard;contextInit 已写的同名 key 不覆盖(用户种子优先)。
|
|
289
|
+
* 返回本次读到的**版本句柄**(没实现 CAS 的 store 恒为 `undefined`)—— 回写时原样递回。 */
|
|
258
290
|
async function hydrateMemory(memory, ctx) {
|
|
259
|
-
const
|
|
260
|
-
|
|
291
|
+
const { store, keys } = memory;
|
|
292
|
+
let loaded;
|
|
293
|
+
let rev;
|
|
294
|
+
if (typeof store.loadWithRev === 'function') {
|
|
295
|
+
const snapshot = await store.loadWithRev(keys);
|
|
296
|
+
loaded = snapshot.values;
|
|
297
|
+
rev = snapshot.rev;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
loaded = await store.load(keys);
|
|
301
|
+
}
|
|
302
|
+
for (const key of keys) {
|
|
261
303
|
// Object.hasOwn 而非 `in`:`in` 会命中 Object.prototype 的继承属性 ——
|
|
262
304
|
// key='toString'/'constructor' 之类会把原型上的函数当成记忆值水合进黑板
|
|
263
305
|
if (Object.hasOwn(loaded, key) && !ctx.has(key))
|
|
264
306
|
ctx.set(key, loaded[key]);
|
|
265
307
|
}
|
|
308
|
+
return rev;
|
|
309
|
+
}
|
|
310
|
+
/** CAS 能力是否在场。成对性由 `assertMemoryStoreShape` 在入口保证,这里只看有没有 */
|
|
311
|
+
function hasMemoryCas(store) {
|
|
312
|
+
return typeof store.loadWithRev === 'function' && typeof store.saveIfRev === 'function';
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* 入口形状检查:版本号的两个成员**必须成对**实现。
|
|
316
|
+
*
|
|
317
|
+
* 为什么半个 CAS 要当场拒(而不是「当作不支持、退回 last-write-wins」):只实现 `loadWithRev`
|
|
318
|
+
* 看起来是「读到了版本、很上心」,但只要回写不走 CAS,覆盖照样静默发生 —— 白多一次读,
|
|
319
|
+
* 却给出「这件事有据可查」的错觉。这是本仓反复收口的那类失败(**看起来在岗、其实不在岗**),
|
|
320
|
+
* 所以在入口响亮失败,与 `resolveTraceLimits` / 各构造期校验同款(坏值不静默降级)。
|
|
321
|
+
*/
|
|
322
|
+
function assertMemoryStoreShape(store) {
|
|
323
|
+
const hasRead = typeof store.loadWithRev === 'function';
|
|
324
|
+
const hasWrite = typeof store.saveIfRev === 'function';
|
|
325
|
+
if (hasRead === hasWrite)
|
|
326
|
+
return;
|
|
327
|
+
throw new TypeError(`MemoryStore 的版本号能力必须成对实现:${hasRead ? '只实现了 loadWithRev' : '只实现了 saveIfRev'} —— ` +
|
|
328
|
+
'半个 CAS 与「不支持」等效(回写仍会静默覆盖别人的改动),却让「有没有被覆盖」看起来有据可查。' +
|
|
329
|
+
'两个都实现(冲突时框架不写 + 出声),或两个都不实现(默认 last-write-wins,框架检测不到覆盖)。');
|
|
266
330
|
}
|
|
267
|
-
/**
|
|
268
|
-
|
|
331
|
+
/** 已提示过「这个 store 没有版本号」的实例 —— 一次性(每个 store 实例一次,不是每次 run 都吵) */
|
|
332
|
+
const casNoticeSent = new WeakSet();
|
|
333
|
+
/**
|
|
334
|
+
* 没有版本号的 store:**出声降级**(一次性提示)。
|
|
335
|
+
*
|
|
336
|
+
* 为什么要有这一句:默认路径(last-write-wins)下同 keys 的并发 run 会丢写,而框架
|
|
337
|
+
* **没有任何依据**发现它(版本号是唯一依据)—— 于是「我们在用这个 store」这件事本身就
|
|
338
|
+
* 值得说一声,让宿主自己决定要不要补 CAS。每个实例只吵一次:提示是给「装配的人」看的,
|
|
339
|
+
* 不是每轮都给日志刷屏。
|
|
340
|
+
*/
|
|
341
|
+
function noticeMissingMemoryCasOnce(store) {
|
|
342
|
+
if (casNoticeSent.has(store))
|
|
343
|
+
return;
|
|
344
|
+
casNoticeSent.add(store);
|
|
345
|
+
console.warn('[agentia] 记忆 store 未实现 loadWithRev / saveIfRev(版本号)⇒ 回写是 last-write-wins:' +
|
|
346
|
+
'同 keys 的并发 run 之间**丢写不会被发现**。要能发现请实现这两个成员' +
|
|
347
|
+
'(见 usage-guide「并发 run 共用同一 keys」)。本提示每个 store 实例只说一次。');
|
|
348
|
+
}
|
|
349
|
+
/** 回写:blackboard 里这些 key 的当前值存回 store。
|
|
350
|
+
*
|
|
351
|
+
* 有版本号(CAS)时走 `saveIfRev`:被拒(`committed !== true`)就**不写**并出声 ——
|
|
352
|
+
* 不合并、不重试(合并语义是 store 与宿主的决定,框架不替你猜);没版本号时退回 `save`。 */
|
|
353
|
+
async function flushMemory(memory, ctx, rev) {
|
|
269
354
|
// 无原型对象:`entries['__proto__'] = v` 在 {} 上会走 Object.prototype 的 setter
|
|
270
355
|
// (改掉原型而非建属性),该 key 的回写值会静默丢失
|
|
271
356
|
const entries = Object.create(null);
|
|
@@ -273,6 +358,18 @@ async function flushMemory(memory, ctx) {
|
|
|
273
358
|
if (ctx.has(key))
|
|
274
359
|
entries[key] = ctx.get(key);
|
|
275
360
|
}
|
|
276
|
-
|
|
361
|
+
const { store } = memory;
|
|
362
|
+
if (typeof store.saveIfRev === 'function') {
|
|
363
|
+
const outcome = await store.saveIfRev(entries, rev);
|
|
364
|
+
// 判据是 `!== true`(不是 `=== false`):`saveIfRev` 忘返回结果(`undefined`)时
|
|
365
|
+
// 按「没提交」处理 —— 宁可多报一次,也不把「不知道写没写」记成写成功。
|
|
366
|
+
if (outcome?.committed !== true) {
|
|
367
|
+
console.warn(`[agentia] 记忆回写被拒绝(${outcome?.reason ?? '未确认'},一个字都没写): ` +
|
|
368
|
+
`keys=[${memory.keys.join(', ')}] —— 同一份 store + keys 上有并发的 run 在你读之后写过` +
|
|
369
|
+
'(这就是 lost update 的本体:接着写就是覆盖掉它的改动)。本轮结果未落库,重新 load 后重写即可。');
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
await store.save(entries);
|
|
277
374
|
}
|
|
278
375
|
//# sourceMappingURL=run.js.map
|
package/dist/runtime/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/runtime/run.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAI1D;;;;GAIG;AACH,MAAM,OAAO,GAAG;IACL,QAAQ,CAAgB;IACxB,KAAK,CAAS;IACd,cAAc,CAAqB;IACnC,SAAS,GAAW,IAAI,CAAC,GAAG,EAAE,CAAC;IAChC,OAAO,GAAc,QAAQ,CAAC;IACtC,SAAS,CAAU;IACnB,UAAU,CAAU;IACZ,OAAO,CAAkB;IAEjC,YAAY,IAAI,GAA2D,EAAE;QAC3E,wDAAwD;QACxD,iBAAiB;QACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,aAAa,CAC/B,IAAI,CAAC,WAAW,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,CAC3F,CAAC;QACF,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;IAC5C,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IACD,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,wBAAwB;IACxB,MAAM;QACJ,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK;
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/runtime/run.ts"],"names":[],"mappings":"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"}
|
package/dist/store/fsStore.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, truncateSync, writeFileSync, } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
|
+
import { parseTaskRecord } from './record.js';
|
|
3
4
|
/**
|
|
4
5
|
* Agentia —— 文件宿主 TaskStore(spec §6.6:异步耐久 = 换宿主不换语义)。
|
|
5
6
|
*
|
|
@@ -18,6 +19,9 @@ import { dirname } from 'node:path';
|
|
|
18
19
|
export class FileTaskStore {
|
|
19
20
|
file;
|
|
20
21
|
onLoadError;
|
|
22
|
+
// 刻意**不实现** `listDue`(到期索引):记录常驻内存 Map(构造期 load 之后 `list()` 只是
|
|
23
|
+
// snapshot),每轮扫描本来就便宜 —— 它真正贵的全表读在构造期,索引管不到那里
|
|
24
|
+
// (实测读数见 spec §10 2026-09-28 ⑤)。runner 对缺失的 listDue 回退全表,语义不变。
|
|
21
25
|
byTask = new Map();
|
|
22
26
|
byKey = new Map(); // idempotencyKey → taskId
|
|
23
27
|
constructor(file, onLoadError) {
|
|
@@ -45,8 +49,9 @@ export class FileTaskStore {
|
|
|
45
49
|
if (!line.trim())
|
|
46
50
|
continue;
|
|
47
51
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
// 解析 + 旧形状读时归一都在唯一入口里(src/store/record.ts)
|
|
53
|
+
const rec = parseTaskRecord(line);
|
|
54
|
+
if (!rec)
|
|
50
55
|
continue;
|
|
51
56
|
this.byTask.set(rec.taskId, rec);
|
|
52
57
|
if (rec.idempotencyKey)
|
|
@@ -70,8 +75,9 @@ export class FileTaskStore {
|
|
|
70
75
|
const tail = raw.slice(cut);
|
|
71
76
|
let complete = false;
|
|
72
77
|
try {
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
// 只探「这半截是不是一条完整记录」:解析结果丢掉(归一由上面的 load 循环做,
|
|
79
|
+
// 那才是记录真正入内存的地方)
|
|
80
|
+
complete = parseTaskRecord(tail) !== undefined;
|
|
75
81
|
}
|
|
76
82
|
catch {
|
|
77
83
|
complete = false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fsStore.js","sourceRoot":"","sources":["../../src/store/fsStore.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,UAAU,EACV,SAAS,EACT,YAAY,EACZ,UAAU,EACV,MAAM,EACN,YAAY,EACZ,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"fsStore.js","sourceRoot":"","sources":["../../src/store/fsStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 任务记录的**读回唯一入口**(bytes → `TaskRecord`):JSON 解析 + 旧版本形状的读时归一。
|
|
3
|
+
*
|
|
4
|
+
* 为什么单独成件:**落盘的数据不受类型系统保护**。`JSON.parse(raw) as TaskRecord` 里那个 `as`
|
|
5
|
+
* 是一句「我保证它是」—— TS 只检查你**写进**类型的值,不检查**盘上**的值。于是改名这类破坏性
|
|
6
|
+
* 变更(2026-09-28 ①:`awaiting_approval` → `suspended` + `suspendedReason`、
|
|
7
|
+
* `approvalPendingSince` → `suspendedSince`)在这条接缝上会**静默失效**:旧记录带着一个
|
|
8
|
+
* `RunStatus` 里**已经不存在**的值被读回来,而每一处判断都按新语义去读它。
|
|
9
|
+
*
|
|
10
|
+
* 失效的具体后果(一条在等审批的 run 成孤儿,四个角度同时错):
|
|
11
|
+
* - `isTerminalTask` 认它**终态**(不是 queued / running / suspended)⇒ 事件流早关、`resumePending` 不捡;
|
|
12
|
+
* - `resume-policy` 给出的 skip 原因正是 `'terminal'`(静默说错话);
|
|
13
|
+
* - `/healthz` 的 `suspended` 读数不计数(原因缺);
|
|
14
|
+
* - `approve` 回 409「状态不对」—— 人想批也批不了。
|
|
15
|
+
*
|
|
16
|
+
* 所以本文件是那六个「bytes → 记录」的**唯一入口**:三条 store、六处调用
|
|
17
|
+
* (`fsStore` 的全量扫 + 残行探测、`sqliteStore` 的 get / byIdempotency / list、
|
|
18
|
+
* `redisStore` 的单键读)。将来加第七处也用同一个函数 —— `tests/store/record.test.ts` 里那条
|
|
19
|
+
* 「`src/store/*.ts` 中除本文件外不得出现 `JSON.parse`」的守卫会盯着。
|
|
20
|
+
*
|
|
21
|
+
* 边界:本函数**不吞坏 JSON**。三条 store 对「单条损坏」的取舍各不相同(文件与 Redis 跳过该条、
|
|
22
|
+
* SQLite 照旧抛),异常留给调用方按各自口径处理 —— 归一层不越权决定「坏了怎么办」。
|
|
23
|
+
*/
|
|
24
|
+
import type { TaskRecord } from './store.js';
|
|
25
|
+
/**
|
|
26
|
+
* 把一条记录里的旧版本形状**就地**归一,返回「有没有动过」。
|
|
27
|
+
*
|
|
28
|
+
* 目前只认 2026-09-28 ① 那一批改名(状态值 + 挂起时刻字段)。原则:**只搬运盘上真有的东西**,
|
|
29
|
+
* 不凭空补造 —— 新形状的记录若缺 `suspendedReason`,本函数不动它(那是另一个缺陷,
|
|
30
|
+
* 不该由垫片掩盖成「看起来正常」)。
|
|
31
|
+
*
|
|
32
|
+
* 返回值留给需要自己决定要不要回写的调用方(`sqliteStore` 的派生列)。
|
|
33
|
+
*/
|
|
34
|
+
export declare function normalizeLegacyRecord(rec: TaskRecord): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* bytes → 记录(**唯一入口**):解析 + 形状守卫 + 读时归一。
|
|
37
|
+
*
|
|
38
|
+
* - 坏 JSON 照旧抛(调用方按自己的口径吞或冒泡,见文件头的边界);
|
|
39
|
+
* - 形状不合格(不是对象 / 没有 `taskId`)⇒ `undefined`(「这条不是记录」,与「解析失败」分开)。
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseTaskRecord(raw: string): TaskRecord | undefined;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ① 之前的状态值。它在本版的 `RunStatus` 联合类型里**不存在**,所以下面那处比较只能走字符串
|
|
3
|
+
* (`rec.status === 'awaiting_approval'` 根本编译不过)—— 这本身就说明问题:类型看不见盘上的数据。
|
|
4
|
+
*/
|
|
5
|
+
const LEGACY_STATUS = 'awaiting_approval';
|
|
6
|
+
/** 同一次改名之前的挂起时刻字段(当时叫 `approvalPendingSince`) */
|
|
7
|
+
const LEGACY_SINCE_KEY = 'approvalPendingSince';
|
|
8
|
+
/**
|
|
9
|
+
* 把一条记录里的旧版本形状**就地**归一,返回「有没有动过」。
|
|
10
|
+
*
|
|
11
|
+
* 目前只认 2026-09-28 ① 那一批改名(状态值 + 挂起时刻字段)。原则:**只搬运盘上真有的东西**,
|
|
12
|
+
* 不凭空补造 —— 新形状的记录若缺 `suspendedReason`,本函数不动它(那是另一个缺陷,
|
|
13
|
+
* 不该由垫片掩盖成「看起来正常」)。
|
|
14
|
+
*
|
|
15
|
+
* 返回值留给需要自己决定要不要回写的调用方(`sqliteStore` 的派生列)。
|
|
16
|
+
*/
|
|
17
|
+
export function normalizeLegacyRecord(rec) {
|
|
18
|
+
const raw = rec;
|
|
19
|
+
let changed = false;
|
|
20
|
+
if (raw.status === LEGACY_STATUS) {
|
|
21
|
+
raw.status = 'suspended';
|
|
22
|
+
// 原因必须**一起**补:两条闸(`approvalExpired` / `approve`)都按 `suspendedReason` 判,
|
|
23
|
+
// 只改状态的话这条记录会被两条闸**都**漏掉 —— 比不归一更坏(状态看着对了、原因空着,
|
|
24
|
+
// 再也说不出它在等谁)。
|
|
25
|
+
if (raw.suspendedReason === undefined)
|
|
26
|
+
raw.suspendedReason = 'approval';
|
|
27
|
+
changed = true;
|
|
28
|
+
}
|
|
29
|
+
// 挂起时刻:旧字段搬过来之后**删掉**。留着就是半新半旧的两个键,任何读者都可能读到错的那个
|
|
30
|
+
// (而「等多久了」这个数会直接进审批超时判定)。缺了不影响超时判定的正确性(基准链本来就退到
|
|
31
|
+
// `startedAt` / `createdAt`),所以这一条是**修正**而不是修复:不搬会让「已经等了 3 天」
|
|
32
|
+
// 从头重新计时。
|
|
33
|
+
if (typeof raw[LEGACY_SINCE_KEY] === 'number') {
|
|
34
|
+
if (raw.suspendedSince === undefined)
|
|
35
|
+
raw.suspendedSince = raw[LEGACY_SINCE_KEY];
|
|
36
|
+
delete raw[LEGACY_SINCE_KEY];
|
|
37
|
+
changed = true;
|
|
38
|
+
}
|
|
39
|
+
return changed;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* bytes → 记录(**唯一入口**):解析 + 形状守卫 + 读时归一。
|
|
43
|
+
*
|
|
44
|
+
* - 坏 JSON 照旧抛(调用方按自己的口径吞或冒泡,见文件头的边界);
|
|
45
|
+
* - 形状不合格(不是对象 / 没有 `taskId`)⇒ `undefined`(「这条不是记录」,与「解析失败」分开)。
|
|
46
|
+
*/
|
|
47
|
+
export function parseTaskRecord(raw) {
|
|
48
|
+
const rec = JSON.parse(raw);
|
|
49
|
+
if (!rec || typeof rec.taskId !== 'string')
|
|
50
|
+
return undefined;
|
|
51
|
+
normalizeLegacyRecord(rec);
|
|
52
|
+
return rec;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/store/record.ts"],"names":[],"mappings":"AAyBA;;;GAGG;AACH,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAC1C,iDAAiD;AACjD,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AAEhD;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAe;IACnD,MAAM,GAAG,GAAG,GAAyC,CAAC;IACtD,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,IAAI,GAAG,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACjC,GAAG,CAAC,MAAM,GAAG,WAAW,CAAC;QACzB,wEAAwE;QACxE,gDAAgD;QAChD,cAAc;QACd,IAAI,GAAG,CAAC,eAAe,KAAK,SAAS;YAAE,GAAG,CAAC,eAAe,GAAG,UAAU,CAAC;QACxE,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IAED,iDAAiD;IACjD,gDAAgD;IAChD,8DAA8D;IAC9D,UAAU;IACV,IAAI,OAAO,GAAG,CAAC,gBAAgB,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC9C,IAAI,GAAG,CAAC,cAAc,KAAK,SAAS;YAAE,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACjF,OAAO,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC7B,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;IAC1C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC7D,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -9,7 +9,9 @@ import type { TaskRecord, TaskStore } from './store.js';
|
|
|
9
9
|
* 存储模型(prefix 缺省 'agentia:'):
|
|
10
10
|
* - `${prefix}task:<taskId>` → 整行记录 JSON(save = SET 覆写,last-wins);
|
|
11
11
|
* - `${prefix}idem:<idempotencyKey>` → taskId(同键重提覆写,byIdempotency last-wins)。
|
|
12
|
-
* 两者都按 `ttlSeconds`(若设)过期(SET 覆写 + EXPIRE
|
|
12
|
+
* 两者都按 `ttlSeconds`(若设)过期(SET 覆写 + EXPIRE 刷新窗口),见该选项 ——
|
|
13
|
+
* 但**只有终态记录真的拿到窗口**:非终态覆写会把窗口抹掉(挂起中的任务不能被 TTL 吃掉,
|
|
14
|
+
* 见 `save` 的注释)。
|
|
13
15
|
*
|
|
14
16
|
* 语义对照 FileTaskStore / SqliteTaskStore:save 覆写、byIdempotency 取最近、
|
|
15
17
|
* clear 清空本前缀全部 key。差异在 list 序:Redis 本身无序,按 createdAt
|
package/dist/store/redisStore.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isTerminalStatus } from '../core/run.js';
|
|
2
|
+
import { parseTaskRecord } from './record.js';
|
|
1
3
|
export class RedisTaskStore {
|
|
2
4
|
client;
|
|
3
5
|
prefix;
|
|
@@ -40,10 +42,20 @@ export class RedisTaskStore {
|
|
|
40
42
|
}
|
|
41
43
|
async save(rec) {
|
|
42
44
|
const json = JSON.stringify(rec);
|
|
45
|
+
// TTL 只加在**终态**记录上(2026-09-28 外部深评 S1)。此前无条件加:一条在等审批的
|
|
46
|
+
// 任务,TTL 一到记录就没了 —— 那条「批了它」的决定无家可归(`InMemoryTaskStore.evict`
|
|
47
|
+
// 里那句「suspended 不可淘汰」是同一条道理,两处此前读数相反)。
|
|
48
|
+
// ⚠️ 机制是 `SET` 的语义:不带选项的 `SET` **清除**既有 TTL ⇒ 非终态的每次覆写都把
|
|
49
|
+
// 先前那条窗口抹掉、记录回到「永不过期」,直到进终态才第一次拿到窗口。
|
|
50
|
+
// 代价如实记:一条**永远等不到**审批的挂起记录会永久驻留(批不批是人的事,
|
|
51
|
+
// store 无从判死)—— 与内存 store 同款取舍,清理属部署层职责。
|
|
52
|
+
const expires = isTerminalStatus(rec.status);
|
|
43
53
|
// 写入顺序是 load-bearing(AsyncRunner 的延迟幂等去重依赖它):先记录、后幂等索引
|
|
44
|
-
await this.write(this.taskKey(rec.taskId), json);
|
|
54
|
+
await this.write(this.taskKey(rec.taskId), json, expires);
|
|
45
55
|
if (rec.idempotencyKey) {
|
|
46
|
-
|
|
56
|
+
// 幂等索引与记录**同生共死**:非终态时索引若先过期,同一键重提会绕过去重、
|
|
57
|
+
// 把一条在跑的任务再跑一遍(比「记录丢了」更坏的后果)
|
|
58
|
+
await this.write(this.idemKey(rec.idempotencyKey), rec.taskId, expires);
|
|
47
59
|
}
|
|
48
60
|
}
|
|
49
61
|
/**
|
|
@@ -59,10 +71,11 @@ export class RedisTaskStore {
|
|
|
59
71
|
* **没有 TTL 的键**(多活一条本该到期的记录),不会损坏数据。TTL 只是查询窗口,用这个
|
|
60
72
|
* 窗口换「两家客户端都真的生效」,是本 store 有意的取舍(见 spec §10)。
|
|
61
73
|
*/
|
|
62
|
-
async write(key, value) {
|
|
74
|
+
async write(key, value, expires) {
|
|
63
75
|
await this.client.set(key, value);
|
|
64
|
-
//
|
|
65
|
-
|
|
76
|
+
// 只有终态记录拿到查询窗口(非终态:无选项的 SET 已把既有 TTL 清掉 ⇒ 永不过期)。
|
|
77
|
+
// 窗口从**进入终态那次写**起算 —— 既不是创建时刻,也不是每次状态推进(见 save)。
|
|
78
|
+
if (expires && this.applyTtl)
|
|
66
79
|
await this.applyTtl(key);
|
|
67
80
|
}
|
|
68
81
|
async get(taskId) {
|
|
@@ -120,8 +133,9 @@ function parseRecord(raw) {
|
|
|
120
133
|
if (!raw)
|
|
121
134
|
return undefined;
|
|
122
135
|
try {
|
|
123
|
-
|
|
124
|
-
|
|
136
|
+
// 解析 + 旧形状读时归一都在唯一入口里(src/store/record.ts);本函数只负责
|
|
137
|
+
// 「坏了就跳过」这条取舍
|
|
138
|
+
return parseTaskRecord(raw);
|
|
125
139
|
}
|
|
126
140
|
catch {
|
|
127
141
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redisStore.js","sourceRoot":"","sources":["../../src/store/redisStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"redisStore.js","sourceRoot":"","sources":["../../src/store/redisStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,8 @@ import type { TaskRecord, TaskStore } from './store.js';
|
|
|
6
6
|
* - save = INSERT OR REPLACE(整行记录 JSON 存 json 列,last-wins);
|
|
7
7
|
* - byIdempotency 取该键最近一次 save 的记录(按 rowid 倒序,失败重提产生的新任务胜出);
|
|
8
8
|
* - list 按写入顺序返回;clear 清空整表;
|
|
9
|
+
* - `listDue(before)`(到期索引,2026-09-28 ⑤):派生列 `wake_at` + `(status, wake_at)`
|
|
10
|
+
* 索引,只回「在睡且到点」的记录(存量库构造期就地迁移:ALTER + 从 json 回填);
|
|
9
11
|
* - 构造即就绪(建表 + 索引,无异步),重启续跑同样交给 AsyncRunner.resumePending()。
|
|
10
12
|
*
|
|
11
13
|
* 对比 FileTaskStore 的**单写者前提**:多进程安全由 SQLite WAL/事务天然保证 ——
|
|
@@ -13,11 +15,32 @@ import type { TaskRecord, TaskStore } from './store.js';
|
|
|
13
15
|
*/
|
|
14
16
|
export declare class SqliteTaskStore implements TaskStore {
|
|
15
17
|
private readonly db;
|
|
18
|
+
/** 列自愈失败已告警过的 taskId(同一条记录每次读都会进自愈路径,去重防止刷屏) */
|
|
19
|
+
private readonly healWarned;
|
|
16
20
|
constructor(path: string);
|
|
17
21
|
save(rec: TaskRecord): void;
|
|
22
|
+
/**
|
|
23
|
+
* 把派生列(`status` / `wake_at`)拉回与 json 一致(**只在不一致时写**,一次代价)。
|
|
24
|
+
*
|
|
25
|
+
* 为什么只有 SQLite 需要这一步:派生列是 json 的反规范化副本(`status` 供外部/DBA
|
|
26
|
+
* 按状态统计;`wake_at` 供 `listDue` 的索引过滤)。只在读时归一 json 的话,
|
|
27
|
+
* 那个统计/过滤会**继续**按旧值走 —— 于是「框架说一种事实、查询看到另一种」
|
|
28
|
+
* (对 `wake_at` 来说症状更脆:一条该醒的在睡任务从此醒不来)。
|
|
29
|
+
* fsStore / Redis 没有派生副本,读时归一对它们已经够了(写回时机是这条记录的下一次 save)。
|
|
30
|
+
*
|
|
31
|
+
* 判据是「列 ≠ json」而不是「刚才归一过没有」:任何原因(手改过、旧版本写的、将来又一次
|
|
32
|
+
* 改名)造成的漂移都该被顺手修掉,不依赖调用方传一个「我改过」的旗子。
|
|
33
|
+
*/
|
|
34
|
+
private healRow;
|
|
18
35
|
get(taskId: string): TaskRecord | undefined;
|
|
19
36
|
byIdempotency(key: string): TaskRecord | undefined;
|
|
20
37
|
list(): TaskRecord[];
|
|
38
|
+
/**
|
|
39
|
+
* 到期索引(2026-09-28 ⑤ 落地):只回「在睡且到点」的记录,走 `idx_tasks_due` 索引
|
|
40
|
+
* —— 代价是 O(到期数) 而不是 O(全表)。口径与 `wake-policy.ts` 的 `timerDue` 对齐
|
|
41
|
+
* (status + wake_at 两列;`suspendedReason` 那一半由调用方复核,见 #redispatch)。
|
|
42
|
+
*/
|
|
43
|
+
listDue(before: number): TaskRecord[];
|
|
21
44
|
clear(): void;
|
|
22
45
|
/** 关闭底层连接(测试/进程收尾用;TaskStore 接口之外的能力) */
|
|
23
46
|
close(): void;
|