@axiom-lattice/protocols 4.1.0 → 4.1.1

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.
@@ -65,19 +65,63 @@ export interface PluginConnection {
65
65
 
66
66
  /**
67
67
  * 工具元信息(用于前端 allowedTools 筛选)
68
+ *
69
+ * New connection-backed plugin tools that select one connection must accept
70
+ * the selected key as `args.connectionKey`. Existing legacy tools retain their
71
+ * established resource argument names.
68
72
  */
69
73
  export interface PluginToolMeta {
70
74
  name: string;
71
75
  description: string;
72
76
  }
73
77
 
78
+ /**
79
+ * A text file included in a plugin skill bundle.
80
+ *
81
+ * @property content - Text content written to the skill resource file.
82
+ * @property mimeType - Optional MIME type for consumers that need it.
83
+ */
84
+ export interface PluginSkillResource {
85
+ content: string;
86
+ mimeType?: string;
87
+ }
88
+
89
+ /**
90
+ * Versioned definition of a plugin-provided skill.
91
+ *
92
+ * @property version - Bundle version used to detect resource updates.
93
+ * @property content - Complete SKILL.md markdown content.
94
+ * @property resources - Optional text resources keyed by safe relative paths.
95
+ */
96
+ export interface PluginSkillDefinition {
97
+ version: string;
98
+ content: string;
99
+ resources?: Record<string, PluginSkillResource>;
100
+ }
101
+
102
+ /**
103
+ * Standard configuration for a new connection-backed plugin.
104
+ *
105
+ * `PluginMeta.type` is also the Connection Store type. `connections` selects
106
+ * connection keys of that type; `connectAll` opts into using all available
107
+ * connections of that type. Do not add a separate connection or resource type
108
+ * or selector field to this configuration.
109
+ */
110
+ export type PluginStandardConnectionConfig = {
111
+ connections: string[];
112
+ connectAll?: boolean;
113
+ };
114
+
74
115
  /**
75
116
  * 插件元数据(开发者声明)
76
117
  *
77
118
  * connectionSchema 不在此类型中——由 serializePluginMeta 自动推导后注入 PluginMetaOutput。
78
119
  */
79
120
  export interface PluginMeta {
80
- /** 插件唯一标识,如 "erp" */
121
+ /**
122
+ * 插件唯一标识,如 "erp"; for connection-backed plugins this is also the
123
+ * Connection Store type.
124
+ */
81
125
  type: string;
82
126
  /** 显示名称 */
83
127
  name: string;
@@ -91,9 +135,13 @@ export interface PluginMeta {
91
135
  icon?: string;
92
136
  /** 工具清单(可选,middleware 能自动提取时不需要写) */
93
137
  tools?: PluginToolMeta[];
94
- /** 中间件配置 schema(用于 agent 配置面板) */
138
+ /**
139
+ * 中间件配置 schema(用于 agent 配置面板)。新的 connection-backed
140
+ * plugins use `connections: string[]` and optional `connectAll?: boolean`.
141
+ * Do not introduce connectionType, resourceType, or resourceSelector fields.
142
+ */
95
143
  configSchema?: Record<string, unknown>;
96
- /** 默认配置 */
144
+ /** 默认配置;connection-backed plugins use PluginStandardConnectionConfig. */
97
145
  defaultConfig?: Record<string, unknown>;
98
146
  /** 推荐配置的 companion 插件 */
99
147
  recommends?: string[];
@@ -103,6 +151,8 @@ export interface PluginMeta {
103
151
  * 第三方插件可自定义分类名,前端会原样显示;未提供时归入 "Other"。
104
152
  */
105
153
  category?: string;
154
+ /** Whether this plugin's middleware may be included in capability bundles. */
155
+ capabilityBundleEligible?: boolean;
106
156
  }
107
157
 
108
158
  /**
@@ -133,7 +183,7 @@ export interface PluginMetaOutput extends PluginMeta {
133
183
  * const myPlugin: Plugin = {
134
184
  * meta: { ... },
135
185
  * middleware: (config, context) => {
136
- * const skills = context?.pluginSkillContents ?? {};
186
+ * const skills = context?.pluginSkills ?? {};
137
187
  * return createMyMiddleware({ ...config, pluginSkills: skills });
138
188
  * },
139
189
  * };
@@ -141,12 +191,14 @@ export interface PluginMetaOutput extends PluginMeta {
141
191
  */
142
192
  export interface PluginContext {
143
193
  /**
144
- * Cross-plugin aggregated skill contents (SKILL.md, keyed by skill name).
194
+ * Cross-plugin aggregated skill bundles, keyed by skill name.
145
195
  * Collected from all enabled plugins before the main middleware loop.
146
196
  * Most plugins should ignore this; only cross-plugin coordination
147
197
  * middleware (e.g. skillMiddleware) consumes it.
148
198
  */
149
- pluginSkillContents?: Record<string, string>;
199
+ pluginSkills?: Record<string, PluginSkillDefinition>;
200
+ /** Owning plugin type for each enabled plugin skill bundle. */
201
+ pluginSkillOwners?: Record<string, string>;
150
202
  }
151
203
 
152
204
  /**
@@ -197,7 +249,7 @@ export interface Plugin {
197
249
  * 名称必须以 "{pluginType}-" 为前缀,注册时校验。
198
250
  * Builder 在构建中间件之前从所有启用的插件中收集。
199
251
  */
200
- skills?: Record<string, string>;
252
+ skills?: Record<string, PluginSkillDefinition>;
201
253
  /**
202
254
  * 插件贡献的 Agent 定义(以 agent key 为键)。
203
255
  * 在租户首次访问时通过 ensurePluginAgentsForTenant 按租户注册到
@@ -59,6 +59,36 @@ export interface Skill {
59
59
  */
60
60
  subSkills?: string[];
61
61
 
62
+ /**
63
+ * Source of the skill (optional)
64
+ * e.g. "builtin-plugin" for read-only plugin-provided skills
65
+ */
66
+ source?: string;
67
+
68
+ /**
69
+ * Owning plugin type (optional)
70
+ * Set for skills contributed by a registered plugin
71
+ */
72
+ pluginType?: string;
73
+
74
+ /**
75
+ * Plugin skill bundle version (optional)
76
+ * Set for skills contributed by a registered plugin
77
+ */
78
+ version?: string;
79
+
80
+ /**
81
+ * Resource catalog (optional)
82
+ * Safe relative paths of a plugin skill's bundled resources with their MIME types
83
+ */
84
+ resourcePaths?: Array<{ path: string; mimeType?: string }>;
85
+
86
+ /**
87
+ * Read-only flag (optional)
88
+ * True for immutable sources (e.g. plugin-provided skills) that cannot be created/updated/deleted
89
+ */
90
+ readOnly?: boolean;
91
+
62
92
  /**
63
93
  * Skill creation timestamp
64
94
  */
@@ -1,4 +1,9 @@
1
- /** A single canonical belief recorded in a task description. */
1
+ /**
2
+ * A single canonical belief recorded in a task description.
3
+ *
4
+ * `probability` is retained as the persisted field name, but task guidance uses
5
+ * it as an evidence-support percentage rather than a calibrated probability.
6
+ */
2
7
  export interface TaskBeliefEntry {
3
8
  key: string;
4
9
  probability: number;
@@ -56,10 +56,62 @@ export interface TaskWorkItemListFilter {
56
56
  offset?: number;
57
57
  }
58
58
 
59
+ /** Canonical prefix for public task execution-result event identities. */
60
+ export const EXECUTION_RESULT_EVENT_KEY_PREFIX = "execution-result:";
61
+
62
+ /**
63
+ * Portable regular-expression source for canonical execution-result event keys.
64
+ *
65
+ * The entire key is the literal `execution-result:` prefix followed by a nonempty
66
+ * suffix containing only ASCII letters, digits, period, underscore, colon, or hyphen.
67
+ * Colon is intentionally allowed so callers can compose structured suffixes.
68
+ */
69
+ export const EXECUTION_RESULT_EVENT_KEY_PATTERN_SOURCE =
70
+ "^execution-result:[A-Za-z0-9._:-]+$";
71
+
72
+ /** Compiled runtime expression for canonical execution-result event keys. */
73
+ export const EXECUTION_RESULT_EVENT_KEY_PATTERN =
74
+ new RegExp(EXECUTION_RESULT_EVENT_KEY_PATTERN_SOURCE);
75
+
76
+ /** Maximum pending execution-result rows accepted by one store query. */
77
+ export const MAX_PENDING_EXECUTION_RESULTS_LIMIT = 1_000;
78
+
79
+ /**
80
+ * Determines whether a runtime value is a canonical execution-result event key.
81
+ *
82
+ * @param value Runtime value to validate.
83
+ * @returns True only for the portable canonical ASCII grammar.
84
+ */
85
+ export function isExecutionResultEventKey(value: unknown): value is string {
86
+ return typeof value === "string" && EXECUTION_RESULT_EVENT_KEY_PATTERN.test(value);
87
+ }
88
+
59
89
  export interface TaskWorkItemStore {
60
90
  create(params: CreateWorkItemRequest): Promise<TaskWorkItem>;
61
91
  list(filter: TaskWorkItemListFilter): Promise<TaskWorkItem[]>;
62
92
 
93
+ /**
94
+ * List the newest bounded set of execution results awaiting reconciliation.
95
+ *
96
+ * Only `execution_result` items with a canonical ASCII
97
+ * `execution-result:[A-Za-z0-9._:-]+` event key are returned. An item is excluded when
98
+ * a task-scoped `execution_reconciled` item has a
99
+ * `detail.executionResultId` equal to that event key. Results are ordered by
100
+ * `createdAt` descending and then `id` descending for deterministic ties.
101
+ *
102
+ * @param params Tenant/task scope and required maximum number of rows.
103
+ * @returns At most `limit` pending execution-result work items, newest first.
104
+ * @throws RangeError with code `INVALID_LIMIT` unless limit is a safe integer from zero through
105
+ * {@link MAX_PENDING_EXECUTION_RESULTS_LIMIT}.
106
+ * @remarks Optional optimization. Stores that omit it remain compatible; callers may use a
107
+ * bounded, non-authoritative fallback through the pre-existing list and event-key methods.
108
+ */
109
+ listPendingExecutionResults?(params: {
110
+ tenantId: string;
111
+ taskId: string;
112
+ limit: number;
113
+ }): Promise<TaskWorkItem[]>;
114
+
63
115
  /**
64
116
  * Find an event by deterministic identity without list pagination.
65
117
  *
@@ -102,6 +102,25 @@ export interface UpdateProjectRequest {
102
102
  kind?: ProjectKind;
103
103
  }
104
104
 
105
+ /** Error raised when generic project writes attempt to change capability Bundle references. */
106
+ export class InvalidProjectCapabilityBundleConfigError extends Error {
107
+ /** Stable machine-readable error code. */
108
+ readonly code = "INVALID_BUNDLE_CONFIG" as const;
109
+
110
+ /** Creates the reserved-config error returned by generic Project writes. */
111
+ constructor() {
112
+ super("Use the project capability-bundles endpoint to update capability bundle IDs");
113
+ this.name = "InvalidProjectCapabilityBundleConfigError";
114
+ }
115
+ }
116
+
117
+ /** Rejects capability Bundle references supplied through generic Project config writes. */
118
+ export function assertGenericProjectConfig(config: Record<string, unknown> | undefined): void {
119
+ if (config !== undefined && Object.prototype.hasOwnProperty.call(config, "capabilityBundleIds")) {
120
+ throw new InvalidProjectCapabilityBundleConfigError();
121
+ }
122
+ }
123
+
105
124
  /**
106
125
  * Filter options for listing projects within a workspace
107
126
  */
@@ -109,6 +128,16 @@ export interface ProjectFilter {
109
128
  kind?: ProjectKind;
110
129
  }
111
130
 
131
+ /** Atomic result of replacing a Project's capability Bundle IDs. */
132
+ export type UpdateProjectCapabilityBundlesResult =
133
+ | { status: "updated"; project: Project }
134
+ | { status: "project_not_found" }
135
+ | { status: "bundle_not_found" }
136
+ | { status: "bundle_conflict" };
137
+
138
+ /** Revision preconditions for the bundles reviewed before project assignment. */
139
+ export type ExpectedCapabilityBundleRevisions = Record<string, string>;
140
+
112
141
  /**
113
142
  * ProjectStore interface
114
143
  * Provides CRUD operations for project data
@@ -118,5 +147,9 @@ export interface ProjectStore {
118
147
  getProjectById(tenantId: string, id: string): Promise<Project | null>;
119
148
  createProject(tenantId: string, workspaceId: string, id: string, data: CreateProjectRequest): Promise<Project>;
120
149
  updateProject(tenantId: string, id: string, updates: UpdateProjectRequest): Promise<Project | null>;
150
+ /** Omitted expectedRevisions is reserved for internal maintenance callers. */
151
+ updateCapabilityBundleIds(tenantId: string, projectId: string, bundleIds: string[], expectedRevisions?: ExpectedCapabilityBundleRevisions): Promise<UpdateProjectCapabilityBundlesResult>;
121
152
  deleteProject(tenantId: string, id: string): Promise<boolean>;
153
+ /** Returns whether a tenant project references the given capability bundle. */
154
+ isCapabilityBundleReferenced(tenantId: string, bundleId: string): Promise<boolean>;
122
155
  }
@@ -0,0 +1,69 @@
1
+ import {
2
+ EXECUTION_RESULT_EVENT_KEY_PATTERN_SOURCE,
3
+ isExecutionResultEventKey,
4
+ } from "../TaskWorkItemProtocol";
5
+ import { readFileSync } from "node:fs";
6
+ import type {
7
+ TaskWorkItem,
8
+ TaskWorkItemStore,
9
+ } from "../TaskWorkItemProtocol";
10
+
11
+ class LegacyTaskWorkItemStore implements TaskWorkItemStore {
12
+ async create(): Promise<TaskWorkItem> {
13
+ throw new Error("fixture only");
14
+ }
15
+
16
+ async list(): Promise<TaskWorkItem[]> {
17
+ return [];
18
+ }
19
+
20
+ async findByEventKey(): Promise<TaskWorkItem | null> {
21
+ return null;
22
+ }
23
+
24
+ async createIfAbsentByEventKey(): Promise<TaskWorkItem> {
25
+ throw new Error("fixture only");
26
+ }
27
+ }
28
+
29
+ const legacyStore: TaskWorkItemStore = new LegacyTaskWorkItemStore();
30
+
31
+ describe("execution result event keys", () => {
32
+ it("runs the legacy-store compatibility fixture during normal package tests", () => {
33
+ const packageJson = JSON.parse(readFileSync("package.json", "utf8")) as {
34
+ scripts?: Record<string, string>;
35
+ };
36
+
37
+ expect(packageJson.scripts?.["typecheck:compat"]).toBe(
38
+ "tsc -p tsconfig.type-tests.json",
39
+ );
40
+ expect(packageJson.scripts?.test).toBe("jest && pnpm typecheck:compat");
41
+ });
42
+
43
+ it("keeps stores without the optimized pending query protocol-compatible", () => {
44
+ expect(legacyStore).toBeInstanceOf(LegacyTaskWorkItemStore);
45
+ });
46
+
47
+ it.each([
48
+ "execution-result:550e8400-e29b-41d4-a716-446655440000",
49
+ "execution-result:thread.segment_1:attempt-2",
50
+ ])("accepts canonical ASCII key %s", (value) => {
51
+ expect(isExecutionResultEventKey(value)).toBe(true);
52
+ expect(new RegExp(EXECUTION_RESULT_EVENT_KEY_PATTERN_SOURCE).test(value)).toBe(true);
53
+ });
54
+
55
+ it.each([
56
+ undefined,
57
+ 1,
58
+ "execution-result:",
59
+ "wrong-prefix:value",
60
+ "execution-result:internal space",
61
+ "execution-result:non-breaking\u00a0space",
62
+ "execution-result:next-line\u0085",
63
+ "execution-result:em-space\u2003",
64
+ "execution-result:invalid/punctuation",
65
+ "execution-result:query?value",
66
+ ])("rejects non-canonical key %p", (value) => {
67
+ expect(isExecutionResultEventKey(value)).toBe(false);
68
+ });
69
+ });
@@ -0,0 +1,177 @@
1
+ import type {
2
+ AgentMiddlewareConfig,
3
+ CapabilityBundle,
4
+ CapabilityBundleStore,
5
+ CapabilityFieldSource,
6
+ CapabilityOverride,
7
+ CapabilityPreview,
8
+ CapabilityPreviewIssue,
9
+ CapabilityRuntime,
10
+ CreateCapabilityBundleInput,
11
+ PluginMetaOutput,
12
+ Plugin,
13
+ PluginStandardConnectionConfig,
14
+ ProjectCapabilityConfig,
15
+ ProjectStore,
16
+ UpdateCapabilityBundleInput,
17
+ } from "../index";
18
+
19
+ const standardConnectionConfig: PluginStandardConnectionConfig = {
20
+ connections: ["crm-prod"],
21
+ connectAll: false,
22
+ };
23
+
24
+ const standardConnectionPlugin: Plugin = {
25
+ meta: {
26
+ type: "crm",
27
+ name: "CRM",
28
+ description: "CRM plugin",
29
+ capabilityBundleEligible: true,
30
+ configSchema: {
31
+ connections: { type: "array", items: { type: "string" } },
32
+ connectAll: { type: "boolean" },
33
+ },
34
+ defaultConfig: standardConnectionConfig,
35
+ },
36
+ connection: {
37
+ fields: [{ key: "apiKey", type: "password", title: "API key" }],
38
+ },
39
+ };
40
+
41
+ const middleware: AgentMiddlewareConfig = {
42
+ id: "filesystem",
43
+ type: "filesystem",
44
+ name: "Filesystem",
45
+ description: "Read project files",
46
+ enabled: true,
47
+ config: {},
48
+ };
49
+
50
+ const createInput: CreateCapabilityBundleInput = {
51
+ name: "Research",
52
+ description: "Research capabilities",
53
+ capabilities: [middleware],
54
+ };
55
+
56
+ const updateInput: UpdateCapabilityBundleInput = {
57
+ name: "Updated research",
58
+ expectedUpdatedAt: "2026-08-27T00:00:00.000Z",
59
+ };
60
+
61
+ const bundle: CapabilityBundle = {
62
+ id: "bundle-1",
63
+ tenantId: "tenant-1",
64
+ key: "research",
65
+ ...createInput,
66
+ createdAt: "2026-08-27T00:00:00.000Z",
67
+ updatedAt: "2026-08-27T00:00:00.000Z",
68
+ };
69
+
70
+ const source: CapabilityFieldSource = {
71
+ capabilityType: "filesystem",
72
+ fieldPath: "config.root",
73
+ sourceBundleId: bundle.id,
74
+ value: "/workspace",
75
+ };
76
+
77
+ const override: CapabilityOverride = {
78
+ capabilityType: "filesystem",
79
+ fieldPath: "config.root",
80
+ previousValue: "/project",
81
+ nextValue: "/workspace",
82
+ sourceBundleId: bundle.id,
83
+ };
84
+
85
+ const warning: CapabilityPreviewIssue = {
86
+ code: "DUPLICATE_FIELD",
87
+ message: "A later bundle overrides this field",
88
+ bundleId: bundle.id,
89
+ capabilityType: "filesystem",
90
+ fieldPath: "config.root",
91
+ };
92
+
93
+ const preview: CapabilityPreview = {
94
+ bundleIds: [bundle.id],
95
+ capabilities: [middleware],
96
+ overrides: [override],
97
+ sources: [source],
98
+ warnings: [warning],
99
+ errors: [],
100
+ revision: "revision-1",
101
+ };
102
+
103
+ const runtime: CapabilityRuntime = {
104
+ revision: preview.revision,
105
+ middleware: { [middleware.id]: middleware },
106
+ };
107
+
108
+ const projectConfig: ProjectCapabilityConfig = {
109
+ capabilityBundleIds: bundle.id ? [bundle.id] : [],
110
+ };
111
+
112
+ const pluginMeta: PluginMetaOutput = {
113
+ type: "research",
114
+ name: "Research",
115
+ description: "Research plugin",
116
+ capabilityBundleEligible: true,
117
+ };
118
+
119
+ const bundleStore: CapabilityBundleStore = {
120
+ async listByTenant() {
121
+ return [bundle];
122
+ },
123
+ async getById() {
124
+ return bundle;
125
+ },
126
+ async getManyByIds() {
127
+ return [bundle];
128
+ },
129
+ async create(tenantId, input) {
130
+ return { ...bundle, tenantId, ...input };
131
+ },
132
+ async update() {
133
+ return bundle;
134
+ },
135
+ async deleteIfUnreferenced() {
136
+ return "deleted";
137
+ },
138
+ };
139
+
140
+ const projectStore: ProjectStore = {
141
+ getProjectsByWorkspace: async () => [],
142
+ getProjectById: async () => null,
143
+ createProject: async () => {
144
+ throw new Error("not used");
145
+ },
146
+ updateProject: async () => null,
147
+ deleteProject: async () => false,
148
+ isCapabilityBundleReferenced: async () => projectConfig.capabilityBundleIds.includes(bundle.id),
149
+ updateCapabilityBundleIds: async () => ({ status: "project_not_found" }),
150
+ };
151
+
152
+ describe("capability bundle protocol types", () => {
153
+ it("represents bundle storage, runtime, preview provenance, and eligibility", async () => {
154
+ expect(bundle.capabilities).toEqual([middleware]);
155
+ expect(runtime.middleware[middleware.id]).toBe(middleware);
156
+ expect(preview.sources[0]).toEqual(source);
157
+ expect(preview.overrides[0]).toEqual(override);
158
+ expect(pluginMeta.capabilityBundleEligible).toBe(true);
159
+ await expect(projectStore.isCapabilityBundleReferenced("tenant-1", bundle.id)).resolves.toBe(true);
160
+ });
161
+
162
+ it("defines standard connection-backed plugins without duplicate resource fields", () => {
163
+ expect(standardConnectionPlugin.meta.type).toBe("crm");
164
+ expect(standardConnectionPlugin.meta.capabilityBundleEligible).toBe(true);
165
+ expect(standardConnectionPlugin.connection).toBeDefined();
166
+ expect(standardConnectionPlugin.meta.defaultConfig).toEqual(standardConnectionConfig);
167
+ expect(standardConnectionPlugin.meta.configSchema).not.toHaveProperty("connectionType");
168
+ expect(standardConnectionPlugin.meta.configSchema).not.toHaveProperty("resourceType");
169
+ expect(standardConnectionPlugin.meta.configSchema).not.toHaveProperty("resourceSelector");
170
+ expect(standardConnectionPlugin.meta.defaultConfig).not.toHaveProperty("connectionType");
171
+ expect(standardConnectionPlugin.meta.defaultConfig).not.toHaveProperty("resourceType");
172
+ expect(standardConnectionPlugin.meta.defaultConfig).not.toHaveProperty("resourceSelector");
173
+ });
174
+ });
175
+
176
+ void updateInput;
177
+ void bundleStore;
package/src/index.ts CHANGED
@@ -48,6 +48,8 @@ export * from "./A2AApiKeyStoreProtocol";
48
48
  export * from "./ConversationStoreProtocol";
49
49
  export * from "./AgentWebAppStoreProtocol";
50
50
  export * from "./AgentWebAppRuntimeProtocol";
51
+ export * from "./CapabilityBundleStoreProtocol";
52
+ export * from "./CapabilityRuntimeProtocol";
51
53
 
52
54
  // Workflow DSL (concise, public API)
53
55
  export * from "./WorkflowDSL";
@@ -68,6 +70,9 @@ export type {
68
70
  PluginDiscoveredResource,
69
71
  PluginContext,
70
72
  PluginToolMeta,
73
+ PluginSkillResource,
74
+ PluginSkillDefinition,
75
+ PluginStandardConnectionConfig,
71
76
  PluginMiddlewareFactory,
72
77
  } from "./PluginProtocol";
73
78
 
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "incremental": false,
5
+ "noEmit": true,
6
+ "rootDir": "."
7
+ },
8
+ "include": ["type-tests/**/*.ts"]
9
+ }
@@ -0,0 +1,25 @@
1
+ import type {
2
+ TaskWorkItem,
3
+ TaskWorkItemStore,
4
+ } from "../src/TaskWorkItemProtocol";
5
+
6
+ class LegacyTaskWorkItemStore implements TaskWorkItemStore {
7
+ async create(): Promise<TaskWorkItem> {
8
+ throw new Error("compile fixture");
9
+ }
10
+
11
+ async list(): Promise<TaskWorkItem[]> {
12
+ return [];
13
+ }
14
+
15
+ async findByEventKey(): Promise<TaskWorkItem | null> {
16
+ return null;
17
+ }
18
+
19
+ async createIfAbsentByEventKey(): Promise<TaskWorkItem> {
20
+ throw new Error("compile fixture");
21
+ }
22
+ }
23
+
24
+ const store: TaskWorkItemStore = new LegacyTaskWorkItemStore();
25
+ void store;