@collegium/sdk 0.0.1-beta.4 → 0.0.1-beta.5
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/README.md +40 -41
- package/dist/index.d.ts +64 -113
- package/dist/index.js +11 -172
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -2,59 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
The authoring surface for [Collegium](https://collegium.sh) plugins.
|
|
4
4
|
|
|
5
|
-
A plugin is a directory of TypeScript that
|
|
6
|
-
settings, durable storage, and skills. The deployment mounts it, compiles it at boot, and grants it
|
|
7
|
-
to the agents that need it. This package is what you write it against.
|
|
5
|
+
A plugin is a directory of TypeScript. The deployment mounts it, compiles it at boot, and grants it to the agents that need it. The layout declares the contents: `src/config.ts` declares settings and storage, each `src/tools/<name>.ts` declares one tool named by its filename, and each `src/skills/<name>.md` ships one skill.
|
|
8
6
|
|
|
9
7
|
```sh
|
|
10
|
-
npm install @collegium/sdk
|
|
8
|
+
npm install @collegium/sdk zod
|
|
11
9
|
```
|
|
12
10
|
|
|
11
|
+
`src/config.ts`:
|
|
12
|
+
|
|
13
13
|
```ts
|
|
14
|
-
import {
|
|
14
|
+
import { defineConfig } from '@collegium/sdk';
|
|
15
|
+
import { z } from 'zod';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
name: 'contacts',
|
|
17
|
+
const config = defineConfig({
|
|
18
18
|
settings: z.strictObject({ maxContacts: z.number().int().positive().default(200) }),
|
|
19
|
-
storage: { contacts: z.object({ email: z.email(), name: z.string().min(1) }) }
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
value.name.toLowerCase().includes(args.query.toLowerCase())
|
|
26
|
-
);
|
|
27
|
-
return ok(matches.map(({ key, value }) => `- ${key}: ${value.name} <${value.email}>`).join('\n'));
|
|
28
|
-
},
|
|
29
|
-
parameters: z.object({ query: z.string().min(1) }),
|
|
30
|
-
retryable: true
|
|
31
|
-
},
|
|
32
|
-
save: {
|
|
33
|
-
approval: (args) => ({ body: `save contact "${args.id}": ${args.name}`, presentation: 'verbatim' }),
|
|
34
|
-
description: 'Save or update a contact.',
|
|
35
|
-
execute: async (args, { settings, storage }) => {
|
|
36
|
-
if ((await storage.contacts.list()).length >= settings.maxContacts) {
|
|
37
|
-
return fail.invalidArguments('contact limit reached; delete one first');
|
|
38
|
-
}
|
|
39
|
-
await storage.contacts.put(args.id, { email: args.email, name: args.name });
|
|
40
|
-
return ok(`contact ${args.id} saved`);
|
|
41
|
-
},
|
|
42
|
-
parameters: z.object({ email: z.email(), id: z.string().min(1), name: z.string().min(1) })
|
|
43
|
-
}
|
|
19
|
+
storage: { contacts: z.object({ email: z.email(), name: z.string().min(1) }) }
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
declare module '@collegium/sdk' {
|
|
23
|
+
interface Register {
|
|
24
|
+
config: typeof config;
|
|
44
25
|
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default config;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`src/tools/save.ts`:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { defineTool } from '@collegium/sdk';
|
|
35
|
+
import { z } from 'zod';
|
|
36
|
+
|
|
37
|
+
export default defineTool({
|
|
38
|
+
approval: (args) => ({ body: `save contact "${args.id}": ${args.name}`, presentation: 'verbatim' }),
|
|
39
|
+
description: 'Save or update a contact.',
|
|
40
|
+
execute: async (args, { err, settings, storage }) => {
|
|
41
|
+
if ((await storage.contacts.list()).length >= settings.maxContacts) {
|
|
42
|
+
err.invalidArguments('contact limit reached; delete one first');
|
|
43
|
+
}
|
|
44
|
+
await storage.contacts.put(args.id, { email: args.email, name: args.name });
|
|
45
|
+
return `contact ${args.id} saved`;
|
|
46
|
+
},
|
|
47
|
+
parameters: z.object({ email: z.email(), id: z.string().min(1), name: z.string().min(1) })
|
|
45
48
|
});
|
|
46
49
|
```
|
|
47
50
|
|
|
48
|
-
A tool with
|
|
49
|
-
|
|
51
|
+
A tool with `approval` always stops for a human, who sees the full payload before it runs; one without never gates. The channel and the trace disclose both, line by line. `execute` returns the text the model reads, and raises the two failures a tool controls through `err`: `invalidArguments` continues the turn, `unresolved` ends it as an unconfirmed side effect.
|
|
52
|
+
|
|
53
|
+
**zod is a peer dependency.** Install it beside the SDK and import it directly. A plugin may import `@collegium/sdk`, `zod`, and `node:` builtins; the compiler refuses every other bare specifier at boot.
|
|
50
54
|
|
|
51
|
-
**
|
|
52
|
-
SDK the deployment's image carries, not the copy in your `node_modules` — that copy is what your
|
|
53
|
-
editor, `tsc`, and your tests use. Import `z` from here rather than installing `zod` yourself; the
|
|
54
|
-
compiler refuses any import but this one.
|
|
55
|
+
**Your installed copies are for development.** The deployment compiles a mounted plugin against the SDK and zod its image carries, not the copies in your `node_modules` — those serve your editor, `tsc`, and your tests. Exactly one zod runs in the process.
|
|
55
56
|
|
|
56
|
-
**Versioning.**
|
|
57
|
-
range you declare names the deployment you are writing for. Boot refuses a plugin whose declared
|
|
58
|
-
range the deployment's version does not satisfy. Until v1, breaking changes land in minor releases.
|
|
57
|
+
**Versioning.** The SDK is released with Collegium itself and carries the same version, so the range you declare names the deployment you are writing for. Boot refuses a plugin whose declared ranges the deployment's versions do not satisfy. Before v1 any release may break a plugin, so declare the versions you tested against and re-declare them when you update.
|
|
59
58
|
|
|
60
59
|
Full guide: [collegium.sh/docs/guides/write-a-plugin](https://collegium.sh/docs/guides/write-a-plugin)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,39 +1,41 @@
|
|
|
1
|
-
import { z
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
import { Promisable } from "type-fest";
|
|
3
|
+
//#region src/config.d.ts
|
|
4
|
+
type CollectionsDeclaration$1 = {
|
|
5
|
+
readonly [key: string]: z.ZodType;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* The config file's default export: the settings schema agents are configured by, and the storage
|
|
9
|
+
* collections the plugin owns. Its generics are what carry the settings and storage types to every
|
|
10
|
+
* tool, through `Register`.
|
|
11
|
+
*/
|
|
12
|
+
type PluginConfig<TSettings extends undefined | z.ZodType = undefined | z.ZodType, TCollections extends CollectionsDeclaration$1 = CollectionsDeclaration$1> = {
|
|
13
|
+
readonly settings: TSettings;
|
|
14
|
+
readonly storage: TCollections;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The augmentation point. `src/config.ts` declares its config here, and every tool file receives
|
|
18
|
+
* the declared settings and storage types without importing the config:
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* declare module '@collegium/sdk' {
|
|
22
|
+
* interface Register { config: typeof config }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
interface Register {}
|
|
27
|
+
type RegisteredConfig = Register extends {
|
|
28
|
+
readonly config: infer TConfig extends PluginConfig;
|
|
29
|
+
} ? TConfig : PluginConfig;
|
|
30
|
+
declare function defineConfig<TSettings extends undefined | z.ZodType = undefined, const TCollections extends CollectionsDeclaration$1 = {}>(config: {
|
|
31
|
+
readonly settings?: TSettings;
|
|
32
|
+
readonly storage?: TCollections;
|
|
33
|
+
}): PluginConfig<TSettings, TCollections>;
|
|
34
|
+
//#endregion
|
|
3
35
|
//#region ../core/src/approvals/approvals.types.d.ts
|
|
4
36
|
/** how a gated tool's approval payload is presented (§6.2): collapsed behind a control, or verbatim */
|
|
5
37
|
type ApprovalPayloadPresentation = 'collapse' | 'verbatim';
|
|
6
38
|
//#endregion
|
|
7
|
-
//#region ../core/src/utils/result.utils.d.ts
|
|
8
|
-
declare namespace Result {
|
|
9
|
-
const symbol: unique symbol;
|
|
10
|
-
type InferOkTypes<TResult> = TResult extends Ok<infer TValue, unknown> ? TValue : never;
|
|
11
|
-
type InferErrTypes<TResult> = TResult extends Err<infer TError> ? TError : never;
|
|
12
|
-
type InferPipeReturnType<TReturn, TError> = [TReturn] extends [infer TResult extends Result] ? Result<InferOkTypes<TResult>, InferErrTypes<TResult> | TError> : Result<TReturn, TError>;
|
|
13
|
-
export interface Err<TError> {
|
|
14
|
-
error: TError;
|
|
15
|
-
pipe<TReturn>(fn: (value: never) => TReturn): InferPipeReturnType<TReturn, TError>;
|
|
16
|
-
success: false;
|
|
17
|
-
[symbol]: true;
|
|
18
|
-
unwrap(): never;
|
|
19
|
-
value?: never;
|
|
20
|
-
}
|
|
21
|
-
export interface Ok<TValue, TError> {
|
|
22
|
-
error?: never;
|
|
23
|
-
pipe<TReturn>(fn: (value: TValue) => TReturn): InferPipeReturnType<TReturn, TError>;
|
|
24
|
-
success: true;
|
|
25
|
-
[symbol]: true;
|
|
26
|
-
unwrap(): TValue;
|
|
27
|
-
value: TValue;
|
|
28
|
-
}
|
|
29
|
-
export function err(): Err<void>;
|
|
30
|
-
export function err<TError>(error: TError): Err<TError>;
|
|
31
|
-
export function ok(): Ok<void, never>;
|
|
32
|
-
export function ok<TValue>(value: TValue): Ok<TValue, never>;
|
|
33
|
-
export {};
|
|
34
|
-
}
|
|
35
|
-
type Result<TValue = unknown, TError = unknown> = Result.Err<TError> | Result.Ok<TValue, TError>;
|
|
36
|
-
//#endregion
|
|
37
39
|
//#region ../core/src/utils/token.utils.d.ts
|
|
38
40
|
declare const SERVICE_INSTANCE: unique symbol;
|
|
39
41
|
/**
|
|
@@ -70,74 +72,35 @@ type ToolDisclosure = {
|
|
|
70
72
|
readonly reference: string;
|
|
71
73
|
readonly supersededDescriptions?: readonly string[];
|
|
72
74
|
};
|
|
73
|
-
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region ../core/src/plugins/plugins.types.d.ts
|
|
77
|
+
/** what a plugin tool body may return: the text alone, or the text beside a durable record's disclosure (§3.4) */
|
|
78
|
+
type PluginToolOutput = string | {
|
|
74
79
|
readonly disclosure?: ToolDisclosure;
|
|
75
80
|
readonly text: string;
|
|
76
81
|
};
|
|
77
|
-
declare namespace ToolFailure {
|
|
78
|
-
/** the tool body threw — a semantic failure that terminates the turn (§7.1, §7.2) */
|
|
79
|
-
type Exception = {
|
|
80
|
-
kind: 'exception';
|
|
81
|
-
message: string;
|
|
82
|
-
};
|
|
83
|
-
/** the arguments were rejected — returned to the model as the tool result; the turn continues */
|
|
84
|
-
type InvalidArguments = {
|
|
85
|
-
kind: 'invalid-arguments';
|
|
86
|
-
message: string;
|
|
87
|
-
};
|
|
88
|
-
/** execution outlived `timeoutMs` — for a mutation the side effect is unconfirmed (§7.1, §7.2) */
|
|
89
|
-
type Timeout = {
|
|
90
|
-
kind: 'timeout';
|
|
91
|
-
timeoutMs: number;
|
|
92
|
-
};
|
|
93
|
-
/**
|
|
94
|
-
* The tool committed something whose outcome cannot be established — a send that may or may not
|
|
95
|
-
* have left. The turn ends stating the ambiguity (§7.1); it is never returned to the model,
|
|
96
|
-
* because a model told "unresolved" will try again, which is precisely what must not happen.
|
|
97
|
-
*/
|
|
98
|
-
type Unresolved = {
|
|
99
|
-
kind: 'unresolved';
|
|
100
|
-
message: string;
|
|
101
|
-
};
|
|
102
|
-
/** the model named a tool that does not exist or sits outside its configured set (§6.1, §7.2) */
|
|
103
|
-
type UnknownTool = {
|
|
104
|
-
kind: 'unknown-tool';
|
|
105
|
-
message: string;
|
|
106
|
-
};
|
|
107
|
-
type Any = Exception | InvalidArguments | Timeout | UnknownTool | Unresolved;
|
|
108
|
-
}
|
|
109
|
-
type ToolFailure = ToolFailure.Any;
|
|
110
|
-
/** what an execution settles to; `execute` may return it sync or promised */
|
|
111
|
-
type ToolResult = Result<ToolOutput, ToolFailure>;
|
|
112
82
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
83
|
+
* The two failures a tool body may raise itself — the rest of the taxonomy (§7.1) is the
|
|
84
|
+
* framework's to raise. Each throws; the perimeter wrapper maps the throw into the taxonomy.
|
|
115
85
|
*/
|
|
116
|
-
type
|
|
86
|
+
type PluginToolErr = {
|
|
87
|
+
/** the arguments were rejected — returned to the model as the tool result; the turn continues */
|
|
88
|
+
invalidArguments(message: string): never;
|
|
89
|
+
/** a committed side effect whose outcome cannot be established; the turn ends stating the ambiguity */
|
|
90
|
+
unresolved(message: string): never;
|
|
91
|
+
};
|
|
92
|
+
/** one tool as a plugin declares it: the framework's tool minus `budgetExempt`, returning plain output */
|
|
93
|
+
type PluginToolDeclaration<TContext, TParams extends z.ZodType> = {
|
|
117
94
|
/** present ⇒ the tool always gates (§5); renders the payload the approver reads and cannot decline */
|
|
118
|
-
approval?(args: z
|
|
119
|
-
/** §5.3 — never billed against the action budget; framework toolsets only, rejected at the plugin perimeter (§6) */
|
|
120
|
-
readonly budgetExempt?: boolean;
|
|
95
|
+
approval?(args: z.infer<TParams>): ToolApprovalPayload;
|
|
121
96
|
readonly description: string;
|
|
122
|
-
execute(args: z
|
|
97
|
+
execute(args: z.infer<TParams>, context: TContext): Promisable<PluginToolOutput>;
|
|
123
98
|
readonly parameters: TParams;
|
|
124
99
|
/** §7.2 — whether a timed-out call may be reported to the model as a plain failure; false ends the turn as unconfirmable */
|
|
125
100
|
readonly retryable?: boolean;
|
|
126
101
|
readonly timeoutMs?: number;
|
|
127
102
|
/** §8.1 — the one-line summary beside the name in the status post; absent shows the name alone */
|
|
128
|
-
traceDetail?(args: z
|
|
129
|
-
};
|
|
130
|
-
//#endregion
|
|
131
|
-
//#region src/tool.utils.d.ts
|
|
132
|
-
declare function ok(text: string): ToolResult;
|
|
133
|
-
/** the failures a tool body may raise itself; timeout and unknown-tool are the framework's to raise */
|
|
134
|
-
declare const fail: {
|
|
135
|
-
/** a semantic failure that terminates the turn (§7.1) */
|
|
136
|
-
exception: (message: string) => ToolResult;
|
|
137
|
-
/** returned to the model as the tool result; the turn continues */
|
|
138
|
-
invalidArguments: (message: string) => ToolResult;
|
|
139
|
-
/** a committed side effect whose outcome cannot be established; the turn ends stating the ambiguity (§7.1) */
|
|
140
|
-
unresolved: (message: string) => ToolResult;
|
|
103
|
+
traceDetail?(args: z.infer<TParams>): string;
|
|
141
104
|
};
|
|
142
105
|
//#endregion
|
|
143
106
|
//#region ../core/src/toolsets/toolsets.types.d.ts
|
|
@@ -150,10 +113,7 @@ type ServicesDeclaration = {
|
|
|
150
113
|
readonly turn?: never;
|
|
151
114
|
};
|
|
152
115
|
type CollectionsDeclaration = {
|
|
153
|
-
readonly [key: string]: z
|
|
154
|
-
};
|
|
155
|
-
type ParametersDeclaration = {
|
|
156
|
-
readonly [key: string]: z$1.ZodType;
|
|
116
|
+
readonly [key: string]: z.ZodType;
|
|
157
117
|
};
|
|
158
118
|
type EmptyDeclaration = {};
|
|
159
119
|
/** a toolset-scoped handle over one declared storage collection; rows are validated on write and parsed on read */
|
|
@@ -171,30 +131,21 @@ type ToolsetCollection<TValue> = {
|
|
|
171
131
|
* under its own name, `settings` and `storage` only when declared, and always the turn. Reaching
|
|
172
132
|
* anything undeclared is a compile error.
|
|
173
133
|
*/
|
|
174
|
-
type ToolsetContext<TServices extends ServicesDeclaration = EmptyDeclaration, TSettings extends undefined | z
|
|
134
|
+
type ToolsetContext<TServices extends ServicesDeclaration = EmptyDeclaration, TSettings extends undefined | z.ZodType = undefined, TCollections extends CollectionsDeclaration = EmptyDeclaration> = { readonly [K in keyof TServices]: TServices[K] extends ServiceToken<infer TInstance> ? TInstance : never; } & {
|
|
175
135
|
readonly turn: ToolTurnScope;
|
|
176
136
|
} & (keyof TCollections extends never ? unknown : {
|
|
177
|
-
readonly storage: { readonly [K in keyof TCollections]: ToolsetCollection<z
|
|
178
|
-
}) & (TSettings extends z
|
|
179
|
-
readonly settings: z
|
|
137
|
+
readonly storage: { readonly [K in keyof TCollections]: ToolsetCollection<z.infer<TCollections[K]>>; };
|
|
138
|
+
}) & (TSettings extends z.ZodType ? {
|
|
139
|
+
readonly settings: z.infer<TSettings>;
|
|
180
140
|
} : unknown);
|
|
181
|
-
/** a plugin's toolset: the same declaration, refused `services` and `budgetExempt` (§7) */
|
|
182
|
-
type PluginToolsetDeclaration<TName extends string, TSettings extends undefined | z$1.ZodType, TCollections extends CollectionsDeclaration, TParamsMap extends ParametersDeclaration> = {
|
|
183
|
-
readonly name: TName;
|
|
184
|
-
readonly settings?: TSettings;
|
|
185
|
-
readonly skills?: readonly string[];
|
|
186
|
-
readonly storage?: TCollections;
|
|
187
|
-
readonly tools: { readonly [K in keyof TParamsMap]: Omit<ToolDefinition<ToolsetContext<EmptyDeclaration, TSettings, TCollections>, TParamsMap[K]>, 'budgetExempt'>; };
|
|
188
|
-
};
|
|
189
|
-
/** the SDK's narrowed view of `defineToolset` — one runtime function under one name for both audiences (§7) */
|
|
190
|
-
type DefinePluginToolset = <const TName extends string, TParamsMap extends ParametersDeclaration, TSettings extends undefined | z$1.ZodType = undefined, const TCollections extends CollectionsDeclaration = EmptyDeclaration>(declaration: PluginToolsetDeclaration<TName, TSettings, TCollections, TParamsMap>) => PluginToolsetDeclaration<TName, TSettings, TCollections, TParamsMap>;
|
|
191
141
|
//#endregion
|
|
192
|
-
//#region src/
|
|
193
|
-
/**
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
142
|
+
//#region src/tool.d.ts
|
|
143
|
+
/** what `execute` receives: the registered config's settings and storage, the failure raisers, and the four facts of the turn */
|
|
144
|
+
type ToolContext = ToolsetContext<EmptyDeclaration, RegisteredConfig['settings'], RegisteredConfig['storage']> & {
|
|
145
|
+
readonly err: PluginToolErr;
|
|
146
|
+
};
|
|
147
|
+
type PluginTool<TParams extends z.ZodType> = PluginToolDeclaration<ToolContext, TParams>;
|
|
148
|
+
/** identity at runtime; what it is for is typing `args` from `parameters` across the whole declaration */
|
|
149
|
+
declare function defineTool<TParams extends z.ZodType>(tool: PluginTool<TParams>): PluginTool<TParams>;
|
|
199
150
|
//#endregion
|
|
200
|
-
export { type
|
|
151
|
+
export { type PluginTool, type Register, type ToolApprovalPayload, type ToolContext, type ToolDisclosure, type ToolTurnScope, defineConfig, defineTool };
|
package/dist/index.js
CHANGED
|
@@ -1,176 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* @returns True if the value is a plain object, otherwise false.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* // ✅👇 True
|
|
12
|
-
*
|
|
13
|
-
* isPlainObject({ }); // ✅
|
|
14
|
-
* isPlainObject({ key: 'value' }); // ✅
|
|
15
|
-
* isPlainObject({ key: new Date() }); // ✅
|
|
16
|
-
* isPlainObject(new Object()); // ✅
|
|
17
|
-
* isPlainObject(Object.create(null)); // ✅
|
|
18
|
-
* isPlainObject({ nested: { key: true} }); // ✅
|
|
19
|
-
* isPlainObject(new Proxy({}, {})); // ✅
|
|
20
|
-
* isPlainObject({ [Symbol('tag')]: 'A' }); // ✅
|
|
21
|
-
*
|
|
22
|
-
* // ✅👇 (cross-realms, node context, workers, ...)
|
|
23
|
-
* const runInNewContext = await import('node:vm').then(
|
|
24
|
-
* (mod) => mod.runInNewContext
|
|
25
|
-
* );
|
|
26
|
-
* isPlainObject(runInNewContext('({})')); // ✅
|
|
27
|
-
*
|
|
28
|
-
* // ❌👇 False
|
|
29
|
-
*
|
|
30
|
-
* class Test { };
|
|
31
|
-
* isPlainObject(new Test()) // ❌
|
|
32
|
-
* isPlainObject(10); // ❌
|
|
33
|
-
* isPlainObject(null); // ❌
|
|
34
|
-
* isPlainObject('hello'); // ❌
|
|
35
|
-
* isPlainObject([]); // ❌
|
|
36
|
-
* isPlainObject(new Date()); // ❌
|
|
37
|
-
* isPlainObject(new Uint8Array([1])); // ❌
|
|
38
|
-
* isPlainObject(Buffer.from('ABC')); // ❌
|
|
39
|
-
* isPlainObject(Promise.resolve({})); // ❌
|
|
40
|
-
* isPlainObject(Object.create({})); // ❌
|
|
41
|
-
* isPlainObject(new (class Cls {})); // ❌
|
|
42
|
-
* isPlainObject(globalThis); // ❌,
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
function isPlainObject(value) {
|
|
46
|
-
if (!value || typeof value !== "object") return false;
|
|
47
|
-
const proto = Object.getPrototypeOf(value);
|
|
48
|
-
if (!(proto === null || proto === Object.prototype || Object.getPrototypeOf(proto) === null)) return false;
|
|
49
|
-
return Object.prototype.toString.call(value) === "[object Object]";
|
|
1
|
+
//#region src/config.ts
|
|
2
|
+
function defineConfig(config) {
|
|
3
|
+
return {
|
|
4
|
+
settings: config.settings,
|
|
5
|
+
storage: config.storage ?? {}
|
|
6
|
+
};
|
|
50
7
|
}
|
|
51
8
|
//#endregion
|
|
52
|
-
//#region
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
function isResult(value) {
|
|
57
|
-
if (!isPlainObject(value)) return false;
|
|
58
|
-
return value[symbol] === true;
|
|
59
|
-
}
|
|
60
|
-
function err(error) {
|
|
61
|
-
return {
|
|
62
|
-
error,
|
|
63
|
-
pipe() {
|
|
64
|
-
return err(this.error);
|
|
65
|
-
},
|
|
66
|
-
success: false,
|
|
67
|
-
[symbol]: true,
|
|
68
|
-
unwrap() {
|
|
69
|
-
throw this.error;
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
Result.err = err;
|
|
74
|
-
function ok(value) {
|
|
75
|
-
return {
|
|
76
|
-
pipe(fn) {
|
|
77
|
-
const value = fn(this.value);
|
|
78
|
-
if (isResult(value)) return value;
|
|
79
|
-
return ok(value);
|
|
80
|
-
},
|
|
81
|
-
success: true,
|
|
82
|
-
[symbol]: true,
|
|
83
|
-
unwrap() {
|
|
84
|
-
return this.value;
|
|
85
|
-
},
|
|
86
|
-
value
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
Result.ok = ok;
|
|
90
|
-
})(Result || (Result = {}));
|
|
91
|
-
//#endregion
|
|
92
|
-
//#region src/tool.utils.ts
|
|
93
|
-
function ok(text) {
|
|
94
|
-
return Result.ok({ text });
|
|
95
|
-
}
|
|
96
|
-
/** the failures a tool body may raise itself; timeout and unknown-tool are the framework's to raise */
|
|
97
|
-
const fail = {
|
|
98
|
-
/** a semantic failure that terminates the turn (§7.1) */
|
|
99
|
-
exception: (message) => Result.err({
|
|
100
|
-
kind: "exception",
|
|
101
|
-
message
|
|
102
|
-
}),
|
|
103
|
-
/** returned to the model as the tool result; the turn continues */
|
|
104
|
-
invalidArguments: (message) => Result.err({
|
|
105
|
-
kind: "invalid-arguments",
|
|
106
|
-
message
|
|
107
|
-
}),
|
|
108
|
-
/** a committed side effect whose outcome cannot be established; the turn ends stating the ambiguity (§7.1) */
|
|
109
|
-
unresolved: (message) => Result.err({
|
|
110
|
-
kind: "unresolved",
|
|
111
|
-
message
|
|
112
|
-
})
|
|
113
|
-
};
|
|
114
|
-
//#endregion
|
|
115
|
-
//#region ../core/dist/tools/tools.constants.js
|
|
116
|
-
/**
|
|
117
|
-
* One segment of a tool identity (§1): lowercase snake_case with single underscores, never
|
|
118
|
-
* doubled — which is what makes `__` an unambiguous join in the wire form.
|
|
119
|
-
*/
|
|
120
|
-
const TOOL_SEGMENT_PATTERN = /^[a-z](?:_?[a-z0-9])*$/;
|
|
121
|
-
//#endregion
|
|
122
|
-
//#region ../core/dist/tools/tools.utils.js
|
|
123
|
-
/**
|
|
124
|
-
* The model-facing form, produced at request assembly and applied to the tool schemas and the
|
|
125
|
-
* replayed call history together; nothing downstream of the provider response retains it.
|
|
126
|
-
*/
|
|
127
|
-
function renderToolWireName([namespace, name]) {
|
|
128
|
-
return `${namespace}__${name}`;
|
|
129
|
-
}
|
|
130
|
-
function assertToolSegment(value, subject) {
|
|
131
|
-
if (!TOOL_SEGMENT_PATTERN.test(value)) throw new Error(`${subject} "${value}" is not lowercase snake_case with single underscores`);
|
|
132
|
-
}
|
|
133
|
-
function assertWireNameWithinLimit(id) {
|
|
134
|
-
const wireName = renderToolWireName(id);
|
|
135
|
-
if (wireName.length > 64) throw new Error(`tool name "${wireName}" exceeds the 64-character provider limit`);
|
|
9
|
+
//#region src/tool.ts
|
|
10
|
+
/** identity at runtime; what it is for is typing `args` from `parameters` across the whole declaration */
|
|
11
|
+
function defineTool(tool) {
|
|
12
|
+
return tool;
|
|
136
13
|
}
|
|
137
14
|
//#endregion
|
|
138
|
-
|
|
139
|
-
/** §9 — in every agent's manifest, never grantable; naming one in config is an error */
|
|
140
|
-
const BUILTIN_CORE_SKILL_NAMES = ["handing-work-to-a-peer"];
|
|
141
|
-
/** the library skills an operator may assign — the bare-name half of the `agents[].skills` grammar */
|
|
142
|
-
const BUILTIN_GRANTABLE_SKILL_NAMES = [];
|
|
143
|
-
[...BUILTIN_CORE_SKILL_NAMES, ...BUILTIN_GRANTABLE_SKILL_NAMES];
|
|
144
|
-
/** skill names keep the dashed convention of skill files; a skill never reaches a provider as a tool name (§9) */
|
|
145
|
-
const SKILL_NAME_PATTERN = /^[a-z](?:-?[a-z0-9])*$/;
|
|
146
|
-
//#endregion
|
|
147
|
-
//#region ../core/dist/skills/skills.utils.js
|
|
148
|
-
function assertSkillName(value) {
|
|
149
|
-
if (!SKILL_NAME_PATTERN.test(value)) throw new Error(`skill name "${value}" is not in the dashed skill-name grammar`);
|
|
150
|
-
}
|
|
151
|
-
//#endregion
|
|
152
|
-
//#region ../core/dist/toolsets/toolsets.utils.js
|
|
153
|
-
/**
|
|
154
|
-
* The single declaration function, framework and plugin alike (§7). Purely a perimeter for the
|
|
155
|
-
* grammar the type system cannot state; the declaration itself is inert data, returned as given.
|
|
156
|
-
*/
|
|
157
|
-
function defineToolset$1(declaration) {
|
|
158
|
-
assertToolSegment(declaration.name, "toolset namespace");
|
|
159
|
-
for (const toolName of Object.keys(declaration.tools)) {
|
|
160
|
-
assertToolSegment(toolName, "tool name");
|
|
161
|
-
assertWireNameWithinLimit([declaration.name, toolName]);
|
|
162
|
-
}
|
|
163
|
-
for (const collectionName of Object.keys(declaration.storage ?? {})) assertToolSegment(collectionName, "storage collection name");
|
|
164
|
-
for (const skillName of declaration.skills ?? []) assertSkillName(skillName);
|
|
165
|
-
return declaration;
|
|
166
|
-
}
|
|
167
|
-
//#endregion
|
|
168
|
-
//#region src/toolset.ts
|
|
169
|
-
/**
|
|
170
|
-
* The same function the framework declares toolsets with (§7), narrowed to the plugin surface: no
|
|
171
|
-
* `services`, no `budgetExempt`. The narrowing is type-level here and structural at the load
|
|
172
|
-
* perimeter, where either key is an unrecognized-key refusal.
|
|
173
|
-
*/
|
|
174
|
-
const defineToolset = defineToolset$1;
|
|
175
|
-
//#endregion
|
|
176
|
-
export { Result, defineToolset, fail, ok, z };
|
|
15
|
+
export { defineConfig, defineTool };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@collegium/sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
4
|
+
"version": "0.0.1-beta.5",
|
|
5
5
|
"description": "Write a Collegium plugin: declare a toolset with its tools, settings, storage, and skills.",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
7
7
|
"homepage": "https://collegium.sh",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
".": {
|
|
21
21
|
"types": "./dist/index.d.ts",
|
|
22
22
|
"default": "./dist/index.js"
|
|
23
|
-
}
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
24
25
|
},
|
|
25
26
|
"files": [
|
|
26
27
|
"dist"
|
|
@@ -28,12 +29,15 @@
|
|
|
28
29
|
"engines": {
|
|
29
30
|
"node": "24.x"
|
|
30
31
|
},
|
|
31
|
-
"
|
|
32
|
-
"type-fest": "^5.8.0",
|
|
32
|
+
"peerDependencies": {
|
|
33
33
|
"zod": "^4.4.3"
|
|
34
34
|
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"type-fest": "^5.8.0"
|
|
37
|
+
},
|
|
35
38
|
"devDependencies": {
|
|
36
39
|
"tsdown": "^0.22.14",
|
|
40
|
+
"zod": "^4.4.3",
|
|
37
41
|
"@collegium/core": "0.0.0"
|
|
38
42
|
},
|
|
39
43
|
"publishConfig": {
|