@sentry/junior-plugin-api 0.126.1 → 0.128.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 +9 -7
- package/dist/context.d.ts +3 -1
- package/dist/index.js +120 -30
- package/dist/operations.d.ts +4 -4
- package/dist/registration.d.ts +2 -0
- package/dist/resource-events.d.ts +41 -8
- package/dist/schemas.d.ts +23 -0
- package/dist/tools.d.ts +23 -26
- package/dist/user-pages.d.ts +2 -7
- package/package.json +1 -1
- package/src/context.ts +4 -0
- package/src/operations.ts +4 -4
- package/src/registration.ts +13 -0
- package/src/resource-events.ts +124 -13
- package/src/schemas.ts +22 -0
- package/src/tools.ts +33 -34
- package/src/user-pages.ts +2 -8
package/README.md
CHANGED
|
@@ -55,12 +55,14 @@ reports, and other typed hook surfaces exported by this package.
|
|
|
55
55
|
binds the plugin namespace and owns access to the conversation event log.
|
|
56
56
|
Event `costUsd` is additive operation cost and must not duplicate cost
|
|
57
57
|
already recorded in the conversation's agent model usage.
|
|
58
|
-
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
- Tool hooks may lazily resolve the active actor's canonical identity and linked
|
|
59
|
+
user through `ctx.users.resolveActor()`.
|
|
60
|
+
- Authenticated API route hooks receive `ctx.users.resolve(email)` for lazy
|
|
61
|
+
canonical user resolution. Routes that do not need personal ownership do not
|
|
62
|
+
query identity storage.
|
|
63
|
+
- User page readers receive the canonical viewer `User` with linked identities.
|
|
64
|
+
Plugins return bounded data and do not mount their own page routes or browser
|
|
65
|
+
code.
|
|
64
66
|
|
|
65
67
|
## User Pages
|
|
66
68
|
|
|
@@ -72,7 +74,7 @@ Set `navigation: "primary"` for a top-level dashboard navigation item. The
|
|
|
72
74
|
default `profile` placement keeps account-oriented pages in the signed-in user
|
|
73
75
|
menu. Junior always renders core System navigation after plugin pages.
|
|
74
76
|
Records may expose bounded `DELETE` actions inside their own authenticated
|
|
75
|
-
plugin API namespace. Core owns discovery, authentication,
|
|
77
|
+
plugin API namespace. Core owns discovery, authentication, user resolution,
|
|
76
78
|
routing, response validation, rendering, confirmation, and query state.
|
|
77
79
|
|
|
78
80
|
## Durable Work
|
package/dist/context.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { ZodTypeAny } from "zod";
|
|
3
|
-
import { destinationSchema, localActorSchema, platformSchema, actorSchema, slackActorSchema, systemActorSchema, sourceSchema } from "./schemas";
|
|
3
|
+
import { destinationSchema, identitySchema, localActorSchema, platformSchema, actorSchema, slackActorSchema, systemActorSchema, sourceSchema, userSchema } from "./schemas";
|
|
4
4
|
/** Runtime platform name without source or destination coordinates. */
|
|
5
5
|
export type Platform = z.output<typeof platformSchema>;
|
|
6
6
|
export type Actor = z.output<typeof actorSchema>;
|
|
7
7
|
export type SlackActor = z.output<typeof slackActorSchema>;
|
|
8
8
|
export type LocalActor = z.output<typeof localActorSchema>;
|
|
9
9
|
export type SystemActor = z.output<typeof systemActorSchema>;
|
|
10
|
+
export type Identity = z.output<typeof identitySchema>;
|
|
11
|
+
export type User = z.output<typeof userSchema>;
|
|
10
12
|
export type Source = z.output<typeof sourceSchema>;
|
|
11
13
|
export type SlackSource = Extract<Source, {
|
|
12
14
|
platform: "slack";
|
package/dist/index.js
CHANGED
|
@@ -154,6 +154,20 @@ var actorSchema = z3.discriminatedUnion("platform", [
|
|
|
154
154
|
localActorSchema,
|
|
155
155
|
systemActorSchema
|
|
156
156
|
]);
|
|
157
|
+
var identitySchema = z3.object({
|
|
158
|
+
displayName: nonBlankStringSchema.optional(),
|
|
159
|
+
handle: nonBlankStringSchema.optional(),
|
|
160
|
+
id: exactNonBlankStringSchema,
|
|
161
|
+
provider: exactNonBlankStringSchema,
|
|
162
|
+
providerSubjectId: exactNonBlankStringSchema,
|
|
163
|
+
providerTenantId: exactNonBlankStringSchema.optional()
|
|
164
|
+
}).strict();
|
|
165
|
+
var userSchema = z3.object({
|
|
166
|
+
displayName: nonBlankStringSchema.optional(),
|
|
167
|
+
email: nonBlankStringSchema.max(320),
|
|
168
|
+
id: exactNonBlankStringSchema,
|
|
169
|
+
identities: z3.array(identitySchema).max(100)
|
|
170
|
+
}).strict();
|
|
157
171
|
var dispatchMetadataSchema = z3.record(z3.string(), z3.string()).superRefine((metadata, ctx) => {
|
|
158
172
|
const entries = Object.entries(metadata);
|
|
159
173
|
if (entries.length > 20) {
|
|
@@ -276,24 +290,94 @@ function definePromptContext(definition) {
|
|
|
276
290
|
|
|
277
291
|
// src/resource-events.ts
|
|
278
292
|
import { z as z5 } from "zod";
|
|
293
|
+
var RESOURCE_EVENT_SUMMARY_MAX_LENGTH = 4e3;
|
|
294
|
+
var RESOURCE_EVENT_TEXT_MAX_LENGTH = 8e3;
|
|
295
|
+
var resourceEventTypeSchema = z5.string().regex(/^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)+$/).describe(
|
|
296
|
+
"Canonical dotted event type, such as issue.closed or pull_request.review.changes_requested."
|
|
297
|
+
);
|
|
298
|
+
var resourceTypeSchema = z5.string().regex(/^[a-z][a-z0-9_]*$/).describe(
|
|
299
|
+
"Canonical plugin-defined resource type, such as issue or pull_request."
|
|
300
|
+
);
|
|
301
|
+
var pluginResourceEventTypeSchema = z5.object({
|
|
302
|
+
type: resourceTypeSchema,
|
|
303
|
+
supportedEvents: z5.array(resourceEventTypeSchema).min(1),
|
|
304
|
+
suggestedEvents: z5.array(resourceEventTypeSchema).optional()
|
|
305
|
+
}).strict().superRefine((resourceType, context) => {
|
|
306
|
+
const supported = /* @__PURE__ */ new Set();
|
|
307
|
+
resourceType.supportedEvents.forEach((eventType, index) => {
|
|
308
|
+
if (supported.has(eventType)) {
|
|
309
|
+
context.addIssue({
|
|
310
|
+
code: "custom",
|
|
311
|
+
message: `Duplicate supported resource event type "${eventType}".`,
|
|
312
|
+
path: ["supportedEvents", index]
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
supported.add(eventType);
|
|
316
|
+
});
|
|
317
|
+
const suggested = /* @__PURE__ */ new Set();
|
|
318
|
+
resourceType.suggestedEvents?.forEach((eventType, index) => {
|
|
319
|
+
if (suggested.has(eventType)) {
|
|
320
|
+
context.addIssue({
|
|
321
|
+
code: "custom",
|
|
322
|
+
message: `Duplicate suggested resource event type "${eventType}".`,
|
|
323
|
+
path: ["suggestedEvents", index]
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
if (!supported.has(eventType)) {
|
|
327
|
+
context.addIssue({
|
|
328
|
+
code: "custom",
|
|
329
|
+
message: `Suggested resource event type "${eventType}" is not supported.`,
|
|
330
|
+
path: ["suggestedEvents", index]
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
suggested.add(eventType);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
var pluginResourceEventsSchema = z5.object({
|
|
337
|
+
resourceTypes: z5.array(pluginResourceEventTypeSchema).min(1),
|
|
338
|
+
isEnabled: z5.function({ input: [], output: z5.boolean() }).optional(),
|
|
339
|
+
normalizeIdentifier: z5.function({ input: [z5.string()], output: z5.string() }).optional()
|
|
340
|
+
}).strict().superRefine((registration, context) => {
|
|
341
|
+
const seen = /* @__PURE__ */ new Set();
|
|
342
|
+
registration.resourceTypes.forEach((resourceType, index) => {
|
|
343
|
+
if (seen.has(resourceType.type)) {
|
|
344
|
+
context.addIssue({
|
|
345
|
+
code: "custom",
|
|
346
|
+
message: `Duplicate resource type "${resourceType.type}".`,
|
|
347
|
+
path: ["resourceTypes", index, "type"]
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
seen.add(resourceType.type);
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
function normalizeResourceEventIdentifier(registration, identifier) {
|
|
354
|
+
const trimmed = identifier.trim();
|
|
355
|
+
const normalized = (registration?.normalizeIdentifier?.(trimmed) ?? trimmed).trim();
|
|
356
|
+
if (!normalized) {
|
|
357
|
+
throw new Error("Resource event identifier must not normalize to empty");
|
|
358
|
+
}
|
|
359
|
+
return normalized;
|
|
360
|
+
}
|
|
279
361
|
var subscribableResourceSchema = z5.object({
|
|
362
|
+
identifier: z5.string().min(1),
|
|
280
363
|
label: z5.string().min(1),
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
type: z5.string().min(1)
|
|
364
|
+
namespace: z5.string().min(1),
|
|
365
|
+
suggestedEvents: z5.array(resourceEventTypeSchema).optional(),
|
|
366
|
+
supportedEvents: z5.array(resourceEventTypeSchema),
|
|
367
|
+
type: resourceTypeSchema
|
|
286
368
|
}).strict();
|
|
287
|
-
var
|
|
369
|
+
var resourceEventInputSchema = z5.object({
|
|
288
370
|
eventKey: z5.string().min(1),
|
|
289
|
-
eventType:
|
|
371
|
+
eventType: resourceEventTypeSchema,
|
|
372
|
+
identifier: z5.string().min(1),
|
|
290
373
|
occurredAtMs: z5.number().finite(),
|
|
291
|
-
provider: z5.string().min(1),
|
|
292
|
-
resourceRef: z5.string().min(1),
|
|
293
374
|
terminal: z5.boolean().optional(),
|
|
294
|
-
trustedSummary: z5.string().min(1),
|
|
295
|
-
untrustedText: z5.string().optional()
|
|
375
|
+
trustedSummary: z5.string().min(1).transform((value) => value.slice(0, RESOURCE_EVENT_SUMMARY_MAX_LENGTH)),
|
|
376
|
+
untrustedText: z5.string().transform((value) => value.slice(0, RESOURCE_EVENT_TEXT_MAX_LENGTH)).optional()
|
|
296
377
|
}).strict();
|
|
378
|
+
var resourceEventSchema = resourceEventInputSchema.extend({
|
|
379
|
+
namespace: z5.string().min(1)
|
|
380
|
+
});
|
|
297
381
|
|
|
298
382
|
// src/tasks.ts
|
|
299
383
|
import { z as z6 } from "zod";
|
|
@@ -359,19 +443,10 @@ var pluginToolContinuationSchema = z7.object({
|
|
|
359
443
|
arguments: z7.record(z7.string(), z7.unknown()),
|
|
360
444
|
reason: z7.string().min(1).optional()
|
|
361
445
|
}).strict();
|
|
362
|
-
var
|
|
363
|
-
kind: z7.string().min(1),
|
|
364
|
-
message: z7.string().min(1),
|
|
365
|
-
retryable: z7.boolean().optional()
|
|
366
|
-
}).strict();
|
|
367
|
-
var pluginToolResultSchema = z7.object({
|
|
368
|
-
ok: z7.boolean(),
|
|
369
|
-
status: z7.enum(["success", "error"]),
|
|
446
|
+
var pluginToolOutputSchema = z7.object({
|
|
370
447
|
target: z7.string().min(1).optional(),
|
|
371
|
-
data: z7.unknown().optional(),
|
|
372
448
|
truncated: z7.boolean().optional(),
|
|
373
|
-
continuation: pluginToolContinuationSchema.optional()
|
|
374
|
-
error: z7.union([pluginToolErrorSchema, z7.string()]).optional()
|
|
449
|
+
continuation: pluginToolContinuationSchema.optional()
|
|
375
450
|
}).passthrough();
|
|
376
451
|
var toolApprovalModeSchema = z7.enum(["auto", "review", "approve"]);
|
|
377
452
|
var REQUIRED_TOOL_ANNOTATION_KEYS = [
|
|
@@ -385,7 +460,7 @@ function missingToolAnnotationKeys(annotations) {
|
|
|
385
460
|
(key) => typeof annotations?.[key] !== "boolean"
|
|
386
461
|
);
|
|
387
462
|
}
|
|
388
|
-
function
|
|
463
|
+
function isPluginToolOutputEnvelope(value) {
|
|
389
464
|
return value !== null && typeof value === "object" && Array.isArray(value.content) && "details" in value;
|
|
390
465
|
}
|
|
391
466
|
function formatZodPath(path) {
|
|
@@ -441,15 +516,13 @@ function createZodTool(definition, helperName) {
|
|
|
441
516
|
input,
|
|
442
517
|
options
|
|
443
518
|
);
|
|
444
|
-
if (
|
|
519
|
+
if (isPluginToolOutputEnvelope(result)) {
|
|
445
520
|
return {
|
|
446
521
|
content: z7.array(pluginToolContentSchema).parse(result.content),
|
|
447
|
-
details: outputSchema.parse(
|
|
448
|
-
pluginToolResultSchema.parse(result.details)
|
|
449
|
-
)
|
|
522
|
+
details: outputSchema.parse(result.details)
|
|
450
523
|
};
|
|
451
524
|
}
|
|
452
|
-
return outputSchema.parse(
|
|
525
|
+
return outputSchema.parse(result);
|
|
453
526
|
}
|
|
454
527
|
} : {}
|
|
455
528
|
};
|
|
@@ -597,6 +670,14 @@ function defineJuniorPlugin(plugin) {
|
|
|
597
670
|
`Junior plugin "${name}" conversationEvents must be an array.`
|
|
598
671
|
);
|
|
599
672
|
}
|
|
673
|
+
if (plugin.resourceEvents !== void 0) {
|
|
674
|
+
const parsed = pluginResourceEventsSchema.safeParse(plugin.resourceEvents);
|
|
675
|
+
if (!parsed.success) {
|
|
676
|
+
throw new Error(`Junior plugin "${name}" resourceEvents is invalid.`, {
|
|
677
|
+
cause: parsed.error
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
}
|
|
600
681
|
const conversationEventIds = /* @__PURE__ */ new Set();
|
|
601
682
|
for (const event of plugin.conversationEvents ?? []) {
|
|
602
683
|
if (!event || typeof event !== "object" && typeof event !== "function" || typeof event.parse !== "function" || typeof event.renderEvent !== "function") {
|
|
@@ -707,6 +788,8 @@ export {
|
|
|
707
788
|
EgressPolicyDenied,
|
|
708
789
|
PluginToolInputError,
|
|
709
790
|
REQUIRED_TOOL_ANNOTATION_KEYS,
|
|
791
|
+
RESOURCE_EVENT_SUMMARY_MAX_LENGTH,
|
|
792
|
+
RESOURCE_EVENT_TEXT_MAX_LENGTH,
|
|
710
793
|
actorSchema,
|
|
711
794
|
conversationAnnotationInputSchema,
|
|
712
795
|
conversationEventIconSchema,
|
|
@@ -721,6 +804,7 @@ export {
|
|
|
721
804
|
destinationVisibilitySchema,
|
|
722
805
|
dispatchOptionsSchema,
|
|
723
806
|
getSourceKey,
|
|
807
|
+
identitySchema,
|
|
724
808
|
isPrivateSource,
|
|
725
809
|
isSlackDestination,
|
|
726
810
|
localActorSchema,
|
|
@@ -728,6 +812,7 @@ export {
|
|
|
728
812
|
localSourceSchema,
|
|
729
813
|
missingToolAnnotationKeys,
|
|
730
814
|
nonBlankStringSchema,
|
|
815
|
+
normalizeResourceEventIdentifier,
|
|
731
816
|
platformSchema,
|
|
732
817
|
pluginApiRouteRequestContextSchema,
|
|
733
818
|
pluginAuthorizationSchema,
|
|
@@ -737,14 +822,15 @@ export {
|
|
|
737
822
|
pluginCredentialSubjectSchema,
|
|
738
823
|
pluginGrantSchema,
|
|
739
824
|
pluginProviderAccountSchema,
|
|
825
|
+
pluginResourceEventTypeSchema,
|
|
826
|
+
pluginResourceEventsSchema,
|
|
740
827
|
pluginRunContextSchema,
|
|
741
828
|
pluginRunTranscriptEntrySchema,
|
|
742
829
|
pluginRunTranscriptProvenanceSchema,
|
|
743
830
|
pluginStoredTokensSchema,
|
|
744
831
|
pluginToolContentSchema,
|
|
745
832
|
pluginToolContinuationSchema,
|
|
746
|
-
|
|
747
|
-
pluginToolResultSchema,
|
|
833
|
+
pluginToolOutputSchema,
|
|
748
834
|
pluginUserPageContentSchema,
|
|
749
835
|
pluginUserPageInputSchema,
|
|
750
836
|
pluginUserPageLinkSchema,
|
|
@@ -752,8 +838,11 @@ export {
|
|
|
752
838
|
promptContextSchema,
|
|
753
839
|
promptMessageSchema,
|
|
754
840
|
replyAttributionSchema,
|
|
841
|
+
resourceEventInputSchema,
|
|
755
842
|
resourceEventSchema,
|
|
843
|
+
resourceEventTypeSchema,
|
|
756
844
|
resourceLinkAnnotationSchema,
|
|
845
|
+
resourceTypeSchema,
|
|
757
846
|
slackActorSchema,
|
|
758
847
|
slackDestinationSchema,
|
|
759
848
|
slackSourceSchema,
|
|
@@ -762,5 +851,6 @@ export {
|
|
|
762
851
|
subscribableResourceSchema,
|
|
763
852
|
systemActorSchema,
|
|
764
853
|
toolApprovalModeSchema,
|
|
854
|
+
userSchema,
|
|
765
855
|
zodTool
|
|
766
856
|
};
|
package/dist/operations.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type {
|
|
2
|
+
import type { PluginContext, User } from "./context";
|
|
3
3
|
import type { Dispatch, DispatchOptions, DispatchResult } from "./dispatch";
|
|
4
4
|
import type { PluginReadState, PluginState } from "./state";
|
|
5
5
|
import type { ResourceEventPublisher } from "./resource-events";
|
|
@@ -93,9 +93,9 @@ export interface RouteRegistrationHookContext extends PluginContext {
|
|
|
93
93
|
}
|
|
94
94
|
export interface ApiRouteRegistrationHookContext extends PluginContext {
|
|
95
95
|
eventStats: PluginConversationEventStats;
|
|
96
|
-
|
|
97
|
-
/** Resolve
|
|
98
|
-
|
|
96
|
+
users: {
|
|
97
|
+
/** Resolve or create the canonical user for one verified email. */
|
|
98
|
+
resolve(email: string): Promise<User | undefined>;
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
101
|
/** Per-request context Junior passes to authenticated plugin product API routes. */
|
package/dist/registration.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { PluginHooks } from "./hooks";
|
|
|
4
4
|
import type { PluginManifest } from "./manifest";
|
|
5
5
|
import type { PluginTasks } from "./tasks";
|
|
6
6
|
import type { PluginUserPageDefinition } from "./user-pages";
|
|
7
|
+
import { type PluginResourceEvents } from "./resource-events";
|
|
7
8
|
export interface PluginModelConfig {
|
|
8
9
|
/** Host model family used when no explicit structured model id is configured. */
|
|
9
10
|
structuredModel?: "default" | "fast";
|
|
@@ -17,6 +18,7 @@ export type PluginRegistrationInput = {
|
|
|
17
18
|
manifest: PluginManifest;
|
|
18
19
|
model?: PluginModelConfig;
|
|
19
20
|
packageName?: string;
|
|
21
|
+
resourceEvents?: PluginResourceEvents;
|
|
20
22
|
tasks?: PluginTasks;
|
|
21
23
|
userPages?: PluginUserPageDefinition[];
|
|
22
24
|
};
|
|
@@ -1,25 +1,58 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
export declare const RESOURCE_EVENT_SUMMARY_MAX_LENGTH = 4000;
|
|
3
|
+
export declare const RESOURCE_EVENT_TEXT_MAX_LENGTH = 8000;
|
|
4
|
+
/** Canonical dotted event type published and selected across plugins. */
|
|
5
|
+
export declare const resourceEventTypeSchema: z.ZodString;
|
|
6
|
+
export declare const resourceTypeSchema: z.ZodString;
|
|
7
|
+
export declare const pluginResourceEventTypeSchema: z.ZodObject<{
|
|
8
|
+
type: z.ZodString;
|
|
9
|
+
supportedEvents: z.ZodArray<z.ZodString>;
|
|
10
|
+
suggestedEvents: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
export declare const pluginResourceEventsSchema: z.ZodObject<{
|
|
13
|
+
resourceTypes: z.ZodArray<z.ZodObject<{
|
|
14
|
+
type: z.ZodString;
|
|
15
|
+
supportedEvents: z.ZodArray<z.ZodString>;
|
|
16
|
+
suggestedEvents: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
17
|
+
}, z.core.$strict>>;
|
|
18
|
+
isEnabled: z.ZodOptional<z.ZodFunction<z.ZodTuple<readonly [], null>, z.ZodBoolean>>;
|
|
19
|
+
normalizeIdentifier: z.ZodOptional<z.ZodFunction<z.ZodTuple<readonly [z.ZodString], null>, z.ZodString>>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
export type PluginResourceEventType = z.output<typeof pluginResourceEventTypeSchema>;
|
|
22
|
+
export type PluginResourceEvents = z.output<typeof pluginResourceEventsSchema>;
|
|
23
|
+
/** Apply a plugin's identifier convention at resource-event boundaries. */
|
|
24
|
+
export declare function normalizeResourceEventIdentifier(registration: Pick<PluginResourceEvents, "normalizeIdentifier"> | undefined, identifier: string): string;
|
|
2
25
|
export declare const subscribableResourceSchema: z.ZodObject<{
|
|
26
|
+
identifier: z.ZodString;
|
|
3
27
|
label: z.ZodString;
|
|
4
|
-
|
|
5
|
-
resourceRef: z.ZodString;
|
|
28
|
+
namespace: z.ZodString;
|
|
6
29
|
suggestedEvents: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7
30
|
supportedEvents: z.ZodArray<z.ZodString>;
|
|
8
31
|
type: z.ZodString;
|
|
9
32
|
}, z.core.$strict>;
|
|
10
33
|
export type SubscribableResource = z.output<typeof subscribableResourceSchema>;
|
|
34
|
+
export declare const resourceEventInputSchema: z.ZodObject<{
|
|
35
|
+
eventKey: z.ZodString;
|
|
36
|
+
eventType: z.ZodString;
|
|
37
|
+
identifier: z.ZodString;
|
|
38
|
+
occurredAtMs: z.ZodNumber;
|
|
39
|
+
terminal: z.ZodOptional<z.ZodBoolean>;
|
|
40
|
+
trustedSummary: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
41
|
+
untrustedText: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
42
|
+
}, z.core.$strict>;
|
|
43
|
+
export type ResourceEventInput = z.output<typeof resourceEventInputSchema>;
|
|
11
44
|
export declare const resourceEventSchema: z.ZodObject<{
|
|
12
45
|
eventKey: z.ZodString;
|
|
13
46
|
eventType: z.ZodString;
|
|
47
|
+
identifier: z.ZodString;
|
|
14
48
|
occurredAtMs: z.ZodNumber;
|
|
15
|
-
provider: z.ZodString;
|
|
16
|
-
resourceRef: z.ZodString;
|
|
17
49
|
terminal: z.ZodOptional<z.ZodBoolean>;
|
|
18
|
-
trustedSummary: z.ZodString
|
|
19
|
-
untrustedText: z.ZodOptional<z.ZodString
|
|
50
|
+
trustedSummary: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
51
|
+
untrustedText: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
52
|
+
namespace: z.ZodString;
|
|
20
53
|
}, z.core.$strict>;
|
|
21
54
|
export type ResourceEvent = z.output<typeof resourceEventSchema>;
|
|
22
55
|
export interface ResourceEventPublisher {
|
|
23
|
-
/** Publish one normalized event
|
|
24
|
-
publish(event:
|
|
56
|
+
/** Publish one normalized event under the owning plugin's namespace. */
|
|
57
|
+
publish(event: ResourceEventInput): Promise<void>;
|
|
25
58
|
}
|
package/dist/schemas.d.ts
CHANGED
|
@@ -117,6 +117,29 @@ export declare const actorSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
117
117
|
platform: z.ZodLiteral<"system">;
|
|
118
118
|
name: z.ZodString;
|
|
119
119
|
}, z.core.$strict>], "platform">;
|
|
120
|
+
/** Core-owned provider account linked to a user when verified. */
|
|
121
|
+
export declare const identitySchema: z.ZodObject<{
|
|
122
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
123
|
+
handle: z.ZodOptional<z.ZodString>;
|
|
124
|
+
id: z.ZodString;
|
|
125
|
+
provider: z.ZodString;
|
|
126
|
+
providerSubjectId: z.ZodString;
|
|
127
|
+
providerTenantId: z.ZodOptional<z.ZodString>;
|
|
128
|
+
}, z.core.$strict>;
|
|
129
|
+
/** Core-owned person with every linked provider identity. */
|
|
130
|
+
export declare const userSchema: z.ZodObject<{
|
|
131
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
132
|
+
email: z.ZodString;
|
|
133
|
+
id: z.ZodString;
|
|
134
|
+
identities: z.ZodArray<z.ZodObject<{
|
|
135
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
136
|
+
handle: z.ZodOptional<z.ZodString>;
|
|
137
|
+
id: z.ZodString;
|
|
138
|
+
provider: z.ZodString;
|
|
139
|
+
providerSubjectId: z.ZodString;
|
|
140
|
+
providerTenantId: z.ZodOptional<z.ZodString>;
|
|
141
|
+
}, z.core.$strict>>;
|
|
142
|
+
}, z.core.$strict>;
|
|
120
143
|
/** Compact destination-visible context explaining what produced a reply. */
|
|
121
144
|
export declare const replyAttributionSchema: z.ZodObject<{
|
|
122
145
|
label: z.ZodPipe<z.ZodString, z.ZodString>;
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { PluginContext, LocalInvocationContext, PluginEmbedder, PluginModel, Actor, SlackInvocationContext } from "./context";
|
|
1
|
+
import type { PluginContext, LocalInvocationContext, PluginEmbedder, Identity, PluginModel, Actor, SlackInvocationContext, User } from "./context";
|
|
2
2
|
import type { PluginCredentialSubject } from "./credentials";
|
|
3
3
|
import type { PluginAnnotations } from "./annotations";
|
|
4
4
|
import type { SlackConversationLink } from "./operations";
|
|
5
5
|
import type { PluginState } from "./state";
|
|
6
|
-
import { z, type
|
|
6
|
+
import { z, type ZodTypeAny } from "zod";
|
|
7
7
|
export interface PluginEnv {
|
|
8
8
|
get(key: string): string | undefined;
|
|
9
9
|
set(key: string, value: string): void;
|
|
@@ -63,8 +63,8 @@ export declare const pluginToolContentSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
63
63
|
}, z.core.$strict>], "type">;
|
|
64
64
|
/** Model-visible content returned by a plugin tool. */
|
|
65
65
|
export type PluginToolContent = z.output<typeof pluginToolContentSchema>;
|
|
66
|
-
/**
|
|
67
|
-
export interface
|
|
66
|
+
/** Pi-native projection with model-facing content and canonical runtime details. */
|
|
67
|
+
export interface PluginToolOutputEnvelope<TDetails = unknown> {
|
|
68
68
|
content: PluginToolContent[];
|
|
69
69
|
details: TDetails;
|
|
70
70
|
}
|
|
@@ -159,31 +159,16 @@ export declare const pluginToolContinuationSchema: z.ZodObject<{
|
|
|
159
159
|
arguments: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
160
160
|
reason: z.ZodOptional<z.ZodString>;
|
|
161
161
|
}, z.core.$strict>;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
message: z.ZodString;
|
|
165
|
-
retryable: z.ZodOptional<z.ZodBoolean>;
|
|
166
|
-
}, z.core.$strict>;
|
|
167
|
-
export declare const pluginToolResultSchema: z.ZodObject<{
|
|
168
|
-
ok: z.ZodBoolean;
|
|
169
|
-
status: z.ZodEnum<{
|
|
170
|
-
error: "error";
|
|
171
|
-
success: "success";
|
|
172
|
-
}>;
|
|
162
|
+
/** Shared optional fields for canonical plugin tool outputs. */
|
|
163
|
+
export declare const pluginToolOutputSchema: z.ZodObject<{
|
|
173
164
|
target: z.ZodOptional<z.ZodString>;
|
|
174
|
-
data: z.ZodOptional<z.ZodUnknown>;
|
|
175
165
|
truncated: z.ZodOptional<z.ZodBoolean>;
|
|
176
166
|
continuation: z.ZodOptional<z.ZodObject<{
|
|
177
167
|
arguments: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
178
168
|
reason: z.ZodOptional<z.ZodString>;
|
|
179
169
|
}, z.core.$strict>>;
|
|
180
|
-
error: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
181
|
-
kind: z.ZodString;
|
|
182
|
-
message: z.ZodString;
|
|
183
|
-
retryable: z.ZodOptional<z.ZodBoolean>;
|
|
184
|
-
}, z.core.$strict>, z.ZodString]>>;
|
|
185
170
|
}, z.core.$loose>;
|
|
186
|
-
export type
|
|
171
|
+
export type PluginToolOutput = z.output<typeof pluginToolOutputSchema>;
|
|
187
172
|
export type PluginToolExecute<TInput = unknown, TOutput = unknown> = {
|
|
188
173
|
bivarianceHack(input: TInput, options: PluginToolExecuteOptions): Promise<TOutput> | TOutput;
|
|
189
174
|
}["bivarianceHack"];
|
|
@@ -262,17 +247,17 @@ export interface PluginToolDefinition<TInput = unknown, TOutput = unknown, TExec
|
|
|
262
247
|
promptSnippet?: string;
|
|
263
248
|
execute?: PluginToolExecute<TInput, TExecuteOutput>;
|
|
264
249
|
}
|
|
265
|
-
type ZodPluginToolDefinition<TInputSchema extends ZodTypeAny, TOutputSchema extends
|
|
250
|
+
type ZodPluginToolDefinition<TInputSchema extends ZodTypeAny, TOutputSchema extends ZodTypeAny, TExecuteResult extends z.input<TOutputSchema> | PluginToolOutputEnvelope<z.input<TOutputSchema>>> = Omit<PluginToolDefinition<z.output<TInputSchema>, z.output<TOutputSchema>>, "inputSchema" | "outputSchema" | "prepareArguments" | "execute"> & {
|
|
266
251
|
inputSchema: TInputSchema;
|
|
267
252
|
outputSchema: TOutputSchema;
|
|
268
253
|
prepareArguments?: (args: unknown) => z.input<TInputSchema>;
|
|
269
254
|
execute?: (input: z.output<TInputSchema>, options: PluginToolExecuteOptions) => Promise<TExecuteResult> | TExecuteResult;
|
|
270
255
|
};
|
|
271
|
-
type ParsedPluginToolExecuteResult<TOutputSchema extends ZodTypeAny, TResult> = TResult extends
|
|
256
|
+
type ParsedPluginToolExecuteResult<TOutputSchema extends ZodTypeAny, TResult> = TResult extends PluginToolOutputEnvelope<unknown> ? PluginToolOutputEnvelope<z.output<TOutputSchema>> : z.output<TOutputSchema>;
|
|
272
257
|
/** Define a plugin tool with Zod input parsing and validated structured results. */
|
|
273
|
-
export declare function zodTool<TInputSchema extends ZodTypeAny, TOutputSchema extends
|
|
258
|
+
export declare function zodTool<TInputSchema extends ZodTypeAny, TOutputSchema extends ZodTypeAny, TExecuteResult extends z.input<TOutputSchema> | PluginToolOutputEnvelope<z.input<TOutputSchema>>>(definition: ZodPluginToolDefinition<TInputSchema, TOutputSchema, TExecuteResult>): PluginToolDefinition<z.output<TInputSchema>, z.output<TOutputSchema>, ParsedPluginToolExecuteResult<TOutputSchema, TExecuteResult>>;
|
|
274
259
|
/** Define a plugin tool with Zod input parsing and the structured result contract. */
|
|
275
|
-
export declare function definePluginTool<TInputSchema extends ZodTypeAny, TOutputSchema extends
|
|
260
|
+
export declare function definePluginTool<TInputSchema extends ZodTypeAny, TOutputSchema extends ZodTypeAny, TExecuteResult extends z.input<TOutputSchema> | PluginToolOutputEnvelope<z.input<TOutputSchema>>>(definition: ZodPluginToolDefinition<TInputSchema, TOutputSchema, TExecuteResult>): PluginToolDefinition<z.output<TInputSchema>, z.output<TOutputSchema>, ParsedPluginToolExecuteResult<TOutputSchema, TExecuteResult>>;
|
|
276
261
|
export interface SlackToolRegistrationHookContext {
|
|
277
262
|
/**
|
|
278
263
|
* Capabilities of the source Slack conversation exposed to this plugin.
|
|
@@ -289,6 +274,10 @@ export interface SlackToolRegistrationHookContext {
|
|
|
289
274
|
allowedWhen: "private-direct-conversation";
|
|
290
275
|
}>;
|
|
291
276
|
}
|
|
277
|
+
export interface PluginResourceEventToolContext {
|
|
278
|
+
/** Whether this invocation can create a working resource-event subscription. */
|
|
279
|
+
canSubscribe: boolean;
|
|
280
|
+
}
|
|
292
281
|
interface BaseToolRegistrationHookContext extends PluginContext {
|
|
293
282
|
/**
|
|
294
283
|
* Opaque Junior conversation/session identity for this turn.
|
|
@@ -302,7 +291,15 @@ interface BaseToolRegistrationHookContext extends PluginContext {
|
|
|
302
291
|
egress: PluginEgress;
|
|
303
292
|
mcp?: PluginMcp;
|
|
304
293
|
model: PluginModel;
|
|
294
|
+
resourceEvents: PluginResourceEventToolContext;
|
|
305
295
|
state: PluginState;
|
|
296
|
+
users: {
|
|
297
|
+
/** Resolve the current actor's stored identity and linked user. */
|
|
298
|
+
resolveActor(): Promise<{
|
|
299
|
+
identity: Identity;
|
|
300
|
+
user?: User;
|
|
301
|
+
} | undefined>;
|
|
302
|
+
};
|
|
306
303
|
userText?: string;
|
|
307
304
|
}
|
|
308
305
|
interface SlackToolRegistrationContext extends BaseToolRegistrationHookContext, SlackInvocationContext {
|
package/dist/user-pages.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* routing, response validation, and browser rendering.
|
|
6
6
|
*/
|
|
7
7
|
import { z } from "zod";
|
|
8
|
-
import type {
|
|
8
|
+
import type { PluginContext, User } from "./context";
|
|
9
9
|
/** Bounded list content returned by a plugin-owned user page. */
|
|
10
10
|
export declare const pluginUserPageContentSchema: z.ZodObject<{
|
|
11
11
|
emptyText: z.ZodOptional<z.ZodString>;
|
|
@@ -76,14 +76,9 @@ export declare const pluginUserPageInputSchema: z.ZodObject<{
|
|
|
76
76
|
query: z.ZodOptional<z.ZodString>;
|
|
77
77
|
}, z.core.$strict>;
|
|
78
78
|
export type PluginUserPageInput = z.output<typeof pluginUserPageInputSchema>;
|
|
79
|
-
/** Runtime-owned actor shapes that may belong to an authenticated viewer. */
|
|
80
|
-
export type PluginUserPageActor = SlackActor | LocalActor;
|
|
81
79
|
/** Trusted host context supplied while reading one plugin user page. */
|
|
82
80
|
export interface PluginUserPageContext extends PluginContext {
|
|
83
|
-
viewer:
|
|
84
|
-
actors: PluginUserPageActor[];
|
|
85
|
-
email: string;
|
|
86
|
-
};
|
|
81
|
+
viewer: User;
|
|
87
82
|
}
|
|
88
83
|
/** Navigation metadata and reader for one core-rendered plugin user page. */
|
|
89
84
|
export interface PluginUserPageDefinition {
|
package/package.json
CHANGED
package/src/context.ts
CHANGED
|
@@ -2,12 +2,14 @@ import { z } from "zod";
|
|
|
2
2
|
import type { ZodTypeAny } from "zod";
|
|
3
3
|
import {
|
|
4
4
|
destinationSchema,
|
|
5
|
+
identitySchema,
|
|
5
6
|
localActorSchema,
|
|
6
7
|
platformSchema,
|
|
7
8
|
actorSchema,
|
|
8
9
|
slackActorSchema,
|
|
9
10
|
systemActorSchema,
|
|
10
11
|
sourceSchema,
|
|
12
|
+
userSchema,
|
|
11
13
|
} from "./schemas";
|
|
12
14
|
|
|
13
15
|
/** Runtime platform name without source or destination coordinates. */
|
|
@@ -16,6 +18,8 @@ export type Actor = z.output<typeof actorSchema>;
|
|
|
16
18
|
export type SlackActor = z.output<typeof slackActorSchema>;
|
|
17
19
|
export type LocalActor = z.output<typeof localActorSchema>;
|
|
18
20
|
export type SystemActor = z.output<typeof systemActorSchema>;
|
|
21
|
+
export type Identity = z.output<typeof identitySchema>;
|
|
22
|
+
export type User = z.output<typeof userSchema>;
|
|
19
23
|
export type Source = z.output<typeof sourceSchema>;
|
|
20
24
|
export type SlackSource = Extract<Source, { platform: "slack" }>;
|
|
21
25
|
export type LocalSource = Extract<Source, { platform: "local" }>;
|
package/src/operations.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type {
|
|
2
|
+
import type { PluginContext, User } from "./context";
|
|
3
3
|
import type { Dispatch, DispatchOptions, DispatchResult } from "./dispatch";
|
|
4
4
|
import { nonBlankStringSchema } from "./schemas";
|
|
5
5
|
import type { PluginReadState, PluginState } from "./state";
|
|
@@ -124,9 +124,9 @@ export interface RouteRegistrationHookContext extends PluginContext {
|
|
|
124
124
|
|
|
125
125
|
export interface ApiRouteRegistrationHookContext extends PluginContext {
|
|
126
126
|
eventStats: PluginConversationEventStats;
|
|
127
|
-
|
|
128
|
-
/** Resolve
|
|
129
|
-
|
|
127
|
+
users: {
|
|
128
|
+
/** Resolve or create the canonical user for one verified email. */
|
|
129
|
+
resolve(email: string): Promise<User | undefined>;
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
132
|
|
package/src/registration.ts
CHANGED
|
@@ -4,6 +4,10 @@ import type { PluginHooks } from "./hooks";
|
|
|
4
4
|
import type { PluginManifest } from "./manifest";
|
|
5
5
|
import type { PluginTasks } from "./tasks";
|
|
6
6
|
import type { PluginUserPageDefinition } from "./user-pages";
|
|
7
|
+
import {
|
|
8
|
+
pluginResourceEventsSchema,
|
|
9
|
+
type PluginResourceEvents,
|
|
10
|
+
} from "./resource-events";
|
|
7
11
|
|
|
8
12
|
export interface PluginModelConfig {
|
|
9
13
|
/** Host model family used when no explicit structured model id is configured. */
|
|
@@ -19,6 +23,7 @@ export type PluginRegistrationInput = {
|
|
|
19
23
|
manifest: PluginManifest;
|
|
20
24
|
model?: PluginModelConfig;
|
|
21
25
|
packageName?: string;
|
|
26
|
+
resourceEvents?: PluginResourceEvents;
|
|
22
27
|
tasks?: PluginTasks;
|
|
23
28
|
userPages?: PluginUserPageDefinition[];
|
|
24
29
|
};
|
|
@@ -87,6 +92,14 @@ export function defineJuniorPlugin(
|
|
|
87
92
|
`Junior plugin "${name}" conversationEvents must be an array.`,
|
|
88
93
|
);
|
|
89
94
|
}
|
|
95
|
+
if (plugin.resourceEvents !== undefined) {
|
|
96
|
+
const parsed = pluginResourceEventsSchema.safeParse(plugin.resourceEvents);
|
|
97
|
+
if (!parsed.success) {
|
|
98
|
+
throw new Error(`Junior plugin "${name}" resourceEvents is invalid.`, {
|
|
99
|
+
cause: parsed.error,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
90
103
|
const conversationEventIds = new Set<string>();
|
|
91
104
|
for (const event of plugin.conversationEvents ?? []) {
|
|
92
105
|
if (
|
package/src/resource-events.ts
CHANGED
|
@@ -1,34 +1,145 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
+
export const RESOURCE_EVENT_SUMMARY_MAX_LENGTH = 4_000;
|
|
4
|
+
export const RESOURCE_EVENT_TEXT_MAX_LENGTH = 8_000;
|
|
5
|
+
|
|
6
|
+
/** Canonical dotted event type published and selected across plugins. */
|
|
7
|
+
export const resourceEventTypeSchema = z
|
|
8
|
+
.string()
|
|
9
|
+
.regex(/^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)+$/)
|
|
10
|
+
.describe(
|
|
11
|
+
"Canonical dotted event type, such as issue.closed or pull_request.review.changes_requested.",
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export const resourceTypeSchema = z
|
|
15
|
+
.string()
|
|
16
|
+
.regex(/^[a-z][a-z0-9_]*$/)
|
|
17
|
+
.describe(
|
|
18
|
+
"Canonical plugin-defined resource type, such as issue or pull_request.",
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const pluginResourceEventTypeSchema = z
|
|
22
|
+
.object({
|
|
23
|
+
type: resourceTypeSchema,
|
|
24
|
+
supportedEvents: z.array(resourceEventTypeSchema).min(1),
|
|
25
|
+
suggestedEvents: z.array(resourceEventTypeSchema).optional(),
|
|
26
|
+
})
|
|
27
|
+
.strict()
|
|
28
|
+
.superRefine((resourceType, context) => {
|
|
29
|
+
const supported = new Set<string>();
|
|
30
|
+
resourceType.supportedEvents.forEach((eventType, index) => {
|
|
31
|
+
if (supported.has(eventType)) {
|
|
32
|
+
context.addIssue({
|
|
33
|
+
code: "custom",
|
|
34
|
+
message: `Duplicate supported resource event type "${eventType}".`,
|
|
35
|
+
path: ["supportedEvents", index],
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
supported.add(eventType);
|
|
39
|
+
});
|
|
40
|
+
const suggested = new Set<string>();
|
|
41
|
+
resourceType.suggestedEvents?.forEach((eventType, index) => {
|
|
42
|
+
if (suggested.has(eventType)) {
|
|
43
|
+
context.addIssue({
|
|
44
|
+
code: "custom",
|
|
45
|
+
message: `Duplicate suggested resource event type "${eventType}".`,
|
|
46
|
+
path: ["suggestedEvents", index],
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (!supported.has(eventType)) {
|
|
50
|
+
context.addIssue({
|
|
51
|
+
code: "custom",
|
|
52
|
+
message: `Suggested resource event type "${eventType}" is not supported.`,
|
|
53
|
+
path: ["suggestedEvents", index],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
suggested.add(eventType);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export const pluginResourceEventsSchema = z
|
|
61
|
+
.object({
|
|
62
|
+
resourceTypes: z.array(pluginResourceEventTypeSchema).min(1),
|
|
63
|
+
isEnabled: z.function({ input: [], output: z.boolean() }).optional(),
|
|
64
|
+
normalizeIdentifier: z
|
|
65
|
+
.function({ input: [z.string()], output: z.string() })
|
|
66
|
+
.optional(),
|
|
67
|
+
})
|
|
68
|
+
.strict()
|
|
69
|
+
.superRefine((registration, context) => {
|
|
70
|
+
const seen = new Set<string>();
|
|
71
|
+
registration.resourceTypes.forEach((resourceType, index) => {
|
|
72
|
+
if (seen.has(resourceType.type)) {
|
|
73
|
+
context.addIssue({
|
|
74
|
+
code: "custom",
|
|
75
|
+
message: `Duplicate resource type "${resourceType.type}".`,
|
|
76
|
+
path: ["resourceTypes", index, "type"],
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
seen.add(resourceType.type);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export type PluginResourceEventType = z.output<
|
|
84
|
+
typeof pluginResourceEventTypeSchema
|
|
85
|
+
>;
|
|
86
|
+
export type PluginResourceEvents = z.output<typeof pluginResourceEventsSchema>;
|
|
87
|
+
|
|
88
|
+
/** Apply a plugin's identifier convention at resource-event boundaries. */
|
|
89
|
+
export function normalizeResourceEventIdentifier(
|
|
90
|
+
registration: Pick<PluginResourceEvents, "normalizeIdentifier"> | undefined,
|
|
91
|
+
identifier: string,
|
|
92
|
+
): string {
|
|
93
|
+
const trimmed = identifier.trim();
|
|
94
|
+
const normalized = (
|
|
95
|
+
registration?.normalizeIdentifier?.(trimmed) ?? trimmed
|
|
96
|
+
).trim();
|
|
97
|
+
if (!normalized) {
|
|
98
|
+
throw new Error("Resource event identifier must not normalize to empty");
|
|
99
|
+
}
|
|
100
|
+
return normalized;
|
|
101
|
+
}
|
|
102
|
+
|
|
3
103
|
export const subscribableResourceSchema = z
|
|
4
104
|
.object({
|
|
105
|
+
identifier: z.string().min(1),
|
|
5
106
|
label: z.string().min(1),
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type: z.string().min(1),
|
|
107
|
+
namespace: z.string().min(1),
|
|
108
|
+
suggestedEvents: z.array(resourceEventTypeSchema).optional(),
|
|
109
|
+
supportedEvents: z.array(resourceEventTypeSchema),
|
|
110
|
+
type: resourceTypeSchema,
|
|
11
111
|
})
|
|
12
112
|
.strict();
|
|
13
113
|
|
|
14
114
|
export type SubscribableResource = z.output<typeof subscribableResourceSchema>;
|
|
15
115
|
|
|
16
|
-
export const
|
|
116
|
+
export const resourceEventInputSchema = z
|
|
17
117
|
.object({
|
|
18
118
|
eventKey: z.string().min(1),
|
|
19
|
-
eventType:
|
|
119
|
+
eventType: resourceEventTypeSchema,
|
|
120
|
+
identifier: z.string().min(1),
|
|
20
121
|
occurredAtMs: z.number().finite(),
|
|
21
|
-
provider: z.string().min(1),
|
|
22
|
-
resourceRef: z.string().min(1),
|
|
23
122
|
terminal: z.boolean().optional(),
|
|
24
|
-
trustedSummary: z
|
|
25
|
-
|
|
123
|
+
trustedSummary: z
|
|
124
|
+
.string()
|
|
125
|
+
.min(1)
|
|
126
|
+
.transform((value) => value.slice(0, RESOURCE_EVENT_SUMMARY_MAX_LENGTH)),
|
|
127
|
+
untrustedText: z
|
|
128
|
+
.string()
|
|
129
|
+
.transform((value) => value.slice(0, RESOURCE_EVENT_TEXT_MAX_LENGTH))
|
|
130
|
+
.optional(),
|
|
26
131
|
})
|
|
27
132
|
.strict();
|
|
28
133
|
|
|
134
|
+
export type ResourceEventInput = z.output<typeof resourceEventInputSchema>;
|
|
135
|
+
|
|
136
|
+
export const resourceEventSchema = resourceEventInputSchema.extend({
|
|
137
|
+
namespace: z.string().min(1),
|
|
138
|
+
});
|
|
139
|
+
|
|
29
140
|
export type ResourceEvent = z.output<typeof resourceEventSchema>;
|
|
30
141
|
|
|
31
142
|
export interface ResourceEventPublisher {
|
|
32
|
-
/** Publish one normalized event
|
|
33
|
-
publish(event:
|
|
143
|
+
/** Publish one normalized event under the owning plugin's namespace. */
|
|
144
|
+
publish(event: ResourceEventInput): Promise<void>;
|
|
34
145
|
}
|
package/src/schemas.ts
CHANGED
|
@@ -136,6 +136,28 @@ export const actorSchema = z.discriminatedUnion("platform", [
|
|
|
136
136
|
systemActorSchema,
|
|
137
137
|
]);
|
|
138
138
|
|
|
139
|
+
/** Core-owned provider account linked to a user when verified. */
|
|
140
|
+
export const identitySchema = z
|
|
141
|
+
.object({
|
|
142
|
+
displayName: nonBlankStringSchema.optional(),
|
|
143
|
+
handle: nonBlankStringSchema.optional(),
|
|
144
|
+
id: exactNonBlankStringSchema,
|
|
145
|
+
provider: exactNonBlankStringSchema,
|
|
146
|
+
providerSubjectId: exactNonBlankStringSchema,
|
|
147
|
+
providerTenantId: exactNonBlankStringSchema.optional(),
|
|
148
|
+
})
|
|
149
|
+
.strict();
|
|
150
|
+
|
|
151
|
+
/** Core-owned person with every linked provider identity. */
|
|
152
|
+
export const userSchema = z
|
|
153
|
+
.object({
|
|
154
|
+
displayName: nonBlankStringSchema.optional(),
|
|
155
|
+
email: nonBlankStringSchema.max(320),
|
|
156
|
+
id: exactNonBlankStringSchema,
|
|
157
|
+
identities: z.array(identitySchema).max(100),
|
|
158
|
+
})
|
|
159
|
+
.strict();
|
|
160
|
+
|
|
139
161
|
const dispatchMetadataSchema = z
|
|
140
162
|
.record(z.string(), z.string())
|
|
141
163
|
.superRefine((metadata, ctx) => {
|
package/src/tools.ts
CHANGED
|
@@ -2,15 +2,17 @@ import type {
|
|
|
2
2
|
PluginContext,
|
|
3
3
|
LocalInvocationContext,
|
|
4
4
|
PluginEmbedder,
|
|
5
|
+
Identity,
|
|
5
6
|
PluginModel,
|
|
6
7
|
Actor,
|
|
7
8
|
SlackInvocationContext,
|
|
9
|
+
User,
|
|
8
10
|
} from "./context";
|
|
9
11
|
import type { PluginCredentialSubject } from "./credentials";
|
|
10
12
|
import type { PluginAnnotations } from "./annotations";
|
|
11
13
|
import type { SlackConversationLink } from "./operations";
|
|
12
14
|
import type { PluginState } from "./state";
|
|
13
|
-
import { z, type
|
|
15
|
+
import { z, type ZodTypeAny } from "zod";
|
|
14
16
|
|
|
15
17
|
export interface PluginEnv {
|
|
16
18
|
get(key: string): string | undefined;
|
|
@@ -81,8 +83,8 @@ export const pluginToolContentSchema = z.discriminatedUnion("type", [
|
|
|
81
83
|
/** Model-visible content returned by a plugin tool. */
|
|
82
84
|
export type PluginToolContent = z.output<typeof pluginToolContentSchema>;
|
|
83
85
|
|
|
84
|
-
/**
|
|
85
|
-
export interface
|
|
86
|
+
/** Pi-native projection with model-facing content and canonical runtime details. */
|
|
87
|
+
export interface PluginToolOutputEnvelope<TDetails = unknown> {
|
|
86
88
|
content: PluginToolContent[];
|
|
87
89
|
details: TDetails;
|
|
88
90
|
}
|
|
@@ -194,27 +196,16 @@ export const pluginToolContinuationSchema = z
|
|
|
194
196
|
})
|
|
195
197
|
.strict();
|
|
196
198
|
|
|
197
|
-
|
|
199
|
+
/** Shared optional fields for canonical plugin tool outputs. */
|
|
200
|
+
export const pluginToolOutputSchema = z
|
|
198
201
|
.object({
|
|
199
|
-
kind: z.string().min(1),
|
|
200
|
-
message: z.string().min(1),
|
|
201
|
-
retryable: z.boolean().optional(),
|
|
202
|
-
})
|
|
203
|
-
.strict();
|
|
204
|
-
|
|
205
|
-
export const pluginToolResultSchema = z
|
|
206
|
-
.object({
|
|
207
|
-
ok: z.boolean(),
|
|
208
|
-
status: z.enum(["success", "error"]),
|
|
209
202
|
target: z.string().min(1).optional(),
|
|
210
|
-
data: z.unknown().optional(),
|
|
211
203
|
truncated: z.boolean().optional(),
|
|
212
204
|
continuation: pluginToolContinuationSchema.optional(),
|
|
213
|
-
error: z.union([pluginToolErrorSchema, z.string()]).optional(),
|
|
214
205
|
})
|
|
215
206
|
.passthrough();
|
|
216
207
|
|
|
217
|
-
export type
|
|
208
|
+
export type PluginToolOutput = z.output<typeof pluginToolOutputSchema>;
|
|
218
209
|
|
|
219
210
|
export type PluginToolExecute<TInput = unknown, TOutput = unknown> = {
|
|
220
211
|
bivarianceHack(
|
|
@@ -320,10 +311,10 @@ export interface PluginToolDefinition<
|
|
|
320
311
|
|
|
321
312
|
type ZodPluginToolDefinition<
|
|
322
313
|
TInputSchema extends ZodTypeAny,
|
|
323
|
-
TOutputSchema extends
|
|
314
|
+
TOutputSchema extends ZodTypeAny,
|
|
324
315
|
TExecuteResult extends
|
|
325
316
|
| z.input<TOutputSchema>
|
|
326
|
-
|
|
|
317
|
+
| PluginToolOutputEnvelope<z.input<TOutputSchema>>,
|
|
327
318
|
> = Omit<
|
|
328
319
|
PluginToolDefinition<z.output<TInputSchema>, z.output<TOutputSchema>>,
|
|
329
320
|
"inputSchema" | "outputSchema" | "prepareArguments" | "execute"
|
|
@@ -338,13 +329,13 @@ type ZodPluginToolDefinition<
|
|
|
338
329
|
};
|
|
339
330
|
|
|
340
331
|
type ParsedPluginToolExecuteResult<TOutputSchema extends ZodTypeAny, TResult> =
|
|
341
|
-
TResult extends
|
|
342
|
-
?
|
|
332
|
+
TResult extends PluginToolOutputEnvelope<unknown>
|
|
333
|
+
? PluginToolOutputEnvelope<z.output<TOutputSchema>>
|
|
343
334
|
: z.output<TOutputSchema>;
|
|
344
335
|
|
|
345
|
-
function
|
|
336
|
+
function isPluginToolOutputEnvelope(
|
|
346
337
|
value: unknown,
|
|
347
|
-
): value is
|
|
338
|
+
): value is PluginToolOutputEnvelope<unknown> {
|
|
348
339
|
return (
|
|
349
340
|
value !== null &&
|
|
350
341
|
typeof value === "object" &&
|
|
@@ -380,10 +371,10 @@ function parsePluginToolInput<TInputSchema extends ZodTypeAny>(
|
|
|
380
371
|
|
|
381
372
|
function createZodTool<
|
|
382
373
|
TInputSchema extends ZodTypeAny,
|
|
383
|
-
TOutputSchema extends
|
|
374
|
+
TOutputSchema extends ZodTypeAny,
|
|
384
375
|
TExecuteResult extends
|
|
385
376
|
| z.input<TOutputSchema>
|
|
386
|
-
|
|
|
377
|
+
| PluginToolOutputEnvelope<z.input<TOutputSchema>>,
|
|
387
378
|
>(
|
|
388
379
|
definition: ZodPluginToolDefinition<
|
|
389
380
|
TInputSchema,
|
|
@@ -434,15 +425,13 @@ function createZodTool<
|
|
|
434
425
|
input as z.output<TInputSchema>,
|
|
435
426
|
options,
|
|
436
427
|
);
|
|
437
|
-
if (
|
|
428
|
+
if (isPluginToolOutputEnvelope(result)) {
|
|
438
429
|
return {
|
|
439
430
|
content: z.array(pluginToolContentSchema).parse(result.content),
|
|
440
|
-
details: outputSchema.parse(
|
|
441
|
-
pluginToolResultSchema.parse(result.details),
|
|
442
|
-
),
|
|
431
|
+
details: outputSchema.parse(result.details),
|
|
443
432
|
};
|
|
444
433
|
}
|
|
445
|
-
return outputSchema.parse(
|
|
434
|
+
return outputSchema.parse(result);
|
|
446
435
|
},
|
|
447
436
|
}
|
|
448
437
|
: {}),
|
|
@@ -456,10 +445,10 @@ function createZodTool<
|
|
|
456
445
|
/** Define a plugin tool with Zod input parsing and validated structured results. */
|
|
457
446
|
export function zodTool<
|
|
458
447
|
TInputSchema extends ZodTypeAny,
|
|
459
|
-
TOutputSchema extends
|
|
448
|
+
TOutputSchema extends ZodTypeAny,
|
|
460
449
|
TExecuteResult extends
|
|
461
450
|
| z.input<TOutputSchema>
|
|
462
|
-
|
|
|
451
|
+
| PluginToolOutputEnvelope<z.input<TOutputSchema>>,
|
|
463
452
|
>(
|
|
464
453
|
definition: ZodPluginToolDefinition<
|
|
465
454
|
TInputSchema,
|
|
@@ -477,10 +466,10 @@ export function zodTool<
|
|
|
477
466
|
/** Define a plugin tool with Zod input parsing and the structured result contract. */
|
|
478
467
|
export function definePluginTool<
|
|
479
468
|
TInputSchema extends ZodTypeAny,
|
|
480
|
-
TOutputSchema extends
|
|
469
|
+
TOutputSchema extends ZodTypeAny,
|
|
481
470
|
TExecuteResult extends
|
|
482
471
|
| z.input<TOutputSchema>
|
|
483
|
-
|
|
|
472
|
+
| PluginToolOutputEnvelope<z.input<TOutputSchema>>,
|
|
484
473
|
>(
|
|
485
474
|
definition: ZodPluginToolDefinition<
|
|
486
475
|
TInputSchema,
|
|
@@ -513,6 +502,11 @@ export interface SlackToolRegistrationHookContext {
|
|
|
513
502
|
>;
|
|
514
503
|
}
|
|
515
504
|
|
|
505
|
+
export interface PluginResourceEventToolContext {
|
|
506
|
+
/** Whether this invocation can create a working resource-event subscription. */
|
|
507
|
+
canSubscribe: boolean;
|
|
508
|
+
}
|
|
509
|
+
|
|
516
510
|
interface BaseToolRegistrationHookContext extends PluginContext {
|
|
517
511
|
/**
|
|
518
512
|
* Opaque Junior conversation/session identity for this turn.
|
|
@@ -526,7 +520,12 @@ interface BaseToolRegistrationHookContext extends PluginContext {
|
|
|
526
520
|
egress: PluginEgress;
|
|
527
521
|
mcp?: PluginMcp;
|
|
528
522
|
model: PluginModel;
|
|
523
|
+
resourceEvents: PluginResourceEventToolContext;
|
|
529
524
|
state: PluginState;
|
|
525
|
+
users: {
|
|
526
|
+
/** Resolve the current actor's stored identity and linked user. */
|
|
527
|
+
resolveActor(): Promise<{ identity: Identity; user?: User } | undefined>;
|
|
528
|
+
};
|
|
530
529
|
userText?: string;
|
|
531
530
|
}
|
|
532
531
|
|
package/src/user-pages.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* routing, response validation, and browser rendering.
|
|
6
6
|
*/
|
|
7
7
|
import { z } from "zod";
|
|
8
|
-
import type {
|
|
8
|
+
import type { PluginContext, User } from "./context";
|
|
9
9
|
import { nonBlankStringSchema } from "./schemas";
|
|
10
10
|
|
|
11
11
|
const userPageIdSchema = nonBlankStringSchema
|
|
@@ -98,15 +98,9 @@ export const pluginUserPageInputSchema = z
|
|
|
98
98
|
|
|
99
99
|
export type PluginUserPageInput = z.output<typeof pluginUserPageInputSchema>;
|
|
100
100
|
|
|
101
|
-
/** Runtime-owned actor shapes that may belong to an authenticated viewer. */
|
|
102
|
-
export type PluginUserPageActor = SlackActor | LocalActor;
|
|
103
|
-
|
|
104
101
|
/** Trusted host context supplied while reading one plugin user page. */
|
|
105
102
|
export interface PluginUserPageContext extends PluginContext {
|
|
106
|
-
viewer:
|
|
107
|
-
actors: PluginUserPageActor[];
|
|
108
|
-
email: string;
|
|
109
|
-
};
|
|
103
|
+
viewer: User;
|
|
110
104
|
}
|
|
111
105
|
|
|
112
106
|
/** Navigation metadata and reader for one core-rendered plugin user page. */
|