@sentry/junior-plugin-api 0.116.1 → 0.118.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 +14 -1
- package/dist/hooks.d.ts +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +81 -0
- package/dist/prompt.d.ts +21 -0
- package/dist/registration.d.ts +2 -0
- package/dist/user-pages.d.ts +56 -0
- package/package.json +1 -1
- package/src/hooks.ts +5 -1
- package/src/index.ts +6 -0
- package/src/prompt.ts +48 -0
- package/src/registration.ts +44 -0
- package/src/user-pages.ts +81 -0
package/README.md
CHANGED
|
@@ -21,7 +21,8 @@ The manifest declares runtime metadata such as:
|
|
|
21
21
|
- provider domains, grants, OAuth, API-header transformations, and safe command
|
|
22
22
|
environment placeholders;
|
|
23
23
|
- runtime dependencies and snapshot installation steps;
|
|
24
|
-
- configuration fields
|
|
24
|
+
- configuration fields;
|
|
25
|
+
- signed-in user pages backed by core-owned discovery and read routes.
|
|
25
26
|
|
|
26
27
|
Manifest values are validated before runtime activation. Secret deployment
|
|
27
28
|
values remain host-only; sandbox-exposed command environment must be explicitly
|
|
@@ -39,6 +40,18 @@ reports, and other typed hook surfaces exported by this package.
|
|
|
39
40
|
- Tool hooks return model-visible schemas aligned with their executor inputs.
|
|
40
41
|
- Host-owned structured model and embedding calls do not expose provider
|
|
41
42
|
credentials to plugins.
|
|
43
|
+
- User page readers receive the signed-in email plus runtime-owned actors linked
|
|
44
|
+
to that user. Plugins return bounded data and do not mount their own page
|
|
45
|
+
routes or browser code.
|
|
46
|
+
|
|
47
|
+
## User Pages
|
|
48
|
+
|
|
49
|
+
Register signed-in pages through `userPages` beside `hooks`, `tasks`, and `cli`.
|
|
50
|
+
Each definition owns its navigation metadata and `read(ctx)` function, so a
|
|
51
|
+
page cannot be advertised without an implementation. Core owns discovery,
|
|
52
|
+
authentication, actor resolution, routing, response validation, and rendering.
|
|
53
|
+
The initial response type is the bounded `list` page; add another core-rendered
|
|
54
|
+
type only when a concrete product need proves it.
|
|
42
55
|
|
|
43
56
|
## Durable Work
|
|
44
57
|
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { EgressHookContext, EgressResponseHookContext, IssueCredentialHookContext, PluginCredentialResult, PluginGrant, PluginProviderAccount, ResolveOAuthAccountHookContext } from "./credentials";
|
|
2
2
|
import type { HeartbeatHookContext, HeartbeatResult, OperationalReportHookContext, ApiRouteRegistrationHookContext, PluginOperationalReportContent, PluginRoute, PluginRouteApp, RouteRegistrationHookContext, SlackConversationLink, SlackConversationLinkHookContext } from "./operations";
|
|
3
3
|
import type { BeforeToolExecuteHookContext, PluginToolDefinition, SandboxPrepareHookContext, ToolRegistrationHookContext } from "./tools";
|
|
4
|
-
import type { PromptMessage, SystemPromptContext, UserPromptContext } from "./prompt";
|
|
4
|
+
import type { PromptMessage, SystemPromptContext, UserPromptContext, UserPromptContribution } from "./prompt";
|
|
5
5
|
export interface PluginHooks {
|
|
6
6
|
systemPrompt?(ctx: SystemPromptContext): Promise<PromptMessage[]> | PromptMessage[];
|
|
7
|
-
userPrompt?(ctx: UserPromptContext): Promise<
|
|
7
|
+
userPrompt?(ctx: UserPromptContext): Promise<UserPromptContribution[] | undefined> | UserPromptContribution[] | undefined;
|
|
8
8
|
beforeToolExecute?(ctx: BeforeToolExecuteHookContext): Promise<void> | void;
|
|
9
9
|
grantForEgress?(ctx: EgressHookContext): Promise<PluginGrant | undefined> | PluginGrant | undefined;
|
|
10
10
|
heartbeat?(ctx: HeartbeatHookContext): Promise<HeartbeatResult | void> | HeartbeatResult | void;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export * from "./annotations";
|
|
|
2
2
|
export * from "./schemas";
|
|
3
3
|
export * from "./context";
|
|
4
4
|
export * from "./state";
|
|
5
|
-
export { promptMessageSchema, type PromptMessage, type SystemPromptContext, type UserPromptContext, } from "./prompt";
|
|
5
|
+
export { definePromptContext, promptContextSchema, promptMessageSchema, type PromptContext, type PromptContextContribution, type PromptMessage, type SystemPromptContext, type UserPromptContribution, type UserPromptContext, } from "./prompt";
|
|
6
6
|
export * from "./dispatch";
|
|
7
7
|
export * from "./resource-events";
|
|
8
8
|
export * from "./tasks";
|
|
@@ -13,3 +13,4 @@ export * from "./hooks";
|
|
|
13
13
|
export * from "./cli";
|
|
14
14
|
export * from "./manifest";
|
|
15
15
|
export * from "./registration";
|
|
16
|
+
export * from "./user-pages";
|
package/dist/index.js
CHANGED
|
@@ -198,9 +198,25 @@ function isSlackDestination(destination) {
|
|
|
198
198
|
|
|
199
199
|
// src/prompt.ts
|
|
200
200
|
import { z as z3 } from "zod";
|
|
201
|
+
var promptContextKindSchema = z3.string().trim().min(1).max(64).regex(/^[a-z][a-z0-9_-]*$/);
|
|
201
202
|
var promptMessageSchema = z3.object({
|
|
202
203
|
text: z3.string().trim().min(1).max(8e3)
|
|
203
204
|
}).strict();
|
|
205
|
+
var promptContextSchema = z3.object({
|
|
206
|
+
kind: promptContextKindSchema,
|
|
207
|
+
version: z3.number().int().positive(),
|
|
208
|
+
content: z3.record(z3.string(), z3.unknown())
|
|
209
|
+
}).strict();
|
|
210
|
+
function definePromptContext(definition) {
|
|
211
|
+
const identity = promptContextSchema.pick({ kind: true, version: true }).parse({ kind: definition.kind, version: definition.version });
|
|
212
|
+
return (content) => {
|
|
213
|
+
const parsed = definition.schema.parse(content);
|
|
214
|
+
return {
|
|
215
|
+
context: { ...identity, content: parsed },
|
|
216
|
+
renderPrompt: () => definition.renderPrompt(parsed)
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
}
|
|
204
220
|
|
|
205
221
|
// src/resource-events.ts
|
|
206
222
|
import { z as z4 } from "zod";
|
|
@@ -446,6 +462,7 @@ var EgressPolicyDenied = class extends Error {
|
|
|
446
462
|
|
|
447
463
|
// src/registration.ts
|
|
448
464
|
var PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
465
|
+
var USER_PAGE_ID_RE = /^[a-z][a-z0-9-]*$/;
|
|
449
466
|
function defineJuniorPlugin(plugin) {
|
|
450
467
|
if ("pluginConfig" in plugin) {
|
|
451
468
|
throw new Error(
|
|
@@ -480,10 +497,69 @@ function defineJuniorPlugin(plugin) {
|
|
|
480
497
|
`Junior plugin "${name}" manifest.description is required.`
|
|
481
498
|
);
|
|
482
499
|
}
|
|
500
|
+
if (plugin.userPages !== void 0 && !Array.isArray(plugin.userPages)) {
|
|
501
|
+
throw new Error(`Junior plugin "${name}" userPages must be an array.`);
|
|
502
|
+
}
|
|
503
|
+
const userPageIds = /* @__PURE__ */ new Set();
|
|
504
|
+
for (const page of plugin.userPages ?? []) {
|
|
505
|
+
if (!page || typeof page !== "object") {
|
|
506
|
+
throw new Error(`Junior plugin "${name}" user pages must be objects.`);
|
|
507
|
+
}
|
|
508
|
+
if (typeof page.id !== "string" || page.id.length > 64 || !USER_PAGE_ID_RE.test(page.id)) {
|
|
509
|
+
throw new Error(
|
|
510
|
+
`Junior plugin "${name}" user page id "${page.id}" must be a lowercase identifier.`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
if (userPageIds.has(page.id)) {
|
|
514
|
+
throw new Error(
|
|
515
|
+
`Junior plugin "${name}" has duplicate user page id "${page.id}".`
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
if (typeof page.label !== "string" || !page.label.trim() || page.label.length > 80 || typeof page.description !== "string" || !page.description.trim() || page.description.length > 500) {
|
|
519
|
+
throw new Error(
|
|
520
|
+
`Junior plugin "${name}" user page "${page.id}" requires label and description.`
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
if (typeof page.read !== "function") {
|
|
524
|
+
throw new Error(
|
|
525
|
+
`Junior plugin "${name}" user page "${page.id}" requires a read function.`
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
userPageIds.add(page.id);
|
|
529
|
+
}
|
|
483
530
|
return {
|
|
484
531
|
...plugin
|
|
485
532
|
};
|
|
486
533
|
}
|
|
534
|
+
|
|
535
|
+
// src/user-pages.ts
|
|
536
|
+
import { z as z9 } from "zod";
|
|
537
|
+
var userPageIdSchema = nonBlankStringSchema.max(64).regex(/^[a-z][a-z0-9-]*$/);
|
|
538
|
+
var userPageLabelSchema = nonBlankStringSchema.max(80);
|
|
539
|
+
var userPageDescriptionSchema = nonBlankStringSchema.max(500);
|
|
540
|
+
var pluginUserPageMetadataSchema = z9.object({
|
|
541
|
+
label: nonBlankStringSchema.max(80),
|
|
542
|
+
value: nonBlankStringSchema.max(500)
|
|
543
|
+
}).strict();
|
|
544
|
+
var pluginUserPageRecordSchema = z9.object({
|
|
545
|
+
description: nonBlankStringSchema.max(1e3).optional(),
|
|
546
|
+
id: nonBlankStringSchema.max(128),
|
|
547
|
+
metadata: z9.array(pluginUserPageMetadataSchema).max(8).optional(),
|
|
548
|
+
title: nonBlankStringSchema.max(4e3)
|
|
549
|
+
}).strict();
|
|
550
|
+
var pluginUserPageContentSchema = z9.object({
|
|
551
|
+
emptyText: nonBlankStringSchema.max(500).optional(),
|
|
552
|
+
records: z9.array(pluginUserPageRecordSchema).max(100),
|
|
553
|
+
type: z9.literal("list")
|
|
554
|
+
}).strict();
|
|
555
|
+
var pluginUserPageLinkSchema = z9.object({
|
|
556
|
+
description: userPageDescriptionSchema,
|
|
557
|
+
id: userPageIdSchema,
|
|
558
|
+
label: userPageLabelSchema,
|
|
559
|
+
pluginDisplayName: nonBlankStringSchema.max(200),
|
|
560
|
+
pluginName: nonBlankStringSchema.max(100)
|
|
561
|
+
}).strict();
|
|
562
|
+
var pluginUserPageLinksSchema = z9.array(pluginUserPageLinkSchema);
|
|
487
563
|
export {
|
|
488
564
|
EgressAuthRequired,
|
|
489
565
|
EgressPolicyDenied,
|
|
@@ -494,6 +570,7 @@ export {
|
|
|
494
570
|
createSlackSource,
|
|
495
571
|
defineJuniorPlugin,
|
|
496
572
|
definePluginTool,
|
|
573
|
+
definePromptContext,
|
|
497
574
|
destinationSchema,
|
|
498
575
|
destinationVisibilitySchema,
|
|
499
576
|
dispatchOptionsSchema,
|
|
@@ -520,6 +597,10 @@ export {
|
|
|
520
597
|
pluginToolContinuationSchema,
|
|
521
598
|
pluginToolErrorSchema,
|
|
522
599
|
pluginToolResultSchema,
|
|
600
|
+
pluginUserPageContentSchema,
|
|
601
|
+
pluginUserPageLinkSchema,
|
|
602
|
+
pluginUserPageLinksSchema,
|
|
603
|
+
promptContextSchema,
|
|
523
604
|
promptMessageSchema,
|
|
524
605
|
resourceEventSchema,
|
|
525
606
|
resourceLinkAnnotationSchema,
|
package/dist/prompt.d.ts
CHANGED
|
@@ -6,6 +6,27 @@ export declare const promptMessageSchema: z.ZodObject<{
|
|
|
6
6
|
}, z.core.$strict>;
|
|
7
7
|
/** Small plugin-owned prompt text block rendered by Junior core. */
|
|
8
8
|
export type PromptMessage = z.output<typeof promptMessageSchema>;
|
|
9
|
+
export declare const promptContextSchema: z.ZodObject<{
|
|
10
|
+
kind: z.ZodString;
|
|
11
|
+
version: z.ZodNumber;
|
|
12
|
+
content: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
13
|
+
}, z.core.$strict>;
|
|
14
|
+
/** Structured plugin context retained alongside its model-visible rendering. */
|
|
15
|
+
export type PromptContext = z.output<typeof promptContextSchema>;
|
|
16
|
+
/** Runtime contribution produced from one validated plugin context value. */
|
|
17
|
+
export interface PromptContextContribution {
|
|
18
|
+
context: PromptContext;
|
|
19
|
+
renderPrompt(): string;
|
|
20
|
+
}
|
|
21
|
+
/** Define one typed, versioned plugin context contribution. */
|
|
22
|
+
export declare function definePromptContext<TSchema extends z.ZodType<Record<string, unknown>>>(definition: {
|
|
23
|
+
kind: string;
|
|
24
|
+
version: number;
|
|
25
|
+
schema: TSchema;
|
|
26
|
+
renderPrompt(content: z.output<TSchema>): string;
|
|
27
|
+
}): (content: z.input<TSchema>) => PromptContextContribution;
|
|
28
|
+
/** One request-scoped plugin contribution to the model-visible user prompt. */
|
|
29
|
+
export type UserPromptContribution = PromptMessage | PromptContextContribution;
|
|
9
30
|
/** Stable platform context for plugin system prompt guidance. */
|
|
10
31
|
export type SystemPromptContext = Pick<PluginContext, "db" | "log" | "plugin"> & {
|
|
11
32
|
platform: Platform;
|
package/dist/registration.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { PluginCliDefinition } from "./cli";
|
|
|
2
2
|
import type { PluginHooks } from "./hooks";
|
|
3
3
|
import type { PluginManifest } from "./manifest";
|
|
4
4
|
import type { PluginTasks } from "./tasks";
|
|
5
|
+
import type { PluginUserPageDefinition } from "./user-pages";
|
|
5
6
|
export interface PluginModelConfig {
|
|
6
7
|
/** Host model family used when no explicit structured model id is configured. */
|
|
7
8
|
structuredModel?: "default" | "fast";
|
|
@@ -15,6 +16,7 @@ export type PluginRegistrationInput = {
|
|
|
15
16
|
model?: PluginModelConfig;
|
|
16
17
|
packageName?: string;
|
|
17
18
|
tasks?: PluginTasks;
|
|
19
|
+
userPages?: PluginUserPageDefinition[];
|
|
18
20
|
};
|
|
19
21
|
export interface PluginRegistration extends PluginRegistrationInput {
|
|
20
22
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core-rendered plugin user pages.
|
|
3
|
+
*
|
|
4
|
+
* Plugins own bounded data projection; Junior owns viewer authorization,
|
|
5
|
+
* routing, response validation, and browser rendering.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import type { LocalActor, PluginContext, SlackActor } from "./context";
|
|
9
|
+
/** Bounded list content returned by a plugin-owned user page. */
|
|
10
|
+
export declare const pluginUserPageContentSchema: z.ZodObject<{
|
|
11
|
+
emptyText: z.ZodOptional<z.ZodString>;
|
|
12
|
+
records: z.ZodArray<z.ZodObject<{
|
|
13
|
+
description: z.ZodOptional<z.ZodString>;
|
|
14
|
+
id: z.ZodString;
|
|
15
|
+
metadata: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
16
|
+
label: z.ZodString;
|
|
17
|
+
value: z.ZodString;
|
|
18
|
+
}, z.core.$strict>>>;
|
|
19
|
+
title: z.ZodString;
|
|
20
|
+
}, z.core.$strict>>;
|
|
21
|
+
type: z.ZodLiteral<"list">;
|
|
22
|
+
}, z.core.$strict>;
|
|
23
|
+
/** Validated content for the current core-rendered list page type. */
|
|
24
|
+
export type PluginUserPageContent = z.output<typeof pluginUserPageContentSchema>;
|
|
25
|
+
export declare const pluginUserPageLinkSchema: z.ZodObject<{
|
|
26
|
+
description: z.ZodString;
|
|
27
|
+
id: z.ZodString;
|
|
28
|
+
label: z.ZodString;
|
|
29
|
+
pluginDisplayName: z.ZodString;
|
|
30
|
+
pluginName: z.ZodString;
|
|
31
|
+
}, z.core.$strict>;
|
|
32
|
+
export declare const pluginUserPageLinksSchema: z.ZodArray<z.ZodObject<{
|
|
33
|
+
description: z.ZodString;
|
|
34
|
+
id: z.ZodString;
|
|
35
|
+
label: z.ZodString;
|
|
36
|
+
pluginDisplayName: z.ZodString;
|
|
37
|
+
pluginName: z.ZodString;
|
|
38
|
+
}, z.core.$strict>>;
|
|
39
|
+
/** Safe navigation metadata for one registered plugin user page. */
|
|
40
|
+
export type PluginUserPageLink = z.output<typeof pluginUserPageLinkSchema>;
|
|
41
|
+
/** Runtime-owned actor shapes that may belong to an authenticated viewer. */
|
|
42
|
+
export type PluginUserPageActor = SlackActor | LocalActor;
|
|
43
|
+
/** Trusted host context supplied while reading one plugin user page. */
|
|
44
|
+
export interface PluginUserPageContext extends PluginContext {
|
|
45
|
+
viewer: {
|
|
46
|
+
actors: PluginUserPageActor[];
|
|
47
|
+
email: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Navigation metadata and reader for one core-rendered plugin user page. */
|
|
51
|
+
export interface PluginUserPageDefinition {
|
|
52
|
+
description: string;
|
|
53
|
+
id: string;
|
|
54
|
+
label: string;
|
|
55
|
+
read(ctx: PluginUserPageContext): Promise<PluginUserPageContent> | PluginUserPageContent;
|
|
56
|
+
}
|
package/package.json
CHANGED
package/src/hooks.ts
CHANGED
|
@@ -29,6 +29,7 @@ import type {
|
|
|
29
29
|
PromptMessage,
|
|
30
30
|
SystemPromptContext,
|
|
31
31
|
UserPromptContext,
|
|
32
|
+
UserPromptContribution,
|
|
32
33
|
} from "./prompt";
|
|
33
34
|
|
|
34
35
|
export interface PluginHooks {
|
|
@@ -37,7 +38,10 @@ export interface PluginHooks {
|
|
|
37
38
|
): Promise<PromptMessage[]> | PromptMessage[];
|
|
38
39
|
userPrompt?(
|
|
39
40
|
ctx: UserPromptContext,
|
|
40
|
-
):
|
|
41
|
+
):
|
|
42
|
+
| Promise<UserPromptContribution[] | undefined>
|
|
43
|
+
| UserPromptContribution[]
|
|
44
|
+
| undefined;
|
|
41
45
|
beforeToolExecute?(ctx: BeforeToolExecuteHookContext): Promise<void> | void;
|
|
42
46
|
grantForEgress?(
|
|
43
47
|
ctx: EgressHookContext,
|
package/src/index.ts
CHANGED
|
@@ -3,9 +3,14 @@ export * from "./schemas";
|
|
|
3
3
|
export * from "./context";
|
|
4
4
|
export * from "./state";
|
|
5
5
|
export {
|
|
6
|
+
definePromptContext,
|
|
7
|
+
promptContextSchema,
|
|
6
8
|
promptMessageSchema,
|
|
9
|
+
type PromptContext,
|
|
10
|
+
type PromptContextContribution,
|
|
7
11
|
type PromptMessage,
|
|
8
12
|
type SystemPromptContext,
|
|
13
|
+
type UserPromptContribution,
|
|
9
14
|
type UserPromptContext,
|
|
10
15
|
} from "./prompt";
|
|
11
16
|
export * from "./dispatch";
|
|
@@ -18,3 +23,4 @@ export * from "./hooks";
|
|
|
18
23
|
export * from "./cli";
|
|
19
24
|
export * from "./manifest";
|
|
20
25
|
export * from "./registration";
|
|
26
|
+
export * from "./user-pages";
|
package/src/prompt.ts
CHANGED
|
@@ -9,6 +9,13 @@ import type {
|
|
|
9
9
|
} from "./context";
|
|
10
10
|
import type { PluginState } from "./state";
|
|
11
11
|
|
|
12
|
+
const promptContextKindSchema = z
|
|
13
|
+
.string()
|
|
14
|
+
.trim()
|
|
15
|
+
.min(1)
|
|
16
|
+
.max(64)
|
|
17
|
+
.regex(/^[a-z][a-z0-9_-]*$/);
|
|
18
|
+
|
|
12
19
|
export const promptMessageSchema = z
|
|
13
20
|
.object({
|
|
14
21
|
text: z.string().trim().min(1).max(8_000),
|
|
@@ -18,6 +25,47 @@ export const promptMessageSchema = z
|
|
|
18
25
|
/** Small plugin-owned prompt text block rendered by Junior core. */
|
|
19
26
|
export type PromptMessage = z.output<typeof promptMessageSchema>;
|
|
20
27
|
|
|
28
|
+
export const promptContextSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
kind: promptContextKindSchema,
|
|
31
|
+
version: z.number().int().positive(),
|
|
32
|
+
content: z.record(z.string(), z.unknown()),
|
|
33
|
+
})
|
|
34
|
+
.strict();
|
|
35
|
+
|
|
36
|
+
/** Structured plugin context retained alongside its model-visible rendering. */
|
|
37
|
+
export type PromptContext = z.output<typeof promptContextSchema>;
|
|
38
|
+
|
|
39
|
+
/** Runtime contribution produced from one validated plugin context value. */
|
|
40
|
+
export interface PromptContextContribution {
|
|
41
|
+
context: PromptContext;
|
|
42
|
+
renderPrompt(): string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Define one typed, versioned plugin context contribution. */
|
|
46
|
+
export function definePromptContext<
|
|
47
|
+
TSchema extends z.ZodType<Record<string, unknown>>,
|
|
48
|
+
>(definition: {
|
|
49
|
+
kind: string;
|
|
50
|
+
version: number;
|
|
51
|
+
schema: TSchema;
|
|
52
|
+
renderPrompt(content: z.output<TSchema>): string;
|
|
53
|
+
}): (content: z.input<TSchema>) => PromptContextContribution {
|
|
54
|
+
const identity = promptContextSchema
|
|
55
|
+
.pick({ kind: true, version: true })
|
|
56
|
+
.parse({ kind: definition.kind, version: definition.version });
|
|
57
|
+
return (content) => {
|
|
58
|
+
const parsed = definition.schema.parse(content);
|
|
59
|
+
return {
|
|
60
|
+
context: { ...identity, content: parsed },
|
|
61
|
+
renderPrompt: () => definition.renderPrompt(parsed),
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** One request-scoped plugin contribution to the model-visible user prompt. */
|
|
67
|
+
export type UserPromptContribution = PromptMessage | PromptContextContribution;
|
|
68
|
+
|
|
21
69
|
/** Stable platform context for plugin system prompt guidance. */
|
|
22
70
|
export type SystemPromptContext = Pick<
|
|
23
71
|
PluginContext,
|
package/src/registration.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { PluginCliDefinition } from "./cli";
|
|
|
2
2
|
import type { PluginHooks } from "./hooks";
|
|
3
3
|
import type { PluginManifest } from "./manifest";
|
|
4
4
|
import type { PluginTasks } from "./tasks";
|
|
5
|
+
import type { PluginUserPageDefinition } from "./user-pages";
|
|
5
6
|
|
|
6
7
|
export interface PluginModelConfig {
|
|
7
8
|
/** Host model family used when no explicit structured model id is configured. */
|
|
@@ -17,11 +18,13 @@ export type PluginRegistrationInput = {
|
|
|
17
18
|
model?: PluginModelConfig;
|
|
18
19
|
packageName?: string;
|
|
19
20
|
tasks?: PluginTasks;
|
|
21
|
+
userPages?: PluginUserPageDefinition[];
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
export interface PluginRegistration extends PluginRegistrationInput {}
|
|
23
25
|
|
|
24
26
|
const PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
27
|
+
const USER_PAGE_ID_RE = /^[a-z][a-z0-9-]*$/;
|
|
25
28
|
|
|
26
29
|
/** Define one Junior plugin registration for app and build-time wiring. */
|
|
27
30
|
export function defineJuniorPlugin(
|
|
@@ -66,6 +69,47 @@ export function defineJuniorPlugin(
|
|
|
66
69
|
`Junior plugin "${name}" manifest.description is required.`,
|
|
67
70
|
);
|
|
68
71
|
}
|
|
72
|
+
if (plugin.userPages !== undefined && !Array.isArray(plugin.userPages)) {
|
|
73
|
+
throw new Error(`Junior plugin "${name}" userPages must be an array.`);
|
|
74
|
+
}
|
|
75
|
+
const userPageIds = new Set<string>();
|
|
76
|
+
for (const page of plugin.userPages ?? []) {
|
|
77
|
+
if (!page || typeof page !== "object") {
|
|
78
|
+
throw new Error(`Junior plugin "${name}" user pages must be objects.`);
|
|
79
|
+
}
|
|
80
|
+
if (
|
|
81
|
+
typeof page.id !== "string" ||
|
|
82
|
+
page.id.length > 64 ||
|
|
83
|
+
!USER_PAGE_ID_RE.test(page.id)
|
|
84
|
+
) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Junior plugin "${name}" user page id "${page.id}" must be a lowercase identifier.`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (userPageIds.has(page.id)) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Junior plugin "${name}" has duplicate user page id "${page.id}".`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (
|
|
95
|
+
typeof page.label !== "string" ||
|
|
96
|
+
!page.label.trim() ||
|
|
97
|
+
page.label.length > 80 ||
|
|
98
|
+
typeof page.description !== "string" ||
|
|
99
|
+
!page.description.trim() ||
|
|
100
|
+
page.description.length > 500
|
|
101
|
+
) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Junior plugin "${name}" user page "${page.id}" requires label and description.`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (typeof page.read !== "function") {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Junior plugin "${name}" user page "${page.id}" requires a read function.`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
userPageIds.add(page.id);
|
|
112
|
+
}
|
|
69
113
|
return {
|
|
70
114
|
...plugin,
|
|
71
115
|
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core-rendered plugin user pages.
|
|
3
|
+
*
|
|
4
|
+
* Plugins own bounded data projection; Junior owns viewer authorization,
|
|
5
|
+
* routing, response validation, and browser rendering.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import type { LocalActor, PluginContext, SlackActor } from "./context";
|
|
9
|
+
import { nonBlankStringSchema } from "./schemas";
|
|
10
|
+
|
|
11
|
+
const userPageIdSchema = nonBlankStringSchema
|
|
12
|
+
.max(64)
|
|
13
|
+
.regex(/^[a-z][a-z0-9-]*$/);
|
|
14
|
+
const userPageLabelSchema = nonBlankStringSchema.max(80);
|
|
15
|
+
const userPageDescriptionSchema = nonBlankStringSchema.max(500);
|
|
16
|
+
|
|
17
|
+
const pluginUserPageMetadataSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
label: nonBlankStringSchema.max(80),
|
|
20
|
+
value: nonBlankStringSchema.max(500),
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
|
|
24
|
+
const pluginUserPageRecordSchema = z
|
|
25
|
+
.object({
|
|
26
|
+
description: nonBlankStringSchema.max(1_000).optional(),
|
|
27
|
+
id: nonBlankStringSchema.max(128),
|
|
28
|
+
metadata: z.array(pluginUserPageMetadataSchema).max(8).optional(),
|
|
29
|
+
title: nonBlankStringSchema.max(4_000),
|
|
30
|
+
})
|
|
31
|
+
.strict();
|
|
32
|
+
|
|
33
|
+
/** Bounded list content returned by a plugin-owned user page. */
|
|
34
|
+
export const pluginUserPageContentSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
emptyText: nonBlankStringSchema.max(500).optional(),
|
|
37
|
+
records: z.array(pluginUserPageRecordSchema).max(100),
|
|
38
|
+
type: z.literal("list"),
|
|
39
|
+
})
|
|
40
|
+
.strict();
|
|
41
|
+
|
|
42
|
+
/** Validated content for the current core-rendered list page type. */
|
|
43
|
+
export type PluginUserPageContent = z.output<
|
|
44
|
+
typeof pluginUserPageContentSchema
|
|
45
|
+
>;
|
|
46
|
+
|
|
47
|
+
export const pluginUserPageLinkSchema = z
|
|
48
|
+
.object({
|
|
49
|
+
description: userPageDescriptionSchema,
|
|
50
|
+
id: userPageIdSchema,
|
|
51
|
+
label: userPageLabelSchema,
|
|
52
|
+
pluginDisplayName: nonBlankStringSchema.max(200),
|
|
53
|
+
pluginName: nonBlankStringSchema.max(100),
|
|
54
|
+
})
|
|
55
|
+
.strict();
|
|
56
|
+
|
|
57
|
+
export const pluginUserPageLinksSchema = z.array(pluginUserPageLinkSchema);
|
|
58
|
+
|
|
59
|
+
/** Safe navigation metadata for one registered plugin user page. */
|
|
60
|
+
export type PluginUserPageLink = z.output<typeof pluginUserPageLinkSchema>;
|
|
61
|
+
|
|
62
|
+
/** Runtime-owned actor shapes that may belong to an authenticated viewer. */
|
|
63
|
+
export type PluginUserPageActor = SlackActor | LocalActor;
|
|
64
|
+
|
|
65
|
+
/** Trusted host context supplied while reading one plugin user page. */
|
|
66
|
+
export interface PluginUserPageContext extends PluginContext {
|
|
67
|
+
viewer: {
|
|
68
|
+
actors: PluginUserPageActor[];
|
|
69
|
+
email: string;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Navigation metadata and reader for one core-rendered plugin user page. */
|
|
74
|
+
export interface PluginUserPageDefinition {
|
|
75
|
+
description: string;
|
|
76
|
+
id: string;
|
|
77
|
+
label: string;
|
|
78
|
+
read(
|
|
79
|
+
ctx: PluginUserPageContext,
|
|
80
|
+
): Promise<PluginUserPageContent> | PluginUserPageContent;
|
|
81
|
+
}
|