@sentry/junior-plugin-api 0.71.3 → 0.73.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/dist/index.d.ts CHANGED
@@ -1,23 +1,84 @@
1
1
  import { z } from "zod";
2
+ /** Runtime-owned Slack address for routing future work or side effects. */
3
+ export declare const slackDestinationSchema: z.ZodObject<{
4
+ platform: z.ZodLiteral<"slack">;
5
+ teamId: z.ZodString;
6
+ channelId: z.ZodString;
7
+ }, z.core.$strict>;
8
+ /** Runtime-owned local CLI conversation address. */
9
+ export declare const localDestinationSchema: z.ZodObject<{
10
+ platform: z.ZodLiteral<"local">;
11
+ conversationId: z.ZodString;
12
+ }, z.core.$strict>;
2
13
  /** Runtime-owned provider-neutral address for routing future work or side effects. */
3
- export declare const destinationSchema: z.ZodObject<{
14
+ export declare const destinationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
4
15
  platform: z.ZodLiteral<"slack">;
5
16
  teamId: z.ZodString;
6
17
  channelId: z.ZodString;
18
+ }, z.core.$strict>, z.ZodObject<{
19
+ platform: z.ZodLiteral<"local">;
20
+ conversationId: z.ZodString;
21
+ }, z.core.$strict>], "platform">;
22
+ /** Runtime-owned Slack coordinates for the inbound invocation. */
23
+ export declare const slackSourceSchema: z.ZodObject<{
24
+ platform: z.ZodLiteral<"slack">;
25
+ teamId: z.ZodString;
26
+ channelId: z.ZodString;
27
+ messageTs: z.ZodOptional<z.ZodString>;
28
+ threadTs: z.ZodOptional<z.ZodString>;
29
+ }, z.core.$strict>;
30
+ /** Runtime-owned local CLI coordinates for the inbound invocation. */
31
+ export declare const localSourceSchema: z.ZodObject<{
32
+ platform: z.ZodLiteral<"local">;
33
+ conversationId: z.ZodString;
7
34
  }, z.core.$strict>;
35
+ /** Runtime-owned provider-neutral coordinates for the inbound invocation. */
36
+ export declare const sourceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
37
+ platform: z.ZodLiteral<"slack">;
38
+ teamId: z.ZodString;
39
+ channelId: z.ZodString;
40
+ messageTs: z.ZodOptional<z.ZodString>;
41
+ threadTs: z.ZodOptional<z.ZodString>;
42
+ }, z.core.$strict>, z.ZodObject<{
43
+ platform: z.ZodLiteral<"local">;
44
+ conversationId: z.ZodString;
45
+ }, z.core.$strict>], "platform">;
8
46
  /** Stable user credential subject shape accepted from plugins. */
9
47
  export declare const agentPluginCredentialSubjectSchema: z.ZodObject<{
10
48
  type: z.ZodLiteral<"user">;
11
49
  userId: z.ZodString;
12
50
  allowedWhen: z.ZodLiteral<"private-direct-conversation">;
13
51
  }, z.core.$strict>;
14
- /** Runtime-provided requester identity visible to plugin hooks. */
15
- export declare const agentPluginRequesterSchema: z.ZodObject<{
16
- userId: z.ZodOptional<z.ZodString>;
17
- userName: z.ZodOptional<z.ZodString>;
52
+ export declare const slackRequesterSchema: z.ZodObject<{
53
+ platform: z.ZodLiteral<"slack">;
54
+ teamId: z.ZodString;
55
+ email: z.ZodOptional<z.ZodString>;
18
56
  fullName: z.ZodOptional<z.ZodString>;
57
+ userId: z.ZodString;
58
+ userName: z.ZodOptional<z.ZodString>;
59
+ }, z.core.$strict>;
60
+ export declare const localRequesterSchema: z.ZodObject<{
61
+ platform: z.ZodLiteral<"local">;
19
62
  email: z.ZodOptional<z.ZodString>;
63
+ fullName: z.ZodOptional<z.ZodString>;
64
+ userId: z.ZodString;
65
+ userName: z.ZodOptional<z.ZodString>;
20
66
  }, z.core.$strict>;
67
+ /** Runtime-provided requester identity visible to plugin hooks. */
68
+ export declare const requesterSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
69
+ platform: z.ZodLiteral<"slack">;
70
+ teamId: z.ZodString;
71
+ email: z.ZodOptional<z.ZodString>;
72
+ fullName: z.ZodOptional<z.ZodString>;
73
+ userId: z.ZodString;
74
+ userName: z.ZodOptional<z.ZodString>;
75
+ }, z.core.$strict>, z.ZodObject<{
76
+ platform: z.ZodLiteral<"local">;
77
+ email: z.ZodOptional<z.ZodString>;
78
+ fullName: z.ZodOptional<z.ZodString>;
79
+ userId: z.ZodString;
80
+ userName: z.ZodOptional<z.ZodString>;
81
+ }, z.core.$strict>], "platform">;
21
82
  /** Plugin dispatch request accepted by Junior core. */
22
83
  export declare const dispatchOptionsSchema: z.ZodObject<{
23
84
  idempotencyKey: z.ZodPipe<z.ZodString, z.ZodString>;
@@ -34,7 +95,16 @@ export declare const dispatchOptionsSchema: z.ZodObject<{
34
95
  input: z.ZodPipe<z.ZodString, z.ZodString>;
35
96
  metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
36
97
  }, z.core.$strict>;
37
- export type AgentPluginRequester = z.output<typeof agentPluginRequesterSchema>;
98
+ export type Requester = z.output<typeof requesterSchema>;
99
+ export type SlackRequester = z.output<typeof slackRequesterSchema>;
100
+ export type LocalRequester = z.output<typeof localRequesterSchema>;
101
+ export type Source = z.output<typeof sourceSchema>;
102
+ export type SlackSource = Extract<Source, {
103
+ platform: "slack";
104
+ }>;
105
+ export type LocalSource = Extract<Source, {
106
+ platform: "local";
107
+ }>;
38
108
  export interface AgentPluginMetadata {
39
109
  name: string;
40
110
  }
@@ -61,6 +131,28 @@ export interface AgentPluginContext {
61
131
  log: AgentPluginLogger;
62
132
  plugin: AgentPluginMetadata;
63
133
  }
134
+ interface BaseInvocationContext {
135
+ /**
136
+ * Opaque Junior conversation/session identity for this invocation.
137
+ * Interactive Slack turns use `slack:{channelId}:{threadTs}`.
138
+ */
139
+ conversationId?: string;
140
+ }
141
+ export interface SlackInvocationContext extends BaseInvocationContext {
142
+ /** Runtime-owned default outbound destination for this invocation, if any. */
143
+ destination?: SlackDestination;
144
+ requester?: SlackRequester;
145
+ /** Runtime-owned source where the invocation came from. */
146
+ source: SlackSource;
147
+ }
148
+ export interface LocalInvocationContext extends BaseInvocationContext {
149
+ /** Runtime-owned default outbound destination for this invocation, if any. */
150
+ destination?: LocalDestination;
151
+ requester?: LocalRequester;
152
+ /** Runtime-owned source where the invocation came from. */
153
+ source: LocalSource;
154
+ }
155
+ export type InvocationContext = LocalInvocationContext | SlackInvocationContext;
64
156
  export interface AgentPluginSandbox {
65
157
  juniorRoot: string;
66
158
  root: string;
@@ -83,13 +175,13 @@ export interface AgentPluginSandbox {
83
175
  }): Promise<void>;
84
176
  }
85
177
  export interface SandboxPrepareHookContext extends AgentPluginContext {
86
- requester?: AgentPluginRequester;
178
+ requester?: Requester;
87
179
  sandbox: AgentPluginSandbox;
88
180
  }
89
181
  export interface BeforeToolExecuteHookContext extends AgentPluginContext {
90
182
  decision: AgentPluginDecision;
91
183
  env: AgentPluginEnv;
92
- requester?: AgentPluginRequester;
184
+ requester?: Requester;
93
185
  tool: {
94
186
  input: Record<string, unknown>;
95
187
  name: string;
@@ -120,23 +212,19 @@ export interface AgentPluginToolDefinition<TInput = unknown> {
120
212
  promptSnippet?: string;
121
213
  execute?: AgentPluginToolExecute<TInput>;
122
214
  }
123
- export interface ToolRegistrationHookContext extends AgentPluginContext {
215
+ export interface SlackToolRegistrationHookContext {
124
216
  /**
125
- * Capabilities of `channelId` the raw conversation channel exposed to
126
- * this plugin. Recomputed from `channelId`, not from `destination`.
217
+ * Capabilities of the source Slack conversation exposed to this plugin.
218
+ * Recomputed from `source.channelId`, not from `destination`.
127
219
  */
128
- channelCapabilities?: {
220
+ channelCapabilities: {
129
221
  canAddReactions: boolean;
130
222
  canCreateCanvas: boolean;
131
223
  canPostToChannel: boolean;
132
224
  };
133
- /**
134
- * The raw Slack channel ID for this conversation — the DM or channel where
135
- * this turn is happening, without any assistant-context-source override.
136
- * Use this as the stable binding key for state scoped to a Slack conversation.
137
- * `channelCapabilities` describes this channel.
138
- */
139
- channelId?: string;
225
+ credentialSubject?: AgentPluginCredentialSubject;
226
+ }
227
+ interface BaseToolRegistrationHookContext extends AgentPluginContext {
140
228
  /**
141
229
  * Opaque Junior conversation/session identity for this turn.
142
230
  * Interactive Slack turns use `slack:{channelId}:{threadTs}`.
@@ -144,22 +232,26 @@ export interface ToolRegistrationHookContext extends AgentPluginContext {
144
232
  * Do not parse as Slack unless the value starts with `slack:`.
145
233
  */
146
234
  conversationId?: string;
147
- credentialSubject?: AgentPluginCredentialSubject;
148
- /**
149
- * Runtime-owned destination suitable for future autonomous dispatch. For
150
- * Slack, this is the raw conversation channel, not a thread timestamp or
151
- * assistant-context source channel.
152
- */
153
- destination?: Destination;
154
- messageTs?: string;
155
- requester?: AgentPluginRequester;
156
235
  state: AgentPluginState;
157
- teamId?: string;
158
- threadTs?: string;
159
236
  userText?: string;
160
237
  }
238
+ interface SlackToolRegistrationContext extends BaseToolRegistrationHookContext, SlackInvocationContext {
239
+ slack: SlackToolRegistrationHookContext;
240
+ }
241
+ interface LocalToolRegistrationContext extends BaseToolRegistrationHookContext, LocalInvocationContext {
242
+ slack?: never;
243
+ }
244
+ export type ToolRegistrationHookContext = LocalToolRegistrationContext | SlackToolRegistrationContext;
161
245
  export type AgentPluginCredentialSubject = z.output<typeof agentPluginCredentialSubjectSchema>;
162
246
  export type Destination = z.output<typeof destinationSchema>;
247
+ export type SlackDestination = Extract<Destination, {
248
+ platform: "slack";
249
+ }>;
250
+ export type LocalDestination = Extract<Destination, {
251
+ platform: "local";
252
+ }>;
253
+ /** Narrow a runtime destination to the Slack-specific address shape. */
254
+ export declare function isSlackDestination(destination: Destination | undefined): destination is SlackDestination;
163
255
  export type DispatchOptions = z.output<typeof dispatchOptionsSchema>;
164
256
  export interface DispatchResult {
165
257
  id: string;
@@ -479,6 +571,7 @@ export interface JuniorPluginManifest {
479
571
  configKeys?: string[];
480
572
  credentials?: JuniorPluginCredentials;
481
573
  description: string;
574
+ displayName: string;
482
575
  domains?: string[];
483
576
  envVars?: Record<string, JuniorPluginEnvVarDeclaration>;
484
577
  mcp?: JuniorPluginMcpConfig;
package/dist/index.js CHANGED
@@ -2,26 +2,60 @@
2
2
  import { z } from "zod";
3
3
  var slackTeamIdSchema = z.string().regex(/^T[A-Z0-9]+$/);
4
4
  var slackConversationIdSchema = z.string().regex(/^(C|G|D)[A-Z0-9]+$/);
5
+ var localConversationIdSchema = z.string().regex(/^local:[a-z0-9_-]+:[a-z0-9][a-z0-9_-]*$/);
5
6
  var exactActorUserIdSchema = z.string().min(1).refine(
6
7
  (value) => value === value.trim() && value.toLowerCase() !== "unknown"
7
8
  );
8
9
  var nonBlankStringSchema = z.string().refine((value) => value.trim().length > 0);
9
- var destinationSchema = z.object({
10
+ var slackDestinationSchema = z.object({
10
11
  platform: z.literal("slack"),
11
12
  teamId: slackTeamIdSchema,
12
13
  channelId: slackConversationIdSchema
13
14
  }).strict();
15
+ var localDestinationSchema = z.object({
16
+ platform: z.literal("local"),
17
+ conversationId: localConversationIdSchema
18
+ }).strict();
19
+ var destinationSchema = z.discriminatedUnion("platform", [
20
+ slackDestinationSchema,
21
+ localDestinationSchema
22
+ ]);
23
+ var slackSourceSchema = z.object({
24
+ platform: z.literal("slack"),
25
+ teamId: slackTeamIdSchema,
26
+ channelId: slackConversationIdSchema,
27
+ messageTs: nonBlankStringSchema.optional(),
28
+ threadTs: nonBlankStringSchema.optional()
29
+ }).strict();
30
+ var localSourceSchema = localDestinationSchema;
31
+ var sourceSchema = z.discriminatedUnion("platform", [
32
+ slackSourceSchema,
33
+ localSourceSchema
34
+ ]);
14
35
  var agentPluginCredentialSubjectSchema = z.object({
15
36
  type: z.literal("user"),
16
37
  userId: exactActorUserIdSchema,
17
38
  allowedWhen: z.literal("private-direct-conversation")
18
39
  }).strict();
19
- var agentPluginRequesterSchema = z.object({
20
- userId: exactActorUserIdSchema.optional(),
21
- userName: nonBlankStringSchema.optional(),
40
+ var requesterProfileSchema = {
41
+ email: nonBlankStringSchema.optional(),
22
42
  fullName: nonBlankStringSchema.optional(),
23
- email: nonBlankStringSchema.optional()
43
+ userId: exactActorUserIdSchema,
44
+ userName: nonBlankStringSchema.optional()
45
+ };
46
+ var slackRequesterSchema = z.object({
47
+ ...requesterProfileSchema,
48
+ platform: z.literal("slack"),
49
+ teamId: slackTeamIdSchema
50
+ }).strict();
51
+ var localRequesterSchema = z.object({
52
+ ...requesterProfileSchema,
53
+ platform: z.literal("local")
24
54
  }).strict();
55
+ var requesterSchema = z.discriminatedUnion("platform", [
56
+ slackRequesterSchema,
57
+ localRequesterSchema
58
+ ]);
25
59
  var dispatchMetadataSchema = z.record(z.string(), z.string()).superRefine((metadata, ctx) => {
26
60
  const entries = Object.entries(metadata);
27
61
  if (entries.length > 20) {
@@ -59,7 +93,7 @@ var dispatchMetadataSchema = z.record(z.string(), z.string()).superRefine((metad
59
93
  var dispatchOptionsSchema = z.object({
60
94
  idempotencyKey: nonBlankStringSchema.pipe(z.string().max(512)),
61
95
  credentialSubject: agentPluginCredentialSubjectSchema.optional(),
62
- destination: destinationSchema,
96
+ destination: slackDestinationSchema,
63
97
  input: nonBlankStringSchema.pipe(z.string().max(32e3)),
64
98
  metadata: dispatchMetadataSchema.optional()
65
99
  }).strict();
@@ -69,6 +103,9 @@ var AgentPluginToolInputError = class extends Error {
69
103
  this.name = "AgentPluginToolInputError";
70
104
  }
71
105
  };
106
+ function isSlackDestination(destination) {
107
+ return destination?.platform === "slack";
108
+ }
72
109
  var agentPluginProviderNameSchema = z.string().regex(/^[a-z][a-z0-9-]*$/);
73
110
  var agentPluginGrantNameSchema = z.string().regex(/^[a-z][a-z0-9.-]*$/);
74
111
  var agentPluginGrantAccessSchema = z.union([
@@ -148,6 +185,11 @@ function defineJuniorPlugin(plugin) {
148
185
  `Junior plugin registration name "${name}" must be a lowercase plugin identifier.`
149
186
  );
150
187
  }
188
+ if (typeof manifest.displayName !== "string" || !manifest.displayName.trim()) {
189
+ throw new Error(
190
+ `Junior plugin "${name}" manifest.displayName is required.`
191
+ );
192
+ }
151
193
  if (typeof manifest.description !== "string" || !manifest.description.trim()) {
152
194
  throw new Error(
153
195
  `Junior plugin "${name}" manifest.description is required.`
@@ -173,8 +215,16 @@ export {
173
215
  agentPluginCredentialSubjectSchema,
174
216
  agentPluginGrantSchema,
175
217
  agentPluginProviderAccountSchema,
176
- agentPluginRequesterSchema,
177
218
  defineJuniorPlugin,
178
219
  destinationSchema,
179
- dispatchOptionsSchema
220
+ dispatchOptionsSchema,
221
+ isSlackDestination,
222
+ localDestinationSchema,
223
+ localRequesterSchema,
224
+ localSourceSchema,
225
+ requesterSchema,
226
+ slackDestinationSchema,
227
+ slackRequesterSchema,
228
+ slackSourceSchema,
229
+ sourceSchema
180
230
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-plugin-api",
3
- "version": "0.71.3",
3
+ "version": "0.73.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
package/src/index.ts CHANGED
@@ -2,6 +2,9 @@ import { z } from "zod";
2
2
 
3
3
  const slackTeamIdSchema = z.string().regex(/^T[A-Z0-9]+$/);
4
4
  const slackConversationIdSchema = z.string().regex(/^(C|G|D)[A-Z0-9]+$/);
5
+ const localConversationIdSchema = z
6
+ .string()
7
+ .regex(/^local:[a-z0-9_-]+:[a-z0-9][a-z0-9_-]*$/);
5
8
  const exactActorUserIdSchema = z
6
9
  .string()
7
10
  .min(1)
@@ -12,15 +15,49 @@ const nonBlankStringSchema = z
12
15
  .string()
13
16
  .refine((value) => value.trim().length > 0);
14
17
 
18
+ /** Runtime-owned Slack address for routing future work or side effects. */
19
+ export const slackDestinationSchema = z
20
+ .object({
21
+ platform: z.literal("slack"),
22
+ teamId: slackTeamIdSchema,
23
+ channelId: slackConversationIdSchema,
24
+ })
25
+ .strict();
26
+
27
+ /** Runtime-owned local CLI conversation address. */
28
+ export const localDestinationSchema = z
29
+ .object({
30
+ platform: z.literal("local"),
31
+ conversationId: localConversationIdSchema,
32
+ })
33
+ .strict();
34
+
15
35
  /** Runtime-owned provider-neutral address for routing future work or side effects. */
16
- export const destinationSchema = z
36
+ export const destinationSchema = z.discriminatedUnion("platform", [
37
+ slackDestinationSchema,
38
+ localDestinationSchema,
39
+ ]);
40
+
41
+ /** Runtime-owned Slack coordinates for the inbound invocation. */
42
+ export const slackSourceSchema = z
17
43
  .object({
18
44
  platform: z.literal("slack"),
19
45
  teamId: slackTeamIdSchema,
20
46
  channelId: slackConversationIdSchema,
47
+ messageTs: nonBlankStringSchema.optional(),
48
+ threadTs: nonBlankStringSchema.optional(),
21
49
  })
22
50
  .strict();
23
51
 
52
+ /** Runtime-owned local CLI coordinates for the inbound invocation. */
53
+ export const localSourceSchema = localDestinationSchema;
54
+
55
+ /** Runtime-owned provider-neutral coordinates for the inbound invocation. */
56
+ export const sourceSchema = z.discriminatedUnion("platform", [
57
+ slackSourceSchema,
58
+ localSourceSchema,
59
+ ]);
60
+
24
61
  /** Stable user credential subject shape accepted from plugins. */
25
62
  export const agentPluginCredentialSubjectSchema = z
26
63
  .object({
@@ -30,16 +67,35 @@ export const agentPluginCredentialSubjectSchema = z
30
67
  })
31
68
  .strict();
32
69
 
33
- /** Runtime-provided requester identity visible to plugin hooks. */
34
- export const agentPluginRequesterSchema = z
70
+ /** Shared exact actor profile fields for platform-scoped requesters. */
71
+ const requesterProfileSchema = {
72
+ email: nonBlankStringSchema.optional(),
73
+ fullName: nonBlankStringSchema.optional(),
74
+ userId: exactActorUserIdSchema,
75
+ userName: nonBlankStringSchema.optional(),
76
+ };
77
+
78
+ export const slackRequesterSchema = z
35
79
  .object({
36
- userId: exactActorUserIdSchema.optional(),
37
- userName: nonBlankStringSchema.optional(),
38
- fullName: nonBlankStringSchema.optional(),
39
- email: nonBlankStringSchema.optional(),
80
+ ...requesterProfileSchema,
81
+ platform: z.literal("slack"),
82
+ teamId: slackTeamIdSchema,
40
83
  })
41
84
  .strict();
42
85
 
86
+ export const localRequesterSchema = z
87
+ .object({
88
+ ...requesterProfileSchema,
89
+ platform: z.literal("local"),
90
+ })
91
+ .strict();
92
+
93
+ /** Runtime-provided requester identity visible to plugin hooks. */
94
+ export const requesterSchema = z.discriminatedUnion("platform", [
95
+ slackRequesterSchema,
96
+ localRequesterSchema,
97
+ ]);
98
+
43
99
  const dispatchMetadataSchema = z
44
100
  .record(z.string(), z.string())
45
101
  .superRefine((metadata, ctx) => {
@@ -82,13 +138,18 @@ export const dispatchOptionsSchema = z
82
138
  .object({
83
139
  idempotencyKey: nonBlankStringSchema.pipe(z.string().max(512)),
84
140
  credentialSubject: agentPluginCredentialSubjectSchema.optional(),
85
- destination: destinationSchema,
141
+ destination: slackDestinationSchema,
86
142
  input: nonBlankStringSchema.pipe(z.string().max(32_000)),
87
143
  metadata: dispatchMetadataSchema.optional(),
88
144
  })
89
145
  .strict();
90
146
 
91
- export type AgentPluginRequester = z.output<typeof agentPluginRequesterSchema>;
147
+ export type Requester = z.output<typeof requesterSchema>;
148
+ export type SlackRequester = z.output<typeof slackRequesterSchema>;
149
+ export type LocalRequester = z.output<typeof localRequesterSchema>;
150
+ export type Source = z.output<typeof sourceSchema>;
151
+ export type SlackSource = Extract<Source, { platform: "slack" }>;
152
+ export type LocalSource = Extract<Source, { platform: "local" }>;
92
153
 
93
154
  export interface AgentPluginMetadata {
94
155
  name: string;
@@ -123,6 +184,32 @@ export interface AgentPluginContext {
123
184
  plugin: AgentPluginMetadata;
124
185
  }
125
186
 
187
+ interface BaseInvocationContext {
188
+ /**
189
+ * Opaque Junior conversation/session identity for this invocation.
190
+ * Interactive Slack turns use `slack:{channelId}:{threadTs}`.
191
+ */
192
+ conversationId?: string;
193
+ }
194
+
195
+ export interface SlackInvocationContext extends BaseInvocationContext {
196
+ /** Runtime-owned default outbound destination for this invocation, if any. */
197
+ destination?: SlackDestination;
198
+ requester?: SlackRequester;
199
+ /** Runtime-owned source where the invocation came from. */
200
+ source: SlackSource;
201
+ }
202
+
203
+ export interface LocalInvocationContext extends BaseInvocationContext {
204
+ /** Runtime-owned default outbound destination for this invocation, if any. */
205
+ destination?: LocalDestination;
206
+ requester?: LocalRequester;
207
+ /** Runtime-owned source where the invocation came from. */
208
+ source: LocalSource;
209
+ }
210
+
211
+ export type InvocationContext = LocalInvocationContext | SlackInvocationContext;
212
+
126
213
  export interface AgentPluginSandbox {
127
214
  juniorRoot: string;
128
215
  root: string;
@@ -146,14 +233,14 @@ export interface AgentPluginSandbox {
146
233
  }
147
234
 
148
235
  export interface SandboxPrepareHookContext extends AgentPluginContext {
149
- requester?: AgentPluginRequester;
236
+ requester?: Requester;
150
237
  sandbox: AgentPluginSandbox;
151
238
  }
152
239
 
153
240
  export interface BeforeToolExecuteHookContext extends AgentPluginContext {
154
241
  decision: AgentPluginDecision;
155
242
  env: AgentPluginEnv;
156
- requester?: AgentPluginRequester;
243
+ requester?: Requester;
157
244
  tool: {
158
245
  input: Record<string, unknown>;
159
246
  name: string;
@@ -188,23 +275,20 @@ export interface AgentPluginToolDefinition<TInput = unknown> {
188
275
  execute?: AgentPluginToolExecute<TInput>;
189
276
  }
190
277
 
191
- export interface ToolRegistrationHookContext extends AgentPluginContext {
278
+ export interface SlackToolRegistrationHookContext {
192
279
  /**
193
- * Capabilities of `channelId` the raw conversation channel exposed to
194
- * this plugin. Recomputed from `channelId`, not from `destination`.
280
+ * Capabilities of the source Slack conversation exposed to this plugin.
281
+ * Recomputed from `source.channelId`, not from `destination`.
195
282
  */
196
- channelCapabilities?: {
283
+ channelCapabilities: {
197
284
  canAddReactions: boolean;
198
285
  canCreateCanvas: boolean;
199
286
  canPostToChannel: boolean;
200
287
  };
201
- /**
202
- * The raw Slack channel ID for this conversation — the DM or channel where
203
- * this turn is happening, without any assistant-context-source override.
204
- * Use this as the stable binding key for state scoped to a Slack conversation.
205
- * `channelCapabilities` describes this channel.
206
- */
207
- channelId?: string;
288
+ credentialSubject?: AgentPluginCredentialSubject;
289
+ }
290
+
291
+ interface BaseToolRegistrationHookContext extends AgentPluginContext {
208
292
  /**
209
293
  * Opaque Junior conversation/session identity for this turn.
210
294
  * Interactive Slack turns use `slack:{channelId}:{threadTs}`.
@@ -212,27 +296,41 @@ export interface ToolRegistrationHookContext extends AgentPluginContext {
212
296
  * Do not parse as Slack unless the value starts with `slack:`.
213
297
  */
214
298
  conversationId?: string;
215
- credentialSubject?: AgentPluginCredentialSubject;
216
- /**
217
- * Runtime-owned destination suitable for future autonomous dispatch. For
218
- * Slack, this is the raw conversation channel, not a thread timestamp or
219
- * assistant-context source channel.
220
- */
221
- destination?: Destination;
222
- messageTs?: string;
223
- requester?: AgentPluginRequester;
224
299
  state: AgentPluginState;
225
- teamId?: string;
226
- threadTs?: string;
227
300
  userText?: string;
228
301
  }
229
302
 
303
+ interface SlackToolRegistrationContext
304
+ extends BaseToolRegistrationHookContext, SlackInvocationContext {
305
+ slack: SlackToolRegistrationHookContext;
306
+ }
307
+
308
+ interface LocalToolRegistrationContext
309
+ extends BaseToolRegistrationHookContext, LocalInvocationContext {
310
+ slack?: never;
311
+ }
312
+
313
+ export type ToolRegistrationHookContext =
314
+ | LocalToolRegistrationContext
315
+ | SlackToolRegistrationContext;
316
+
230
317
  export type AgentPluginCredentialSubject = z.output<
231
318
  typeof agentPluginCredentialSubjectSchema
232
319
  >;
233
320
 
234
321
  export type Destination = z.output<typeof destinationSchema>;
235
322
 
323
+ export type SlackDestination = Extract<Destination, { platform: "slack" }>;
324
+
325
+ export type LocalDestination = Extract<Destination, { platform: "local" }>;
326
+
327
+ /** Narrow a runtime destination to the Slack-specific address shape. */
328
+ export function isSlackDestination(
329
+ destination: Destination | undefined,
330
+ ): destination is SlackDestination {
331
+ return destination?.platform === "slack";
332
+ }
333
+
236
334
  export type DispatchOptions = z.output<typeof dispatchOptionsSchema>;
237
335
 
238
336
  export interface DispatchResult {
@@ -670,6 +768,7 @@ export interface JuniorPluginManifest {
670
768
  configKeys?: string[];
671
769
  credentials?: JuniorPluginCredentials;
672
770
  description: string;
771
+ displayName: string;
673
772
  domains?: string[];
674
773
  envVars?: Record<string, JuniorPluginEnvVarDeclaration>;
675
774
  mcp?: JuniorPluginMcpConfig;
@@ -724,6 +823,14 @@ export function defineJuniorPlugin(
724
823
  `Junior plugin registration name "${name}" must be a lowercase plugin identifier.`,
725
824
  );
726
825
  }
826
+ if (
827
+ typeof manifest.displayName !== "string" ||
828
+ !manifest.displayName.trim()
829
+ ) {
830
+ throw new Error(
831
+ `Junior plugin "${name}" manifest.displayName is required.`,
832
+ );
833
+ }
727
834
  if (
728
835
  typeof manifest.description !== "string" ||
729
836
  !manifest.description.trim()