@sentry/junior-plugin-api 0.261.0 → 0.263.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
@@ -505,6 +505,29 @@ export declare const dispatchOptionsSchema: z.ZodObject<{
505
505
  detail: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodString>>;
506
506
  }, z.core.$strict>>;
507
507
  }, z.core.$strict>;
508
+ /** A loaded value and how long to cache it. */
509
+ export interface CacheEntry<T> {
510
+ /** Omit to return the value without caching it, such as for an incomplete response. */
511
+ ttlMs?: number;
512
+ /** Must survive JSON serialization. */
513
+ value: T;
514
+ }
515
+ /**
516
+ * Short-lived copies of values that are slow or costly to load, such as
517
+ * provider API reads. Junior can drop an entry at any time. A lost entry only
518
+ * costs a reload, so never keep data here that Junior must not lose. Use
519
+ * `PluginState` for that.
520
+ */
521
+ export interface PluginCache {
522
+ /**
523
+ * Return the cached value for `key`, or call `load` and cache its result.
524
+ * Concurrent callers share one load. A load that throws is not cached.
525
+ * `refresh` ignores the cached value and replaces it.
526
+ */
527
+ getOrLoad<T>(key: string, load: () => Promise<CacheEntry<T>>, options?: {
528
+ refresh?: boolean;
529
+ }): Promise<T>;
530
+ }
508
531
  /** Runtime platform name without source or destination coordinates. */
509
532
  export type Platform = z.output<typeof platformSchema>;
510
533
  export type Actor = z.output<typeof actorSchema>;
@@ -586,6 +609,8 @@ export interface PluginEmbedder {
586
609
  }>;
587
610
  }
588
611
  export interface PluginContext {
612
+ /** Short-lived cache scoped to this plugin. */
613
+ cache: PluginCache;
589
614
  /** Shared Drizzle database connection for plugin runtime code. */
590
615
  db: unknown;
591
616
  log: PluginLogger;
@@ -1805,8 +1830,23 @@ export interface PluginEnv {
1805
1830
  get(key: string): string | undefined;
1806
1831
  set(key: string, value: string): void;
1807
1832
  }
1833
+ /**
1834
+ * Decide what happens to one tool call in `beforeToolExecute`.
1835
+ *
1836
+ * `redirect` and `deny` both stop the tool before it runs. Neither ends the
1837
+ * turn: the agent gets the message as the tool result.
1838
+ */
1808
1839
  export interface PluginDecision {
1809
- deny(message: string): void;
1840
+ /**
1841
+ * Block the call as a policy refusal. The agent is told not to retry or
1842
+ * reach the same result another way, and to explain the block.
1843
+ */
1844
+ deny(reason: string): void;
1845
+ /**
1846
+ * Block the call because a different path is correct, such as another
1847
+ * tool. The message tells the agent what to do instead.
1848
+ */
1849
+ redirect(message: string): void;
1810
1850
  replaceInput(input: Record<string, unknown>): void;
1811
1851
  }
1812
1852
  /** Thrown when a plugin tool rejects invalid model or user input. */
@@ -2180,6 +2220,13 @@ export interface PluginEventToolContext {
2180
2220
  }): Promise<WatchResult>;
2181
2221
  }
2182
2222
  export interface PluginWorkspaceToolContext {
2223
+ /**
2224
+ * Workspace that the current Sandbox started from. Returns undefined for
2225
+ * the default Sandbox. `name` is missing when the recipe no longer exists.
2226
+ */
2227
+ active(): Promise<{
2228
+ name?: string;
2229
+ } | undefined>;
2183
2230
  /** Find named Workspaces that include one provider repository. */
2184
2231
  findByRepository(input: {
2185
2232
  provider: string;
@@ -2235,6 +2282,20 @@ export declare function enforceEgressPolicy(input: {
2235
2282
  allowed: boolean;
2236
2283
  denialMessage: string;
2237
2284
  }): void;
2285
+ /**
2286
+ * Split bash command text into simple commands.
2287
+ *
2288
+ * Plugins use this in `beforeToolExecute` to check `bash` tool input. It is a
2289
+ * small parser, not a full shell. It reads quotes, backslashes, control
2290
+ * operators, comments, redirections, and heredocs. It does not expand
2291
+ * variables, aliases, or globs. It does not look inside `sh -c` or `eval`
2292
+ * strings. Use it for best-effort steering only, not as a security boundary.
2293
+ *
2294
+ * Each result is the argv of one simple command. Leading `NAME=value`
2295
+ * assignments, redirections, and heredoc bodies are removed. Text inside
2296
+ * `$(...)`, backticks, and subshells becomes separate commands.
2297
+ */
2298
+ export declare function parseShellCommands(text: string): string[][];
2238
2299
  /** Input for a pure Markdown rewrite before destination delivery formatting. */
2239
2300
  export interface FormatMarkdownHookContext {
2240
2301
  text: string;
package/dist/index.js CHANGED
@@ -1124,6 +1124,132 @@ var EgressPolicyDenied = class extends Error {
1124
1124
  function enforceEgressPolicy(input) {
1125
1125
  if (!input.allowed) throw new EgressPolicyDenied(input.denialMessage);
1126
1126
  }
1127
+ // src/shell-command.ts
1128
+ /**
1129
+ * Split bash command text into simple commands.
1130
+ *
1131
+ * Plugins use this in `beforeToolExecute` to check `bash` tool input. It is a
1132
+ * small parser, not a full shell. It reads quotes, backslashes, control
1133
+ * operators, comments, redirections, and heredocs. It does not expand
1134
+ * variables, aliases, or globs. It does not look inside `sh -c` or `eval`
1135
+ * strings. Use it for best-effort steering only, not as a security boundary.
1136
+ *
1137
+ * Each result is the argv of one simple command. Leading `NAME=value`
1138
+ * assignments, redirections, and heredoc bodies are removed. Text inside
1139
+ * `$(...)`, backticks, and subshells becomes separate commands.
1140
+ */
1141
+ function parseShellCommands(text) {
1142
+ const commands = [];
1143
+ const heredocs = [];
1144
+ let words = [];
1145
+ let word = "";
1146
+ let inWord = false;
1147
+ let skipNextWord = false;
1148
+ const endWord = () => {
1149
+ if (inWord) {
1150
+ if (!skipNextWord) words.push(word);
1151
+ skipNextWord = false;
1152
+ }
1153
+ word = "";
1154
+ inWord = false;
1155
+ };
1156
+ const endCommand = () => {
1157
+ endWord();
1158
+ skipNextWord = false;
1159
+ let start = 0;
1160
+ while (start < words.length && ASSIGNMENT_RE.test(words[start])) start += 1;
1161
+ if (start < words.length) commands.push(words.slice(start));
1162
+ words = [];
1163
+ };
1164
+ for (let index = 0; index < text.length; index += 1) {
1165
+ const char = text[index];
1166
+ if (char === "'") {
1167
+ const end = text.indexOf("'", index + 1);
1168
+ const close = end === -1 ? text.length : end;
1169
+ word += text.slice(index + 1, close);
1170
+ inWord = true;
1171
+ index = close;
1172
+ } else if (char === "\"") {
1173
+ inWord = true;
1174
+ for (index += 1; index < text.length && text[index] !== "\""; index += 1) {
1175
+ if (text[index] === "\\" && DOUBLE_QUOTE_ESCAPES.has(text[index + 1])) index += 1;
1176
+ word += text[index];
1177
+ }
1178
+ } else if (char === "\\") {
1179
+ index += 1;
1180
+ if (index < text.length && text[index] !== "\n") {
1181
+ word += text[index];
1182
+ inWord = true;
1183
+ }
1184
+ } else if (char === "#" && !inWord) {
1185
+ const end = text.indexOf("\n", index);
1186
+ index = (end === -1 ? text.length : end) - 1;
1187
+ } else if (char === "\n") {
1188
+ endCommand();
1189
+ for (const heredoc of heredocs.splice(0)) index = skipHeredocBody(text, index, heredoc);
1190
+ } else if (char === "<" || char === ">") {
1191
+ if (/^\d+$/.test(word)) {
1192
+ word = "";
1193
+ inWord = false;
1194
+ }
1195
+ endWord();
1196
+ if (text.startsWith("<<<", index)) {
1197
+ index += 2;
1198
+ skipNextWord = true;
1199
+ } else if (text.startsWith("<<", index)) {
1200
+ index += 2;
1201
+ const stripTabs = text[index] === "-";
1202
+ if (stripTabs) index += 1;
1203
+ while (text[index] === " " || text[index] === " ") index += 1;
1204
+ let delimiter = "";
1205
+ while (index < text.length && !/[\s;&|()<>]/.test(text[index])) {
1206
+ if (!/['"\\]/.test(text[index])) delimiter += text[index];
1207
+ index += 1;
1208
+ }
1209
+ index -= 1;
1210
+ heredocs.push({
1211
+ delimiter,
1212
+ stripTabs
1213
+ });
1214
+ } else {
1215
+ while (/[<>&|]/.test(text[index + 1] ?? "")) index += 1;
1216
+ skipNextWord = true;
1217
+ }
1218
+ } else if (/[;&|()`]/.test(char)) {
1219
+ if (char === "(" && word.endsWith("$")) {
1220
+ word = word.slice(0, -1);
1221
+ inWord = word.length > 0;
1222
+ }
1223
+ endCommand();
1224
+ } else if (/\s/.test(char)) endWord();
1225
+ else {
1226
+ word += char;
1227
+ inWord = true;
1228
+ }
1229
+ }
1230
+ endCommand();
1231
+ return commands;
1232
+ }
1233
+ const ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;
1234
+ const DOUBLE_QUOTE_ESCAPES = /* @__PURE__ */ new Set([
1235
+ "\"",
1236
+ "\\",
1237
+ "$",
1238
+ "`"
1239
+ ]);
1240
+ /** Return the index of the newline that ends the heredoc delimiter line. */
1241
+ function skipHeredocBody(text, newlineIndex, heredoc) {
1242
+ let index = newlineIndex;
1243
+ while (index < text.length) {
1244
+ const start = index + 1;
1245
+ const found = text.indexOf("\n", start);
1246
+ const end = found === -1 ? text.length : found;
1247
+ const line = text.slice(start, end);
1248
+ index = end;
1249
+ if ((heredoc.stripTabs ? line.replace(/^\t+/, "") : line) === heredoc.delimiter) break;
1250
+ }
1251
+ return index;
1252
+ }
1127
1253
  // src/registration.ts
1128
1254
  const PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
1129
1255
  const USER_PAGE_ID_RE = /^[a-z][a-z0-9-]*$/;
@@ -1242,4 +1368,4 @@ const pluginUserPageInputSchema = z.object({
1242
1368
  limit: z.number().int().min(1).max(50),
1243
1369
  query: pluginUserPageQuerySchema
1244
1370
  }).strict();
1245
- export { EVENT_DATA_MAX_JSON_BYTES, EVENT_DATA_MAX_KEYS, EVENT_GUIDANCE_MAX_LENGTH, EVENT_SUMMARY_MAX_LENGTH, EVENT_TEXT_MAX_LENGTH, EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, REQUIRED_TOOL_ANNOTATION_KEYS, actorSchema, actorUserIdSchema, agentInvocationSourceSchema, codeChangeInputSchema, codeChangeStateSchema, conversationAnnotationInputSchema, conversationEventIconSchema, conversationEventPresentationSchema, conversationSidebarAnnotationSchema, conversationSidebarIconSchema, createEventSource, createLocalSource, createSlackSource, createWebSource, defineConversationEvent, defineJuniorPlugin, definePluginTool, definePromptContext, destinationSchema, destinationVisibilitySchema, dispatchOptionsSchema, enforceEgressPolicy, eventAutomationSourceSchema, eventDataSchema, eventIdentifierSchema, eventInputSchema, eventMatchFieldSchema, eventMatchFieldsSchema, eventMatchSchema, eventMatches, eventPersonSchema, eventSchema, eventSourceSchema, eventTypeSchema, getSourceKey, identitySchema, isPrivateSource, isSlackDestination, localActorSchema, localDestinationSchema, localSourceSchema, locationSchema, missingToolAnnotationKeys, nonBlankStringSchema, normalizeEventIdentifier, objectAnnotationSchema, objectFactFields, objectFactsSchema, objectIconPath, objectIconPaths, objectPresentation, objectTypeSchema, ownedObjectAnnotationSchema, platformSchema, pluginApiRouteRequestContextSchema, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialLeaseSchema, pluginCredentialResultSchema, pluginCredentialSubjectSchema, pluginDispatchSourceSchema, pluginEventTypeSchema, pluginEventsSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginRunContextSchema, pluginRunTranscriptEntrySchema, pluginRunTranscriptProvenanceSchema, pluginStoredTokensSchema, pluginToolContentSchema, pluginToolContinuationSchema, pluginToolOutputSchema, pluginUserPageContentSchema, pluginUserPageCursorSchema, pluginUserPageFilterSchema, pluginUserPageInputSchema, pluginUserPageLinkSchema, pluginUserPageLinksSchema, pluginUserPageQuerySchema, promptContextSchema, promptMessageSchema, replyAttributionSchema, resourceLinkAnnotationSchema, resourceTypeSchema, scheduledAutomationSourceSchema, slackActorSchema, slackDestinationSchema, slackLocationSchema, slackSourceSchema, sourceSchema, sourceVisibilitySchema, stableEventMatchKey, subscribableResourceSchema, systemActorSchema, taskOutcomeSchema, toolApprovalModeSchema, userSchema, watchResultSchema, webActorSchema, webSourceSchema, zodTool };
1371
+ export { EVENT_DATA_MAX_JSON_BYTES, EVENT_DATA_MAX_KEYS, EVENT_GUIDANCE_MAX_LENGTH, EVENT_SUMMARY_MAX_LENGTH, EVENT_TEXT_MAX_LENGTH, EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, REQUIRED_TOOL_ANNOTATION_KEYS, actorSchema, actorUserIdSchema, agentInvocationSourceSchema, codeChangeInputSchema, codeChangeStateSchema, conversationAnnotationInputSchema, conversationEventIconSchema, conversationEventPresentationSchema, conversationSidebarAnnotationSchema, conversationSidebarIconSchema, createEventSource, createLocalSource, createSlackSource, createWebSource, defineConversationEvent, defineJuniorPlugin, definePluginTool, definePromptContext, destinationSchema, destinationVisibilitySchema, dispatchOptionsSchema, enforceEgressPolicy, eventAutomationSourceSchema, eventDataSchema, eventIdentifierSchema, eventInputSchema, eventMatchFieldSchema, eventMatchFieldsSchema, eventMatchSchema, eventMatches, eventPersonSchema, eventSchema, eventSourceSchema, eventTypeSchema, getSourceKey, identitySchema, isPrivateSource, isSlackDestination, localActorSchema, localDestinationSchema, localSourceSchema, locationSchema, missingToolAnnotationKeys, nonBlankStringSchema, normalizeEventIdentifier, objectAnnotationSchema, objectFactFields, objectFactsSchema, objectIconPath, objectIconPaths, objectPresentation, objectTypeSchema, ownedObjectAnnotationSchema, parseShellCommands, platformSchema, pluginApiRouteRequestContextSchema, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialLeaseSchema, pluginCredentialResultSchema, pluginCredentialSubjectSchema, pluginDispatchSourceSchema, pluginEventTypeSchema, pluginEventsSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginRunContextSchema, pluginRunTranscriptEntrySchema, pluginRunTranscriptProvenanceSchema, pluginStoredTokensSchema, pluginToolContentSchema, pluginToolContinuationSchema, pluginToolOutputSchema, pluginUserPageContentSchema, pluginUserPageCursorSchema, pluginUserPageFilterSchema, pluginUserPageInputSchema, pluginUserPageLinkSchema, pluginUserPageLinksSchema, pluginUserPageQuerySchema, promptContextSchema, promptMessageSchema, replyAttributionSchema, resourceLinkAnnotationSchema, resourceTypeSchema, scheduledAutomationSourceSchema, slackActorSchema, slackDestinationSchema, slackLocationSchema, slackSourceSchema, sourceSchema, sourceVisibilitySchema, stableEventMatchKey, subscribableResourceSchema, systemActorSchema, taskOutcomeSchema, toolApprovalModeSchema, userSchema, watchResultSchema, webActorSchema, webSourceSchema, zodTool };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-plugin-api",
3
- "version": "0.261.0",
3
+ "version": "0.263.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"