Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/junior-plugin-api/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import { z } from "zod";
import {
destinationSchema,
localRequesterSchema,
platformSchema,
requesterSchema,
slackRequesterSchema,
sourceSchema,
} from "./schemas";
import type { PluginDb } from "./database";

/** Runtime platform name without source or destination coordinates. */
export type Platform = z.output<typeof platformSchema>;
export type Requester = z.output<typeof requesterSchema>;
export type SlackRequester = z.output<typeof slackRequesterSchema>;
export type LocalRequester = z.output<typeof localRequesterSchema>;
Expand Down
15 changes: 15 additions & 0 deletions packages/junior-plugin-api/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,23 @@ import type {
SandboxPrepareHookContext,
ToolRegistrationHookContext,
} from "./tools";
import type {
PromptContribution,
SystemPromptHookContext,
UserPromptContributionResult,
UserPromptHookContext,
} from "./prompt";

export interface PluginHooks {
systemPrompt?(
ctx: SystemPromptHookContext,
): Promise<PromptContribution[]> | PromptContribution[];
userPrompt?(
ctx: UserPromptHookContext,
):
| Promise<UserPromptContributionResult | undefined>
| UserPromptContributionResult
| undefined;
beforeToolExecute?(ctx: BeforeToolExecuteHookContext): Promise<void> | void;
grantForEgress?(
ctx: EgressHookContext,
Expand Down
12 changes: 12 additions & 0 deletions packages/junior-plugin-api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
export * from "./schemas";
export * from "./context";
export * from "./json";
export * from "./state";
export {
pluginSessionStateAppendSchema,
pluginSessionStateKeySchema,
pluginSessionStateValueMaxChars,
promptContributionSchema,
userPromptContributionResultSchema,
type PromptContribution,
type SystemPromptHookContext,
type UserPromptContributionResult,
type UserPromptHookContext,
} from "./prompt";
export * from "./dispatch";
export * from "./database";
export * from "./tools";
Expand Down
22 changes: 22 additions & 0 deletions packages/junior-plugin-api/src/json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { z } from "zod";

/** JSON value shape accepted by plugin public contracts. */
export type PluginJsonValue =
| string
| number
| boolean
| null
| PluginJsonValue[]
| { [key: string]: PluginJsonValue };

/** Runtime schema for JSON values accepted from plugin public contracts. */
export const pluginJsonValueSchema: z.ZodType<PluginJsonValue> = z.lazy(() =>
z.union([
z.string(),
z.number().finite(),
z.boolean(),
z.null(),
z.array(pluginJsonValueSchema),
z.record(z.string(), pluginJsonValueSchema),
]),
);
98 changes: 57 additions & 41 deletions packages/junior-plugin-api/src/prompt.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,75 @@
import type { InvocationContext, PluginContext } from "./context";
import { z } from "zod";
import type { InvocationContext, Platform, PluginContext } from "./context";
import { pluginJsonValueSchema } from "./json";
import type {
PluginSessionState,
PluginSessionStateAppend,
PluginState,
} from "./state";

export interface UserPromptContribution {
id: string;
text: string;
}
const promptContributionIdSchema = z.string().regex(/^[A-Za-z0-9_.:-]{1,80}$/);

/** Maximum encoded JSON size accepted for one plugin session-state append. */
export const pluginSessionStateValueMaxChars = 4_000;

export const pluginSessionStateKeySchema = z
.string()
.regex(/^[a-z][a-z0-9_:-]{0,63}$/);

export const pluginSessionStateAppendSchema = z
.object({
key: pluginSessionStateKeySchema,
value: pluginJsonValueSchema,
})
.strict()
.superRefine((append, ctx) => {
const encoded = JSON.stringify(append.value);
if (
encoded === undefined ||
encoded.length > pluginSessionStateValueMaxChars
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Plugin session state value exceeds the maximum encoded size",
path: ["value"],
});
}
});

export const promptContributionSchema = z
.object({
id: promptContributionIdSchema,
text: z.string().trim().min(1).max(8_000),
})
.strict();

export const userPromptContributionResultSchema = z
.object({
contributions: z.array(promptContributionSchema).min(1),
sessionState: z.array(pluginSessionStateAppendSchema).optional(),
})
.strict();

/** Small plugin-owned prompt text block rendered by Junior core. */
export type PromptContribution = z.output<typeof promptContributionSchema>;

/** Stable platform context for plugin system prompt guidance. */
export type SystemPromptHookContext = Pick<PluginContext, "log" | "plugin"> & {
platform: Platform;
};

/** Request-scoped plugin prompt result with optional matching session bookkeeping. */
export interface UserPromptContributionResult {
contributions?: UserPromptContribution[];
contributions: PromptContribution[];
sessionState?: PluginSessionStateAppend[];
}

/** Runtime facts available while building plugin user prompt context. */
export type UserPromptHookContext = PluginContext &
InvocationContext & {
/** True for the first model-visible user prompt in the current session projection. */
isFirstPrompt: boolean;
session: PluginSessionState;
state: PluginState;
userText: string;
};

export interface PluginTaskEnqueueOptions {
idempotencyKey: string;
name: string;
payload?: unknown;
}

export interface PluginTaskEnqueueResult {
id: string;
status: "created" | "already_exists";
}

export interface PluginTaskQueue {
enqueue(options: PluginTaskEnqueueOptions): Promise<PluginTaskEnqueueResult>;
}

export type TurnObservationHookContext = PluginContext &
InvocationContext & {
observationId: string;
tasks: PluginTaskQueue;
};

export interface PluginTaskContext extends PluginContext {
id: string;
name: string;
observation?: {
load(): Promise<unknown | undefined>;
};
payload?: unknown;
state: PluginState;
}

export type PluginTaskHandler = (
ctx: PluginTaskContext,
) => Promise<void> | void;
3 changes: 3 additions & 0 deletions packages/junior-plugin-api/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export const nonBlankStringSchema = z
.string()
.refine((value) => value.trim().length > 0);

/** Runtime platform names supported by plugin public contracts. */
export const platformSchema = z.enum(["slack", "local"]);

/** Runtime-owned Slack address for routing future work or side effects. */
export const slackDestinationSchema = z
.object({
Expand Down
6 changes: 5 additions & 1 deletion packages/junior-plugin-api/src/state.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { PluginJsonValue } from "./json";

export interface PluginState {
delete(key: string): Promise<void>;
get<T = unknown>(key: string): Promise<T | undefined>;
Expand All @@ -14,11 +16,13 @@ export interface PluginReadState {
get<T = unknown>(key: string): Promise<T | undefined>;
}

/** Append-only plugin bookkeeping tied to the current model-visible session projection. */
export interface PluginSessionStateAppend {
key: string;
value: unknown;
value: PluginJsonValue;
}

/** Read plugin-scoped session bookkeeping for prompt continuity decisions. */
export interface PluginSessionState {
list<T = unknown>(
key: string,
Expand Down
Loading
Loading