@qjp88995/agent-runtime-redis 0.1.0 → 0.2.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.
@@ -1,21 +1,31 @@
1
1
  import type { Redis } from 'ioredis';
2
- import { type LoggerFactory, type SessionLock, type SubagentAwaitLease, type TreeBudget, type WakeChannel } from '@qjp88995/agent-runtime';
2
+ import { type LoggerFactory, type SessionLock, type SubagentAwaitLease, type TaskLease, type TaskSignals, type TreeBudget, type WakeChannel } from '@qjp88995/agent-runtime';
3
3
  import { type RedisKeyOptions } from './keys.js';
4
4
  export interface RedisSessionLockOptions extends RedisKeyOptions {
5
5
  /** 缺省 `LOCK_TTL_MS`(60s)。 */
6
6
  ttlMs?: number;
7
7
  }
8
- /** SET NX PX 抢锁,Lua 比对 token 再 DEL / PEXPIRE。TTL 兜底持有者崩溃。 */
8
+ /** 会话锁。TTL 兜底持有者崩溃。 */
9
9
  export declare class RedisSessionLock implements SessionLock {
10
- private readonly redis;
11
- private readonly keys;
12
- private readonly ttlMs;
10
+ private readonly lock;
13
11
  constructor(redis: Redis, options?: RedisSessionLockOptions);
14
12
  acquire(sessionId: string): Promise<string | null>;
15
13
  renew(sessionId: string, token: string): Promise<boolean>;
16
14
  release(sessionId: string, token: string): Promise<void>;
17
15
  isHeld(sessionId: string): Promise<boolean>;
18
16
  }
17
+ export interface RedisTaskLeaseOptions extends RedisKeyOptions {
18
+ /** 缺省 `LOCK_TTL_MS`(60s)。监视器每段等待(runtime 缺省 20s)之后续一次。 */
19
+ ttlMs?: number;
20
+ }
21
+ /** 任务监视租约:与会话锁同一套写法,键 `agent-task:lease:<taskId>`。 */
22
+ export declare class RedisTaskLease implements TaskLease {
23
+ private readonly lock;
24
+ constructor(redis: Redis, options?: RedisTaskLeaseOptions);
25
+ acquire(taskId: string): Promise<string | null>;
26
+ renew(taskId: string, token: string): Promise<boolean>;
27
+ release(taskId: string, token: string): Promise<void>;
28
+ }
19
29
  export interface RedisWakeChannelOptions extends RedisKeyOptions {
20
30
  /**
21
31
  * 订阅用的连接。缺省 `redis.duplicate()`,由本对象负责关闭;宿主自己给的不关。
@@ -54,6 +64,32 @@ export declare class RedisWakeChannel implements WakeChannel {
54
64
  /** 摘掉所有处理函数;订阅连接是本对象建的就关掉。 */
55
65
  dispose(): Promise<void>;
56
66
  }
67
+ export interface RedisTaskSignalsOptions extends RedisKeyOptions {
68
+ /** 订阅用的连接。缺省 `redis.duplicate()`,由本对象负责关闭;宿主自己给的不关。 */
69
+ subscriber?: Redis;
70
+ logger?: LoggerFactory;
71
+ }
72
+ /**
73
+ * 「任务转后台」广播走 Redis pub/sub,载荷就是任务 id。与 `RedisWakeChannel` 同样用
74
+ * `create()` 等 SUBSCRIBE 应答回来才返回。广播丢了不要紧:前台等待方每段等待之后都回看任务行
75
+ * 的 `background`,信号只是让它不必等满这一段。
76
+ */
77
+ export declare class RedisTaskSignals implements TaskSignals {
78
+ private readonly redis;
79
+ private readonly sub;
80
+ private readonly ownsSub;
81
+ private readonly channel;
82
+ private readonly logger;
83
+ private readonly handlers;
84
+ private disposed;
85
+ private constructor();
86
+ static create(redis: Redis, options?: RedisTaskSignalsOptions): Promise<RedisTaskSignals>;
87
+ private dispatch;
88
+ background(taskId: string): Promise<void>;
89
+ onBackground(handler: Handler): () => void;
90
+ /** 摘掉处理函数;订阅连接是本对象建的就关掉,否则只退订本频道。 */
91
+ dispose(): Promise<void>;
92
+ }
57
93
  export interface RedisSubagentAwaitLeaseOptions extends RedisKeyOptions {
58
94
  /** 缺省 `AWAIT_LEASE_TTL_MS`(60s)。 */
59
95
  ttlMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"coordination.d.ts","sourceRoot":"","sources":["../src/coordination.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"coordination.d.ts","sourceRoot":"","sources":["../src/coordination.ts"],"names":[],"mappings":"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"}
@@ -2,41 +2,76 @@ import { randomUUID } from 'node:crypto';
2
2
  import { AWAIT_LEASE_TTL_MS, consoleLogger, LOCK_TTL_MS, } from '@qjp88995/agent-runtime';
3
3
  import { redisKeys } from './keys.js';
4
4
  /**
5
- * 会话锁、唤醒 / 空闲广播、子 Agent 等待租约、树级名额的 Redis 实现。
5
+ * 会话锁、唤醒 / 空闲广播、子 Agent 等待租约、树级名额、任务监视租约与转后台广播的 Redis 实现。
6
6
  * 搬自 agent-base `agent-core/loop/{session-lock,session-wake}.service.ts`、`session-idle.ts`、
7
7
  * `agent-core/subagent/{subagent-await,tree-budget}.service.ts`,语义见 runtime 的 `ports/coordination`。
8
8
  *
9
- * 锁 / 租约 / 名额都直接用宿主给的共享连接(全是非阻塞的常规命令),只有唤醒频道要一条
10
- * 独立的订阅连接(进了 subscribe 模式的连接不能再发普通命令)。
9
+ * 锁 / 租约 / 名额都直接用宿主给的共享连接(全是非阻塞的常规命令),只有唤醒频道与转后台广播
10
+ * 各要一条独立的订阅连接(进了 subscribe 模式的连接不能再发普通命令)。
11
11
  */
12
12
  /** DEL 前比对 token,避免误删「TTL 过期后被别人重新抢到」的锁。 */
13
13
  const RELEASE_LUA = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
14
14
  /** PEXPIRE 前比对 token,只续自己的锁。 */
15
15
  const RENEW_LUA = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('pexpire', KEYS[1], ARGV[2]) else return 0 end";
16
- /** SET NX PX 抢锁,Lua 比对 token 再 DEL / PEXPIRE。TTL 兜底持有者崩溃。 */
17
- export class RedisSessionLock {
16
+ /** SET NX PX 抢,Lua 比对 token 再 DEL / PEXPIRE。会话锁与任务租约共用,只是键空间不同。 */
17
+ class RedisTokenLock {
18
18
  redis;
19
- keys;
19
+ keyOf;
20
20
  ttlMs;
21
- constructor(redis, options = {}) {
21
+ constructor(redis, keyOf, ttlMs) {
22
22
  this.redis = redis;
23
- this.keys = redisKeys(options);
24
- this.ttlMs = options.ttlMs ?? LOCK_TTL_MS;
23
+ this.keyOf = keyOf;
24
+ this.ttlMs = ttlMs;
25
25
  }
26
- async acquire(sessionId) {
26
+ async acquire(id) {
27
27
  const token = randomUUID();
28
- const res = await this.redis.set(this.keys.lock(sessionId), token, 'PX', this.ttlMs, 'NX');
28
+ const res = await this.redis.set(this.keyOf(id), token, 'PX', this.ttlMs, 'NX');
29
29
  return res === 'OK' ? token : null;
30
30
  }
31
- async renew(sessionId, token) {
32
- const res = await this.redis.eval(RENEW_LUA, 1, this.keys.lock(sessionId), token, String(this.ttlMs));
31
+ async renew(id, token) {
32
+ const res = await this.redis.eval(RENEW_LUA, 1, this.keyOf(id), token, String(this.ttlMs));
33
33
  return res === 1;
34
34
  }
35
- async release(sessionId, token) {
36
- await this.redis.eval(RELEASE_LUA, 1, this.keys.lock(sessionId), token);
35
+ async release(id, token) {
36
+ await this.redis.eval(RELEASE_LUA, 1, this.keyOf(id), token);
37
37
  }
38
- async isHeld(sessionId) {
39
- return (await this.redis.exists(this.keys.lock(sessionId))) === 1;
38
+ async isHeld(id) {
39
+ return (await this.redis.exists(this.keyOf(id))) === 1;
40
+ }
41
+ }
42
+ /** 会话锁。TTL 兜底持有者崩溃。 */
43
+ export class RedisSessionLock {
44
+ lock;
45
+ constructor(redis, options = {}) {
46
+ this.lock = new RedisTokenLock(redis, redisKeys(options).lock, options.ttlMs ?? LOCK_TTL_MS);
47
+ }
48
+ acquire(sessionId) {
49
+ return this.lock.acquire(sessionId);
50
+ }
51
+ renew(sessionId, token) {
52
+ return this.lock.renew(sessionId, token);
53
+ }
54
+ release(sessionId, token) {
55
+ return this.lock.release(sessionId, token);
56
+ }
57
+ isHeld(sessionId) {
58
+ return this.lock.isHeld(sessionId);
59
+ }
60
+ }
61
+ /** 任务监视租约:与会话锁同一套写法,键 `agent-task:lease:<taskId>`。 */
62
+ export class RedisTaskLease {
63
+ lock;
64
+ constructor(redis, options = {}) {
65
+ this.lock = new RedisTokenLock(redis, redisKeys(options).taskLease, options.ttlMs ?? LOCK_TTL_MS);
66
+ }
67
+ acquire(taskId) {
68
+ return this.lock.acquire(taskId);
69
+ }
70
+ renew(taskId, token) {
71
+ return this.lock.renew(taskId, token);
72
+ }
73
+ release(taskId, token) {
74
+ return this.lock.release(taskId, token);
40
75
  }
41
76
  }
42
77
  /**
@@ -132,6 +167,65 @@ export class RedisWakeChannel {
132
167
  }
133
168
  }
134
169
  }
170
+ /**
171
+ * 「任务转后台」广播走 Redis pub/sub,载荷就是任务 id。与 `RedisWakeChannel` 同样用
172
+ * `create()` 等 SUBSCRIBE 应答回来才返回。广播丢了不要紧:前台等待方每段等待之后都回看任务行
173
+ * 的 `background`,信号只是让它不必等满这一段。
174
+ */
175
+ export class RedisTaskSignals {
176
+ redis;
177
+ sub;
178
+ ownsSub;
179
+ channel;
180
+ logger;
181
+ handlers = new Set();
182
+ disposed = false;
183
+ constructor(redis, sub, ownsSub, channel, logger) {
184
+ this.redis = redis;
185
+ this.sub = sub;
186
+ this.ownsSub = ownsSub;
187
+ this.channel = channel;
188
+ this.logger = logger;
189
+ }
190
+ static async create(redis, options = {}) {
191
+ const sub = options.subscriber ?? redis.duplicate();
192
+ const signals = new RedisTaskSignals(redis, sub, !options.subscriber, redisKeys(options).taskBackgroundChannel, (options.logger ?? consoleLogger)('RedisTaskSignals'));
193
+ sub.on('message', (ch, taskId) => {
194
+ if (ch === signals.channel)
195
+ signals.dispatch(taskId);
196
+ });
197
+ await sub.subscribe(signals.channel);
198
+ return signals;
199
+ }
200
+ dispatch(taskId) {
201
+ for (const handler of [...this.handlers]) {
202
+ try {
203
+ handler(taskId);
204
+ }
205
+ catch (err) {
206
+ this.logger.error(`background handler failed for ${taskId}: ${String(err)}`, err instanceof Error ? err.stack : undefined);
207
+ }
208
+ }
209
+ }
210
+ async background(taskId) {
211
+ await this.redis.publish(this.channel, taskId);
212
+ }
213
+ onBackground(handler) {
214
+ this.handlers.add(handler);
215
+ return () => this.handlers.delete(handler);
216
+ }
217
+ /** 摘掉处理函数;订阅连接是本对象建的就关掉,否则只退订本频道。 */
218
+ async dispose() {
219
+ if (this.disposed)
220
+ return;
221
+ this.disposed = true;
222
+ this.handlers.clear();
223
+ if (this.ownsSub)
224
+ await this.sub.quit().catch(() => undefined);
225
+ else
226
+ await this.sub.unsubscribe(this.channel).catch(() => undefined);
227
+ }
228
+ }
135
229
  /**
136
230
  * 等待租约:一个带 TTL 的键。`claim` 是一条原子 DEL,父与 finalizer 抢同一条,恰有一方胜出。
137
231
  * Redis 出错时按端口约定一律退化成异步(见各方法)。
@@ -1 +1 @@
1
- {"version":3,"file":"coordination.js","sourceRoot":"","sources":["../src/coordination.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"coordination.js","sourceRoot":"","sources":["../src/coordination.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,WAAW,GASZ,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAwC,SAAS,EAAE,MAAM,WAAW,CAAC;AAE5E;;;;;;;GAOG;AAEH,4CAA4C;AAC5C,MAAM,WAAW,GACf,mGAAmG,CAAC;AACtG,gCAAgC;AAChC,MAAM,SAAS,GACb,gHAAgH,CAAC;AAOnH,mEAAmE;AACnE,MAAM,cAAc;IAEC;IACA;IACA;IAHnB,YACmB,KAAY,EACZ,KAA6B,EAC7B,KAAa;QAFb,UAAK,GAAL,KAAK,CAAO;QACZ,UAAK,GAAL,KAAK,CAAwB;QAC7B,UAAK,GAAL,KAAK,CAAQ;IAC7B,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAC9B,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EACd,KAAK,EACL,IAAI,EACJ,IAAI,CAAC,KAAK,EACV,IAAI,CACL,CAAC;QACF,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,EAAU,EAAE,KAAa;QACnC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAC/B,SAAS,EACT,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EACd,KAAK,EACL,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CACnB,CAAC;QACF,OAAO,GAAG,KAAK,CAAC,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,KAAa;QACrC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC;CACF;AAED,uBAAuB;AACvB,MAAM,OAAO,gBAAgB;IACV,IAAI,CAAiB;IAEtC,YAAY,KAAY,EAAE,UAAmC,EAAE;QAC7D,IAAI,CAAC,IAAI,GAAG,IAAI,cAAc,CAC5B,KAAK,EACL,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,EACvB,OAAO,CAAC,KAAK,IAAI,WAAW,CAC7B,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,SAAiB;QACvB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,SAAiB,EAAE,KAAa;QACpC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,CAAC,SAAiB,EAAE,KAAa;QACtC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,CAAC,SAAiB;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;CACF;AAOD,sDAAsD;AACtD,MAAM,OAAO,cAAc;IACR,IAAI,CAAiB;IAEtC,YAAY,KAAY,EAAE,UAAiC,EAAE;QAC3D,IAAI,CAAC,IAAI,GAAG,IAAI,cAAc,CAC5B,KAAK,EACL,SAAS,CAAC,OAAO,CAAC,CAAC,SAAS,EAC5B,OAAO,CAAC,KAAK,IAAI,WAAW,CAC7B,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,MAAc;QACpB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,MAAc,EAAE,KAAa;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,CAAC,MAAc,EAAE,KAAa;QACnC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;CACF;AAYD;;;;;;;;;GASG;AACH,MAAM,OAAO,gBAAgB;IAMR;IACA;IACA;IACA;IACA;IATF,YAAY,GAAG,IAAI,GAAG,EAAW,CAAC;IAClC,YAAY,GAAG,IAAI,GAAG,EAAW,CAAC;IAC3C,QAAQ,GAAG,KAAK,CAAC;IAEzB,YACmB,KAAY,EACZ,GAAU,EACV,OAAgB,EAChB,IAAe,EACf,MAAc;QAJd,UAAK,GAAL,KAAK,CAAO;QACZ,QAAG,GAAH,GAAG,CAAO;QACV,YAAO,GAAP,OAAO,CAAS;QAChB,SAAI,GAAJ,IAAI,CAAW;QACf,WAAM,GAAN,MAAM,CAAQ;IAC9B,CAAC;IAEJ,MAAM,CAAC,KAAK,CAAC,MAAM,CACjB,KAAY,EACZ,UAAmC,EAAE;QAErC,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAClC,KAAK,EACL,GAAG,EACH,CAAC,OAAO,CAAC,UAAU,EACnB,SAAS,CAAC,OAAO,CAAC,EAClB,CAAC,OAAO,CAAC,MAAM,IAAI,aAAa,CAAC,CAAC,kBAAkB,CAAC,CACtD,CAAC;QACF,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,EAAU,EAAE,OAAe,EAAE,EAAE,CAChD,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC,CAC9B,CAAC;QACF,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxE,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,QAAQ,CAAC,EAAU,EAAE,OAAe;QAC1C,IAAI,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAClD,CAAC;aAAM,IAAI,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACxC,IAAI,SAAkB,CAAC;YACvB,IAAI,CAAC;gBACH,SAAS,GAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAA6B,CAAC,SAAS,CAAC;YACzE,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,GAAG,SAAS,CAAC;YACxB,CAAC;YACD,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAClC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oCAAoC,OAAO,EAAE,CAAC,CAAC;gBAChE,OAAO;YACT,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAEO,MAAM,CAAC,QAAsB,EAAE,SAAiB,EAAE,IAAY;QACpE,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC;gBACH,OAAO,CAAC,SAAS,CAAC,CAAC;YACrB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,GAAG,IAAI,uBAAuB,SAAS,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,EACzD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC7C,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,SAAiB;QAC1B,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,CAAC,OAAgB;QACrB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,SAAiB;QAC1B,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CACtB,IAAI,CAAC,IAAI,CAAC,WAAW,EACrB,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,CAC9B,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,OAAgB;QACrB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAED,8BAA8B;IAC9B,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,GAAG;iBACX,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;iBACzD,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;CACF;AAQD;;;;GAIG;AACH,MAAM,OAAO,gBAAgB;IAKR;IACA;IACA;IACA;IACA;IARF,QAAQ,GAAG,IAAI,GAAG,EAAW,CAAC;IACvC,QAAQ,GAAG,KAAK,CAAC;IAEzB,YACmB,KAAY,EACZ,GAAU,EACV,OAAgB,EAChB,OAAe,EACf,MAAc;QAJd,UAAK,GAAL,KAAK,CAAO;QACZ,QAAG,GAAH,GAAG,CAAO;QACV,YAAO,GAAP,OAAO,CAAS;QAChB,YAAO,GAAP,OAAO,CAAQ;QACf,WAAM,GAAN,MAAM,CAAQ;IAC9B,CAAC;IAEJ,MAAM,CAAC,KAAK,CAAC,MAAM,CACjB,KAAY,EACZ,UAAmC,EAAE;QAErC,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAClC,KAAK,EACL,GAAG,EACH,CAAC,OAAO,CAAC,UAAU,EACnB,SAAS,CAAC,OAAO,CAAC,CAAC,qBAAqB,EACxC,CAAC,OAAO,CAAC,MAAM,IAAI,aAAa,CAAC,CAAC,kBAAkB,CAAC,CACtD,CAAC;QACF,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,EAAU,EAAE,MAAc,EAAE,EAAE;YAC/C,IAAI,EAAE,KAAK,OAAO,CAAC,OAAO;gBAAE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QACH,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,QAAQ,CAAC,MAAc;QAC7B,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC;gBACH,OAAO,CAAC,MAAM,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,iCAAiC,MAAM,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,EACzD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC7C,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAc;QAC7B,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,YAAY,CAAC,OAAgB;QAC3B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,qCAAqC;IACrC,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;;YAC1D,MAAM,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACvE,CAAC;CACF;AAQD;;;GAGG;AACH,MAAM,OAAO,uBAAuB;IAMf;IALF,IAAI,CAAY;IAChB,KAAK,CAAS;IACd,MAAM,CAAS;IAEhC,YACmB,KAAY,EAC7B,UAA0C,EAAE;QAD3B,UAAK,GAAL,KAAK,CAAO;QAG7B,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,kBAAkB,CAAC;QACjD,IAAI,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,aAAa,CAAC,CAAC,yBAAyB,CAAC,CAAC;IAC7E,CAAC;IAED,sCAAsC;IACtC,KAAK,CAAC,KAAK,CAAC,cAAsB;QAChC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAClB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,EACjC,GAAG,EACH,IAAI,EACJ,IAAI,CAAC,KAAK,CACX,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,qBAAqB,cAAc,oCAAoC,MAAM,CAAC,GAAG,CAAC,EAAE,CACrF,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,4CAA4C;IAC5C,KAAK,CAAC,KAAK,CAAC,cAAsB;QAChC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1E,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;QAC9C,CAAC;IACH,CAAC;IAED,yCAAyC;IACzC,KAAK,CAAC,SAAS,CAAC,cAAsB;QACpC,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,yBAAyB,cAAc,4BAA4B,MAAM,CAAC,GAAG,CAAC,EAAE,CACjF,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,KAAK,CAAC,KAAK,CACT,cAAsB,EACtB,EAA0B;QAE1B,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACzE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,qBAAqB,cAAc,KAAK,EAAE,aAAa,MAAM,CAAC,GAAG,CAAC,EAAE,CACrE,CAAC;YACF,OAAO,EAAE,KAAK,QAAQ,CAAC;QACzB,CAAC;IACH,CAAC;CACF;AAED,sCAAsC;AACtC,MAAM,iBAAiB,GACrB,2KAA2K,CAAC;AAC9K,6BAA6B;AAC7B,MAAM,QAAQ,GACZ,wFAAwF,CAAC;AAO3F,0CAA0C;AAC1C,MAAM,OAAO,eAAe;IAKP;IAJF,IAAI,CAAY;IAChB,UAAU,CAAS;IAEpC,YACmB,KAAY,EAC7B,UAAkC,EAAE;QADnB,UAAK,GAAL,KAAK,CAAO;QAG7B,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,aAAqB,EAAE,KAAa;QAChD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAC/B,iBAAiB,EACjB,CAAC,EACD,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EACjC,MAAM,CAAC,KAAK,CAAC,EACb,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CACxB,CAAC;QACF,OAAO,GAAG,KAAK,CAAC,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,aAAqB;QAC9B,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,2BAA2B;IAC3B,KAAK,CAAC,GAAG,CAAC,aAAqB,EAAE,CAAS;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;YACjC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACnE,CAAC;CACF"}
package/dist/index.d.ts CHANGED
@@ -1,15 +1,16 @@
1
1
  /**
2
2
  * `@qjp88995/agent-runtime` 的 Redis 适配:多实例部署时替换进程内的总线与协调件。
3
3
  *
4
- * 换的是五个端口——`StreamBus`、`SessionLock`、`WakeChannel`、`SubagentAwaitLease`、`TreeBudget`。
5
- * 存储类端口(跑次、消息、会话、问题、厂商文件)不在这里:它们归宿主的数据库。
4
+ * 换的是七个端口——`StreamBus`、`SessionLock`、`WakeChannel`、`SubagentAwaitLease`、`TreeBudget`、
5
+ * `TaskLease`、`TaskSignals`。
6
+ * 存储类端口(跑次、消息、会话、问题、厂商文件、任务表)不在这里:它们归宿主的数据库。
6
7
  */
7
8
  import type { Redis } from 'ioredis';
8
9
  import type { LoggerFactory } from '@qjp88995/agent-runtime';
9
- import { RedisSessionLock, RedisSubagentAwaitLease, RedisTreeBudget, RedisWakeChannel } from './coordination.js';
10
+ import { RedisSessionLock, RedisSubagentAwaitLease, RedisTaskLease, RedisTaskSignals, RedisTreeBudget, RedisWakeChannel } from './coordination.js';
10
11
  import type { RedisKeyOptions } from './keys.js';
11
12
  import { RedisStreamBus, type RedisStreamBusOptions } from './stream-bus.js';
12
- export { RedisSessionLock, type RedisSessionLockOptions, RedisSubagentAwaitLease, type RedisSubagentAwaitLeaseOptions, RedisTreeBudget, type RedisTreeBudgetOptions, RedisWakeChannel, type RedisWakeChannelOptions, } from './coordination.js';
13
+ export { RedisSessionLock, type RedisSessionLockOptions, RedisSubagentAwaitLease, type RedisSubagentAwaitLeaseOptions, RedisTaskLease, type RedisTaskLeaseOptions, RedisTaskSignals, type RedisTaskSignalsOptions, RedisTreeBudget, type RedisTreeBudgetOptions, RedisWakeChannel, type RedisWakeChannelOptions, } from './coordination.js';
13
14
  export { type RedisKeyOptions, type RedisKeys, redisKeys } from './keys.js';
14
15
  export { RedisStreamBus, type RedisStreamBusOptions } from './stream-bus.js';
15
16
  export interface RedisPortsOptions extends RedisKeyOptions {
@@ -23,7 +24,9 @@ export interface RedisPorts {
23
24
  wake: RedisWakeChannel;
24
25
  awaitLease: RedisSubagentAwaitLease;
25
26
  treeBudget: RedisTreeBudget;
26
- /** 停总线定时器、关唤醒频道的订阅连接。不关 `redis` 本身(它是宿主的)。 */
27
+ taskLease: RedisTaskLease;
28
+ taskSignals: RedisTaskSignals;
29
+ /** 停总线定时器、关唤醒频道与转后台广播的订阅连接。不关 `redis` 本身(它是宿主的)。 */
27
30
  dispose(): Promise<void>;
28
31
  }
29
32
  /**
@@ -33,8 +36,8 @@ export interface RedisPorts {
33
36
  * const ports = { ...createInProcessPorts(), ...(await createRedisPorts(redis)) };
34
37
  * ```
35
38
  *
36
- * `redis` 是宿主的共享连接(锁、租约、名额、写会话流都用它);唤醒频道与每个会话订阅
37
- * 各自 `duplicate()` 一条。等唤醒频道订阅就绪才返回。
39
+ * `redis` 是宿主的共享连接(锁、租约、名额、写会话流都用它);唤醒频道、转后台广播与每个会话订阅
40
+ * 各自 `duplicate()` 一条。等两个频道的订阅都就绪才返回。
38
41
  */
39
42
  export declare function createRedisPorts(redis: Redis, options?: RedisPortsOptions): Promise<RedisPorts>;
40
43
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,eAAe,EACf,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7E,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,uBAAuB,EACvB,KAAK,8BAA8B,EACnC,eAAe,EACf,KAAK,sBAAsB,EAC3B,gBAAgB,EAChB,KAAK,uBAAuB,GAC7B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7E,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,gDAAgD;IAChD,GAAG,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,gBAAgB,CAAC;IACvB,UAAU,EAAE,uBAAuB,CAAC;IACpC,UAAU,EAAE,eAAe,CAAC;IAC5B,8CAA8C;IAC9C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,KAAK,EACZ,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,UAAU,CAAC,CAerB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7E,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,uBAAuB,EACvB,KAAK,8BAA8B,EACnC,cAAc,EACd,KAAK,qBAAqB,EAC1B,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,eAAe,EACf,KAAK,sBAAsB,EAC3B,gBAAgB,EAChB,KAAK,uBAAuB,GAC7B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7E,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,gDAAgD;IAChD,GAAG,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,gBAAgB,CAAC;IACvB,UAAU,EAAE,uBAAuB,CAAC;IACpC,UAAU,EAAE,eAAe,CAAC;IAC5B,SAAS,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,gBAAgB,CAAC;IAC9B,oDAAoD;IACpD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,KAAK,EACZ,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,UAAU,CAAC,CAoBrB"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { RedisSessionLock, RedisSubagentAwaitLease, RedisTreeBudget, RedisWakeChannel, } from './coordination.js';
1
+ import { RedisSessionLock, RedisSubagentAwaitLease, RedisTaskLease, RedisTaskSignals, RedisTreeBudget, RedisWakeChannel, } from './coordination.js';
2
2
  import { RedisStreamBus } from './stream-bus.js';
3
- export { RedisSessionLock, RedisSubagentAwaitLease, RedisTreeBudget, RedisWakeChannel, } from './coordination.js';
3
+ export { RedisSessionLock, RedisSubagentAwaitLease, RedisTaskLease, RedisTaskSignals, RedisTreeBudget, RedisWakeChannel, } from './coordination.js';
4
4
  export { redisKeys } from './keys.js';
5
5
  export { RedisStreamBus } from './stream-bus.js';
6
6
  /**
@@ -10,12 +10,15 @@ export { RedisStreamBus } from './stream-bus.js';
10
10
  * const ports = { ...createInProcessPorts(), ...(await createRedisPorts(redis)) };
11
11
  * ```
12
12
  *
13
- * `redis` 是宿主的共享连接(锁、租约、名额、写会话流都用它);唤醒频道与每个会话订阅
14
- * 各自 `duplicate()` 一条。等唤醒频道订阅就绪才返回。
13
+ * `redis` 是宿主的共享连接(锁、租约、名额、写会话流都用它);唤醒频道、转后台广播与每个会话订阅
14
+ * 各自 `duplicate()` 一条。等两个频道的订阅都就绪才返回。
15
15
  */
16
16
  export async function createRedisPorts(redis, options = {}) {
17
17
  const { keyPrefix, logger } = options;
18
- const wake = await RedisWakeChannel.create(redis, { keyPrefix, logger });
18
+ const [wake, taskSignals] = await Promise.all([
19
+ RedisWakeChannel.create(redis, { keyPrefix, logger }),
20
+ RedisTaskSignals.create(redis, { keyPrefix, logger }),
21
+ ]);
19
22
  const bus = new RedisStreamBus(redis, { ...options.bus, keyPrefix });
20
23
  return {
21
24
  bus,
@@ -23,9 +26,11 @@ export async function createRedisPorts(redis, options = {}) {
23
26
  wake,
24
27
  awaitLease: new RedisSubagentAwaitLease(redis, { keyPrefix, logger }),
25
28
  treeBudget: new RedisTreeBudget(redis, { keyPrefix }),
29
+ taskLease: new RedisTaskLease(redis, { keyPrefix }),
30
+ taskSignals,
26
31
  async dispose() {
27
32
  bus.dispose();
28
- await wake.dispose();
33
+ await Promise.all([wake.dispose(), taskSignals.dispose()]);
29
34
  },
30
35
  };
31
36
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,cAAc,EAA8B,MAAM,iBAAiB,CAAC;AAE7E,OAAO,EACL,gBAAgB,EAEhB,uBAAuB,EAEvB,eAAe,EAEf,gBAAgB,GAEjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAwC,SAAS,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,cAAc,EAA8B,MAAM,iBAAiB,CAAC;AAkB7E;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAY,EACZ,UAA6B,EAAE;IAE/B,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACtC,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IACzE,MAAM,GAAG,GAAG,IAAI,cAAc,CAAC,KAAK,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IACrE,OAAO;QACL,GAAG;QACH,IAAI,EAAE,IAAI,gBAAgB,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC;QAChD,IAAI;QACJ,UAAU,EAAE,IAAI,uBAAuB,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QACrE,UAAU,EAAE,IAAI,eAAe,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC;QACrD,KAAK,CAAC,OAAO;YACX,GAAG,CAAC,OAAO,EAAE,CAAC;YACd,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,cAAc,EAA8B,MAAM,iBAAiB,CAAC;AAE7E,OAAO,EACL,gBAAgB,EAEhB,uBAAuB,EAEvB,cAAc,EAEd,gBAAgB,EAEhB,eAAe,EAEf,gBAAgB,GAEjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAwC,SAAS,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,cAAc,EAA8B,MAAM,iBAAiB,CAAC;AAoB7E;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAY,EACZ,UAA6B,EAAE;IAE/B,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACtC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC5C,gBAAgB,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QACrD,gBAAgB,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;KACtD,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,cAAc,CAAC,KAAK,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IACrE,OAAO;QACL,GAAG;QACH,IAAI,EAAE,IAAI,gBAAgB,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC;QAChD,IAAI;QACJ,UAAU,EAAE,IAAI,uBAAuB,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QACrE,UAAU,EAAE,IAAI,eAAe,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC;QACrD,SAAS,EAAE,IAAI,cAAc,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC;QACnD,WAAW;QACX,KAAK,CAAC,OAAO;YACX,GAAG,CAAC,OAAO,EAAE,CAAC;YACd,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC7D,CAAC;KACF,CAAC;AACJ,CAAC"}
package/dist/keys.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 键与频道名。缺省与 agent-base 线上用的一字不差——agent-base 换上本包时可以滚动发布
2
+ * 键与频道名。缺省与 agent-base 线上用的一字不差(`agent-task:*` 是 0.2.0 新加的,没有旧格式要兼容)——agent-base 换上本包时可以滚动发布
3
3
  * (新旧实例共用一个 Redis 期间锁、唤醒、租约、会话流都互通),空闲广播的载荷也照旧是
4
4
  * JSON `{ sessionId }`,它现有的订阅方不用改。
5
5
  *
@@ -16,6 +16,8 @@ export interface RedisKeys {
16
16
  awaited(childSessionId: string): string;
17
17
  liveSubs(rootSessionId: string): string;
18
18
  sessionStream(rootSessionId: string): string;
19
+ taskLease(taskId: string): string;
20
+ taskBackgroundChannel: string;
19
21
  }
20
22
  export declare function redisKeys(options?: RedisKeyOptions): RedisKeys;
21
23
  //# sourceMappingURL=keys.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;IACxC,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9C;AAED,wBAAgB,SAAS,CAAC,OAAO,GAAE,eAAoB,GAAG,SAAS,CAUlE"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;IACxC,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7C,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,SAAS,CAAC,OAAO,GAAE,eAAoB,GAAG,SAAS,CAYlE"}
package/dist/keys.js CHANGED
@@ -7,6 +7,8 @@ export function redisKeys(options = {}) {
7
7
  awaited: c => `${p}agent-subagent:awaited:${c}`,
8
8
  liveSubs: r => `${p}agent-tree:live-subs:${r}`,
9
9
  sessionStream: r => `${p}agent-session:${r}`,
10
+ taskLease: t => `${p}agent-task:lease:${t}`,
11
+ taskBackgroundChannel: `${p}agent-task:background`,
10
12
  };
11
13
  }
12
14
  //# sourceMappingURL=keys.js.map
package/dist/keys.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAqBA,MAAM,UAAU,SAAS,CAAC,UAA2B,EAAE;IACrD,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;IAClC,OAAO;QACL,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAAE;QACrC,WAAW,EAAE,GAAG,CAAC,iBAAiB;QAClC,WAAW,EAAE,GAAG,CAAC,oBAAoB;QACrC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,0BAA0B,CAAC,EAAE;QAC/C,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,wBAAwB,CAAC,EAAE;QAC9C,aAAa,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE;KAC7C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAuBA,MAAM,UAAU,SAAS,CAAC,UAA2B,EAAE;IACrD,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;IAClC,OAAO;QACL,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAAE;QACrC,WAAW,EAAE,GAAG,CAAC,iBAAiB;QAClC,WAAW,EAAE,GAAG,CAAC,oBAAoB;QACrC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,0BAA0B,CAAC,EAAE;QAC/C,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,wBAAwB,CAAC,EAAE;QAC9C,aAAa,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE;QAC5C,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE;QAC3C,qBAAqB,EAAE,GAAG,CAAC,uBAAuB;KACnD,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qjp88995/agent-runtime-redis",
3
- "version": "0.1.0",
4
- "description": "Redis adapters for @qjp88995/agent-runtime: event bus, session lock, wake/idle channel, subagent await lease and tree budget, for running on more than one instance.",
3
+ "version": "0.2.0",
4
+ "description": "Redis adapters for @qjp88995/agent-runtime: event bus, session lock, wake/idle channel, subagent await lease, tree budget, task lease and task background signal, for running on more than one instance.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
7
7
  "publishConfig": {
@@ -30,14 +30,14 @@
30
30
  },
31
31
  "peerDependencies": {
32
32
  "ioredis": "^5.4.0",
33
- "@qjp88995/agent-runtime": "^0.1.0"
33
+ "@qjp88995/agent-runtime": "^0.2.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^25.9.5",
37
37
  "ioredis": "^5.11.1",
38
38
  "typescript": "^5.9.3",
39
39
  "vitest": "^4.1.10",
40
- "@qjp88995/agent-runtime": "0.1.0"
40
+ "@qjp88995/agent-runtime": "0.2.0"
41
41
  },
42
42
  "scripts": {
43
43
  "build": "rm -rf dist *.tsbuildinfo && tsc -p tsconfig.build.json",
@@ -9,6 +9,8 @@ import {
9
9
  type LoggerFactory,
10
10
  type SessionLock,
11
11
  type SubagentAwaitLease,
12
+ type TaskLease,
13
+ type TaskSignals,
12
14
  type TreeBudget,
13
15
  type WakeChannel,
14
16
  } from '@qjp88995/agent-runtime';
@@ -16,12 +18,12 @@ import {
16
18
  import { type RedisKeyOptions, type RedisKeys, redisKeys } from './keys.js';
17
19
 
18
20
  /**
19
- * 会话锁、唤醒 / 空闲广播、子 Agent 等待租约、树级名额的 Redis 实现。
21
+ * 会话锁、唤醒 / 空闲广播、子 Agent 等待租约、树级名额、任务监视租约与转后台广播的 Redis 实现。
20
22
  * 搬自 agent-base `agent-core/loop/{session-lock,session-wake}.service.ts`、`session-idle.ts`、
21
23
  * `agent-core/subagent/{subagent-await,tree-budget}.service.ts`,语义见 runtime 的 `ports/coordination`。
22
24
  *
23
- * 锁 / 租约 / 名额都直接用宿主给的共享连接(全是非阻塞的常规命令),只有唤醒频道要一条
24
- * 独立的订阅连接(进了 subscribe 模式的连接不能再发普通命令)。
25
+ * 锁 / 租约 / 名额都直接用宿主给的共享连接(全是非阻塞的常规命令),只有唤醒频道与转后台广播
26
+ * 各要一条独立的订阅连接(进了 subscribe 模式的连接不能再发普通命令)。
25
27
  */
26
28
 
27
29
  /** DEL 前比对 token,避免误删「TTL 过期后被别人重新抢到」的锁。 */
@@ -36,23 +38,18 @@ export interface RedisSessionLockOptions extends RedisKeyOptions {
36
38
  ttlMs?: number;
37
39
  }
38
40
 
39
- /** SET NX PX 抢锁,Lua 比对 token 再 DEL / PEXPIRE。TTL 兜底持有者崩溃。 */
40
- export class RedisSessionLock implements SessionLock {
41
- private readonly keys: RedisKeys;
42
- private readonly ttlMs: number;
43
-
41
+ /** SET NX PX 抢,Lua 比对 token 再 DEL / PEXPIRE。会话锁与任务租约共用,只是键空间不同。 */
42
+ class RedisTokenLock {
44
43
  constructor(
45
44
  private readonly redis: Redis,
46
- options: RedisSessionLockOptions = {}
47
- ) {
48
- this.keys = redisKeys(options);
49
- this.ttlMs = options.ttlMs ?? LOCK_TTL_MS;
50
- }
45
+ private readonly keyOf: (id: string) => string,
46
+ private readonly ttlMs: number
47
+ ) {}
51
48
 
52
- async acquire(sessionId: string): Promise<string | null> {
49
+ async acquire(id: string): Promise<string | null> {
53
50
  const token = randomUUID();
54
51
  const res = await this.redis.set(
55
- this.keys.lock(sessionId),
52
+ this.keyOf(id),
56
53
  token,
57
54
  'PX',
58
55
  this.ttlMs,
@@ -61,23 +58,82 @@ export class RedisSessionLock implements SessionLock {
61
58
  return res === 'OK' ? token : null;
62
59
  }
63
60
 
64
- async renew(sessionId: string, token: string): Promise<boolean> {
61
+ async renew(id: string, token: string): Promise<boolean> {
65
62
  const res = await this.redis.eval(
66
63
  RENEW_LUA,
67
64
  1,
68
- this.keys.lock(sessionId),
65
+ this.keyOf(id),
69
66
  token,
70
67
  String(this.ttlMs)
71
68
  );
72
69
  return res === 1;
73
70
  }
74
71
 
75
- async release(sessionId: string, token: string): Promise<void> {
76
- await this.redis.eval(RELEASE_LUA, 1, this.keys.lock(sessionId), token);
72
+ async release(id: string, token: string): Promise<void> {
73
+ await this.redis.eval(RELEASE_LUA, 1, this.keyOf(id), token);
77
74
  }
78
75
 
79
- async isHeld(sessionId: string): Promise<boolean> {
80
- return (await this.redis.exists(this.keys.lock(sessionId))) === 1;
76
+ async isHeld(id: string): Promise<boolean> {
77
+ return (await this.redis.exists(this.keyOf(id))) === 1;
78
+ }
79
+ }
80
+
81
+ /** 会话锁。TTL 兜底持有者崩溃。 */
82
+ export class RedisSessionLock implements SessionLock {
83
+ private readonly lock: RedisTokenLock;
84
+
85
+ constructor(redis: Redis, options: RedisSessionLockOptions = {}) {
86
+ this.lock = new RedisTokenLock(
87
+ redis,
88
+ redisKeys(options).lock,
89
+ options.ttlMs ?? LOCK_TTL_MS
90
+ );
91
+ }
92
+
93
+ acquire(sessionId: string): Promise<string | null> {
94
+ return this.lock.acquire(sessionId);
95
+ }
96
+
97
+ renew(sessionId: string, token: string): Promise<boolean> {
98
+ return this.lock.renew(sessionId, token);
99
+ }
100
+
101
+ release(sessionId: string, token: string): Promise<void> {
102
+ return this.lock.release(sessionId, token);
103
+ }
104
+
105
+ isHeld(sessionId: string): Promise<boolean> {
106
+ return this.lock.isHeld(sessionId);
107
+ }
108
+ }
109
+
110
+ export interface RedisTaskLeaseOptions extends RedisKeyOptions {
111
+ /** 缺省 `LOCK_TTL_MS`(60s)。监视器每段等待(runtime 缺省 20s)之后续一次。 */
112
+ ttlMs?: number;
113
+ }
114
+
115
+ /** 任务监视租约:与会话锁同一套写法,键 `agent-task:lease:<taskId>`。 */
116
+ export class RedisTaskLease implements TaskLease {
117
+ private readonly lock: RedisTokenLock;
118
+
119
+ constructor(redis: Redis, options: RedisTaskLeaseOptions = {}) {
120
+ this.lock = new RedisTokenLock(
121
+ redis,
122
+ redisKeys(options).taskLease,
123
+ options.ttlMs ?? LOCK_TTL_MS
124
+ );
125
+ }
126
+
127
+ acquire(taskId: string): Promise<string | null> {
128
+ return this.lock.acquire(taskId);
129
+ }
130
+
131
+ renew(taskId: string, token: string): Promise<boolean> {
132
+ return this.lock.renew(taskId, token);
133
+ }
134
+
135
+ release(taskId: string, token: string): Promise<void> {
136
+ return this.lock.release(taskId, token);
81
137
  }
82
138
  }
83
139
 
@@ -201,6 +257,80 @@ export class RedisWakeChannel implements WakeChannel {
201
257
  }
202
258
  }
203
259
 
260
+ export interface RedisTaskSignalsOptions extends RedisKeyOptions {
261
+ /** 订阅用的连接。缺省 `redis.duplicate()`,由本对象负责关闭;宿主自己给的不关。 */
262
+ subscriber?: Redis;
263
+ logger?: LoggerFactory;
264
+ }
265
+
266
+ /**
267
+ * 「任务转后台」广播走 Redis pub/sub,载荷就是任务 id。与 `RedisWakeChannel` 同样用
268
+ * `create()` 等 SUBSCRIBE 应答回来才返回。广播丢了不要紧:前台等待方每段等待之后都回看任务行
269
+ * 的 `background`,信号只是让它不必等满这一段。
270
+ */
271
+ export class RedisTaskSignals implements TaskSignals {
272
+ private readonly handlers = new Set<Handler>();
273
+ private disposed = false;
274
+
275
+ private constructor(
276
+ private readonly redis: Redis,
277
+ private readonly sub: Redis,
278
+ private readonly ownsSub: boolean,
279
+ private readonly channel: string,
280
+ private readonly logger: Logger
281
+ ) {}
282
+
283
+ static async create(
284
+ redis: Redis,
285
+ options: RedisTaskSignalsOptions = {}
286
+ ): Promise<RedisTaskSignals> {
287
+ const sub = options.subscriber ?? redis.duplicate();
288
+ const signals = new RedisTaskSignals(
289
+ redis,
290
+ sub,
291
+ !options.subscriber,
292
+ redisKeys(options).taskBackgroundChannel,
293
+ (options.logger ?? consoleLogger)('RedisTaskSignals')
294
+ );
295
+ sub.on('message', (ch: string, taskId: string) => {
296
+ if (ch === signals.channel) signals.dispatch(taskId);
297
+ });
298
+ await sub.subscribe(signals.channel);
299
+ return signals;
300
+ }
301
+
302
+ private dispatch(taskId: string): void {
303
+ for (const handler of [...this.handlers]) {
304
+ try {
305
+ handler(taskId);
306
+ } catch (err) {
307
+ this.logger.error(
308
+ `background handler failed for ${taskId}: ${String(err)}`,
309
+ err instanceof Error ? err.stack : undefined
310
+ );
311
+ }
312
+ }
313
+ }
314
+
315
+ async background(taskId: string): Promise<void> {
316
+ await this.redis.publish(this.channel, taskId);
317
+ }
318
+
319
+ onBackground(handler: Handler): () => void {
320
+ this.handlers.add(handler);
321
+ return () => this.handlers.delete(handler);
322
+ }
323
+
324
+ /** 摘掉处理函数;订阅连接是本对象建的就关掉,否则只退订本频道。 */
325
+ async dispose(): Promise<void> {
326
+ if (this.disposed) return;
327
+ this.disposed = true;
328
+ this.handlers.clear();
329
+ if (this.ownsSub) await this.sub.quit().catch(() => undefined);
330
+ else await this.sub.unsubscribe(this.channel).catch(() => undefined);
331
+ }
332
+ }
333
+
204
334
  export interface RedisSubagentAwaitLeaseOptions extends RedisKeyOptions {
205
335
  /** 缺省 `AWAIT_LEASE_TTL_MS`(60s)。 */
206
336
  ttlMs?: number;
package/src/index.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * `@qjp88995/agent-runtime` 的 Redis 适配:多实例部署时替换进程内的总线与协调件。
3
3
  *
4
- * 换的是五个端口——`StreamBus`、`SessionLock`、`WakeChannel`、`SubagentAwaitLease`、`TreeBudget`。
5
- * 存储类端口(跑次、消息、会话、问题、厂商文件)不在这里:它们归宿主的数据库。
4
+ * 换的是七个端口——`StreamBus`、`SessionLock`、`WakeChannel`、`SubagentAwaitLease`、`TreeBudget`、
5
+ * `TaskLease`、`TaskSignals`。
6
+ * 存储类端口(跑次、消息、会话、问题、厂商文件、任务表)不在这里:它们归宿主的数据库。
6
7
  */
7
8
  import type { Redis } from 'ioredis';
8
9
 
@@ -11,6 +12,8 @@ import type { LoggerFactory } from '@qjp88995/agent-runtime';
11
12
  import {
12
13
  RedisSessionLock,
13
14
  RedisSubagentAwaitLease,
15
+ RedisTaskLease,
16
+ RedisTaskSignals,
14
17
  RedisTreeBudget,
15
18
  RedisWakeChannel,
16
19
  } from './coordination.js';
@@ -22,6 +25,10 @@ export {
22
25
  type RedisSessionLockOptions,
23
26
  RedisSubagentAwaitLease,
24
27
  type RedisSubagentAwaitLeaseOptions,
28
+ RedisTaskLease,
29
+ type RedisTaskLeaseOptions,
30
+ RedisTaskSignals,
31
+ type RedisTaskSignalsOptions,
25
32
  RedisTreeBudget,
26
33
  type RedisTreeBudgetOptions,
27
34
  RedisWakeChannel,
@@ -42,7 +49,9 @@ export interface RedisPorts {
42
49
  wake: RedisWakeChannel;
43
50
  awaitLease: RedisSubagentAwaitLease;
44
51
  treeBudget: RedisTreeBudget;
45
- /** 停总线定时器、关唤醒频道的订阅连接。不关 `redis` 本身(它是宿主的)。 */
52
+ taskLease: RedisTaskLease;
53
+ taskSignals: RedisTaskSignals;
54
+ /** 停总线定时器、关唤醒频道与转后台广播的订阅连接。不关 `redis` 本身(它是宿主的)。 */
46
55
  dispose(): Promise<void>;
47
56
  }
48
57
 
@@ -53,15 +62,18 @@ export interface RedisPorts {
53
62
  * const ports = { ...createInProcessPorts(), ...(await createRedisPorts(redis)) };
54
63
  * ```
55
64
  *
56
- * `redis` 是宿主的共享连接(锁、租约、名额、写会话流都用它);唤醒频道与每个会话订阅
57
- * 各自 `duplicate()` 一条。等唤醒频道订阅就绪才返回。
65
+ * `redis` 是宿主的共享连接(锁、租约、名额、写会话流都用它);唤醒频道、转后台广播与每个会话订阅
66
+ * 各自 `duplicate()` 一条。等两个频道的订阅都就绪才返回。
58
67
  */
59
68
  export async function createRedisPorts(
60
69
  redis: Redis,
61
70
  options: RedisPortsOptions = {}
62
71
  ): Promise<RedisPorts> {
63
72
  const { keyPrefix, logger } = options;
64
- const wake = await RedisWakeChannel.create(redis, { keyPrefix, logger });
73
+ const [wake, taskSignals] = await Promise.all([
74
+ RedisWakeChannel.create(redis, { keyPrefix, logger }),
75
+ RedisTaskSignals.create(redis, { keyPrefix, logger }),
76
+ ]);
65
77
  const bus = new RedisStreamBus(redis, { ...options.bus, keyPrefix });
66
78
  return {
67
79
  bus,
@@ -69,9 +81,11 @@ export async function createRedisPorts(
69
81
  wake,
70
82
  awaitLease: new RedisSubagentAwaitLease(redis, { keyPrefix, logger }),
71
83
  treeBudget: new RedisTreeBudget(redis, { keyPrefix }),
84
+ taskLease: new RedisTaskLease(redis, { keyPrefix }),
85
+ taskSignals,
72
86
  async dispose() {
73
87
  bus.dispose();
74
- await wake.dispose();
88
+ await Promise.all([wake.dispose(), taskSignals.dispose()]);
75
89
  },
76
90
  };
77
91
  }
package/src/keys.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 键与频道名。缺省与 agent-base 线上用的一字不差——agent-base 换上本包时可以滚动发布
2
+ * 键与频道名。缺省与 agent-base 线上用的一字不差(`agent-task:*` 是 0.2.0 新加的,没有旧格式要兼容)——agent-base 换上本包时可以滚动发布
3
3
  * (新旧实例共用一个 Redis 期间锁、唤醒、租约、会话流都互通),空闲广播的载荷也照旧是
4
4
  * JSON `{ sessionId }`,它现有的订阅方不用改。
5
5
  *
@@ -17,6 +17,8 @@ export interface RedisKeys {
17
17
  awaited(childSessionId: string): string;
18
18
  liveSubs(rootSessionId: string): string;
19
19
  sessionStream(rootSessionId: string): string;
20
+ taskLease(taskId: string): string;
21
+ taskBackgroundChannel: string;
20
22
  }
21
23
 
22
24
  export function redisKeys(options: RedisKeyOptions = {}): RedisKeys {
@@ -28,5 +30,7 @@ export function redisKeys(options: RedisKeyOptions = {}): RedisKeys {
28
30
  awaited: c => `${p}agent-subagent:awaited:${c}`,
29
31
  liveSubs: r => `${p}agent-tree:live-subs:${r}`,
30
32
  sessionStream: r => `${p}agent-session:${r}`,
33
+ taskLease: t => `${p}agent-task:lease:${t}`,
34
+ taskBackgroundChannel: `${p}agent-task:background`,
31
35
  };
32
36
  }