@migor/agentia 0.2.0 → 0.2.2
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/LICENSE +21 -0
- package/README.en.md +144 -0
- package/README.md +202 -76
- package/dist/AGENTS.md +811 -0
- package/dist/container/container.d.ts +10 -2
- package/dist/container/container.js +27 -3
- package/dist/container/container.js.map +1 -1
- package/dist/core/abort.d.ts +10 -0
- package/dist/core/abort.js +28 -0
- package/dist/core/abort.js.map +1 -0
- package/dist/core/blackboard.d.ts +37 -0
- package/dist/core/blackboard.js +2 -0
- package/dist/core/blackboard.js.map +1 -0
- package/dist/core/json.d.ts +2 -0
- package/dist/core/json.js +4 -0
- package/dist/core/json.js.map +1 -1
- package/dist/core/run.d.ts +18 -0
- package/dist/core/run.js +2 -0
- package/dist/core/run.js.map +1 -0
- package/dist/core/schema.js +2 -2
- package/dist/core/schema.js.map +1 -1
- package/dist/core/tool.d.ts +62 -7
- package/dist/core/trace.d.ts +20 -4
- package/dist/engine/budget.d.ts +49 -0
- package/dist/engine/budget.js +30 -0
- package/dist/engine/budget.js.map +1 -0
- package/dist/engine/concurrency.d.ts +43 -0
- package/dist/engine/concurrency.js +95 -0
- package/dist/engine/concurrency.js.map +1 -0
- package/dist/engine/errors.d.ts +2 -0
- package/dist/engine/errors.js +7 -0
- package/dist/engine/errors.js.map +1 -1
- package/dist/engine/loop.d.ts +35 -10
- package/dist/engine/loop.js +336 -66
- package/dist/engine/loop.js.map +1 -1
- package/dist/engine/policy.d.ts +9 -1
- package/dist/engine/policy.js +8 -5
- package/dist/engine/policy.js.map +1 -1
- package/dist/engine/replay.d.ts +10 -4
- package/dist/engine/replay.js +57 -27
- package/dist/engine/replay.js.map +1 -1
- package/dist/engine/retry.d.ts +47 -0
- package/dist/engine/retry.js +51 -0
- package/dist/engine/retry.js.map +1 -0
- package/dist/engine/spec.d.ts +61 -0
- package/dist/{run → engine}/spec.js +4 -1
- package/dist/engine/spec.js.map +1 -0
- package/dist/engine/tracer.d.ts +9 -0
- package/dist/engine/tracer.js +57 -1
- package/dist/engine/tracer.js.map +1 -1
- package/dist/engine/{context.d.ts → trimming.d.ts} +19 -20
- package/dist/engine/trimming.js +238 -0
- package/dist/engine/trimming.js.map +1 -0
- package/dist/engine/types.d.ts +109 -13
- package/dist/engine/types.js +9 -1
- package/dist/engine/types.js.map +1 -1
- package/dist/engine/usage.d.ts +28 -2
- package/dist/engine/usage.js +39 -6
- package/dist/engine/usage.js.map +1 -1
- package/dist/eval/defineEval.d.ts +73 -0
- package/dist/eval/defineEval.js +44 -0
- package/dist/eval/defineEval.js.map +1 -0
- package/dist/eval/scripted.d.ts +26 -0
- package/dist/eval/scripted.js +43 -0
- package/dist/eval/scripted.js.map +1 -0
- package/dist/index.d.ts +60 -35
- package/dist/index.js +38 -20
- package/dist/index.js.map +1 -1
- package/dist/integrations/anthropic.d.ts +53 -0
- package/dist/integrations/anthropic.js +24 -0
- package/dist/integrations/anthropic.js.map +1 -0
- package/dist/integrations/mcp.d.ts +79 -0
- package/dist/integrations/mcp.js +108 -0
- package/dist/integrations/mcp.js.map +1 -0
- package/dist/integrations/metrics.d.ts +131 -0
- package/dist/integrations/metrics.js +488 -0
- package/dist/integrations/metrics.js.map +1 -0
- package/dist/{run → integrations}/openai.d.ts +12 -5
- package/dist/integrations/openai.js +386 -0
- package/dist/integrations/openai.js.map +1 -0
- package/dist/integrations/otlp.js.map +1 -0
- package/dist/integrations/report.d.ts +78 -0
- package/dist/integrations/report.js +257 -0
- package/dist/integrations/report.js.map +1 -0
- package/dist/runtime/context.d.ts +25 -0
- package/dist/{run → runtime}/context.js +8 -1
- package/dist/runtime/context.js.map +1 -0
- package/dist/{run → runtime}/memory.d.ts +1 -1
- package/dist/{run → runtime}/memory.js +3 -1
- package/dist/runtime/memory.js.map +1 -0
- package/dist/{run → runtime}/run.d.ts +22 -5
- package/dist/runtime/run.js +218 -0
- package/dist/runtime/run.js.map +1 -0
- package/dist/runtime/session.d.ts +27 -0
- package/dist/runtime/session.js +16 -0
- package/dist/runtime/session.js.map +1 -0
- package/dist/{run → runtime}/spec.d.ts +16 -2
- package/dist/runtime/spec.js +28 -0
- package/dist/runtime/spec.js.map +1 -0
- package/dist/{run → runtime}/systemPrompt.d.ts +12 -0
- package/dist/{run → runtime}/systemPrompt.js +16 -3
- package/dist/runtime/systemPrompt.js.map +1 -0
- package/dist/{run → runtime}/types.js.map +1 -1
- package/dist/{run → store}/fsStore.d.ts +15 -0
- package/dist/{run → store}/fsStore.js +43 -1
- package/dist/store/fsStore.js.map +1 -0
- package/dist/{run → store}/redisStore.d.ts +23 -1
- package/dist/{run → store}/redisStore.js +33 -6
- package/dist/store/redisStore.js.map +1 -0
- package/dist/{run → store}/sqliteStore.js +22 -1
- package/dist/store/sqliteStore.js.map +1 -0
- package/dist/{run → store}/store.d.ts +25 -3
- package/dist/store/store.js +63 -0
- package/dist/store/store.js.map +1 -0
- package/dist/toolkit/asset.d.ts +7 -6
- package/dist/toolkit/asset.js +7 -6
- package/dist/toolkit/asset.js.map +1 -1
- package/dist/toolkit/collect.d.ts +20 -7
- package/dist/toolkit/collect.js +21 -7
- package/dist/toolkit/collect.js.map +1 -1
- package/dist/toolkit/discover.d.ts +21 -1
- package/dist/toolkit/discover.js +59 -12
- package/dist/toolkit/discover.js.map +1 -1
- package/dist/toolkit/middleware.d.ts +10 -10
- package/dist/toolkit/middleware.js +17 -4
- package/dist/toolkit/middleware.js.map +1 -1
- package/dist/toolkit/module.d.ts +100 -34
- package/dist/toolkit/module.js +112 -36
- package/dist/toolkit/module.js.map +1 -1
- package/dist/toolkit/prompt.d.ts +4 -6
- package/dist/toolkit/prompt.js +11 -10
- package/dist/toolkit/prompt.js.map +1 -1
- package/dist/toolkit/skill.d.ts +14 -16
- package/dist/toolkit/skill.js +32 -25
- package/dist/toolkit/skill.js.map +1 -1
- package/dist/toolkit/subagent.d.ts +10 -12
- package/dist/toolkit/subagent.js +23 -21
- package/dist/toolkit/subagent.js.map +1 -1
- package/dist/toolkit/tool.d.ts +19 -11
- package/dist/toolkit/tool.js +11 -8
- package/dist/toolkit/tool.js.map +1 -1
- package/dist/toolkit/zod.d.ts +4 -3
- package/dist/toolkit/zod.js +2 -1
- package/dist/toolkit/zod.js.map +1 -1
- package/dist/transport/async.d.ts +133 -0
- package/dist/transport/async.js +415 -0
- package/dist/transport/async.js.map +1 -0
- package/dist/transport/http.d.ts +146 -0
- package/dist/transport/http.js +387 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/{run → transport}/scheduler.d.ts +9 -1
- package/dist/transport/scheduler.js +133 -0
- package/dist/transport/scheduler.js.map +1 -0
- package/dist/transport/sse.d.ts +43 -0
- package/dist/transport/sse.js +51 -0
- package/dist/transport/sse.js.map +1 -0
- package/dist/{run → transport}/transport.d.ts +1 -1
- package/dist/{run → transport}/transport.js +1 -1
- package/dist/transport/transport.js.map +1 -0
- package/package.json +24 -9
- package/dist/engine/context.js +0 -146
- package/dist/engine/context.js.map +0 -1
- package/dist/run/async.d.ts +0 -59
- package/dist/run/async.js +0 -185
- package/dist/run/async.js.map +0 -1
- package/dist/run/context.d.ts +0 -16
- package/dist/run/context.js.map +0 -1
- package/dist/run/fsStore.js.map +0 -1
- package/dist/run/http.d.ts +0 -47
- package/dist/run/http.js +0 -119
- package/dist/run/http.js.map +0 -1
- package/dist/run/memory.js.map +0 -1
- package/dist/run/openai.js +0 -181
- package/dist/run/openai.js.map +0 -1
- package/dist/run/otlp.js.map +0 -1
- package/dist/run/redisStore.js.map +0 -1
- package/dist/run/run.js +0 -129
- package/dist/run/run.js.map +0 -1
- package/dist/run/scheduler.js +0 -85
- package/dist/run/scheduler.js.map +0 -1
- package/dist/run/spec.js.map +0 -1
- package/dist/run/sqliteStore.js.map +0 -1
- package/dist/run/store.js +0 -28
- package/dist/run/store.js.map +0 -1
- package/dist/run/systemPrompt.js.map +0 -1
- package/dist/run/transport.js.map +0 -1
- /package/dist/{run → integrations}/otlp.d.ts +0 -0
- /package/dist/{run → integrations}/otlp.js +0 -0
- /package/dist/{run → runtime}/types.d.ts +0 -0
- /package/dist/{run → runtime}/types.js +0 -0
- /package/dist/{run → store}/sqliteStore.d.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/runtime/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* SessionStore —— 会话持久化 / 对话历史(C4)。
|
|
4
|
+
*
|
|
5
|
+
* ⚠️ **与 `MemoryStore` 的区别(别混,文档里必须并列讲)**:
|
|
6
|
+
* - `MemoryStore`(runtime/memory.ts)= 键值**黑板**:跨 run 存「状态与事实」
|
|
7
|
+
* (用户是谁、上次查到哪),run 开始水合进 `RunContext.blackboard`;
|
|
8
|
+
* - `SessionStore`(本文件)= **对话历史**:存 messages 序列,run 开始拼在传入 messages
|
|
9
|
+
* **之前**,收尾时把本轮消息追加回去。
|
|
10
|
+
*
|
|
11
|
+
* 两者正交,可以同时用。
|
|
12
|
+
*/
|
|
13
|
+
export interface SessionStore {
|
|
14
|
+
/** 读整段历史(没有则空数组) */
|
|
15
|
+
load(sessionId: string): Anthropic.MessageParam[] | Promise<Anthropic.MessageParam[]>;
|
|
16
|
+
/**
|
|
17
|
+
* 追加消息(**append-only**)—— 不做 upsert:并发写不会互相覆盖,
|
|
18
|
+
* 也便于事后审计「这段历史是怎么长出来的」。
|
|
19
|
+
*/
|
|
20
|
+
append(sessionId: string, messages: Anthropic.MessageParam[]): void | Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/** Map 实现:测试与缺省场景(进程内,无持久化) */
|
|
23
|
+
export declare class InMemorySessionStore implements SessionStore {
|
|
24
|
+
private readonly data;
|
|
25
|
+
load(sessionId: string): Anthropic.MessageParam[];
|
|
26
|
+
append(sessionId: string, messages: Anthropic.MessageParam[]): void;
|
|
27
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Map 实现:测试与缺省场景(进程内,无持久化) */
|
|
2
|
+
export class InMemorySessionStore {
|
|
3
|
+
data = new Map();
|
|
4
|
+
load(sessionId) {
|
|
5
|
+
// 返回浅拷贝:run 会把结果拼进自己的 messages 数组并继续 push,
|
|
6
|
+
// 直接把内部数组交出去会让那些 push 反向污染 store 里的历史。
|
|
7
|
+
const cur = this.data.get(sessionId);
|
|
8
|
+
return cur ? [...cur] : [];
|
|
9
|
+
}
|
|
10
|
+
append(sessionId, messages) {
|
|
11
|
+
const cur = this.data.get(sessionId) ?? [];
|
|
12
|
+
cur.push(...messages);
|
|
13
|
+
this.data.set(sessionId, cur);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/runtime/session.ts"],"names":[],"mappings":"AAwBA,+BAA+B;AAC/B,MAAM,OAAO,oBAAoB;IACd,IAAI,GAAG,IAAI,GAAG,EAAoC,CAAC;IAEpE,IAAI,CAAC,SAAiB;QACpB,2CAA2C;QAC3C,uCAAuC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACrC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7B,CAAC;IAED,MAAM,CAAC,SAAiB,EAAE,QAAkC;QAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAC3C,GAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC;CACF"}
|
|
@@ -2,6 +2,8 @@ import type Anthropic from '@anthropic-ai/sdk';
|
|
|
2
2
|
import type { ModelClient } from '../core/tool.js';
|
|
3
3
|
import type { AgentTool } from '../core/tool.js';
|
|
4
4
|
import type { ContextPolicy } from '../engine/types.js';
|
|
5
|
+
import type { RetryOptions } from '../engine/retry.js';
|
|
6
|
+
import type { BlackboardSeed } from './context.js';
|
|
5
7
|
/**
|
|
6
8
|
* Agentia —— run 调用契约(spec §6.3 三类触发共用同一份入参形态)。
|
|
7
9
|
* transport 层(同步 RPC / 异步任务 / 定时)只谈 RunInput / RunSpec,
|
|
@@ -14,15 +16,27 @@ export interface RunInvocationOptions {
|
|
|
14
16
|
maxIterations?: number;
|
|
15
17
|
client?: ModelClient;
|
|
16
18
|
onText?: (delta: string) => void;
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
+
/** 中断信号:中止则在飞请求被取消,run 以 stopReason='aborted' 收尾 */
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
/** 预置进本次 RunContext.blackboard(扩展过 Blackboard 时键有补全) */
|
|
22
|
+
blackboard?: BlackboardSeed;
|
|
19
23
|
/** 上下文预算策略(compaction / context editing) */
|
|
20
24
|
contextPolicy?: ContextPolicy;
|
|
25
|
+
/** 模型请求重试策略(覆盖应用级缺省);见 RunAgentOptions.retry */
|
|
26
|
+
retry?: RetryOptions | false;
|
|
21
27
|
/** 幂等键:异步宿主的 at-least-once 去重依据 */
|
|
22
28
|
idempotencyKey?: string;
|
|
23
29
|
/** 硬失败是否抛出;缺省 true(异步宿主置 false 落 failed 记录) */
|
|
24
30
|
rethrow?: boolean;
|
|
25
31
|
tools?: AgentTool[];
|
|
32
|
+
/** 成本硬管控:整条 run 累计 token 上限;超限以 stopReason='budget_exceeded' 收尾(算失败) */
|
|
33
|
+
maxTotalTokens?: number;
|
|
34
|
+
/** 成本硬管控:累计成本(美元)上限;依赖模型在价格表内,见 createBudgetGuard */
|
|
35
|
+
maxCostUsd?: number;
|
|
36
|
+
/** 单个工具执行超时(毫秒);超时该条 tool_result 记 is_error,不杀 run */
|
|
37
|
+
toolTimeoutMs?: number;
|
|
38
|
+
/** 同回合并行工具上限;缺省不限 */
|
|
39
|
+
maxToolConcurrency?: number;
|
|
26
40
|
}
|
|
27
41
|
/** 一次任务的规范化入参:messages(已由 normalizeMessages 规整) */
|
|
28
42
|
export interface RunSpec {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** 把任意任务入参规范成 messages(首条缺省包成 user)。 */
|
|
2
|
+
export function normalizeMessages(input) {
|
|
3
|
+
if (typeof input === 'string') {
|
|
4
|
+
// 空串与 []、{prompt:''} 一致报错:返回 [] 会带着空 messages 去调模型
|
|
5
|
+
if (!input)
|
|
6
|
+
throw new Error('任务 messages 不能为空');
|
|
7
|
+
return [{ role: 'user', content: input }];
|
|
8
|
+
}
|
|
9
|
+
if (Array.isArray(input)) {
|
|
10
|
+
if (input.length === 0)
|
|
11
|
+
throw new Error('任务 messages 不能为空');
|
|
12
|
+
return input;
|
|
13
|
+
}
|
|
14
|
+
if (input && typeof input === 'object') {
|
|
15
|
+
const o = input;
|
|
16
|
+
if (Array.isArray(o.messages)) {
|
|
17
|
+
if (o.messages.length === 0)
|
|
18
|
+
throw new Error('任务 messages 不能为空');
|
|
19
|
+
return o.messages;
|
|
20
|
+
}
|
|
21
|
+
if (typeof o.prompt === 'string' && o.prompt)
|
|
22
|
+
return [{ role: 'user', content: o.prompt }];
|
|
23
|
+
if (typeof o.text === 'string' && o.text)
|
|
24
|
+
return [{ role: 'user', content: o.text }];
|
|
25
|
+
}
|
|
26
|
+
throw new Error(`无法识别为任务输入: ${JSON.stringify(input)?.slice(0, 200)}`);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/runtime/spec.ts"],"names":[],"mappings":"AAyDA,wCAAwC;AACxC,MAAM,UAAU,iBAAiB,CAAC,KAAyB;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,mDAAmD;QACnD,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAChD,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAC5D,OAAO,KAAiC,CAAC;IAC3C,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,KAAiE,CAAC;QAC5E,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACjE,OAAO,CAAC,CAAC,QAAoC,CAAC;QAChD,CAAC;QACD,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM;YAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3F,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI;YAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC"}
|
|
@@ -13,8 +13,20 @@ export interface SystemSection {
|
|
|
13
13
|
/** stable=false 的段不进可缓存前缀,放在 breakpoint 之后 */
|
|
14
14
|
stable?: boolean;
|
|
15
15
|
}
|
|
16
|
+
export interface SystemPromptOptions {
|
|
17
|
+
/** 提示词版本号;见 `SystemPrompt.version` */
|
|
18
|
+
version?: string;
|
|
19
|
+
}
|
|
16
20
|
export declare class SystemPrompt {
|
|
17
21
|
private sections;
|
|
22
|
+
/**
|
|
23
|
+
* 提示词版本号(D4):给了它就自动落到 run 根的 `system.version` attribute ——
|
|
24
|
+
* 于是 trace 里能查出「这个结果是哪个版本的提示词产出的」(换 prompt 前后对比
|
|
25
|
+
* 效果、排查回归都靠它)。框架**不做**版本库 / 回滚平台(YAGNI,见 spec §10):
|
|
26
|
+
* 版本号怎么来(git sha / 语义版本 / 手工)由你决定。
|
|
27
|
+
*/
|
|
28
|
+
readonly version?: string;
|
|
29
|
+
constructor(opts?: SystemPromptOptions);
|
|
18
30
|
add(section: SystemSection): this;
|
|
19
31
|
add(name: string, text: string, stable?: boolean): this;
|
|
20
32
|
get stableText(): string;
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
export class SystemPrompt {
|
|
2
2
|
sections = [];
|
|
3
|
+
/**
|
|
4
|
+
* 提示词版本号(D4):给了它就自动落到 run 根的 `system.version` attribute ——
|
|
5
|
+
* 于是 trace 里能查出「这个结果是哪个版本的提示词产出的」(换 prompt 前后对比
|
|
6
|
+
* 效果、排查回归都靠它)。框架**不做**版本库 / 回滚平台(YAGNI,见 spec §10):
|
|
7
|
+
* 版本号怎么来(git sha / 语义版本 / 手工)由你决定。
|
|
8
|
+
*/
|
|
9
|
+
version;
|
|
10
|
+
constructor(opts = {}) {
|
|
11
|
+
this.version = opts.version;
|
|
12
|
+
}
|
|
3
13
|
add(nameOrSection, text, stable = true) {
|
|
4
14
|
if (typeof nameOrSection === 'string') {
|
|
5
15
|
this.sections.push({ name: nameOrSection, text: text ?? '', stable });
|
|
@@ -23,11 +33,13 @@ export class SystemPrompt {
|
|
|
23
33
|
if (!cache) {
|
|
24
34
|
return [...stable, ...volatile].filter(Boolean).join('\n\n');
|
|
25
35
|
}
|
|
36
|
+
// 判空必须按**过滤后的文本**:空段(text: '')不该产出空 text block + breakpoint
|
|
37
|
+
const stableText = stable.filter(Boolean).join('\n\n');
|
|
26
38
|
const blocks = [];
|
|
27
|
-
if (
|
|
39
|
+
if (stableText) {
|
|
28
40
|
blocks.push({
|
|
29
41
|
type: 'text',
|
|
30
|
-
text:
|
|
42
|
+
text: stableText,
|
|
31
43
|
cache_control: { type: 'ephemeral' },
|
|
32
44
|
});
|
|
33
45
|
}
|
|
@@ -35,7 +47,8 @@ export class SystemPrompt {
|
|
|
35
47
|
if (v)
|
|
36
48
|
blocks.push({ type: 'text', text: v });
|
|
37
49
|
}
|
|
38
|
-
|
|
50
|
+
// 无任何段时回 '' 而非 []:engine 对 [] 判真会照发一个空 system
|
|
51
|
+
return blocks.length > 0 ? blocks : '';
|
|
39
52
|
}
|
|
40
53
|
}
|
|
41
54
|
//# sourceMappingURL=systemPrompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"systemPrompt.js","sourceRoot":"","sources":["../../src/runtime/systemPrompt.ts"],"names":[],"mappings":"AAsBA,MAAM,OAAO,YAAY;IACf,QAAQ,GAAoB,EAAE,CAAC;IAEvC;;;;;OAKG;IACM,OAAO,CAAU;IAE1B,YAAY,IAAI,GAAwB,EAAE;QACxC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAC9B,CAAC;IAID,GAAG,CAAC,aAAqC,EAAE,IAAa,EAAE,MAAM,GAAG,IAAI;QACrE,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE,CAAC;YACtC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACxE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,QAAQ;aACjB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC;aACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClB,IAAI,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,0DAA0D;IAC1D,KAAK,CAAC,IAAI,GAAwB,EAAE;QAClC,MAAM,EAAE,KAAK,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAEpF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/D,CAAC;QAED,4DAA4D;QAC5D,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvD,MAAM,MAAM,GAAsB,EAAE,CAAC;QACrC,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,UAAU;gBAChB,aAAa,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;aACrC,CAAC,CAAC;QACL,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,8CAA8C;QAC9C,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACzC,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":""}
|
|
@@ -20,10 +20,25 @@ export declare class FileTaskStore implements TaskStore {
|
|
|
20
20
|
private readonly byKey;
|
|
21
21
|
constructor(file: string);
|
|
22
22
|
private load;
|
|
23
|
+
/**
|
|
24
|
+
* 尾部残行自愈:文件不以 `\n` 结尾说明最后一次 append 写残了(进程被杀、磁盘满、断电)。
|
|
25
|
+
* 不处理的话后续 append 会把新记录**粘在残行尾部**——两行并一行,重启后两条一起丢。
|
|
26
|
+
* - 残行本身是完整记录(只是丢了换行)→ 补一个换行,记录不丢;
|
|
27
|
+
* - 残行是半截 JSON → 截到最后一个换行处,之后 append 从干净的边界开始。
|
|
28
|
+
*/
|
|
29
|
+
private healTail;
|
|
23
30
|
private append;
|
|
24
31
|
save(rec: TaskRecord): void;
|
|
25
32
|
get(taskId: string): TaskRecord | undefined;
|
|
26
33
|
byIdempotency(key: string): TaskRecord | undefined;
|
|
27
34
|
list(): TaskRecord[];
|
|
28
35
|
clear(): void;
|
|
36
|
+
/**
|
|
37
|
+
* 压实日志:把「一 task 一行」的最新快照整体重写回文件,丢弃同一 task 的历史覆写行。
|
|
38
|
+
*
|
|
39
|
+
* append-only 的 JSONL 每 save 一次就追加一行,**容量随 save 次数线性增长**(构造期
|
|
40
|
+
* load 也全量读回)——长期运行的宿主迟早要压。本方法是 TaskStore 接口之外的能力,
|
|
41
|
+
* 由宿主按需周期性调用(如低频 cron);压实时文件短暂不含历史行,但内存态不受影响。
|
|
42
|
+
*/
|
|
43
|
+
compact(): void;
|
|
29
44
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync,
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, rmSync, truncateSync, writeFileSync, } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
/**
|
|
4
4
|
* Agentia —— 文件宿主 TaskStore(spec §6.6:异步耐久 = 换宿主不换语义)。
|
|
@@ -33,6 +33,7 @@ export class FileTaskStore {
|
|
|
33
33
|
catch {
|
|
34
34
|
return; // 读失败按空宿主启动(宿主可另行告警)
|
|
35
35
|
}
|
|
36
|
+
this.healTail(raw);
|
|
36
37
|
for (const line of raw.split('\n')) {
|
|
37
38
|
if (!line.trim())
|
|
38
39
|
continue;
|
|
@@ -49,6 +50,35 @@ export class FileTaskStore {
|
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* 尾部残行自愈:文件不以 `\n` 结尾说明最后一次 append 写残了(进程被杀、磁盘满、断电)。
|
|
55
|
+
* 不处理的话后续 append 会把新记录**粘在残行尾部**——两行并一行,重启后两条一起丢。
|
|
56
|
+
* - 残行本身是完整记录(只是丢了换行)→ 补一个换行,记录不丢;
|
|
57
|
+
* - 残行是半截 JSON → 截到最后一个换行处,之后 append 从干净的边界开始。
|
|
58
|
+
*/
|
|
59
|
+
healTail(raw) {
|
|
60
|
+
if (!raw || raw.endsWith('\n'))
|
|
61
|
+
return;
|
|
62
|
+
const cut = raw.lastIndexOf('\n') + 1; // 最后一个完整行的末尾(字符下标)
|
|
63
|
+
const tail = raw.slice(cut);
|
|
64
|
+
let complete = false;
|
|
65
|
+
try {
|
|
66
|
+
const rec = JSON.parse(tail);
|
|
67
|
+
complete = !!rec && typeof rec.taskId === 'string';
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
complete = false;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
if (complete)
|
|
74
|
+
appendFileSync(this.file, '\n'); // 记录是完整的,只差换行
|
|
75
|
+
else
|
|
76
|
+
truncateSync(this.file, Buffer.byteLength(raw.slice(0, cut), 'utf8'));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// 修不了就照旧读:能解析的行照常入内存,损坏行由下面的 parse 守卫跳过
|
|
80
|
+
}
|
|
81
|
+
}
|
|
52
82
|
append(rec) {
|
|
53
83
|
mkdirSync(dirname(this.file), { recursive: true });
|
|
54
84
|
writeFileSync(this.file, `${JSON.stringify(rec)}\n`, { flag: 'a' });
|
|
@@ -75,5 +105,17 @@ export class FileTaskStore {
|
|
|
75
105
|
if (existsSync(this.file))
|
|
76
106
|
rmSync(this.file);
|
|
77
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* 压实日志:把「一 task 一行」的最新快照整体重写回文件,丢弃同一 task 的历史覆写行。
|
|
110
|
+
*
|
|
111
|
+
* append-only 的 JSONL 每 save 一次就追加一行,**容量随 save 次数线性增长**(构造期
|
|
112
|
+
* load 也全量读回)——长期运行的宿主迟早要压。本方法是 TaskStore 接口之外的能力,
|
|
113
|
+
* 由宿主按需周期性调用(如低频 cron);压实时文件短暂不含历史行,但内存态不受影响。
|
|
114
|
+
*/
|
|
115
|
+
compact() {
|
|
116
|
+
mkdirSync(dirname(this.file), { recursive: true });
|
|
117
|
+
const body = [...this.byTask.values()].map((r) => `${JSON.stringify(r)}\n`).join('');
|
|
118
|
+
writeFileSync(this.file, body);
|
|
119
|
+
}
|
|
78
120
|
}
|
|
79
121
|
//# sourceMappingURL=fsStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fsStore.js","sourceRoot":"","sources":["../../src/store/fsStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,7 @@ 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`(若设)带 EX 过期,见该选项。
|
|
12
13
|
*
|
|
13
14
|
* 语义对照 FileTaskStore / SqliteTaskStore:save 覆写、byIdempotency 取最近、
|
|
14
15
|
* clear 清空本前缀全部 key。差异在 list 序:Redis 本身无序,按 createdAt
|
|
@@ -17,10 +18,20 @@ import type { TaskRecord, TaskStore } from './store.js';
|
|
|
17
18
|
*
|
|
18
19
|
* 前提:**单宿主写者**。save 是两次 SET(记录 + 幂等索引),未走 MULTI 事务;
|
|
19
20
|
* 多写者并发下同键写可能交错,跨进程协调属部署层职责(与 FileTaskStore 一致)。
|
|
21
|
+
*
|
|
22
|
+
* 注意(load-bearing):AsyncRunner 的延迟幂等去重依赖本实现「先 SET 记录、
|
|
23
|
+
* await 后再 SET idem 索引」的写入顺序(见 async.ts #execute 注释)——若改为
|
|
24
|
+
* 先写索引或 MULTI 事务,需同步复核该去重路径。
|
|
20
25
|
*/
|
|
26
|
+
/** SET 选项(ioredis / node-redis 的 `EX` 形态;仅 ttlSeconds 用到) */
|
|
27
|
+
export interface RedisSetOptions {
|
|
28
|
+
/** 过期秒数(EX);<= 0 视为不过期 */
|
|
29
|
+
EX?: number;
|
|
30
|
+
}
|
|
21
31
|
export interface RedisLike {
|
|
22
32
|
get(key: string): Promise<string | null>;
|
|
23
|
-
|
|
33
|
+
/** opts 为可选第三参(ioredis / node-redis 均兼容);老 fake 只实现两参也照常工作 */
|
|
34
|
+
set(key: string, value: string, opts?: RedisSetOptions): Promise<unknown>;
|
|
24
35
|
/** 单键删除(ioredis / node-redis 的公共最小面;批量清理由多次单删组成) */
|
|
25
36
|
del(key: string): Promise<unknown>;
|
|
26
37
|
/** 模式枚举(node-redis / ioredis 均有);与 scanIterator 至少提供其一 */
|
|
@@ -37,10 +48,19 @@ export interface RedisLike {
|
|
|
37
48
|
export interface RedisTaskStoreOptions {
|
|
38
49
|
/** key 前缀,缺省 'agentia:' */
|
|
39
50
|
prefix?: string;
|
|
51
|
+
/**
|
|
52
|
+
* 每条记录的过期秒数;缺省不设(永不过期)。
|
|
53
|
+
*
|
|
54
|
+
* 不设时任务记录(含完整 trace,可能很大)永久驻留,`list()` 的全库 SCAN 也
|
|
55
|
+
* 无界增长。设了之后**任务的查询窗口就是 TTL**:过期即 404,调用方需在窗口内
|
|
56
|
+
* 取走结果。<= 0 视为不设。
|
|
57
|
+
*/
|
|
58
|
+
ttlSeconds?: number;
|
|
40
59
|
}
|
|
41
60
|
export declare class RedisTaskStore implements TaskStore {
|
|
42
61
|
private readonly client;
|
|
43
62
|
private readonly prefix;
|
|
63
|
+
private readonly ttlSeconds;
|
|
44
64
|
constructor(client: RedisLike, opts?: RedisTaskStoreOptions);
|
|
45
65
|
private taskKey;
|
|
46
66
|
private idemKey;
|
|
@@ -49,6 +69,8 @@ export declare class RedisTaskStore implements TaskStore {
|
|
|
49
69
|
byIdempotency(key: string): Promise<TaskRecord | undefined>;
|
|
50
70
|
list(): Promise<TaskRecord[]>;
|
|
51
71
|
clear(): Promise<void>;
|
|
72
|
+
/** `${prefix}task:*` —— prefix 里的 glob 元字符必须转义,否则会被当模式解释 */
|
|
73
|
+
private taskPattern;
|
|
52
74
|
/** 按键枚举:优先 scanIterator,缺则退回 keys(pattern) */
|
|
53
75
|
private enumerate;
|
|
54
76
|
}
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
export class RedisTaskStore {
|
|
2
2
|
client;
|
|
3
3
|
prefix;
|
|
4
|
+
ttlSeconds;
|
|
4
5
|
constructor(client, opts = {}) {
|
|
5
6
|
this.client = client;
|
|
6
7
|
if (!client.scanIterator && !client.keys) {
|
|
7
8
|
throw new Error('RedisTaskStore 需要 client 提供 scanIterator 或 keys 之一(list/clear 依赖按键枚举)');
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
const prefix = opts.prefix ?? 'agentia:';
|
|
11
|
+
if (!prefix) {
|
|
12
|
+
// 空前缀 = 无命名空间:clear() 的 `${prefix}*` 会 SCAN/DEL 整个库
|
|
13
|
+
throw new Error('RedisTaskStore 的 prefix 不能为空串(clear 会清空整个库)');
|
|
14
|
+
}
|
|
15
|
+
this.prefix = prefix;
|
|
16
|
+
const ttl = opts.ttlSeconds ?? 0;
|
|
17
|
+
if (!(ttl >= 0)) {
|
|
18
|
+
// !(ttl >= 0) 同时拦 NaN —— NaN 会让 `ttl > 0` 恒假,静默把 TTL 关掉
|
|
19
|
+
throw new Error(`RedisTaskStore 的 ttlSeconds 必须为 ≥ 0 的数(0 = 不设 TTL),收到 ${opts.ttlSeconds}`);
|
|
20
|
+
}
|
|
21
|
+
this.ttlSeconds = ttl;
|
|
10
22
|
}
|
|
11
23
|
taskKey(taskId) {
|
|
12
24
|
return `${this.prefix}task:${taskId}`;
|
|
@@ -15,9 +27,12 @@ export class RedisTaskStore {
|
|
|
15
27
|
return `${this.prefix}idem:${key}`;
|
|
16
28
|
}
|
|
17
29
|
async save(rec) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
const opts = this.ttlSeconds > 0 ? { EX: this.ttlSeconds } : undefined;
|
|
31
|
+
await this.client.set(this.taskKey(rec.taskId), JSON.stringify(rec), opts);
|
|
32
|
+
// 每次覆写都刷新 TTL:任务的查询窗口从「最后一次状态推进」起算,而不是创建时刻
|
|
33
|
+
if (rec.idempotencyKey) {
|
|
34
|
+
await this.client.set(this.idemKey(rec.idempotencyKey), rec.taskId, opts);
|
|
35
|
+
}
|
|
21
36
|
}
|
|
22
37
|
async get(taskId) {
|
|
23
38
|
return parseRecord(await this.client.get(this.taskKey(taskId)));
|
|
@@ -27,7 +42,7 @@ export class RedisTaskStore {
|
|
|
27
42
|
return taskId ? this.get(taskId) : undefined;
|
|
28
43
|
}
|
|
29
44
|
async list() {
|
|
30
|
-
const keys = await this.enumerate(
|
|
45
|
+
const keys = await this.enumerate(this.taskPattern());
|
|
31
46
|
const recs = [];
|
|
32
47
|
for (const k of keys) {
|
|
33
48
|
const rec = parseRecord(await this.client.get(k));
|
|
@@ -38,10 +53,14 @@ export class RedisTaskStore {
|
|
|
38
53
|
return recs;
|
|
39
54
|
}
|
|
40
55
|
async clear() {
|
|
41
|
-
const keys = await this.enumerate(`${this.prefix}*`);
|
|
56
|
+
const keys = await this.enumerate(`${escapeGlob(this.prefix)}*`);
|
|
42
57
|
for (const k of keys)
|
|
43
58
|
await this.client.del(k);
|
|
44
59
|
}
|
|
60
|
+
/** `${prefix}task:*` —— prefix 里的 glob 元字符必须转义,否则会被当模式解释 */
|
|
61
|
+
taskPattern() {
|
|
62
|
+
return `${escapeGlob(this.prefix)}task:*`;
|
|
63
|
+
}
|
|
45
64
|
/** 按键枚举:优先 scanIterator,缺则退回 keys(pattern) */
|
|
46
65
|
async enumerate(pattern) {
|
|
47
66
|
if (this.client.scanIterator) {
|
|
@@ -57,6 +76,14 @@ export class RedisTaskStore {
|
|
|
57
76
|
return this.client.keys(pattern);
|
|
58
77
|
}
|
|
59
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* 转义 Redis glob 元字符(`\ * ? [ ]`)—— 用于把**字面前缀**拼进 MATCH 模式。
|
|
81
|
+
* 不转义时,prefix 含 `[` 等字符会让模式被解释成字符类(如 `app[1]:*` 匹配不到
|
|
82
|
+
* 字面 `app[1]:`),list/clear 静默查错 key。
|
|
83
|
+
*/
|
|
84
|
+
function escapeGlob(s) {
|
|
85
|
+
return s.replace(/[\\*?[\]]/g, '\\$&');
|
|
86
|
+
}
|
|
60
87
|
/** 解析整行 JSON;单条损坏按缺失处理,不整库崩(与 FileTaskStore 对齐) */
|
|
61
88
|
function parseRecord(raw) {
|
|
62
89
|
if (!raw)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redisStore.js","sourceRoot":"","sources":["../../src/store/redisStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
// node:sqlite 是 Node ≥22.5 才有的内置模块。这里**延迟加载**而非顶层静态 import:
|
|
3
|
+
// 顶层 import 会让**整个包**在未提供该模块的运行时「加载即崩」——`src/index.ts` 对
|
|
4
|
+
// SqliteTaskStore 是 eager 再导出,于是连不用 SQLite 的用户也被殃及。延迟到真正构造
|
|
5
|
+
// store 时才要求它,并给出可操作的报错(而不是一句 ERR_UNKNOWN_BUILTIN_MODULE)。
|
|
6
|
+
const requireBuiltin = createRequire(import.meta.url);
|
|
7
|
+
function loadDatabaseSync() {
|
|
8
|
+
try {
|
|
9
|
+
return requireBuiltin('node:sqlite').DatabaseSync;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
throw new Error('SqliteTaskStore 需要 Node ≥ 22.5(依赖内置 node:sqlite 模块)');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
2
15
|
/**
|
|
3
16
|
* Agentia —— SQLite 宿主 TaskStore(spec §6.6:异步耐久 = 换宿主不换语义,roadmap R3)。
|
|
4
17
|
*
|
|
@@ -14,11 +27,19 @@ import { DatabaseSync } from 'node:sqlite';
|
|
|
14
27
|
export class SqliteTaskStore {
|
|
15
28
|
db;
|
|
16
29
|
constructor(path) {
|
|
30
|
+
const DatabaseSync = loadDatabaseSync();
|
|
17
31
|
this.db = new DatabaseSync(path);
|
|
18
32
|
if (path !== ':memory:') {
|
|
19
33
|
// WAL:读写不互斥,多进程共库的基础(内存库不支持,跳过)
|
|
20
34
|
this.db.exec('PRAGMA journal_mode = WAL');
|
|
21
35
|
}
|
|
36
|
+
// busy_timeout:写事务争用时不立即报 SQLITE_BUSY,而是等待至多 5s 再重试。
|
|
37
|
+
// 没有它,多进程共库时第二个写者立即失败 —— 而 AsyncRunner 的 #safeSave 会把
|
|
38
|
+
// save 失败静默吞掉(不遮罩主流程),结果是任务记录无声丢失。
|
|
39
|
+
// 内存库同样支持该 pragma(且无争用),无需跳过。
|
|
40
|
+
this.db.exec('PRAGMA busy_timeout = 5000');
|
|
41
|
+
// status 列是 json 内 status 的反规范化副本:本 store 的 SELECT 只读 json,
|
|
42
|
+
// 该列专供外部/DBA 直接按状态统计(如 SELECT status, count(*) FROM tasks GROUP BY status)。
|
|
22
43
|
this.db.exec(`
|
|
23
44
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
24
45
|
task_id TEXT PRIMARY KEY,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqliteStore.js","sourceRoot":"","sources":["../../src/store/sqliteStore.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,4DAA4D;AAC5D,yDAAyD;AACzD,2DAA2D;AAC3D,2DAA2D;AAC3D,MAAM,cAAc,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAEtD,SAAS,gBAAgB;IACvB,IAAI,CAAC;QACH,OAAQ,cAAc,CAAC,aAAa,CAAkC,CAAC,YAAY,CAAC;IACtF,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,eAAe;IACT,EAAE,CAAe;IAElC,YAAY,IAAY;QACtB,MAAM,YAAY,GAAG,gBAAgB,EAAE,CAAC;QACxC,IAAI,CAAC,EAAE,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;YACxB,gCAAgC;YAChC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC5C,CAAC;QACD,qDAAqD;QACrD,sDAAsD;QACtD,mCAAmC;QACnC,8BAA8B;QAC9B,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC3C,4DAA4D;QAC5D,4EAA4E;QAC5E,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;KAOZ,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;IAC7F,CAAC;IAED,IAAI,CAAC,GAAe;QAClB,IAAI,CAAC,EAAE;aACJ,OAAO,CACN,2FAA2F,CAC5F;aACA,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,cAAc,IAAI,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAClF,CAAC;IAED,GAAG,CAAC,MAAc;QAChB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC,GAAG,CAAC,MAAM,CAErE,CAAC;QACd,OAAO,GAAG,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,CAAC;IAED,aAAa,CAAC,GAAW;QACvB,0DAA0D;QAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CAAC,8EAA8E,CAAC;aACvF,GAAG,CAAC,GAAG,CAAiC,CAAC;QAC5C,OAAO,GAAG,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,CAAC;IAED,IAAI;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC,GAAG,EAEvE,CAAC;QACH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAe,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK;QACH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACpC,CAAC;IAED,yCAAyC;IACzC,KAAK;QACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentRunResult } from '../engine/types.js';
|
|
2
2
|
import type { SpanError } from '../core/trace.js';
|
|
3
|
-
import type { RunStatus } from '
|
|
4
|
-
import type { RunSpec } from '
|
|
3
|
+
import type { RunStatus } from '../core/run.js';
|
|
4
|
+
import type { RunSpec } from '../engine/spec.js';
|
|
5
5
|
/**
|
|
6
6
|
* Agentia —— run 存储(spec §6.6:异步耐久 = 换宿主不换语义)。
|
|
7
7
|
* 宿主接口:队列/DB 版只需实现 TaskStore,AsyncRunner/调度逻辑不变。
|
|
@@ -14,6 +14,10 @@ import type { RunSpec } from './spec.js';
|
|
|
14
14
|
*/
|
|
15
15
|
/** 同步或异步返回值:await 化后两种实现统一(同步值 await 即自身) */
|
|
16
16
|
export type MaybePromise<T> = T | Promise<T>;
|
|
17
|
+
/** store 返回值可能是同步值或 Promise —— 区分用(同步门面只在同步值上工作) */
|
|
18
|
+
export declare function isThenable<T>(x: MaybePromise<T>): x is Promise<T>;
|
|
19
|
+
/** 生成任务 id(与具体 store 实现无关;AsyncRunner 等统一从这里取) */
|
|
20
|
+
export declare function nextTaskId(): string;
|
|
17
21
|
export interface TaskRecord {
|
|
18
22
|
taskId: string;
|
|
19
23
|
status: RunStatus;
|
|
@@ -24,6 +28,12 @@ export interface TaskRecord {
|
|
|
24
28
|
createdAt: number;
|
|
25
29
|
startedAt?: number;
|
|
26
30
|
finishedAt?: number;
|
|
31
|
+
/**
|
|
32
|
+
* 认领该任务的进程标识(AsyncRunner 在 submit/重派时写入)。
|
|
33
|
+
* 多进程共用一个 store 时,`resumePending` 靠它跳过「自己进程的记录」——
|
|
34
|
+
* 本进程的记录一定还在内存里跑,重派只会让它跑两遍。
|
|
35
|
+
*/
|
|
36
|
+
ownerId?: string;
|
|
27
37
|
result?: AgentRunResult;
|
|
28
38
|
error?: SpanError;
|
|
29
39
|
}
|
|
@@ -38,10 +48,22 @@ export interface TaskStore {
|
|
|
38
48
|
export declare class InMemoryTaskStore implements TaskStore {
|
|
39
49
|
private readonly byTask;
|
|
40
50
|
private readonly byKey;
|
|
51
|
+
/** 记录条数上限;Infinity = 不限(缺省,保持旧行为) */
|
|
52
|
+
private readonly maxRecords;
|
|
53
|
+
/**
|
|
54
|
+
* `maxRecords` 给长期运行的宿主一个内存闸门:超过上限时从最旧的**已终态**记录起
|
|
55
|
+
* 淘汰(queued/running 在飞的记录永不淘汰)。缺省 Infinity = 不淘汰 ——
|
|
56
|
+
* 每条记录含完整 trace(可能很大),长跑宿主(尤其 createHttpHandler 的缺省
|
|
57
|
+
* runner)应显式设一个上限或换耐久 store。
|
|
58
|
+
*/
|
|
59
|
+
constructor(opts?: {
|
|
60
|
+
maxRecords?: number;
|
|
61
|
+
});
|
|
41
62
|
save(rec: TaskRecord): void;
|
|
63
|
+
/** 超过上限时按插入序淘汰已终态记录(在飞记录跳过,避免丢正在跑的任务) */
|
|
64
|
+
private evict;
|
|
42
65
|
get(taskId: string): TaskRecord | undefined;
|
|
43
66
|
byIdempotency(key: string): TaskRecord | undefined;
|
|
44
67
|
list(): TaskRecord[];
|
|
45
68
|
clear(): void;
|
|
46
|
-
static nextTaskId(): string;
|
|
47
69
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
/** store 返回值可能是同步值或 Promise —— 区分用(同步门面只在同步值上工作) */
|
|
3
|
+
export function isThenable(x) {
|
|
4
|
+
return !!x && typeof x.then === 'function';
|
|
5
|
+
}
|
|
6
|
+
/** 生成任务 id(与具体 store 实现无关;AsyncRunner 等统一从这里取) */
|
|
7
|
+
export function nextTaskId() {
|
|
8
|
+
return `task_${randomUUID()}`;
|
|
9
|
+
}
|
|
10
|
+
export class InMemoryTaskStore {
|
|
11
|
+
byTask = new Map();
|
|
12
|
+
byKey = new Map(); // idempotencyKey → taskId
|
|
13
|
+
/** 记录条数上限;Infinity = 不限(缺省,保持旧行为) */
|
|
14
|
+
maxRecords;
|
|
15
|
+
/**
|
|
16
|
+
* `maxRecords` 给长期运行的宿主一个内存闸门:超过上限时从最旧的**已终态**记录起
|
|
17
|
+
* 淘汰(queued/running 在飞的记录永不淘汰)。缺省 Infinity = 不淘汰 ——
|
|
18
|
+
* 每条记录含完整 trace(可能很大),长跑宿主(尤其 createHttpHandler 的缺省
|
|
19
|
+
* runner)应显式设一个上限或换耐久 store。
|
|
20
|
+
*/
|
|
21
|
+
constructor(opts = {}) {
|
|
22
|
+
const max = opts.maxRecords ?? Number.POSITIVE_INFINITY;
|
|
23
|
+
if (max !== Number.POSITIVE_INFINITY && !(max > 0)) {
|
|
24
|
+
throw new Error(`InMemoryTaskStore 的 maxRecords 必须为正数或 Infinity,收到 ${opts.maxRecords}`);
|
|
25
|
+
}
|
|
26
|
+
this.maxRecords = max;
|
|
27
|
+
}
|
|
28
|
+
save(rec) {
|
|
29
|
+
this.byTask.set(rec.taskId, rec);
|
|
30
|
+
if (rec.idempotencyKey)
|
|
31
|
+
this.byKey.set(rec.idempotencyKey, rec.taskId);
|
|
32
|
+
if (this.byTask.size > this.maxRecords)
|
|
33
|
+
this.evict();
|
|
34
|
+
}
|
|
35
|
+
/** 超过上限时按插入序淘汰已终态记录(在飞记录跳过,避免丢正在跑的任务) */
|
|
36
|
+
evict() {
|
|
37
|
+
for (const [taskId, rec] of this.byTask) {
|
|
38
|
+
if (this.byTask.size <= this.maxRecords)
|
|
39
|
+
break;
|
|
40
|
+
if (rec.status === 'queued' || rec.status === 'running')
|
|
41
|
+
continue;
|
|
42
|
+
this.byTask.delete(taskId);
|
|
43
|
+
if (rec.idempotencyKey && this.byKey.get(rec.idempotencyKey) === taskId) {
|
|
44
|
+
this.byKey.delete(rec.idempotencyKey);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
get(taskId) {
|
|
49
|
+
return this.byTask.get(taskId);
|
|
50
|
+
}
|
|
51
|
+
byIdempotency(key) {
|
|
52
|
+
const taskId = this.byKey.get(key);
|
|
53
|
+
return taskId ? this.byTask.get(taskId) : undefined;
|
|
54
|
+
}
|
|
55
|
+
list() {
|
|
56
|
+
return [...this.byTask.values()];
|
|
57
|
+
}
|
|
58
|
+
clear() {
|
|
59
|
+
this.byTask.clear();
|
|
60
|
+
this.byKey.clear();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/store/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoBzC,oDAAoD;AACpD,MAAM,UAAU,UAAU,CAAI,CAAkB;IAC9C,OAAO,CAAC,CAAC,CAAC,IAAI,OAAQ,CAAgB,CAAC,IAAI,KAAK,UAAU,CAAC;AAC7D,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,UAAU;IACxB,OAAO,QAAQ,UAAU,EAAE,EAAE,CAAC;AAChC,CAAC;AA+BD,MAAM,OAAO,iBAAiB;IACX,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IACvC,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,0BAA0B;IAC9E,qCAAqC;IACpB,UAAU,CAAS;IAEpC;;;;;OAKG;IACH,YAAY,IAAI,GAA4B,EAAE;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,iBAAiB,CAAC;QACxD,IAAI,GAAG,KAAK,MAAM,CAAC,iBAAiB,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CACb,qDAAqD,IAAI,CAAC,UAAU,EAAE,CACvE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;IACxB,CAAC;IAED,IAAI,CAAC,GAAe;QAClB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACjC,IAAI,GAAG,CAAC,cAAc;YAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACvE,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IACvD,CAAC;IAED,yCAAyC;IACjC,KAAK;QACX,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACxC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU;gBAAE,MAAM;YAC/C,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;gBAAE,SAAS;YAClE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3B,IAAI,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,MAAM,EAAE,CAAC;gBACxE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;IACH,CAAC;IAED,GAAG,CAAC,MAAc;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IACD,aAAa,CAAC,GAAW;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtD,CAAC;IACD,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACnC,CAAC;IACD,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF"}
|