@axiom-lattice/protocols 4.0.1 → 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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +305 -9
- package/dist/index.d.ts +305 -9
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/CapabilityBundleStoreProtocol.ts +78 -0
- package/src/CapabilityRuntimeProtocol.ts +82 -0
- package/src/PluginProtocol.ts +59 -7
- package/src/SkillStoreProtocol.ts +30 -0
- package/src/TaskBeliefProtocol.ts +6 -1
- package/src/TaskWorkItemProtocol.ts +52 -0
- package/src/WorkspaceStoreProtocol.ts +33 -0
- package/src/__tests__/TaskWorkItemProtocol.test.ts +69 -0
- package/src/__tests__/capability-bundle-types.test.ts +177 -0
- package/src/index.ts +5 -0
- package/tsconfig.type-tests.json +9 -0
- package/type-tests/task-work-item-store-compatibility.ts +25 -0
package/src/PluginProtocol.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
/**
|
|
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?.
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
/**
|
|
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,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;
|