@sentry/junior-plugin-api 0.122.0 → 0.123.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.
- package/README.md +18 -2
- package/dist/conversation-events.d.ts +63 -0
- package/dist/hooks.d.ts +8 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +253 -164
- package/dist/registration.d.ts +2 -0
- package/dist/tasks.d.ts +2 -0
- package/dist/tools.d.ts +34 -11
- package/package.json +1 -1
- package/src/conversation-events.ts +110 -0
- package/src/hooks.ts +8 -0
- package/src/index.ts +1 -0
- package/src/registration.ts +30 -0
- package/src/tasks.ts +2 -0
- package/src/tools.ts +51 -11
package/README.md
CHANGED
|
@@ -5,8 +5,10 @@ exported TypeScript types and runtime validators are authoritative.
|
|
|
5
5
|
|
|
6
6
|
## Registration
|
|
7
7
|
|
|
8
|
-
Use
|
|
9
|
-
|
|
8
|
+
Use
|
|
9
|
+
`defineJuniorPlugin({ manifest, hooks, tasks, cli, model, conversationEvents })`.
|
|
10
|
+
A plugin name is a lowercase identifier and is unique within the enabled app
|
|
11
|
+
plugin set.
|
|
10
12
|
|
|
11
13
|
Packages with JavaScript registration export one callable factory named
|
|
12
14
|
`<domain>Plugin`, such as `githubPlugin(options?)`. Do not use a public
|
|
@@ -68,6 +70,11 @@ routing, response validation, rendering, confirmation, and query state.
|
|
|
68
70
|
repeatedly.
|
|
69
71
|
- Background tasks are registered by name, receive validated parameters, and
|
|
70
72
|
execute through the host queue/callback lifecycle.
|
|
73
|
+
- Conversation-bound background tasks may emit registered structured events
|
|
74
|
+
through `ctx.events`. Define each version with `defineConversationEvent()`;
|
|
75
|
+
the host supplies the plugin namespace, conversation, turn, ordering, and
|
|
76
|
+
timestamps. Event definitions return bounded transcript presentation data,
|
|
77
|
+
while Junior owns browser rendering.
|
|
71
78
|
- `ctx.agent.dispatch` creates durable agent work with an explicit actor,
|
|
72
79
|
destination, source, metadata, and idempotency identity.
|
|
73
80
|
- Delegated credential subjects declare the narrow action that authorized them.
|
|
@@ -76,6 +83,15 @@ routing, response validation, rendering, confirmation, and query state.
|
|
|
76
83
|
- Completed dispatch and task projections are durable plugin inputs, not an
|
|
77
84
|
invitation to inspect unrestricted conversation state.
|
|
78
85
|
|
|
86
|
+
## Conversation Events
|
|
87
|
+
|
|
88
|
+
Register plugin-owned event definitions through `conversationEvents`. A
|
|
89
|
+
definition owns one local name, version, content schema, and `renderEvent()`
|
|
90
|
+
projection. The active plugin context supplies the namespace, so plugins cannot
|
|
91
|
+
emit native events or impersonate another plugin. Stored events remain durable
|
|
92
|
+
when a plugin is removed, but normal transcript projection skips definitions
|
|
93
|
+
that are not currently registered.
|
|
94
|
+
|
|
79
95
|
## Database
|
|
80
96
|
|
|
81
97
|
- Packaged migrations create plugin-owned tables through the host migration
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const conversationEventIconSchema: z.ZodEnum<{
|
|
3
|
+
check: "check";
|
|
4
|
+
key: "key";
|
|
5
|
+
warning: "warning";
|
|
6
|
+
link: "link";
|
|
7
|
+
activity: "activity";
|
|
8
|
+
brain: "brain";
|
|
9
|
+
calendar: "calendar";
|
|
10
|
+
database: "database";
|
|
11
|
+
info: "info";
|
|
12
|
+
sparkles: "sparkles";
|
|
13
|
+
}>;
|
|
14
|
+
/** Safe, core-rendered presentation for one plugin conversation event. */
|
|
15
|
+
export declare const conversationEventPresentationSchema: z.ZodObject<{
|
|
16
|
+
details: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
17
|
+
description: z.ZodOptional<z.ZodString>;
|
|
18
|
+
metadata: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
19
|
+
title: z.ZodString;
|
|
20
|
+
}, z.core.$strict>>>;
|
|
21
|
+
icon: z.ZodOptional<z.ZodEnum<{
|
|
22
|
+
check: "check";
|
|
23
|
+
key: "key";
|
|
24
|
+
warning: "warning";
|
|
25
|
+
link: "link";
|
|
26
|
+
activity: "activity";
|
|
27
|
+
brain: "brain";
|
|
28
|
+
calendar: "calendar";
|
|
29
|
+
database: "database";
|
|
30
|
+
info: "info";
|
|
31
|
+
sparkles: "sparkles";
|
|
32
|
+
}>>;
|
|
33
|
+
preview: z.ZodOptional<z.ZodString>;
|
|
34
|
+
title: z.ZodString;
|
|
35
|
+
}, z.core.$strict>;
|
|
36
|
+
export type ConversationEventPresentation = z.output<typeof conversationEventPresentationSchema>;
|
|
37
|
+
/** One validated plugin event value waiting for conversation-bound emission. */
|
|
38
|
+
export interface PluginConversationEventValue {
|
|
39
|
+
readonly data: Record<string, unknown>;
|
|
40
|
+
readonly definition: PluginConversationEventDefinition;
|
|
41
|
+
}
|
|
42
|
+
/** Registered schema and transcript presentation for one plugin event version. */
|
|
43
|
+
export interface PluginConversationEventDefinition {
|
|
44
|
+
readonly eventName: string;
|
|
45
|
+
readonly version: number;
|
|
46
|
+
parse(data: unknown): Record<string, unknown>;
|
|
47
|
+
renderEvent(data: Record<string, unknown>): ConversationEventPresentation;
|
|
48
|
+
}
|
|
49
|
+
/** Typed factory returned while authoring one plugin conversation event. */
|
|
50
|
+
export interface DefinedConversationEvent<TInput> extends PluginConversationEventDefinition {
|
|
51
|
+
(data: TInput): PluginConversationEventValue;
|
|
52
|
+
}
|
|
53
|
+
/** Define one typed, versioned plugin-owned conversation event. */
|
|
54
|
+
export declare function defineConversationEvent<TSchema extends z.ZodType<Record<string, unknown>>>(definition: {
|
|
55
|
+
name: string;
|
|
56
|
+
version: number;
|
|
57
|
+
schema: TSchema;
|
|
58
|
+
renderEvent(event: z.output<TSchema>): z.input<typeof conversationEventPresentationSchema>;
|
|
59
|
+
}): DefinedConversationEvent<z.input<TSchema>>;
|
|
60
|
+
/** Conversation-bound event writer supplied by Junior core. */
|
|
61
|
+
export interface PluginConversationEvents {
|
|
62
|
+
emit(event: PluginConversationEventValue): Promise<void>;
|
|
63
|
+
}
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import type { EgressHookContext, EgressResponseHookContext, IssueCredentialHookContext, PluginCredentialResult, PluginGrant, PluginProviderAccount, ResolveOAuthAccountHookContext } from "./credentials";
|
|
2
2
|
import type { HeartbeatHookContext, HeartbeatResult, OperationalReportHookContext, ApiRouteRegistrationHookContext, PluginOperationalReportContent, PluginRoute, PluginRouteApp, RouteRegistrationHookContext, SlackConversationLink, SlackConversationLinkHookContext } from "./operations";
|
|
3
|
-
import type { BeforeToolExecuteHookContext, PluginToolDefinition, SandboxPrepareHookContext, ToolRegistrationHookContext } from "./tools";
|
|
3
|
+
import type { AfterMcpToolHookContext, BeforeToolExecuteHookContext, PluginToolDefinition, SandboxPrepareHookContext, ToolRegistrationHookContext } from "./tools";
|
|
4
4
|
import type { PromptMessage, SystemPromptContext, UserPromptContext, UserPromptContribution } from "./prompt";
|
|
5
5
|
export interface PluginHooks {
|
|
6
6
|
systemPrompt?(ctx: SystemPromptContext): Promise<PromptMessage[]> | PromptMessage[];
|
|
7
7
|
userPrompt?(ctx: UserPromptContext): Promise<UserPromptContribution[] | undefined> | UserPromptContribution[] | undefined;
|
|
8
8
|
beforeToolExecute?(ctx: BeforeToolExecuteHookContext): Promise<void> | void;
|
|
9
|
+
/**
|
|
10
|
+
* Run after a successful hosted MCP tool call.
|
|
11
|
+
*
|
|
12
|
+
* Prefer this for junior-owned side effects such as conversation annotations.
|
|
13
|
+
* Do not use it to invent a parallel tool contract for the provider tool.
|
|
14
|
+
*/
|
|
15
|
+
afterMcpTool?(ctx: AfterMcpToolHookContext): Promise<void> | void;
|
|
9
16
|
grantForEgress?(ctx: EgressHookContext): Promise<PluginGrant | undefined> | PluginGrant | undefined;
|
|
10
17
|
heartbeat?(ctx: HeartbeatHookContext): Promise<HeartbeatResult | void> | HeartbeatResult | void;
|
|
11
18
|
issueCredential?(ctx: IssueCredentialHookContext): Promise<PluginCredentialResult> | PluginCredentialResult;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -20,32 +20,84 @@ var conversationAnnotationInputSchema = z.discriminatedUnion("kind", [
|
|
|
20
20
|
resourceLinkAnnotationSchema
|
|
21
21
|
]);
|
|
22
22
|
|
|
23
|
-
// src/
|
|
23
|
+
// src/conversation-events.ts
|
|
24
24
|
import { z as z2 } from "zod";
|
|
25
|
-
var
|
|
26
|
-
var
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
var conversationEventNameSchema = z2.string().trim().min(1).max(64).regex(/^[a-z][a-z0-9_]*$/);
|
|
26
|
+
var conversationEventIconSchema = z2.enum([
|
|
27
|
+
"activity",
|
|
28
|
+
"brain",
|
|
29
|
+
"calendar",
|
|
30
|
+
"check",
|
|
31
|
+
"database",
|
|
32
|
+
"info",
|
|
33
|
+
"key",
|
|
34
|
+
"link",
|
|
35
|
+
"sparkles",
|
|
36
|
+
"warning"
|
|
37
|
+
]);
|
|
38
|
+
var conversationEventDetailSchema = z2.object({
|
|
39
|
+
description: z2.string().trim().min(1).max(2e3).optional(),
|
|
40
|
+
metadata: z2.array(z2.string().trim().min(1).max(120)).max(8).optional(),
|
|
41
|
+
title: z2.string().trim().min(1).max(4e3)
|
|
42
|
+
}).strict();
|
|
43
|
+
var conversationEventPresentationSchema = z2.object({
|
|
44
|
+
details: z2.array(conversationEventDetailSchema).max(100).optional(),
|
|
45
|
+
icon: conversationEventIconSchema.optional(),
|
|
46
|
+
preview: z2.string().trim().min(1).max(500).optional(),
|
|
47
|
+
title: z2.string().trim().min(1).max(120)
|
|
48
|
+
}).strict();
|
|
49
|
+
function defineConversationEvent(definition) {
|
|
50
|
+
const identity = z2.object({
|
|
51
|
+
name: conversationEventNameSchema,
|
|
52
|
+
version: z2.number().int().positive()
|
|
53
|
+
}).strict().parse({ name: definition.name, version: definition.version });
|
|
54
|
+
let eventDefinition;
|
|
55
|
+
const createEvent = (data) => ({
|
|
56
|
+
data: definition.schema.parse(data),
|
|
57
|
+
definition: eventDefinition
|
|
58
|
+
});
|
|
59
|
+
eventDefinition = Object.assign(createEvent, {
|
|
60
|
+
eventName: identity.name,
|
|
61
|
+
version: identity.version,
|
|
62
|
+
parse(data) {
|
|
63
|
+
return definition.schema.parse(data);
|
|
64
|
+
},
|
|
65
|
+
renderEvent(data) {
|
|
66
|
+
const parsed = definition.schema.parse(data);
|
|
67
|
+
return conversationEventPresentationSchema.parse(
|
|
68
|
+
definition.renderEvent(parsed)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return eventDefinition;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/schemas.ts
|
|
76
|
+
import { z as z3 } from "zod";
|
|
77
|
+
var slackTeamIdSchema = z3.string().regex(/^T[A-Z0-9]+$/);
|
|
78
|
+
var slackConversationIdSchema = z3.string().regex(/^(C|G|D)[A-Z0-9]+$/);
|
|
79
|
+
var localConversationIdSchema = z3.string().regex(/^local:[a-z0-9_-]+:[a-z0-9][a-z0-9_-]*$/);
|
|
80
|
+
var exactActorUserIdSchema = z3.string().min(1).refine(
|
|
29
81
|
(value) => value === value.trim() && value.toLowerCase() !== "unknown"
|
|
30
82
|
);
|
|
31
|
-
var nonBlankStringSchema =
|
|
83
|
+
var nonBlankStringSchema = z3.string().refine((value) => value.trim().length > 0);
|
|
32
84
|
var exactNonBlankStringSchema = nonBlankStringSchema.refine(
|
|
33
85
|
(value) => value === value.trim()
|
|
34
86
|
);
|
|
35
|
-
var platformSchema =
|
|
36
|
-
var sourceTypeSchema =
|
|
37
|
-
var destinationVisibilitySchema =
|
|
38
|
-
var slackAddressSchema =
|
|
39
|
-
platform:
|
|
87
|
+
var platformSchema = z3.enum(["slack", "local"]);
|
|
88
|
+
var sourceTypeSchema = z3.enum(["pub", "priv"]);
|
|
89
|
+
var destinationVisibilitySchema = z3.enum(["public", "private"]);
|
|
90
|
+
var slackAddressSchema = z3.object({
|
|
91
|
+
platform: z3.literal("slack"),
|
|
40
92
|
teamId: slackTeamIdSchema,
|
|
41
93
|
channelId: slackConversationIdSchema
|
|
42
94
|
}).strict();
|
|
43
95
|
var slackDestinationSchema = slackAddressSchema;
|
|
44
|
-
var localDestinationSchema =
|
|
45
|
-
platform:
|
|
96
|
+
var localDestinationSchema = z3.object({
|
|
97
|
+
platform: z3.literal("local"),
|
|
46
98
|
conversationId: localConversationIdSchema
|
|
47
99
|
}).strict();
|
|
48
|
-
var destinationSchema =
|
|
100
|
+
var destinationSchema = z3.discriminatedUnion("platform", [
|
|
49
101
|
slackDestinationSchema,
|
|
50
102
|
localDestinationSchema
|
|
51
103
|
]);
|
|
@@ -54,27 +106,27 @@ var slackSourceSchema = slackAddressSchema.extend({
|
|
|
54
106
|
messageTs: nonBlankStringSchema.optional(),
|
|
55
107
|
threadTs: nonBlankStringSchema.optional()
|
|
56
108
|
}).strict();
|
|
57
|
-
var localSourceSchema =
|
|
58
|
-
platform:
|
|
59
|
-
type:
|
|
109
|
+
var localSourceSchema = z3.object({
|
|
110
|
+
platform: z3.literal("local"),
|
|
111
|
+
type: z3.literal("priv"),
|
|
60
112
|
conversationId: localConversationIdSchema
|
|
61
113
|
}).strict();
|
|
62
|
-
var sourceSchema =
|
|
114
|
+
var sourceSchema = z3.discriminatedUnion("platform", [
|
|
63
115
|
slackSourceSchema,
|
|
64
116
|
localSourceSchema
|
|
65
117
|
]);
|
|
66
|
-
var pluginCredentialSubjectSchema =
|
|
118
|
+
var pluginCredentialSubjectSchema = z3.discriminatedUnion(
|
|
67
119
|
"allowedWhen",
|
|
68
120
|
[
|
|
69
|
-
|
|
70
|
-
type:
|
|
121
|
+
z3.object({
|
|
122
|
+
type: z3.literal("user"),
|
|
71
123
|
userId: exactActorUserIdSchema,
|
|
72
|
-
allowedWhen:
|
|
124
|
+
allowedWhen: z3.literal("private-direct-conversation")
|
|
73
125
|
}).strict(),
|
|
74
|
-
|
|
75
|
-
type:
|
|
126
|
+
z3.object({
|
|
127
|
+
type: z3.literal("user"),
|
|
76
128
|
userId: exactActorUserIdSchema,
|
|
77
|
-
allowedWhen:
|
|
129
|
+
allowedWhen: z3.literal("scheduled-task"),
|
|
78
130
|
taskId: exactNonBlankStringSchema
|
|
79
131
|
}).strict()
|
|
80
132
|
]
|
|
@@ -85,29 +137,29 @@ var actorProfileSchema = {
|
|
|
85
137
|
userId: exactActorUserIdSchema,
|
|
86
138
|
userName: nonBlankStringSchema.optional()
|
|
87
139
|
};
|
|
88
|
-
var slackActorSchema =
|
|
140
|
+
var slackActorSchema = z3.object({
|
|
89
141
|
...actorProfileSchema,
|
|
90
|
-
platform:
|
|
142
|
+
platform: z3.literal("slack"),
|
|
91
143
|
teamId: slackTeamIdSchema
|
|
92
144
|
}).strict();
|
|
93
|
-
var localActorSchema =
|
|
145
|
+
var localActorSchema = z3.object({
|
|
94
146
|
...actorProfileSchema,
|
|
95
|
-
platform:
|
|
147
|
+
platform: z3.literal("local")
|
|
96
148
|
}).strict();
|
|
97
|
-
var systemActorSchema =
|
|
98
|
-
platform:
|
|
149
|
+
var systemActorSchema = z3.object({
|
|
150
|
+
platform: z3.literal("system"),
|
|
99
151
|
name: exactActorUserIdSchema
|
|
100
152
|
}).strict();
|
|
101
|
-
var actorSchema =
|
|
153
|
+
var actorSchema = z3.discriminatedUnion("platform", [
|
|
102
154
|
slackActorSchema,
|
|
103
155
|
localActorSchema,
|
|
104
156
|
systemActorSchema
|
|
105
157
|
]);
|
|
106
|
-
var dispatchMetadataSchema =
|
|
158
|
+
var dispatchMetadataSchema = z3.record(z3.string(), z3.string()).superRefine((metadata, ctx) => {
|
|
107
159
|
const entries = Object.entries(metadata);
|
|
108
160
|
if (entries.length > 20) {
|
|
109
161
|
ctx.addIssue({
|
|
110
|
-
code:
|
|
162
|
+
code: z3.ZodIssueCode.custom,
|
|
111
163
|
message: "Dispatch metadata has too many keys"
|
|
112
164
|
});
|
|
113
165
|
return;
|
|
@@ -115,7 +167,7 @@ var dispatchMetadataSchema = z2.record(z2.string(), z2.string()).superRefine((me
|
|
|
115
167
|
for (const [key, value] of entries) {
|
|
116
168
|
if (!key.trim()) {
|
|
117
169
|
ctx.addIssue({
|
|
118
|
-
code:
|
|
170
|
+
code: z3.ZodIssueCode.custom,
|
|
119
171
|
message: "Dispatch metadata values must be strings",
|
|
120
172
|
path: [key]
|
|
121
173
|
});
|
|
@@ -123,40 +175,40 @@ var dispatchMetadataSchema = z2.record(z2.string(), z2.string()).superRefine((me
|
|
|
123
175
|
}
|
|
124
176
|
if (key.length > 128) {
|
|
125
177
|
ctx.addIssue({
|
|
126
|
-
code:
|
|
178
|
+
code: z3.ZodIssueCode.custom,
|
|
127
179
|
message: "Dispatch metadata key exceeds the maximum length",
|
|
128
180
|
path: [key]
|
|
129
181
|
});
|
|
130
182
|
}
|
|
131
183
|
if (/[\r\n]/.test(key)) {
|
|
132
184
|
ctx.addIssue({
|
|
133
|
-
code:
|
|
185
|
+
code: z3.ZodIssueCode.custom,
|
|
134
186
|
message: "Dispatch metadata keys must be single-line strings",
|
|
135
187
|
path: [key]
|
|
136
188
|
});
|
|
137
189
|
}
|
|
138
190
|
if (/[\r\n]/.test(value)) {
|
|
139
191
|
ctx.addIssue({
|
|
140
|
-
code:
|
|
192
|
+
code: z3.ZodIssueCode.custom,
|
|
141
193
|
message: "Dispatch metadata values must be single-line strings",
|
|
142
194
|
path: [key]
|
|
143
195
|
});
|
|
144
196
|
}
|
|
145
197
|
if (value.length > 512) {
|
|
146
198
|
ctx.addIssue({
|
|
147
|
-
code:
|
|
199
|
+
code: z3.ZodIssueCode.custom,
|
|
148
200
|
message: "Dispatch metadata value exceeds the maximum length",
|
|
149
201
|
path: [key]
|
|
150
202
|
});
|
|
151
203
|
}
|
|
152
204
|
}
|
|
153
205
|
});
|
|
154
|
-
var dispatchOptionsSchema =
|
|
155
|
-
idempotencyKey: nonBlankStringSchema.pipe(
|
|
206
|
+
var dispatchOptionsSchema = z3.object({
|
|
207
|
+
idempotencyKey: nonBlankStringSchema.pipe(z3.string().max(512)),
|
|
156
208
|
credentialSubject: pluginCredentialSubjectSchema.optional(),
|
|
157
209
|
destination: slackDestinationSchema,
|
|
158
210
|
destinationVisibility: destinationVisibilitySchema,
|
|
159
|
-
input: nonBlankStringSchema.pipe(
|
|
211
|
+
input: nonBlankStringSchema.pipe(z3.string().max(32e3)),
|
|
160
212
|
metadata: dispatchMetadataSchema.optional(),
|
|
161
213
|
source: sourceSchema
|
|
162
214
|
}).strict();
|
|
@@ -197,15 +249,15 @@ function isSlackDestination(destination) {
|
|
|
197
249
|
}
|
|
198
250
|
|
|
199
251
|
// src/prompt.ts
|
|
200
|
-
import { z as
|
|
201
|
-
var promptContextKindSchema =
|
|
202
|
-
var promptMessageSchema =
|
|
203
|
-
text:
|
|
252
|
+
import { z as z4 } from "zod";
|
|
253
|
+
var promptContextKindSchema = z4.string().trim().min(1).max(64).regex(/^[a-z][a-z0-9_-]*$/);
|
|
254
|
+
var promptMessageSchema = z4.object({
|
|
255
|
+
text: z4.string().trim().min(1).max(8e3)
|
|
204
256
|
}).strict();
|
|
205
|
-
var promptContextSchema =
|
|
257
|
+
var promptContextSchema = z4.object({
|
|
206
258
|
kind: promptContextKindSchema,
|
|
207
|
-
version:
|
|
208
|
-
content:
|
|
259
|
+
version: z4.number().int().positive(),
|
|
260
|
+
content: z4.record(z4.string(), z4.unknown())
|
|
209
261
|
}).strict();
|
|
210
262
|
function definePromptContext(definition) {
|
|
211
263
|
const identity = promptContextSchema.pick({ kind: true, version: true }).parse({ kind: definition.kind, version: definition.version });
|
|
@@ -219,50 +271,50 @@ function definePromptContext(definition) {
|
|
|
219
271
|
}
|
|
220
272
|
|
|
221
273
|
// src/resource-events.ts
|
|
222
|
-
import { z as
|
|
223
|
-
var subscribableResourceSchema =
|
|
224
|
-
label:
|
|
225
|
-
provider:
|
|
226
|
-
resourceRef:
|
|
227
|
-
suggestedEvents:
|
|
228
|
-
supportedEvents:
|
|
229
|
-
type:
|
|
230
|
-
}).strict();
|
|
231
|
-
var resourceEventSchema =
|
|
232
|
-
eventKey:
|
|
233
|
-
eventType:
|
|
234
|
-
occurredAtMs:
|
|
235
|
-
provider:
|
|
236
|
-
resourceRef:
|
|
237
|
-
terminal:
|
|
238
|
-
trustedSummary:
|
|
239
|
-
untrustedText:
|
|
274
|
+
import { z as z5 } from "zod";
|
|
275
|
+
var subscribableResourceSchema = z5.object({
|
|
276
|
+
label: z5.string().min(1),
|
|
277
|
+
provider: z5.string().min(1),
|
|
278
|
+
resourceRef: z5.string().min(1),
|
|
279
|
+
suggestedEvents: z5.array(z5.string().min(1)).optional(),
|
|
280
|
+
supportedEvents: z5.array(z5.string().min(1)),
|
|
281
|
+
type: z5.string().min(1)
|
|
282
|
+
}).strict();
|
|
283
|
+
var resourceEventSchema = z5.object({
|
|
284
|
+
eventKey: z5.string().min(1),
|
|
285
|
+
eventType: z5.string().min(1),
|
|
286
|
+
occurredAtMs: z5.number().finite(),
|
|
287
|
+
provider: z5.string().min(1),
|
|
288
|
+
resourceRef: z5.string().min(1),
|
|
289
|
+
terminal: z5.boolean().optional(),
|
|
290
|
+
trustedSummary: z5.string().min(1),
|
|
291
|
+
untrustedText: z5.string().optional()
|
|
240
292
|
}).strict();
|
|
241
293
|
|
|
242
294
|
// src/tasks.ts
|
|
243
|
-
import { z as
|
|
244
|
-
var pluginRunTranscriptProvenanceSchema =
|
|
245
|
-
authority:
|
|
295
|
+
import { z as z6 } from "zod";
|
|
296
|
+
var pluginRunTranscriptProvenanceSchema = z6.object({
|
|
297
|
+
authority: z6.enum(["instruction", "context"]),
|
|
246
298
|
actor: actorSchema.optional()
|
|
247
299
|
}).strict();
|
|
248
|
-
var pluginRunTranscriptEntrySchema =
|
|
249
|
-
|
|
250
|
-
type:
|
|
251
|
-
role:
|
|
252
|
-
text:
|
|
300
|
+
var pluginRunTranscriptEntrySchema = z6.discriminatedUnion("type", [
|
|
301
|
+
z6.object({
|
|
302
|
+
type: z6.literal("message"),
|
|
303
|
+
role: z6.enum(["user", "assistant"]),
|
|
304
|
+
text: z6.string().min(1),
|
|
253
305
|
provenance: pluginRunTranscriptProvenanceSchema.optional(),
|
|
254
|
-
isRunActor:
|
|
306
|
+
isRunActor: z6.boolean().optional()
|
|
255
307
|
}).strict(),
|
|
256
|
-
|
|
257
|
-
type:
|
|
258
|
-
toolName:
|
|
259
|
-
isError:
|
|
260
|
-
text:
|
|
308
|
+
z6.object({
|
|
309
|
+
type: z6.literal("toolResult"),
|
|
310
|
+
toolName: z6.string().min(1),
|
|
311
|
+
isError: z6.boolean(),
|
|
312
|
+
text: z6.string().min(1).optional()
|
|
261
313
|
}).strict()
|
|
262
314
|
]);
|
|
263
|
-
var pluginRunContextSchema =
|
|
264
|
-
completedAtMs:
|
|
265
|
-
conversationId:
|
|
315
|
+
var pluginRunContextSchema = z6.object({
|
|
316
|
+
completedAtMs: z6.number().finite(),
|
|
317
|
+
conversationId: z6.string().min(1),
|
|
266
318
|
destination: destinationSchema,
|
|
267
319
|
/**
|
|
268
320
|
* All distinct actors annotated on this run's committed instruction-authority
|
|
@@ -272,52 +324,63 @@ var pluginRunContextSchema = z5.object({
|
|
|
272
324
|
* exceed the actors visible in the transcript slice. Usually `[run.actor]`;
|
|
273
325
|
* possibly empty for system runs with no human instructions.
|
|
274
326
|
*/
|
|
275
|
-
actors:
|
|
327
|
+
actors: z6.array(actorSchema),
|
|
276
328
|
/**
|
|
277
329
|
* The single actor this run executes as. Absent only for actor-less legacy
|
|
278
330
|
* system records, so authority-sensitive plugins must fail closed.
|
|
279
331
|
*/
|
|
280
332
|
actor: actorSchema.optional(),
|
|
281
|
-
runId:
|
|
333
|
+
runId: z6.string().min(1),
|
|
282
334
|
source: sourceSchema,
|
|
283
|
-
transcript:
|
|
335
|
+
transcript: z6.array(pluginRunTranscriptEntrySchema)
|
|
284
336
|
}).strict();
|
|
285
337
|
|
|
286
338
|
// src/tools.ts
|
|
287
|
-
import { z as
|
|
339
|
+
import { z as z7 } from "zod";
|
|
288
340
|
var PluginToolInputError = class extends Error {
|
|
289
341
|
constructor(message, options) {
|
|
290
342
|
super(message, options);
|
|
291
343
|
this.name = "PluginToolInputError";
|
|
292
344
|
}
|
|
293
345
|
};
|
|
294
|
-
var pluginToolContentSchema =
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
type:
|
|
298
|
-
data:
|
|
299
|
-
mimeType:
|
|
346
|
+
var pluginToolContentSchema = z7.discriminatedUnion("type", [
|
|
347
|
+
z7.object({ type: z7.literal("text"), text: z7.string() }).strict(),
|
|
348
|
+
z7.object({
|
|
349
|
+
type: z7.literal("image"),
|
|
350
|
+
data: z7.string(),
|
|
351
|
+
mimeType: z7.string()
|
|
300
352
|
}).strict()
|
|
301
353
|
]);
|
|
302
|
-
var pluginToolContinuationSchema =
|
|
303
|
-
arguments:
|
|
304
|
-
reason:
|
|
305
|
-
}).strict();
|
|
306
|
-
var pluginToolErrorSchema =
|
|
307
|
-
kind:
|
|
308
|
-
message:
|
|
309
|
-
retryable:
|
|
310
|
-
}).strict();
|
|
311
|
-
var pluginToolResultSchema =
|
|
312
|
-
ok:
|
|
313
|
-
status:
|
|
314
|
-
target:
|
|
315
|
-
data:
|
|
316
|
-
truncated:
|
|
354
|
+
var pluginToolContinuationSchema = z7.object({
|
|
355
|
+
arguments: z7.record(z7.string(), z7.unknown()),
|
|
356
|
+
reason: z7.string().min(1).optional()
|
|
357
|
+
}).strict();
|
|
358
|
+
var pluginToolErrorSchema = z7.object({
|
|
359
|
+
kind: z7.string().min(1),
|
|
360
|
+
message: z7.string().min(1),
|
|
361
|
+
retryable: z7.boolean().optional()
|
|
362
|
+
}).strict();
|
|
363
|
+
var pluginToolResultSchema = z7.object({
|
|
364
|
+
ok: z7.boolean(),
|
|
365
|
+
status: z7.enum(["success", "error"]),
|
|
366
|
+
target: z7.string().min(1).optional(),
|
|
367
|
+
data: z7.unknown().optional(),
|
|
368
|
+
truncated: z7.boolean().optional(),
|
|
317
369
|
continuation: pluginToolContinuationSchema.optional(),
|
|
318
|
-
error:
|
|
370
|
+
error: z7.union([pluginToolErrorSchema, z7.string()]).optional()
|
|
319
371
|
}).passthrough();
|
|
320
|
-
var toolApprovalModeSchema =
|
|
372
|
+
var toolApprovalModeSchema = z7.enum(["auto", "review", "approve"]);
|
|
373
|
+
var REQUIRED_TOOL_ANNOTATION_KEYS = [
|
|
374
|
+
"destructiveHint",
|
|
375
|
+
"idempotentHint",
|
|
376
|
+
"openWorldHint",
|
|
377
|
+
"readOnlyHint"
|
|
378
|
+
];
|
|
379
|
+
function missingToolAnnotationKeys(annotations) {
|
|
380
|
+
return REQUIRED_TOOL_ANNOTATION_KEYS.filter(
|
|
381
|
+
(key) => typeof annotations?.[key] !== "boolean"
|
|
382
|
+
);
|
|
383
|
+
}
|
|
321
384
|
function isPluginToolResultEnvelope(value) {
|
|
322
385
|
return value !== null && typeof value === "object" && Array.isArray(value.content) && "details" in value;
|
|
323
386
|
}
|
|
@@ -342,7 +405,7 @@ function createZodTool(definition, helperName) {
|
|
|
342
405
|
let modelInputSchema;
|
|
343
406
|
let modelOutputSchema;
|
|
344
407
|
try {
|
|
345
|
-
modelInputSchema =
|
|
408
|
+
modelInputSchema = z7.toJSONSchema(inputSchema);
|
|
346
409
|
} catch (error) {
|
|
347
410
|
throw new TypeError(
|
|
348
411
|
`${helperName}() inputSchema must be representable as JSON Schema.`,
|
|
@@ -350,7 +413,7 @@ function createZodTool(definition, helperName) {
|
|
|
350
413
|
);
|
|
351
414
|
}
|
|
352
415
|
try {
|
|
353
|
-
modelOutputSchema =
|
|
416
|
+
modelOutputSchema = z7.toJSONSchema(outputSchema);
|
|
354
417
|
} catch (error) {
|
|
355
418
|
throw new TypeError(
|
|
356
419
|
`${helperName}() outputSchema must be representable as JSON Schema.`,
|
|
@@ -359,6 +422,7 @@ function createZodTool(definition, helperName) {
|
|
|
359
422
|
}
|
|
360
423
|
return {
|
|
361
424
|
...tool,
|
|
425
|
+
approvalMode: tool.approvalMode ?? "auto",
|
|
362
426
|
inputSchema: modelInputSchema,
|
|
363
427
|
outputSchema: modelOutputSchema,
|
|
364
428
|
prepareArguments(args) {
|
|
@@ -375,7 +439,7 @@ function createZodTool(definition, helperName) {
|
|
|
375
439
|
);
|
|
376
440
|
if (isPluginToolResultEnvelope(result)) {
|
|
377
441
|
return {
|
|
378
|
-
content:
|
|
442
|
+
content: z7.array(pluginToolContentSchema).parse(result.content),
|
|
379
443
|
details: outputSchema.parse(
|
|
380
444
|
pluginToolResultSchema.parse(result.details)
|
|
381
445
|
)
|
|
@@ -394,74 +458,74 @@ function definePluginTool(definition) {
|
|
|
394
458
|
}
|
|
395
459
|
|
|
396
460
|
// src/operations.ts
|
|
397
|
-
import { z as
|
|
398
|
-
var pluginApiRouteRequestContextSchema =
|
|
399
|
-
auth:
|
|
400
|
-
user:
|
|
401
|
-
email:
|
|
402
|
-
emailVerified:
|
|
403
|
-
name:
|
|
461
|
+
import { z as z8 } from "zod";
|
|
462
|
+
var pluginApiRouteRequestContextSchema = z8.object({
|
|
463
|
+
auth: z8.object({
|
|
464
|
+
user: z8.object({
|
|
465
|
+
email: z8.string().nullable().optional(),
|
|
466
|
+
emailVerified: z8.boolean().optional(),
|
|
467
|
+
name: z8.string().nullable().optional()
|
|
404
468
|
}).strict()
|
|
405
469
|
}).strict(),
|
|
406
470
|
pluginName: nonBlankStringSchema
|
|
407
471
|
}).strict();
|
|
408
472
|
|
|
409
473
|
// src/credentials.ts
|
|
410
|
-
import { z as
|
|
411
|
-
var pluginProviderNameSchema =
|
|
412
|
-
var pluginGrantNameSchema =
|
|
413
|
-
var pluginGrantAccessSchema =
|
|
414
|
-
|
|
415
|
-
|
|
474
|
+
import { z as z9 } from "zod";
|
|
475
|
+
var pluginProviderNameSchema = z9.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
476
|
+
var pluginGrantNameSchema = z9.string().regex(/^[a-z][a-z0-9.-]*$/);
|
|
477
|
+
var pluginGrantAccessSchema = z9.union([
|
|
478
|
+
z9.literal("read"),
|
|
479
|
+
z9.literal("write")
|
|
416
480
|
]);
|
|
417
|
-
var pluginAuthorizationSchema =
|
|
481
|
+
var pluginAuthorizationSchema = z9.object({
|
|
418
482
|
provider: pluginProviderNameSchema,
|
|
419
483
|
scope: nonBlankStringSchema.optional(),
|
|
420
|
-
type:
|
|
484
|
+
type: z9.literal("oauth")
|
|
421
485
|
}).strict();
|
|
422
|
-
var pluginProviderAccountSchema =
|
|
486
|
+
var pluginProviderAccountSchema = z9.object({
|
|
423
487
|
id: nonBlankStringSchema,
|
|
424
488
|
label: nonBlankStringSchema.optional(),
|
|
425
489
|
url: nonBlankStringSchema.optional()
|
|
426
490
|
}).strict();
|
|
427
|
-
var pluginStoredTokensSchema =
|
|
491
|
+
var pluginStoredTokensSchema = z9.object({
|
|
428
492
|
account: pluginProviderAccountSchema.optional(),
|
|
429
493
|
accessToken: nonBlankStringSchema,
|
|
430
|
-
expiresAt:
|
|
494
|
+
expiresAt: z9.number().finite().optional(),
|
|
431
495
|
refreshToken: nonBlankStringSchema,
|
|
432
|
-
refreshTokenExpiresAt:
|
|
496
|
+
refreshTokenExpiresAt: z9.number().finite().optional(),
|
|
433
497
|
scope: nonBlankStringSchema.optional()
|
|
434
498
|
}).strict();
|
|
435
|
-
var pluginGrantSchema =
|
|
499
|
+
var pluginGrantSchema = z9.object({
|
|
436
500
|
access: pluginGrantAccessSchema,
|
|
437
501
|
leaseScope: nonBlankStringSchema.optional(),
|
|
438
502
|
name: pluginGrantNameSchema,
|
|
439
503
|
reason: nonBlankStringSchema.optional(),
|
|
440
|
-
requirements:
|
|
504
|
+
requirements: z9.array(nonBlankStringSchema).min(1).optional()
|
|
441
505
|
}).strict();
|
|
442
|
-
var pluginCredentialHeaderTransformSchema =
|
|
443
|
-
domain:
|
|
444
|
-
headers:
|
|
506
|
+
var pluginCredentialHeaderTransformSchema = z9.object({
|
|
507
|
+
domain: z9.string().min(1),
|
|
508
|
+
headers: z9.record(z9.string(), z9.string()).refine((headers) => Object.keys(headers).length > 0)
|
|
445
509
|
}).strict();
|
|
446
|
-
var pluginCredentialLeaseSchema =
|
|
510
|
+
var pluginCredentialLeaseSchema = z9.object({
|
|
447
511
|
account: pluginProviderAccountSchema.optional(),
|
|
448
512
|
authorization: pluginAuthorizationSchema.optional(),
|
|
449
|
-
expiresAt:
|
|
450
|
-
headerTransforms:
|
|
513
|
+
expiresAt: z9.string().refine((value) => Number.isFinite(Date.parse(value))),
|
|
514
|
+
headerTransforms: z9.array(pluginCredentialHeaderTransformSchema).min(1)
|
|
451
515
|
}).strict();
|
|
452
|
-
var pluginCredentialResultSchema =
|
|
453
|
-
|
|
516
|
+
var pluginCredentialResultSchema = z9.discriminatedUnion("type", [
|
|
517
|
+
z9.object({
|
|
454
518
|
lease: pluginCredentialLeaseSchema,
|
|
455
|
-
type:
|
|
519
|
+
type: z9.literal("lease")
|
|
456
520
|
}).strict(),
|
|
457
|
-
|
|
521
|
+
z9.object({
|
|
458
522
|
authorization: pluginAuthorizationSchema.optional(),
|
|
459
523
|
message: nonBlankStringSchema,
|
|
460
|
-
type:
|
|
524
|
+
type: z9.literal("needed")
|
|
461
525
|
}).strict(),
|
|
462
|
-
|
|
526
|
+
z9.object({
|
|
463
527
|
message: nonBlankStringSchema,
|
|
464
|
-
type:
|
|
528
|
+
type: z9.literal("unavailable")
|
|
465
529
|
}).strict()
|
|
466
530
|
]);
|
|
467
531
|
var EgressAuthRequired = class extends Error {
|
|
@@ -519,6 +583,26 @@ function defineJuniorPlugin(plugin) {
|
|
|
519
583
|
if (plugin.userPages !== void 0 && !Array.isArray(plugin.userPages)) {
|
|
520
584
|
throw new Error(`Junior plugin "${name}" userPages must be an array.`);
|
|
521
585
|
}
|
|
586
|
+
if (plugin.conversationEvents !== void 0 && !Array.isArray(plugin.conversationEvents)) {
|
|
587
|
+
throw new Error(
|
|
588
|
+
`Junior plugin "${name}" conversationEvents must be an array.`
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
const conversationEventIds = /* @__PURE__ */ new Set();
|
|
592
|
+
for (const event of plugin.conversationEvents ?? []) {
|
|
593
|
+
if (!event || typeof event !== "object" && typeof event !== "function" || typeof event.parse !== "function" || typeof event.renderEvent !== "function") {
|
|
594
|
+
throw new Error(
|
|
595
|
+
`Junior plugin "${name}" conversation event definitions must be created with defineConversationEvent().`
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const id = `${event.eventName}@${event.version}`;
|
|
599
|
+
if (conversationEventIds.has(id)) {
|
|
600
|
+
throw new Error(
|
|
601
|
+
`Junior plugin "${name}" has duplicate conversation event "${id}".`
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
conversationEventIds.add(id);
|
|
605
|
+
}
|
|
522
606
|
const userPageIds = /* @__PURE__ */ new Set();
|
|
523
607
|
for (const page of plugin.userPages ?? []) {
|
|
524
608
|
if (!page || typeof page !== "object") {
|
|
@@ -552,56 +636,60 @@ function defineJuniorPlugin(plugin) {
|
|
|
552
636
|
}
|
|
553
637
|
|
|
554
638
|
// src/user-pages.ts
|
|
555
|
-
import { z as
|
|
639
|
+
import { z as z10 } from "zod";
|
|
556
640
|
var userPageIdSchema = nonBlankStringSchema.max(64).regex(/^[a-z][a-z0-9-]*$/);
|
|
557
641
|
var userPageLabelSchema = nonBlankStringSchema.max(80);
|
|
558
642
|
var userPageDescriptionSchema = nonBlankStringSchema.max(500);
|
|
559
|
-
var pluginUserPageMetadataSchema =
|
|
643
|
+
var pluginUserPageMetadataSchema = z10.object({
|
|
560
644
|
label: nonBlankStringSchema.max(80),
|
|
561
645
|
value: nonBlankStringSchema.max(500)
|
|
562
646
|
}).strict();
|
|
563
|
-
var pluginUserPageActionSchema =
|
|
647
|
+
var pluginUserPageActionSchema = z10.object({
|
|
564
648
|
confirmation: nonBlankStringSchema.max(500).optional(),
|
|
565
649
|
href: nonBlankStringSchema.max(500).regex(/^\/api\/plugins\/[a-z][a-z0-9-]*(?:\/|$)/),
|
|
566
650
|
label: nonBlankStringSchema.max(80),
|
|
567
|
-
method:
|
|
568
|
-
tone:
|
|
651
|
+
method: z10.literal("DELETE"),
|
|
652
|
+
tone: z10.enum(["danger", "neutral"]).optional()
|
|
569
653
|
}).strict();
|
|
570
|
-
var pluginUserPageRecordSchema =
|
|
571
|
-
actions:
|
|
654
|
+
var pluginUserPageRecordSchema = z10.object({
|
|
655
|
+
actions: z10.array(pluginUserPageActionSchema).max(4).optional(),
|
|
572
656
|
description: nonBlankStringSchema.max(1e3).optional(),
|
|
573
657
|
id: nonBlankStringSchema.max(128),
|
|
574
|
-
metadata:
|
|
658
|
+
metadata: z10.array(pluginUserPageMetadataSchema).max(8).optional(),
|
|
575
659
|
title: nonBlankStringSchema.max(4e3)
|
|
576
660
|
}).strict();
|
|
577
|
-
var pluginUserPageContentSchema =
|
|
661
|
+
var pluginUserPageContentSchema = z10.object({
|
|
578
662
|
emptyText: nonBlankStringSchema.max(500).optional(),
|
|
579
663
|
nextCursor: nonBlankStringSchema.max(1e3).optional(),
|
|
580
|
-
records:
|
|
664
|
+
records: z10.array(pluginUserPageRecordSchema).max(100),
|
|
581
665
|
searchPlaceholder: nonBlankStringSchema.max(120).optional(),
|
|
582
|
-
type:
|
|
666
|
+
type: z10.literal("list")
|
|
583
667
|
}).strict();
|
|
584
|
-
var pluginUserPageLinkSchema =
|
|
668
|
+
var pluginUserPageLinkSchema = z10.object({
|
|
585
669
|
description: userPageDescriptionSchema,
|
|
586
670
|
id: userPageIdSchema,
|
|
587
671
|
label: userPageLabelSchema,
|
|
588
672
|
pluginDisplayName: nonBlankStringSchema.max(200),
|
|
589
673
|
pluginName: nonBlankStringSchema.max(100)
|
|
590
674
|
}).strict();
|
|
591
|
-
var pluginUserPageLinksSchema =
|
|
592
|
-
var pluginUserPageInputSchema =
|
|
675
|
+
var pluginUserPageLinksSchema = z10.array(pluginUserPageLinkSchema);
|
|
676
|
+
var pluginUserPageInputSchema = z10.object({
|
|
593
677
|
cursor: nonBlankStringSchema.max(1e3).optional(),
|
|
594
|
-
limit:
|
|
595
|
-
query:
|
|
678
|
+
limit: z10.number().int().min(1).max(50),
|
|
679
|
+
query: z10.string().trim().max(200).optional()
|
|
596
680
|
}).strict();
|
|
597
681
|
export {
|
|
598
682
|
EgressAuthRequired,
|
|
599
683
|
EgressPolicyDenied,
|
|
600
684
|
PluginToolInputError,
|
|
685
|
+
REQUIRED_TOOL_ANNOTATION_KEYS,
|
|
601
686
|
actorSchema,
|
|
602
687
|
conversationAnnotationInputSchema,
|
|
688
|
+
conversationEventIconSchema,
|
|
689
|
+
conversationEventPresentationSchema,
|
|
603
690
|
createLocalSource,
|
|
604
691
|
createSlackSource,
|
|
692
|
+
defineConversationEvent,
|
|
605
693
|
defineJuniorPlugin,
|
|
606
694
|
definePluginTool,
|
|
607
695
|
definePromptContext,
|
|
@@ -614,6 +702,7 @@ export {
|
|
|
614
702
|
localActorSchema,
|
|
615
703
|
localDestinationSchema,
|
|
616
704
|
localSourceSchema,
|
|
705
|
+
missingToolAnnotationKeys,
|
|
617
706
|
nonBlankStringSchema,
|
|
618
707
|
platformSchema,
|
|
619
708
|
pluginApiRouteRequestContextSchema,
|
package/dist/registration.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PluginCliDefinition } from "./cli";
|
|
2
|
+
import type { PluginConversationEventDefinition } from "./conversation-events";
|
|
2
3
|
import type { PluginHooks } from "./hooks";
|
|
3
4
|
import type { PluginManifest } from "./manifest";
|
|
4
5
|
import type { PluginTasks } from "./tasks";
|
|
@@ -11,6 +12,7 @@ export interface PluginModelConfig {
|
|
|
11
12
|
}
|
|
12
13
|
export type PluginRegistrationInput = {
|
|
13
14
|
cli?: PluginCliDefinition;
|
|
15
|
+
conversationEvents?: PluginConversationEventDefinition[];
|
|
14
16
|
hooks?: PluginHooks;
|
|
15
17
|
manifest: PluginManifest;
|
|
16
18
|
model?: PluginModelConfig;
|
package/dist/tasks.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* scheduling, queue delivery, retries, and the bounded run projection.
|
|
6
6
|
*/
|
|
7
7
|
import { z } from "zod";
|
|
8
|
+
import type { PluginConversationEvents } from "./conversation-events";
|
|
8
9
|
import type { PluginContext, PluginEmbedder, PluginModel } from "./context";
|
|
9
10
|
import type { PluginState } from "./state";
|
|
10
11
|
/**
|
|
@@ -179,6 +180,7 @@ export type PluginRunContext = z.output<typeof pluginRunContextSchema>;
|
|
|
179
180
|
/** Runtime context passed to a plugin-owned background task. */
|
|
180
181
|
export interface PluginTaskContext extends PluginContext {
|
|
181
182
|
embedder: PluginEmbedder;
|
|
183
|
+
events: PluginConversationEvents;
|
|
182
184
|
id: string;
|
|
183
185
|
model: PluginModel;
|
|
184
186
|
name: string;
|
package/dist/tools.d.ts
CHANGED
|
@@ -123,6 +123,29 @@ export interface BeforeToolExecuteHookContext extends PluginContext {
|
|
|
123
123
|
name: string;
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Context for post-success MCP tool processing.
|
|
128
|
+
*
|
|
129
|
+
* Runs after a hosted MCP tool succeeds on the model-facing path. Use for
|
|
130
|
+
* junior-owned side effects such as conversation annotations without replacing
|
|
131
|
+
* the provider tool contract.
|
|
132
|
+
*/
|
|
133
|
+
export interface AfterMcpToolHookContext extends PluginContext {
|
|
134
|
+
/**
|
|
135
|
+
* Opaque Junior conversation/session identity for this turn.
|
|
136
|
+
* Interactive Slack turns use `slack:{channelId}:{threadTs}`.
|
|
137
|
+
*/
|
|
138
|
+
conversationId?: string;
|
|
139
|
+
annotations?: PluginAnnotations;
|
|
140
|
+
result: {
|
|
141
|
+
structuredContent?: unknown;
|
|
142
|
+
};
|
|
143
|
+
tool: {
|
|
144
|
+
arguments: Record<string, unknown>;
|
|
145
|
+
/** Provider-local MCP tool name, for example `save_issue`. */
|
|
146
|
+
name: string;
|
|
147
|
+
};
|
|
148
|
+
}
|
|
126
149
|
export interface PluginToolExecuteOptions {
|
|
127
150
|
/**
|
|
128
151
|
* @deprecated Internal compatibility escape hatch for legacy tool bridges.
|
|
@@ -167,12 +190,11 @@ export type PluginToolExecute<TInput = unknown, TOutput = unknown> = {
|
|
|
167
190
|
/**
|
|
168
191
|
* Tool-declared approval mode.
|
|
169
192
|
*
|
|
170
|
-
* `auto` delegates to core policy, `review` enters
|
|
171
|
-
* `approve` permits execution without review.
|
|
172
|
-
*
|
|
193
|
+
* `auto` delegates to core policy, `review` enters Guardian review, and
|
|
194
|
+
* `approve` permits execution without review. Plugin tool helpers normalize
|
|
195
|
+
* omission to `auto`.
|
|
173
196
|
*
|
|
174
|
-
*
|
|
175
|
-
* TODO(#1053): Enforce effective approval modes before tool execution.
|
|
197
|
+
* Core resolves the effective mode immediately before execution.
|
|
176
198
|
*/
|
|
177
199
|
export declare const toolApprovalModeSchema: z.ZodEnum<{
|
|
178
200
|
auto: "auto";
|
|
@@ -183,7 +205,7 @@ export type ToolApprovalMode = z.output<typeof toolApprovalModeSchema>;
|
|
|
183
205
|
/**
|
|
184
206
|
* Reviewer signals describing a tool's side-effect behavior.
|
|
185
207
|
*
|
|
186
|
-
* These hints follow the MCP tool annotation contract.
|
|
208
|
+
* These hints follow the MCP tool annotation contract. Guardian may use
|
|
187
209
|
* them as signals, but they never grant authority or override deterministic
|
|
188
210
|
* authorization.
|
|
189
211
|
*/
|
|
@@ -195,18 +217,19 @@ export interface ToolAnnotations {
|
|
|
195
217
|
readOnlyHint?: boolean;
|
|
196
218
|
title?: string;
|
|
197
219
|
}
|
|
220
|
+
export declare const REQUIRED_TOOL_ANNOTATION_KEYS: readonly ["destructiveHint", "idempotentHint", "openWorldHint", "readOnlyHint"];
|
|
221
|
+
export type RequiredToolAnnotationKey = (typeof REQUIRED_TOOL_ANNOTATION_KEYS)[number];
|
|
222
|
+
/** Return behavioral annotation keys that a tool did not declare. */
|
|
223
|
+
export declare function missingToolAnnotationKeys(annotations: ToolAnnotations | undefined): RequiredToolAnnotationKey[];
|
|
198
224
|
/**
|
|
199
225
|
* Canonical approval metadata declared by core and plugin tools.
|
|
200
|
-
*
|
|
201
|
-
* This metadata is declaration-only until #1053 adds approval enforcement.
|
|
202
|
-
* Current tool execution is unchanged.
|
|
203
226
|
*/
|
|
204
227
|
export interface ToolApprovalMetadata<TInput = unknown> {
|
|
205
|
-
/** Optional declared approval mode;
|
|
228
|
+
/** Optional declared approval mode; the owning tool boundary selects defaults. */
|
|
206
229
|
approvalMode?: ToolApprovalMode;
|
|
207
230
|
annotations?: ToolAnnotations;
|
|
208
231
|
/**
|
|
209
|
-
* Describe the
|
|
232
|
+
* Describe the reviewed semantic action for the review request.
|
|
210
233
|
*
|
|
211
234
|
* Core owns authoritative tool, actor, source, destination, conversation,
|
|
212
235
|
* credential, and input data. This description adds domain-specific context
|
package/package.json
CHANGED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
const conversationEventNameSchema = z
|
|
4
|
+
.string()
|
|
5
|
+
.trim()
|
|
6
|
+
.min(1)
|
|
7
|
+
.max(64)
|
|
8
|
+
.regex(/^[a-z][a-z0-9_]*$/);
|
|
9
|
+
|
|
10
|
+
export const conversationEventIconSchema = z.enum([
|
|
11
|
+
"activity",
|
|
12
|
+
"brain",
|
|
13
|
+
"calendar",
|
|
14
|
+
"check",
|
|
15
|
+
"database",
|
|
16
|
+
"info",
|
|
17
|
+
"key",
|
|
18
|
+
"link",
|
|
19
|
+
"sparkles",
|
|
20
|
+
"warning",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const conversationEventDetailSchema = z
|
|
24
|
+
.object({
|
|
25
|
+
description: z.string().trim().min(1).max(2_000).optional(),
|
|
26
|
+
metadata: z.array(z.string().trim().min(1).max(120)).max(8).optional(),
|
|
27
|
+
title: z.string().trim().min(1).max(4_000),
|
|
28
|
+
})
|
|
29
|
+
.strict();
|
|
30
|
+
|
|
31
|
+
/** Safe, core-rendered presentation for one plugin conversation event. */
|
|
32
|
+
export const conversationEventPresentationSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
details: z.array(conversationEventDetailSchema).max(100).optional(),
|
|
35
|
+
icon: conversationEventIconSchema.optional(),
|
|
36
|
+
preview: z.string().trim().min(1).max(500).optional(),
|
|
37
|
+
title: z.string().trim().min(1).max(120),
|
|
38
|
+
})
|
|
39
|
+
.strict();
|
|
40
|
+
|
|
41
|
+
export type ConversationEventPresentation = z.output<
|
|
42
|
+
typeof conversationEventPresentationSchema
|
|
43
|
+
>;
|
|
44
|
+
|
|
45
|
+
/** One validated plugin event value waiting for conversation-bound emission. */
|
|
46
|
+
export interface PluginConversationEventValue {
|
|
47
|
+
readonly data: Record<string, unknown>;
|
|
48
|
+
readonly definition: PluginConversationEventDefinition;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Registered schema and transcript presentation for one plugin event version. */
|
|
52
|
+
export interface PluginConversationEventDefinition {
|
|
53
|
+
readonly eventName: string;
|
|
54
|
+
readonly version: number;
|
|
55
|
+
parse(data: unknown): Record<string, unknown>;
|
|
56
|
+
renderEvent(data: Record<string, unknown>): ConversationEventPresentation;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Typed factory returned while authoring one plugin conversation event. */
|
|
60
|
+
export interface DefinedConversationEvent<
|
|
61
|
+
TInput,
|
|
62
|
+
> extends PluginConversationEventDefinition {
|
|
63
|
+
(data: TInput): PluginConversationEventValue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Define one typed, versioned plugin-owned conversation event. */
|
|
67
|
+
export function defineConversationEvent<
|
|
68
|
+
TSchema extends z.ZodType<Record<string, unknown>>,
|
|
69
|
+
>(definition: {
|
|
70
|
+
name: string;
|
|
71
|
+
version: number;
|
|
72
|
+
schema: TSchema;
|
|
73
|
+
renderEvent(
|
|
74
|
+
event: z.output<TSchema>,
|
|
75
|
+
): z.input<typeof conversationEventPresentationSchema>;
|
|
76
|
+
}): DefinedConversationEvent<z.input<TSchema>> {
|
|
77
|
+
const identity = z
|
|
78
|
+
.object({
|
|
79
|
+
name: conversationEventNameSchema,
|
|
80
|
+
version: z.number().int().positive(),
|
|
81
|
+
})
|
|
82
|
+
.strict()
|
|
83
|
+
.parse({ name: definition.name, version: definition.version });
|
|
84
|
+
let eventDefinition: DefinedConversationEvent<z.input<TSchema>>;
|
|
85
|
+
const createEvent = (
|
|
86
|
+
data: z.input<TSchema>,
|
|
87
|
+
): PluginConversationEventValue => ({
|
|
88
|
+
data: definition.schema.parse(data),
|
|
89
|
+
definition: eventDefinition,
|
|
90
|
+
});
|
|
91
|
+
eventDefinition = Object.assign(createEvent, {
|
|
92
|
+
eventName: identity.name,
|
|
93
|
+
version: identity.version,
|
|
94
|
+
parse(data: unknown) {
|
|
95
|
+
return definition.schema.parse(data);
|
|
96
|
+
},
|
|
97
|
+
renderEvent(data: Record<string, unknown>) {
|
|
98
|
+
const parsed = definition.schema.parse(data);
|
|
99
|
+
return conversationEventPresentationSchema.parse(
|
|
100
|
+
definition.renderEvent(parsed),
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
return eventDefinition;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Conversation-bound event writer supplied by Junior core. */
|
|
108
|
+
export interface PluginConversationEvents {
|
|
109
|
+
emit(event: PluginConversationEventValue): Promise<void>;
|
|
110
|
+
}
|
package/src/hooks.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
SlackConversationLinkHookContext,
|
|
21
21
|
} from "./operations";
|
|
22
22
|
import type {
|
|
23
|
+
AfterMcpToolHookContext,
|
|
23
24
|
BeforeToolExecuteHookContext,
|
|
24
25
|
PluginToolDefinition,
|
|
25
26
|
SandboxPrepareHookContext,
|
|
@@ -43,6 +44,13 @@ export interface PluginHooks {
|
|
|
43
44
|
| UserPromptContribution[]
|
|
44
45
|
| undefined;
|
|
45
46
|
beforeToolExecute?(ctx: BeforeToolExecuteHookContext): Promise<void> | void;
|
|
47
|
+
/**
|
|
48
|
+
* Run after a successful hosted MCP tool call.
|
|
49
|
+
*
|
|
50
|
+
* Prefer this for junior-owned side effects such as conversation annotations.
|
|
51
|
+
* Do not use it to invent a parallel tool contract for the provider tool.
|
|
52
|
+
*/
|
|
53
|
+
afterMcpTool?(ctx: AfterMcpToolHookContext): Promise<void> | void;
|
|
46
54
|
grantForEgress?(
|
|
47
55
|
ctx: EgressHookContext,
|
|
48
56
|
): Promise<PluginGrant | undefined> | PluginGrant | undefined;
|
package/src/index.ts
CHANGED
package/src/registration.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PluginCliDefinition } from "./cli";
|
|
2
|
+
import type { PluginConversationEventDefinition } from "./conversation-events";
|
|
2
3
|
import type { PluginHooks } from "./hooks";
|
|
3
4
|
import type { PluginManifest } from "./manifest";
|
|
4
5
|
import type { PluginTasks } from "./tasks";
|
|
@@ -13,6 +14,7 @@ export interface PluginModelConfig {
|
|
|
13
14
|
|
|
14
15
|
export type PluginRegistrationInput = {
|
|
15
16
|
cli?: PluginCliDefinition;
|
|
17
|
+
conversationEvents?: PluginConversationEventDefinition[];
|
|
16
18
|
hooks?: PluginHooks;
|
|
17
19
|
manifest: PluginManifest;
|
|
18
20
|
model?: PluginModelConfig;
|
|
@@ -72,6 +74,34 @@ export function defineJuniorPlugin(
|
|
|
72
74
|
if (plugin.userPages !== undefined && !Array.isArray(plugin.userPages)) {
|
|
73
75
|
throw new Error(`Junior plugin "${name}" userPages must be an array.`);
|
|
74
76
|
}
|
|
77
|
+
if (
|
|
78
|
+
plugin.conversationEvents !== undefined &&
|
|
79
|
+
!Array.isArray(plugin.conversationEvents)
|
|
80
|
+
) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Junior plugin "${name}" conversationEvents must be an array.`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
const conversationEventIds = new Set<string>();
|
|
86
|
+
for (const event of plugin.conversationEvents ?? []) {
|
|
87
|
+
if (
|
|
88
|
+
!event ||
|
|
89
|
+
(typeof event !== "object" && typeof event !== "function") ||
|
|
90
|
+
typeof event.parse !== "function" ||
|
|
91
|
+
typeof event.renderEvent !== "function"
|
|
92
|
+
) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Junior plugin "${name}" conversation event definitions must be created with defineConversationEvent().`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const id = `${event.eventName}@${event.version}`;
|
|
98
|
+
if (conversationEventIds.has(id)) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Junior plugin "${name}" has duplicate conversation event "${id}".`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
conversationEventIds.add(id);
|
|
104
|
+
}
|
|
75
105
|
const userPageIds = new Set<string>();
|
|
76
106
|
for (const page of plugin.userPages ?? []) {
|
|
77
107
|
if (!page || typeof page !== "object") {
|
package/src/tasks.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* scheduling, queue delivery, retries, and the bounded run projection.
|
|
6
6
|
*/
|
|
7
7
|
import { z } from "zod";
|
|
8
|
+
import type { PluginConversationEvents } from "./conversation-events";
|
|
8
9
|
import type { PluginContext, PluginEmbedder, PluginModel } from "./context";
|
|
9
10
|
import { destinationSchema, actorSchema, sourceSchema } from "./schemas";
|
|
10
11
|
import type { PluginState } from "./state";
|
|
@@ -81,6 +82,7 @@ export type PluginRunContext = z.output<typeof pluginRunContextSchema>;
|
|
|
81
82
|
/** Runtime context passed to a plugin-owned background task. */
|
|
82
83
|
export interface PluginTaskContext extends PluginContext {
|
|
83
84
|
embedder: PluginEmbedder;
|
|
85
|
+
events: PluginConversationEvents;
|
|
84
86
|
id: string;
|
|
85
87
|
model: PluginModel;
|
|
86
88
|
name: string;
|
package/src/tools.ts
CHANGED
|
@@ -153,6 +153,30 @@ export interface BeforeToolExecuteHookContext extends PluginContext {
|
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Context for post-success MCP tool processing.
|
|
158
|
+
*
|
|
159
|
+
* Runs after a hosted MCP tool succeeds on the model-facing path. Use for
|
|
160
|
+
* junior-owned side effects such as conversation annotations without replacing
|
|
161
|
+
* the provider tool contract.
|
|
162
|
+
*/
|
|
163
|
+
export interface AfterMcpToolHookContext extends PluginContext {
|
|
164
|
+
/**
|
|
165
|
+
* Opaque Junior conversation/session identity for this turn.
|
|
166
|
+
* Interactive Slack turns use `slack:{channelId}:{threadTs}`.
|
|
167
|
+
*/
|
|
168
|
+
conversationId?: string;
|
|
169
|
+
annotations?: PluginAnnotations;
|
|
170
|
+
result: {
|
|
171
|
+
structuredContent?: unknown;
|
|
172
|
+
};
|
|
173
|
+
tool: {
|
|
174
|
+
arguments: Record<string, unknown>;
|
|
175
|
+
/** Provider-local MCP tool name, for example `save_issue`. */
|
|
176
|
+
name: string;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
156
180
|
export interface PluginToolExecuteOptions {
|
|
157
181
|
/**
|
|
158
182
|
* @deprecated Internal compatibility escape hatch for legacy tool bridges.
|
|
@@ -202,12 +226,11 @@ export type PluginToolExecute<TInput = unknown, TOutput = unknown> = {
|
|
|
202
226
|
/**
|
|
203
227
|
* Tool-declared approval mode.
|
|
204
228
|
*
|
|
205
|
-
* `auto` delegates to core policy, `review` enters
|
|
206
|
-
* `approve` permits execution without review.
|
|
207
|
-
*
|
|
229
|
+
* `auto` delegates to core policy, `review` enters Guardian review, and
|
|
230
|
+
* `approve` permits execution without review. Plugin tool helpers normalize
|
|
231
|
+
* omission to `auto`.
|
|
208
232
|
*
|
|
209
|
-
*
|
|
210
|
-
* TODO(#1053): Enforce effective approval modes before tool execution.
|
|
233
|
+
* Core resolves the effective mode immediately before execution.
|
|
211
234
|
*/
|
|
212
235
|
export const toolApprovalModeSchema = z.enum(["auto", "review", "approve"]);
|
|
213
236
|
|
|
@@ -216,7 +239,7 @@ export type ToolApprovalMode = z.output<typeof toolApprovalModeSchema>;
|
|
|
216
239
|
/**
|
|
217
240
|
* Reviewer signals describing a tool's side-effect behavior.
|
|
218
241
|
*
|
|
219
|
-
* These hints follow the MCP tool annotation contract.
|
|
242
|
+
* These hints follow the MCP tool annotation contract. Guardian may use
|
|
220
243
|
* them as signals, but they never grant authority or override deterministic
|
|
221
244
|
* authorization.
|
|
222
245
|
*/
|
|
@@ -229,18 +252,34 @@ export interface ToolAnnotations {
|
|
|
229
252
|
title?: string;
|
|
230
253
|
}
|
|
231
254
|
|
|
255
|
+
export const REQUIRED_TOOL_ANNOTATION_KEYS = [
|
|
256
|
+
"destructiveHint",
|
|
257
|
+
"idempotentHint",
|
|
258
|
+
"openWorldHint",
|
|
259
|
+
"readOnlyHint",
|
|
260
|
+
] as const;
|
|
261
|
+
|
|
262
|
+
export type RequiredToolAnnotationKey =
|
|
263
|
+
(typeof REQUIRED_TOOL_ANNOTATION_KEYS)[number];
|
|
264
|
+
|
|
265
|
+
/** Return behavioral annotation keys that a tool did not declare. */
|
|
266
|
+
export function missingToolAnnotationKeys(
|
|
267
|
+
annotations: ToolAnnotations | undefined,
|
|
268
|
+
): RequiredToolAnnotationKey[] {
|
|
269
|
+
return REQUIRED_TOOL_ANNOTATION_KEYS.filter(
|
|
270
|
+
(key) => typeof annotations?.[key] !== "boolean",
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
232
274
|
/**
|
|
233
275
|
* Canonical approval metadata declared by core and plugin tools.
|
|
234
|
-
*
|
|
235
|
-
* This metadata is declaration-only until #1053 adds approval enforcement.
|
|
236
|
-
* Current tool execution is unchanged.
|
|
237
276
|
*/
|
|
238
277
|
export interface ToolApprovalMetadata<TInput = unknown> {
|
|
239
|
-
/** Optional declared approval mode;
|
|
278
|
+
/** Optional declared approval mode; the owning tool boundary selects defaults. */
|
|
240
279
|
approvalMode?: ToolApprovalMode;
|
|
241
280
|
annotations?: ToolAnnotations;
|
|
242
281
|
/**
|
|
243
|
-
* Describe the
|
|
282
|
+
* Describe the reviewed semantic action for the review request.
|
|
244
283
|
*
|
|
245
284
|
* Core owns authoritative tool, actor, source, destination, conversation,
|
|
246
285
|
* credential, and input data. This description adds domain-specific context
|
|
@@ -379,6 +418,7 @@ function createZodTool<
|
|
|
379
418
|
}
|
|
380
419
|
return {
|
|
381
420
|
...tool,
|
|
421
|
+
approvalMode: tool.approvalMode ?? "auto",
|
|
382
422
|
inputSchema: modelInputSchema,
|
|
383
423
|
outputSchema: modelOutputSchema,
|
|
384
424
|
prepareArguments(args) {
|