@actiondock/core 2.17.0 → 2.19.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.
@@ -22,6 +22,13 @@ export declare function fetchRemoteRuns(serverUrl: string, token?: string, optio
22
22
  status?: string;
23
23
  actionId?: string;
24
24
  packageId?: string;
25
+ channel?: string;
26
+ view?: string;
27
+ since?: string;
28
+ until?: string;
29
+ rootRunId?: string;
30
+ parentRunId?: string;
31
+ withTotal?: boolean;
25
32
  intent?: string;
26
33
  limit?: number;
27
34
  offset?: number;
@@ -30,6 +37,8 @@ export declare function fetchRemoteRuns(serverUrl: string, token?: string, optio
30
37
  ok: boolean;
31
38
  total?: number;
32
39
  items: RunRecord[];
40
+ limit?: number;
41
+ offset?: number;
33
42
  }>;
34
43
  /**
35
44
  * 清空远端运行记录(可按包、Action、状态、保留时长、保留数量过滤清除范围)。
@@ -35,6 +35,13 @@ export async function fetchRemoteRuns(serverUrl, token, options) {
35
35
  status: options?.status,
36
36
  actionId: options?.actionId,
37
37
  packageId: options?.packageId,
38
+ channel: options?.channel,
39
+ view: options?.view,
40
+ since: options?.since,
41
+ until: options?.until,
42
+ rootRunId: options?.rootRunId,
43
+ parentRunId: options?.parentRunId,
44
+ withTotal: options?.withTotal ? true : undefined,
38
45
  intent: options?.intent,
39
46
  limit: options?.limit,
40
47
  offset: options?.offset,
package/dist/host/host.js CHANGED
@@ -906,6 +906,12 @@ export class DefaultActionDockHost {
906
906
  const batch = await runtime.listRuns({
907
907
  actionId: query.actionId,
908
908
  status: query.status,
909
+ channel: query.channel,
910
+ view: query.view,
911
+ since: query.since,
912
+ until: query.until,
913
+ rootRunId: query.rootRunId,
914
+ parentRunId: query.parentRunId,
909
915
  requestIds: query.requestIds,
910
916
  limit: BATCH_SIZE,
911
917
  offset: batchOffset,
@@ -921,8 +927,21 @@ export class DefaultActionDockHost {
921
927
  ...r,
922
928
  packageId: r.packageId || runtime.packageId,
923
929
  }));
924
- const filtered = filterByIntent(normalized, intentStr, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
925
- matchedRecords.push(...filtered);
930
+ let filtered = normalized;
931
+ if (query.channel)
932
+ filtered = filtered.filter((r) => r.origin?.channel === query.channel);
933
+ if (query.view)
934
+ filtered = filtered.filter((r) => r.origin?.viewName === query.view);
935
+ if (query.since)
936
+ filtered = filtered.filter((r) => r.startedAt >= query.since);
937
+ if (query.until)
938
+ filtered = filtered.filter((r) => r.startedAt <= query.until);
939
+ if (query.rootRunId)
940
+ filtered = filtered.filter((r) => r.rootRunId === query.rootRunId);
941
+ if (query.parentRunId)
942
+ filtered = filtered.filter((r) => r.parentRunId === query.parentRunId);
943
+ const intentMatched = filterByIntent(filtered, intentStr, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
944
+ matchedRecords.push(...intentMatched);
926
945
  if (matchedRecords.length >= neededCount) {
927
946
  break;
928
947
  }
@@ -944,6 +963,12 @@ export class DefaultActionDockHost {
944
963
  const batch = await runtime.listRuns({
945
964
  actionId: query.actionId,
946
965
  status: query.status,
966
+ channel: query.channel,
967
+ view: query.view,
968
+ since: query.since,
969
+ until: query.until,
970
+ rootRunId: query.rootRunId,
971
+ parentRunId: query.parentRunId,
947
972
  requestIds: query.requestIds,
948
973
  limit: BATCH_SIZE,
949
974
  offset: batchOffset,
@@ -959,8 +984,21 @@ export class DefaultActionDockHost {
959
984
  ...r,
960
985
  packageId: r.packageId || runtime.packageId,
961
986
  }));
962
- const filtered = filterByIntent(normalized, intentStr, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
963
- matchedRecords.push(...filtered);
987
+ let filtered = normalized;
988
+ if (query.channel)
989
+ filtered = filtered.filter((r) => r.origin?.channel === query.channel);
990
+ if (query.view)
991
+ filtered = filtered.filter((r) => r.origin?.viewName === query.view);
992
+ if (query.since)
993
+ filtered = filtered.filter((r) => r.startedAt >= query.since);
994
+ if (query.until)
995
+ filtered = filtered.filter((r) => r.startedAt <= query.until);
996
+ if (query.rootRunId)
997
+ filtered = filtered.filter((r) => r.rootRunId === query.rootRunId);
998
+ if (query.parentRunId)
999
+ filtered = filtered.filter((r) => r.parentRunId === query.parentRunId);
1000
+ const intentMatched = filterByIntent(filtered, intentStr, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
1001
+ matchedRecords.push(...intentMatched);
964
1002
  if (batch.length < BATCH_SIZE) {
965
1003
  break;
966
1004
  }
@@ -1043,6 +1081,12 @@ export class DefaultActionDockHost {
1043
1081
  const batch = await runtime.listRuns({
1044
1082
  actionId: query.actionId,
1045
1083
  status: query.status,
1084
+ channel: query.channel,
1085
+ view: query.view,
1086
+ since: query.since,
1087
+ until: query.until,
1088
+ rootRunId: query.rootRunId,
1089
+ parentRunId: query.parentRunId,
1046
1090
  requestIds: query.requestIds,
1047
1091
  limit: BATCH_SIZE,
1048
1092
  offset: batchOffset,
@@ -1058,8 +1102,21 @@ export class DefaultActionDockHost {
1058
1102
  ...r,
1059
1103
  packageId: r.packageId || runtime.packageId,
1060
1104
  }));
1061
- const filtered = filterByIntent(normalized, intentStr, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
1062
- count += filtered.length;
1105
+ let filtered = normalized;
1106
+ if (query.channel)
1107
+ filtered = filtered.filter((r) => r.origin?.channel === query.channel);
1108
+ if (query.view)
1109
+ filtered = filtered.filter((r) => r.origin?.viewName === query.view);
1110
+ if (query.since)
1111
+ filtered = filtered.filter((r) => r.startedAt >= query.since);
1112
+ if (query.until)
1113
+ filtered = filtered.filter((r) => r.startedAt <= query.until);
1114
+ if (query.rootRunId)
1115
+ filtered = filtered.filter((r) => r.rootRunId === query.rootRunId);
1116
+ if (query.parentRunId)
1117
+ filtered = filtered.filter((r) => r.parentRunId === query.parentRunId);
1118
+ const intentMatched = filterByIntent(filtered, intentStr, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
1119
+ count += intentMatched.length;
1063
1120
  if (batch.length < BATCH_SIZE) {
1064
1121
  break;
1065
1122
  }
@@ -1079,6 +1136,12 @@ export class DefaultActionDockHost {
1079
1136
  const all = await runtime.listRuns({
1080
1137
  actionId: query?.actionId,
1081
1138
  status: query?.status,
1139
+ channel: query?.channel,
1140
+ view: query?.view,
1141
+ since: query?.since,
1142
+ until: query?.until,
1143
+ rootRunId: query?.rootRunId,
1144
+ parentRunId: query?.parentRunId,
1082
1145
  requestIds: query?.requestIds,
1083
1146
  limit: 100_000,
1084
1147
  signal: effectiveSignal,
@@ -1086,7 +1149,20 @@ export class DefaultActionDockHost {
1086
1149
  if (effectiveSignal?.aborted) {
1087
1150
  throw createAbortError(effectiveSignal.reason);
1088
1151
  }
1089
- total += all.length;
1152
+ let filtered = all;
1153
+ if (query?.channel)
1154
+ filtered = filtered.filter((r) => r.origin?.channel === query.channel);
1155
+ if (query?.view)
1156
+ filtered = filtered.filter((r) => r.origin?.viewName === query.view);
1157
+ if (query?.since)
1158
+ filtered = filtered.filter((r) => r.startedAt >= query.since);
1159
+ if (query?.until)
1160
+ filtered = filtered.filter((r) => r.startedAt <= query.until);
1161
+ if (query?.rootRunId)
1162
+ filtered = filtered.filter((r) => r.rootRunId === query.rootRunId);
1163
+ if (query?.parentRunId)
1164
+ filtered = filtered.filter((r) => r.parentRunId === query.parentRunId);
1165
+ total += filtered.length;
1090
1166
  }
1091
1167
  }
1092
1168
  return total;
@@ -851,13 +851,31 @@ export class DefaultPackageRuntime {
851
851
  if (typeof storage.listRunsByRequestIds !== "function") {
852
852
  return [];
853
853
  }
854
- let records = storage.listRunsByRequestIds(options.requestIds);
854
+ let records = storage.listRunsByRequestIds(options.requestIds, options);
855
855
  if (options.actionId) {
856
856
  records = records.filter((r) => r.actionId === options.actionId);
857
857
  }
858
858
  if (options.status) {
859
859
  records = records.filter((r) => r.status === options.status);
860
860
  }
861
+ if (options.channel) {
862
+ records = records.filter((r) => r.origin?.channel === options.channel);
863
+ }
864
+ if (options.view) {
865
+ records = records.filter((r) => r.origin?.viewName === options.view);
866
+ }
867
+ if (options.since) {
868
+ records = records.filter((r) => r.startedAt >= options.since);
869
+ }
870
+ if (options.until) {
871
+ records = records.filter((r) => r.startedAt <= options.until);
872
+ }
873
+ if (options.rootRunId) {
874
+ records = records.filter((r) => r.rootRunId === options.rootRunId);
875
+ }
876
+ if (options.parentRunId) {
877
+ records = records.filter((r) => r.parentRunId === options.parentRunId);
878
+ }
861
879
  records.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
862
880
  if (typeof options.offset === "number" && options.offset > 0) {
863
881
  records = records.slice(options.offset);
@@ -870,6 +888,12 @@ export class DefaultPackageRuntime {
870
888
  const records = storage.listRuns({
871
889
  actionId: options?.actionId,
872
890
  status: options?.status,
891
+ channel: options?.channel,
892
+ view: options?.view,
893
+ since: options?.since,
894
+ until: options?.until,
895
+ rootRunId: options?.rootRunId,
896
+ parentRunId: options?.parentRunId,
873
897
  limit: options?.limit,
874
898
  offset: options?.offset,
875
899
  });
@@ -897,18 +921,42 @@ export class DefaultPackageRuntime {
897
921
  return storage.countRuns({
898
922
  actionId: options?.actionId,
899
923
  status: options?.status,
924
+ channel: options?.channel,
925
+ view: options?.view,
926
+ since: options?.since,
927
+ until: options?.until,
928
+ rootRunId: options?.rootRunId,
929
+ parentRunId: options?.parentRunId,
900
930
  requestIds: options?.requestIds,
901
931
  });
902
932
  }
903
933
  if (options?.requestIds && options.requestIds.length > 0) {
904
934
  if (typeof storage.listRunsByRequestIds === "function") {
905
- let records = storage.listRunsByRequestIds(options.requestIds);
935
+ let records = storage.listRunsByRequestIds(options.requestIds, options);
906
936
  if (options.actionId) {
907
937
  records = records.filter((r) => r.actionId === options.actionId);
908
938
  }
909
939
  if (options.status) {
910
940
  records = records.filter((r) => r.status === options.status);
911
941
  }
942
+ if (options.channel) {
943
+ records = records.filter((r) => r.origin?.channel === options.channel);
944
+ }
945
+ if (options.view) {
946
+ records = records.filter((r) => r.origin?.viewName === options.view);
947
+ }
948
+ if (options.since) {
949
+ records = records.filter((r) => r.startedAt >= options.since);
950
+ }
951
+ if (options.until) {
952
+ records = records.filter((r) => r.startedAt <= options.until);
953
+ }
954
+ if (options.rootRunId) {
955
+ records = records.filter((r) => r.rootRunId === options.rootRunId);
956
+ }
957
+ if (options.parentRunId) {
958
+ records = records.filter((r) => r.parentRunId === options.parentRunId);
959
+ }
912
960
  return records.length;
913
961
  }
914
962
  return 0;
@@ -916,9 +964,32 @@ export class DefaultPackageRuntime {
916
964
  const records = storage.listRuns({
917
965
  actionId: options?.actionId,
918
966
  status: options?.status,
967
+ channel: options?.channel,
968
+ view: options?.view,
969
+ since: options?.since,
970
+ until: options?.until,
971
+ rootRunId: options?.rootRunId,
972
+ parentRunId: options?.parentRunId,
919
973
  limit: 100_000,
920
974
  });
921
- return records.length;
975
+ let filtered = records;
976
+ if (options?.actionId)
977
+ filtered = filtered.filter((r) => r.actionId === options.actionId);
978
+ if (options?.status)
979
+ filtered = filtered.filter((r) => r.status === options.status);
980
+ if (options?.channel)
981
+ filtered = filtered.filter((r) => r.origin?.channel === options.channel);
982
+ if (options?.view)
983
+ filtered = filtered.filter((r) => r.origin?.viewName === options.view);
984
+ if (options?.since)
985
+ filtered = filtered.filter((r) => r.startedAt >= options.since);
986
+ if (options?.until)
987
+ filtered = filtered.filter((r) => r.startedAt <= options.until);
988
+ if (options?.rootRunId)
989
+ filtered = filtered.filter((r) => r.rootRunId === options.rootRunId);
990
+ if (options?.parentRunId)
991
+ filtered = filtered.filter((r) => r.parentRunId === options.parentRunId);
992
+ return filtered.length;
922
993
  }
923
994
  async clearRuns(options) {
924
995
  this.assertOpen();
@@ -86,10 +86,10 @@ export function initProject(targetDir, options = {}) {
86
86
  node: ">=24.12.0",
87
87
  },
88
88
  dependencies: {
89
- "@actiondock/sdk": "^2.17.0",
89
+ "@actiondock/sdk": "^2.19.0",
90
90
  },
91
91
  devDependencies: {
92
- "@actiondock/testing": "^2.17.0",
92
+ "@actiondock/testing": "^2.19.0",
93
93
  "@types/node": "^22.13.0",
94
94
  "tsx": "^4.19.0",
95
95
  "typescript": "^5.7.0",
@@ -16,6 +16,13 @@ export async function handleRunsRoutes(ctx) {
16
16
  const status = url.searchParams.get("status") || undefined;
17
17
  const actionId = url.searchParams.get("actionId") || undefined;
18
18
  const packageId = url.searchParams.get("packageId") || undefined;
19
+ const channel = url.searchParams.get("channel") || undefined;
20
+ const view = url.searchParams.get("view") || undefined;
21
+ const since = url.searchParams.get("since") || undefined;
22
+ const until = url.searchParams.get("until") || undefined;
23
+ const rootRunId = url.searchParams.get("rootRunId") || undefined;
24
+ const parentRunId = url.searchParams.get("parentRunId") || undefined;
25
+ const withTotal = url.searchParams.get("withTotal") === "true";
19
26
  const intent = url.searchParams.get("intent") || undefined;
20
27
  // requestId 反查:支持 requestId 与 requestIds 重复查询参数,将每一个参数值作为原样字符串保存
21
28
  const requestIds = [
@@ -69,6 +76,13 @@ export async function handleRunsRoutes(ctx) {
69
76
  actionId,
70
77
  status,
71
78
  packageId,
79
+ channel,
80
+ view,
81
+ since,
82
+ until,
83
+ rootRunId,
84
+ parentRunId,
85
+ withTotal,
72
86
  intent,
73
87
  requestIds: requestIds.length > 0 ? requestIds : undefined,
74
88
  signal: req.signal,
@@ -153,6 +167,8 @@ export async function handleRunsRoutes(ctx) {
153
167
  const responseData = {
154
168
  ok: true,
155
169
  items: runs,
170
+ limit,
171
+ offset,
156
172
  };
157
173
  if (typeof total === "number") {
158
174
  responseData.total = total;
@@ -1,3 +1,4 @@
1
+ import type { ActionDockService } from "../service/types.js";
1
2
  import type { ActionDockServerInstance, CoreHttpServerInstance, ServerOptions, ServerTlsOptions } from "./types.js";
2
3
  /**
3
4
  * 规范化主机地址用于拼接 URL。
@@ -12,4 +13,4 @@ export declare function launchHttpServer(port: number, host: string, fetchHandle
12
13
  * 启动 ActionDock 原生轻量级 HTTP 服务端。
13
14
  * 面向 ActionDockService 服务端口,负责中间件流转、认证拦截与路由分发。
14
15
  */
15
- export declare function startActionDockServer(options?: ServerOptions): Promise<ActionDockServerInstance>;
16
+ export declare function startActionDockServer(arg1?: ServerOptions | ActionDockService, arg2?: ServerOptions): Promise<ActionDockServerInstance>;
@@ -40,8 +40,19 @@ export async function launchHttpServer(port, host, fetchHandler, tls) {
40
40
  * 启动 ActionDock 原生轻量级 HTTP 服务端。
41
41
  * 面向 ActionDockService 服务端口,负责中间件流转、认证拦截与路由分发。
42
42
  */
43
- export async function startActionDockServer(options = {}) {
44
- const hostString = options.hostname ?? options.host ?? "127.0.0.1";
43
+ export async function startActionDockServer(arg1, arg2) {
44
+ let options;
45
+ if (arg1 && typeof arg1 === "object" && ("discovery" in arg1 || "execution" in arg1 || "runs" in arg1)) {
46
+ options = { ...(arg2 || {}), service: arg1 };
47
+ }
48
+ else {
49
+ options = arg1 || {};
50
+ }
51
+ const hostString = typeof options.hostname === "string"
52
+ ? options.hostname
53
+ : typeof options.host === "string"
54
+ ? options.host
55
+ : "127.0.0.1";
45
56
  const port = options.port ?? 5177;
46
57
  const host = hostString;
47
58
  const token = options.token;
@@ -259,6 +259,13 @@ export class RemoteActionDockService {
259
259
  packageId: query?.packageId,
260
260
  actionId: query?.actionId,
261
261
  status: query?.status,
262
+ channel: query?.channel,
263
+ view: query?.view,
264
+ since: query?.since,
265
+ until: query?.until,
266
+ rootRunId: query?.rootRunId,
267
+ parentRunId: query?.parentRunId,
268
+ withTotal: query?.withTotal,
262
269
  intent: query?.intent,
263
270
  limit: batchLimit,
264
271
  offset,
@@ -307,6 +314,13 @@ export class RemoteActionDockService {
307
314
  packageId: query?.packageId,
308
315
  actionId: query?.actionId,
309
316
  status: query?.status,
317
+ channel: query?.channel,
318
+ view: query?.view,
319
+ since: query?.since,
320
+ until: query?.until,
321
+ rootRunId: query?.rootRunId,
322
+ parentRunId: query?.parentRunId,
323
+ withTotal: true,
310
324
  intent: query?.intent,
311
325
  limit: 1,
312
326
  offset: 0,
@@ -72,6 +72,18 @@ export interface ListRunsOptions {
72
72
  actionId?: string;
73
73
  /** 运行终态状态筛选 */
74
74
  status?: string;
75
+ /** 运行通道筛选(如 http、mcp) */
76
+ channel?: string;
77
+ /** 服务视图名称筛选(如 default、admin) */
78
+ view?: string;
79
+ /** 起始时间筛选(UTC ISO 8601 或时间戳字符串,匹配 startedAt >= since) */
80
+ since?: string;
81
+ /** 截止时间筛选(UTC ISO 8601 或时间戳字符串,匹配 startedAt <= until) */
82
+ until?: string;
83
+ /** 根运行标识筛选 */
84
+ rootRunId?: string;
85
+ /** 父运行标识筛选 */
86
+ parentRunId?: string;
75
87
  /** 模糊意图检索模式 */
76
88
  intent?: string;
77
89
  /** 幂等请求标识集合(经 idempotency_keys 反查关联运行记录) */
@@ -80,6 +92,8 @@ export interface ListRunsOptions {
80
92
  offset?: number;
81
93
  /** 最大返回记录条数限制 */
82
94
  limit?: number;
95
+ /** 是否需要附带总数统计 */
96
+ withTotal?: boolean;
83
97
  /** 取消信号,用于中止读取请求 */
84
98
  signal?: AbortSignal;
85
99
  /** 包白名单过滤集合(可选) */
@@ -1,5 +1,5 @@
1
1
  import type { Logger, RuntimeError, RunRecord } from "@actiondock/sdk";
2
- import type { SqliteDriver, TerminalRunStatus, RunsRetentionPolicy, IdempotencyRecord, IdempotencyCheckResult } from "./types.js";
2
+ import type { SqliteDriver, TerminalRunStatus, RunsRetentionPolicy, IdempotencyRecord, IdempotencyCheckResult, RunsQueryFilterOptions } from "./types.js";
3
3
  import type { Clock } from "./clock.js";
4
4
  import { type ProcessLivenessProbe } from "./run-liveness.js";
5
5
  export declare class SqliteRunsStore {
@@ -52,28 +52,16 @@ export declare class SqliteRunsStore {
52
52
  * 查询单条运行记录并补齐幂等请求标识关联(详情路径专用)。
53
53
  */
54
54
  getRunWithRequestId(id: string, isClosed: boolean): RunRecord | null;
55
- listRuns(options?: {
56
- actionId?: string;
57
- status?: string;
58
- limit?: number;
59
- offset?: number;
60
- }): RunRecord[];
55
+ listRuns(options?: RunsQueryFilterOptions): RunRecord[];
61
56
  /**
62
57
  * 统计符合过滤条件的真实运行记录总数。
63
58
  * 支持按 actionId、status 以及包含 requestIds 的反查场景统计总数。
64
59
  */
65
- countRuns(options?: {
66
- actionId?: string;
67
- status?: string;
68
- requestIds?: string[];
69
- }, isClosed?: boolean): number;
60
+ countRuns(options?: RunsQueryFilterOptions, isClosed?: boolean): number;
70
61
  /**
71
62
  * 按客户端幂等请求标识反查关联运行记录总数。
72
63
  */
73
- countRunsByRequestIds(isClosed: boolean, requestIds: string[], options?: {
74
- actionId?: string;
75
- status?: string;
76
- }): number;
64
+ countRunsByRequestIds(isClosed: boolean, requestIds: string[], options?: RunsQueryFilterOptions): number;
77
65
  /**
78
66
  * 构造 IN (…) 占位符列表。
79
67
  */
@@ -85,7 +73,7 @@ export declare class SqliteRunsStore {
85
73
  * 当前包,保证多包共库(全局库)时不越界读取其他包的运行记录。
86
74
  * 不按 owner_id 过滤:写入方与查询方分属不同宿主进程,ownerId 必然不同。
87
75
  */
88
- listRunsByRequestIds(isClosed: boolean, requestIds: string[]): RunRecord[];
76
+ listRunsByRequestIds(isClosed: boolean, requestIds: string[], options?: RunsQueryFilterOptions): RunRecord[];
89
77
  /**
90
78
  * 反查当前包范围内运行记录关联的幂等请求标识。
91
79
  *
@@ -244,6 +244,30 @@ export class SqliteRunsStore {
244
244
  sql += " AND status = ?";
245
245
  params.push(options.status);
246
246
  }
247
+ if (options.channel) {
248
+ sql += " AND json_extract(origin_json, '$.channel') = ?";
249
+ params.push(options.channel);
250
+ }
251
+ if (options.view) {
252
+ sql += " AND json_extract(origin_json, '$.viewName') = ?";
253
+ params.push(options.view);
254
+ }
255
+ if (options.since) {
256
+ sql += " AND started_at >= ?";
257
+ params.push(options.since);
258
+ }
259
+ if (options.until) {
260
+ sql += " AND started_at <= ?";
261
+ params.push(options.until);
262
+ }
263
+ if (options.rootRunId) {
264
+ sql += " AND root_run_id = ?";
265
+ params.push(options.rootRunId);
266
+ }
267
+ if (options.parentRunId) {
268
+ sql += " AND parent_run_id = ?";
269
+ params.push(options.parentRunId);
270
+ }
247
271
  sql += " ORDER BY started_at DESC LIMIT ?";
248
272
  params.push(limit);
249
273
  if (typeof options.offset === "number" && options.offset > 0) {
@@ -281,6 +305,30 @@ export class SqliteRunsStore {
281
305
  sql += " AND runs.status = ?";
282
306
  params.push(options.status);
283
307
  }
308
+ if (options.channel) {
309
+ sql += " AND json_extract(runs.origin_json, '$.channel') = ?";
310
+ params.push(options.channel);
311
+ }
312
+ if (options.view) {
313
+ sql += " AND json_extract(runs.origin_json, '$.viewName') = ?";
314
+ params.push(options.view);
315
+ }
316
+ if (options.since) {
317
+ sql += " AND runs.started_at >= ?";
318
+ params.push(options.since);
319
+ }
320
+ if (options.until) {
321
+ sql += " AND runs.started_at <= ?";
322
+ params.push(options.until);
323
+ }
324
+ if (options.rootRunId) {
325
+ sql += " AND runs.root_run_id = ?";
326
+ params.push(options.rootRunId);
327
+ }
328
+ if (options.parentRunId) {
329
+ sql += " AND runs.parent_run_id = ?";
330
+ params.push(options.parentRunId);
331
+ }
284
332
  const stmt = this.getStatement(sql);
285
333
  const row = stmt.get(...params);
286
334
  return row?.count ?? 0;
@@ -295,6 +343,30 @@ export class SqliteRunsStore {
295
343
  sql += " AND status = ?";
296
344
  params.push(options.status);
297
345
  }
346
+ if (options.channel) {
347
+ sql += " AND json_extract(origin_json, '$.channel') = ?";
348
+ params.push(options.channel);
349
+ }
350
+ if (options.view) {
351
+ sql += " AND json_extract(origin_json, '$.viewName') = ?";
352
+ params.push(options.view);
353
+ }
354
+ if (options.since) {
355
+ sql += " AND started_at >= ?";
356
+ params.push(options.since);
357
+ }
358
+ if (options.until) {
359
+ sql += " AND started_at <= ?";
360
+ params.push(options.until);
361
+ }
362
+ if (options.rootRunId) {
363
+ sql += " AND root_run_id = ?";
364
+ params.push(options.rootRunId);
365
+ }
366
+ if (options.parentRunId) {
367
+ sql += " AND parent_run_id = ?";
368
+ params.push(options.parentRunId);
369
+ }
298
370
  const stmt = this.getStatement(sql);
299
371
  const row = stmt.get(...params);
300
372
  return row?.count ?? 0;
@@ -318,23 +390,56 @@ export class SqliteRunsStore {
318
390
  * 当前包,保证多包共库(全局库)时不越界读取其他包的运行记录。
319
391
  * 不按 owner_id 过滤:写入方与查询方分属不同宿主进程,ownerId 必然不同。
320
392
  */
321
- listRunsByRequestIds(isClosed, requestIds) {
393
+ listRunsByRequestIds(isClosed, requestIds, options = {}) {
322
394
  if (isClosed || requestIds.length === 0)
323
395
  return [];
324
396
  const uniqueIds = Array.from(new Set(requestIds.filter((id) => typeof id === "string" && id.length > 0)));
325
397
  if (uniqueIds.length === 0)
326
398
  return [];
327
- const sql = `
399
+ let sql = `
328
400
  SELECT runs.*, ik.request_id AS request_id
329
401
  FROM runs
330
402
  INNER JOIN idempotency_keys AS ik ON ik.run_id = runs.id
331
403
  WHERE runs.package_id = ?
332
404
  AND ik.request_id IN (${SqliteRunsStore.placeholders(uniqueIds.length)})
333
- ORDER BY runs.started_at DESC
334
405
  `;
406
+ const params = [this.packageId, ...uniqueIds];
407
+ if (options.actionId) {
408
+ sql += " AND runs.action_id = ?";
409
+ params.push(options.actionId);
410
+ }
411
+ if (options.status) {
412
+ sql += " AND runs.status = ?";
413
+ params.push(options.status);
414
+ }
415
+ if (options.channel) {
416
+ sql += " AND json_extract(runs.origin_json, '$.channel') = ?";
417
+ params.push(options.channel);
418
+ }
419
+ if (options.view) {
420
+ sql += " AND json_extract(runs.origin_json, '$.viewName') = ?";
421
+ params.push(options.view);
422
+ }
423
+ if (options.since) {
424
+ sql += " AND runs.started_at >= ?";
425
+ params.push(options.since);
426
+ }
427
+ if (options.until) {
428
+ sql += " AND runs.started_at <= ?";
429
+ params.push(options.until);
430
+ }
431
+ if (options.rootRunId) {
432
+ sql += " AND runs.root_run_id = ?";
433
+ params.push(options.rootRunId);
434
+ }
435
+ if (options.parentRunId) {
436
+ sql += " AND runs.parent_run_id = ?";
437
+ params.push(options.parentRunId);
438
+ }
439
+ sql += " ORDER BY runs.started_at DESC";
335
440
  const stmt = this.getStatement(sql);
336
- const rows = stmt.all(this.packageId, ...uniqueIds);
337
- const records = rows.map((r) => this.mapRunRecord(r));
441
+ const rows = stmt.all(...params);
442
+ let records = rows.map((r) => this.mapRunRecord(r));
338
443
  // requestId 来自联查列,直接补齐(mapRunRecord 无法从 runs 行还原)
339
444
  const requestIdByRunId = new Map();
340
445
  for (const row of rows) {
@@ -347,6 +452,23 @@ export class SqliteRunsStore {
347
452
  if (requestId)
348
453
  record.requestId = requestId;
349
454
  }
455
+ // 内存过滤适配
456
+ if (options.actionId)
457
+ records = records.filter((r) => r.actionId === options.actionId);
458
+ if (options.status)
459
+ records = records.filter((r) => r.status === options.status);
460
+ if (options.channel)
461
+ records = records.filter((r) => r.origin?.channel === options.channel);
462
+ if (options.view)
463
+ records = records.filter((r) => r.origin?.viewName === options.view);
464
+ if (options.since)
465
+ records = records.filter((r) => r.startedAt >= options.since);
466
+ if (options.until)
467
+ records = records.filter((r) => r.startedAt <= options.until);
468
+ if (options.rootRunId)
469
+ records = records.filter((r) => r.rootRunId === options.rootRunId);
470
+ if (options.parentRunId)
471
+ records = records.filter((r) => r.parentRunId === options.parentRunId);
350
472
  return records;
351
473
  }
352
474
  /**
@@ -1,5 +1,5 @@
1
1
  import { decodeStateKey, encodeStateKey, escapeStateSegment, unescapeStateSegment, type Logger, type RuntimeError, type RunRecord } from "@actiondock/sdk";
2
- import { type IdempotencyCheckResult, type IdempotencyRecord, type RunsRetentionPolicy, type RuntimeStorage, type StateEntry, type StorageOptions, type TerminalRunStatus } from "./types.js";
2
+ import { type IdempotencyCheckResult, type IdempotencyRecord, type RunsQueryFilterOptions, type RunsRetentionPolicy, type RuntimeStorage, type StateEntry, type StorageOptions, type TerminalRunStatus } from "./types.js";
3
3
  export { decodeStateKey, encodeStateKey, escapeStateSegment, unescapeStateSegment, };
4
4
  export interface SqliteStorageOptions extends StorageOptions {
5
5
  logger?: Logger;
@@ -48,17 +48,8 @@ export declare class SqliteRuntimeStorage implements RuntimeStorage {
48
48
  createRun(record: RunRecord | any): void;
49
49
  updateRun(id: string, status: TerminalRunStatus, output?: unknown, error?: RuntimeError, finishedAt?: string): void;
50
50
  getRun(id: string): RunRecord | null;
51
- listRuns(options?: {
52
- actionId?: string;
53
- status?: string;
54
- limit?: number;
55
- offset?: number;
56
- }): RunRecord[];
57
- countRuns(options?: {
58
- actionId?: string;
59
- status?: string;
60
- requestIds?: string[];
61
- }): number;
51
+ listRuns(options?: RunsQueryFilterOptions): RunRecord[];
52
+ countRuns(options?: RunsQueryFilterOptions): number;
62
53
  clearRuns(options?: {
63
54
  actionId?: string;
64
55
  status?: string;
@@ -69,11 +60,8 @@ export declare class SqliteRuntimeStorage implements RuntimeStorage {
69
60
  touchRunHeartbeat(runIds: string[]): number;
70
61
  checkAndRecordIdempotency(record: IdempotencyRecord): IdempotencyCheckResult;
71
62
  getIdempotencyRecord(ownerId: string, actionRef: string, requestId: string): IdempotencyRecord | undefined;
72
- listRunsByRequestIds(requestIds: string[]): RunRecord[];
73
- countRunsByRequestIds(requestIds: string[], options?: {
74
- actionId?: string;
75
- status?: string;
76
- }): number;
63
+ listRunsByRequestIds(requestIds: string[], options?: RunsQueryFilterOptions): RunRecord[];
64
+ countRunsByRequestIds(requestIds: string[], options?: RunsQueryFilterOptions): number;
77
65
  getRunRequestIds(runIds: string[]): Record<string, string>;
78
66
  getRunWithRequestId(id: string): RunRecord | null;
79
67
  appendEvent(eventType: string, payload: unknown): void;
@@ -159,8 +159,8 @@ export class SqliteRuntimeStorage {
159
159
  getIdempotencyRecord(ownerId, actionRef, requestId) {
160
160
  return this.runsStore.getIdempotencyRecord(this.isClosed, ownerId, actionRef, requestId);
161
161
  }
162
- listRunsByRequestIds(requestIds) {
163
- return this.runsStore.listRunsByRequestIds(this.isClosed, requestIds);
162
+ listRunsByRequestIds(requestIds, options = {}) {
163
+ return this.runsStore.listRunsByRequestIds(this.isClosed, requestIds, options);
164
164
  }
165
165
  countRunsByRequestIds(requestIds, options = {}) {
166
166
  return this.runsStore.countRunsByRequestIds(this.isClosed, requestIds, options);
@@ -32,6 +32,33 @@ export interface RunsRetentionPolicy {
32
32
  /** 最小保底保留终态记录数(默认 50,防止全清空) */
33
33
  minRetainRuns?: number;
34
34
  }
35
+ /**
36
+ * 运行记录通用检索与统计过滤选项。
37
+ */
38
+ export interface RunsQueryFilterOptions {
39
+ /** 目标动作标识筛选 */
40
+ actionId?: string;
41
+ /** 运行生命周期状态筛选 */
42
+ status?: string;
43
+ /** 运行通道筛选(如 http、mcp) */
44
+ channel?: string;
45
+ /** 服务视图名称筛选(如 default、admin) */
46
+ view?: string;
47
+ /** 起始时间筛选(匹配 started_at >= since) */
48
+ since?: string;
49
+ /** 截止时间筛选(匹配 started_at <= until) */
50
+ until?: string;
51
+ /** 根运行标识筛选 */
52
+ rootRunId?: string;
53
+ /** 父运行标识筛选 */
54
+ parentRunId?: string;
55
+ /** 最大返回记录条数限制 */
56
+ limit?: number;
57
+ /** 偏移游标条数(跳过的记录数) */
58
+ offset?: number;
59
+ /** 幂等请求标识列表(经 idempotency_keys 反查) */
60
+ requestIds?: string[];
61
+ }
35
62
  /**
36
63
  * SQLite 基础参数值类型。
37
64
  */
@@ -196,18 +223,9 @@ export interface RuntimeStorage {
196
223
  createRun(record: RunRecord): void;
197
224
  updateRun(id: string, status: TerminalRunStatus, output?: unknown, error?: RuntimeError, finishedAt?: string): void;
198
225
  getRun(id: string): RunRecord | null;
199
- listRuns(options?: {
200
- actionId?: string;
201
- status?: string;
202
- limit?: number;
203
- offset?: number;
204
- }): RunRecord[];
226
+ listRuns(options?: RunsQueryFilterOptions): RunRecord[];
205
227
  /** 统计符合条件的运行记录总数 */
206
- countRuns?(options?: {
207
- actionId?: string;
208
- status?: string;
209
- requestIds?: string[];
210
- }): number;
228
+ countRuns?(options?: RunsQueryFilterOptions): number;
211
229
  /** 查询单条运行记录并补齐幂等请求标识关联(详情路径专用) */
212
230
  getRunWithRequestId?(id: string): RunRecord | null;
213
231
  clearRuns(options?: {
@@ -241,12 +259,9 @@ export interface RuntimeStorage {
241
259
  * 不同宿主会话,ownerId 天然不一致;requestId 本身即跨进程全局唯一的外部
242
260
  * 事实源,包维度隔离已提供足够边界。
243
261
  */
244
- listRunsByRequestIds?(requestIds: string[]): RunRecord[];
262
+ listRunsByRequestIds?(requestIds: string[], options?: RunsQueryFilterOptions): RunRecord[];
245
263
  /** 按客户端幂等请求标识反查关联运行记录总数 */
246
- countRunsByRequestIds?(requestIds: string[], options?: {
247
- actionId?: string;
248
- status?: string;
249
- }): number;
264
+ countRunsByRequestIds?(requestIds: string[], options?: RunsQueryFilterOptions): number;
250
265
  /** 反查运行记录关联的幂等请求标识映射(仅限当前包范围,无关联时不含对应键) */
251
266
  getRunRequestIds?(runIds: string[]): Record<string, string>;
252
267
  appendEvent?(eventType: string, payload: unknown): void;
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * ActionDock 核心版本号单一事实源。
3
3
  */
4
- export declare const ACTIONDOCK_VERSION = "2.17.0";
4
+ export declare const ACTIONDOCK_VERSION = "2.19.0";
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * ActionDock 核心版本号单一事实源。
3
3
  */
4
- export const ACTIONDOCK_VERSION = "2.17.0";
4
+ export const ACTIONDOCK_VERSION = "2.19.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actiondock/core",
3
- "version": "2.17.0",
3
+ "version": "2.19.0",
4
4
  "description": "ActionDock Core Engine - Project loader, runtime execution, SQLite storage, standalone builder, and skill exporter",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -59,7 +59,7 @@
59
59
  "test": "node ../../scripts/run-tests.ts"
60
60
  },
61
61
  "dependencies": {
62
- "@actiondock/sdk": "^2.17.0",
62
+ "@actiondock/sdk": "^2.19.0",
63
63
  "ajv": "^8.17.1",
64
64
  "ajv-formats": "^3.0.1",
65
65
  "undici": "^7.29.1"