@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 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
- - Authenticated API route apps receive one verified viewer in their request
59
- context. The registration hook exposes actor resolution for plugins whose
60
- viewer-owned data spans platform identities.
61
- - User page readers receive the signed-in email plus runtime-owned actors linked
62
- to that user. Plugins return bounded data and do not mount their own page
63
- routes or browser code.
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, actor resolution,
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
- provider: z5.string().min(1),
282
- resourceRef: z5.string().min(1),
283
- suggestedEvents: z5.array(z5.string().min(1)).optional(),
284
- supportedEvents: z5.array(z5.string().min(1)),
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 resourceEventSchema = z5.object({
369
+ var resourceEventInputSchema = z5.object({
288
370
  eventKey: z5.string().min(1),
289
- eventType: z5.string().min(1),
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 pluginToolErrorSchema = z7.object({
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 isPluginToolResultEnvelope(value) {
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 (isPluginToolResultEnvelope(result)) {
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(pluginToolResultSchema.parse(result));
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
- pluginToolErrorSchema,
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
  };
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import type { LocalActor, PluginContext, SlackActor } from "./context";
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
- viewer: {
97
- /** Resolve every runtime actor linked to one authenticated viewer email. */
98
- actors(email: string): Promise<Array<LocalActor | SlackActor>>;
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. */
@@ -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
- provider: z.ZodString;
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 whose provider matches the owning plugin name. */
24
- publish(event: ResourceEvent): Promise<void>;
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 ZodType, type ZodTypeAny } from "zod";
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
- /** Standard tool result envelope with separate model content and runtime details. */
67
- export interface PluginToolResultEnvelope<TDetails = PluginToolResult> {
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
- export declare const pluginToolErrorSchema: z.ZodObject<{
163
- kind: z.ZodString;
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 PluginToolResult = z.output<typeof pluginToolResultSchema>;
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 ZodType<PluginToolResult>, TExecuteResult extends z.input<TOutputSchema> | PluginToolResultEnvelope<z.input<TOutputSchema>>> = Omit<PluginToolDefinition<z.output<TInputSchema>, z.output<TOutputSchema>>, "inputSchema" | "outputSchema" | "prepareArguments" | "execute"> & {
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 PluginToolResultEnvelope<unknown> ? PluginToolResultEnvelope<z.output<TOutputSchema>> : z.output<TOutputSchema>;
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 ZodType<PluginToolResult>, TExecuteResult extends z.input<TOutputSchema> | PluginToolResultEnvelope<z.input<TOutputSchema>>>(definition: ZodPluginToolDefinition<TInputSchema, TOutputSchema, TExecuteResult>): PluginToolDefinition<z.output<TInputSchema>, z.output<TOutputSchema>, ParsedPluginToolExecuteResult<TOutputSchema, TExecuteResult>>;
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 ZodType<PluginToolResult>, TExecuteResult extends z.input<TOutputSchema> | PluginToolResultEnvelope<z.input<TOutputSchema>>>(definition: ZodPluginToolDefinition<TInputSchema, TOutputSchema, TExecuteResult>): PluginToolDefinition<z.output<TInputSchema>, z.output<TOutputSchema>, ParsedPluginToolExecuteResult<TOutputSchema, TExecuteResult>>;
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 {
@@ -5,7 +5,7 @@
5
5
  * routing, response validation, and browser rendering.
6
6
  */
7
7
  import { z } from "zod";
8
- import type { LocalActor, PluginContext, SlackActor } from "./context";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-plugin-api",
3
- "version": "0.126.1",
3
+ "version": "0.128.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
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 { LocalActor, PluginContext, SlackActor } from "./context";
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
- viewer: {
128
- /** Resolve every runtime actor linked to one authenticated viewer email. */
129
- actors(email: string): Promise<Array<LocalActor | SlackActor>>;
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
 
@@ -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 (
@@ -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
- provider: z.string().min(1),
7
- resourceRef: z.string().min(1),
8
- suggestedEvents: z.array(z.string().min(1)).optional(),
9
- supportedEvents: z.array(z.string().min(1)),
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 resourceEventSchema = z
116
+ export const resourceEventInputSchema = z
17
117
  .object({
18
118
  eventKey: z.string().min(1),
19
- eventType: z.string().min(1),
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.string().min(1),
25
- untrustedText: z.string().optional(),
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 whose provider matches the owning plugin name. */
33
- publish(event: ResourceEvent): Promise<void>;
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 ZodType, type ZodTypeAny } from "zod";
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
- /** Standard tool result envelope with separate model content and runtime details. */
85
- export interface PluginToolResultEnvelope<TDetails = PluginToolResult> {
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
- export const pluginToolErrorSchema = z
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 PluginToolResult = z.output<typeof pluginToolResultSchema>;
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 ZodType<PluginToolResult>,
314
+ TOutputSchema extends ZodTypeAny,
324
315
  TExecuteResult extends
325
316
  | z.input<TOutputSchema>
326
- | PluginToolResultEnvelope<z.input<TOutputSchema>>,
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 PluginToolResultEnvelope<unknown>
342
- ? PluginToolResultEnvelope<z.output<TOutputSchema>>
332
+ TResult extends PluginToolOutputEnvelope<unknown>
333
+ ? PluginToolOutputEnvelope<z.output<TOutputSchema>>
343
334
  : z.output<TOutputSchema>;
344
335
 
345
- function isPluginToolResultEnvelope(
336
+ function isPluginToolOutputEnvelope(
346
337
  value: unknown,
347
- ): value is PluginToolResultEnvelope<unknown> {
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 ZodType<PluginToolResult>,
374
+ TOutputSchema extends ZodTypeAny,
384
375
  TExecuteResult extends
385
376
  | z.input<TOutputSchema>
386
- | PluginToolResultEnvelope<z.input<TOutputSchema>>,
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 (isPluginToolResultEnvelope(result)) {
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(pluginToolResultSchema.parse(result));
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 ZodType<PluginToolResult>,
448
+ TOutputSchema extends ZodTypeAny,
460
449
  TExecuteResult extends
461
450
  | z.input<TOutputSchema>
462
- | PluginToolResultEnvelope<z.input<TOutputSchema>>,
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 ZodType<PluginToolResult>,
469
+ TOutputSchema extends ZodTypeAny,
481
470
  TExecuteResult extends
482
471
  | z.input<TOutputSchema>
483
- | PluginToolResultEnvelope<z.input<TOutputSchema>>,
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 { LocalActor, PluginContext, SlackActor } from "./context";
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. */