You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

veryfront

Package Overview
Dependencies
Maintainers
1
Versions
303
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

veryfront - npm Package Compare versions

Comparing version
0.1.62
to
0.1.63
+92
esm/src/agent/middleware/security/validator.d.ts
import type { AgentContext, AgentResponse } from "../../types.js";
export interface SecurityConfig {
/** Input validation rules */
input?: {
/** Maximum input length */
maxLength?: number;
/** Blocked patterns (regex) */
blockedPatterns?: RegExp[];
/** Sanitize input */
sanitize?: boolean;
/** Custom validator */
validate?: (input: string) => boolean | Promise<boolean>;
};
/** Output filtering rules */
output?: {
/** Blocked patterns in output */
blockedPatterns?: RegExp[];
/** Filter PII (Personal Identifiable Information) */
filterPII?: boolean;
/** Custom filter */
filter?: (output: string) => string | Promise<string>;
};
/** Action when violation detected */
onViolation?: (violation: SecurityViolation) => void;
}
export interface SecurityViolation {
/** Violation type */
type: "input" | "output";
/** Violation reason */
reason: string;
/** Original content */
content: string;
/** Matched pattern (if any) */
pattern?: RegExp;
}
/**
* Common blocked patterns
*/
export declare const COMMON_BLOCKED_PATTERNS: {
/** Prompt injection attempts */
promptInjection: RegExp[];
/** Potential data exfiltration */
dataExfiltration: RegExp[];
/** SQL injection patterns */
sqlInjection: RegExp[];
/** XSS patterns */
xss: RegExp[];
};
/**
* Input Validator
*/
export declare class InputValidator {
private config;
constructor(config?: SecurityConfig["input"]);
/**
* Validate input
*/
validate(input: string): Promise<{
valid: boolean;
sanitized?: string;
violations: SecurityViolation[];
}>;
/** Sanitization patterns to remove harmful content */
private static readonly SANITIZE_PATTERNS;
/**
* Sanitize input (remove potentially harmful content)
*/
private sanitizeInput;
}
/**
* Output Filter
*/
export declare class OutputFilter {
private config;
constructor(config?: SecurityConfig["output"]);
/**
* Filter output
*/
filter(output: string): Promise<{
filtered: string;
violations: SecurityViolation[];
}>;
/**
* Filter PII from output
*/
private filterPII;
}
/**
* Create security middleware for agents
*/
export declare function securityMiddleware(config: SecurityConfig): (context: AgentContext, next: () => Promise<AgentResponse>) => Promise<AgentResponse>;
//# sourceMappingURL=validator.d.ts.map
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../../../src/src/agent/middleware/security/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGlE,MAAM,WAAW,cAAc;IAC7B,6BAA6B;IAC7B,KAAK,CAAC,EAAE;QACN,2BAA2B;QAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;QAEnB,+BAA+B;QAC/B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAE3B,qBAAqB;QACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;QAEnB,uBAAuB;QACvB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAC1D,CAAC;IAEF,6BAA6B;IAC7B,MAAM,CAAC,EAAE;QACP,iCAAiC;QACjC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAE3B,qDAAqD;QACrD,SAAS,CAAC,EAAE,OAAO,CAAC;QAEpB,oBAAoB;QACpB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;KACvD,CAAC;IAEF,qCAAqC;IACrC,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;CACtD;AAED,MAAM,WAAW,iBAAiB;IAChC,qBAAqB;IACrB,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAEzB,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IAEf,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;IAEhB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB;IAClC,gCAAgC;;IAWhC,kCAAkC;;IASlC,6BAA6B;;IAM7B,mBAAmB;;CAMpB,CAAC;AAeF;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuC;gBAEzC,MAAM,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC;IAI5C;;OAEG;IACG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QACrC,KAAK,EAAE,OAAO,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,iBAAiB,EAAE,CAAC;KACjC,CAAC;IA4CF,sDAAsD;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAIvC;IAEF;;OAEG;IACH,OAAO,CAAC,aAAa;CAMtB;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAwC;gBAE1C,MAAM,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC;IAI7C;;OAEG;IACG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QACpC,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,iBAAiB,EAAE,CAAC;KACjC,CAAC;IA6BF;;OAEG;IACH,OAAO,CAAC,SAAS;CAMlB;AAaD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,cAAc,GACrB,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,KAAK,OAAO,CAAC,aAAa,CAAC,CAoCvF"}
import { createError, toError } from "../../../errors/veryfront-error.js";
/**
* Common blocked patterns
*/
export const COMMON_BLOCKED_PATTERNS = {
/** Prompt injection attempts */
promptInjection: [
/ignore\s+previous\s+instructions/i,
/ignore\s+all\s+previous\s+prompts/i,
/you\s+are\s+now\s+a/i,
/pretend\s+you\s+are/i,
/system:\s*/i,
/<\|im_start\|>/i,
/<\|im_end\|>/i,
],
/** Potential data exfiltration */
dataExfiltration: [
/password/i,
/api[_\s-]?key/i,
/secret/i,
/token/i,
/credit\s+card/i,
],
/** SQL injection patterns */
sqlInjection: [
/(\bUNION\b|\bSELECT\b).*\bFROM\b/i,
/;\s*(DROP|DELETE|UPDATE|INSERT)/i,
],
/** XSS patterns */
xss: [
/<script[^>]*>.*?<\/script>/gi,
/javascript:/i,
/on\w+\s*=/i, // Event handlers
],
};
/**
* PII patterns with replacement labels
*/
const PII_REPLACEMENTS = [
{ pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, label: "[EMAIL]" },
{
pattern: /\b(\+\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
label: "[PHONE]",
},
{ pattern: /\b\d{3}-\d{2}-\d{4}\b/g, label: "[SSN]" },
{ pattern: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g, label: "[CREDIT_CARD]" },
];
/**
* Input Validator
*/
export class InputValidator {
config;
constructor(config) {
this.config = config ?? {};
}
/**
* Validate input
*/
async validate(input) {
const violations = [];
const maxLength = this.config.maxLength;
if (maxLength != null && input.length > maxLength) {
violations.push({
type: "input",
reason: `Input exceeds maximum length of ${maxLength}`,
content: `${input.substring(0, 100)}...`,
});
}
for (const pattern of this.config.blockedPatterns ?? []) {
if (!pattern.test(input))
continue;
violations.push({
type: "input",
reason: "Input matches blocked pattern",
content: input,
pattern,
});
}
const customValidate = this.config.validate;
if (customValidate) {
const customValid = await customValidate(input);
if (!customValid) {
violations.push({
type: "input",
reason: "Custom validation failed",
content: input,
});
}
}
const sanitized = this.config.sanitize ? this.sanitizeInput(input) : undefined;
return {
valid: violations.length === 0,
sanitized,
violations,
};
}
/** Sanitization patterns to remove harmful content */
static SANITIZE_PATTERNS = [
/<script[^>]*>.*?<\/script>/gi, // Script tags
/on\w+\s*=\s*["'][^"']*["']/gi, // Event handlers
/javascript:/gi, // JavaScript protocol
];
/**
* Sanitize input (remove potentially harmful content)
*/
sanitizeInput(input) {
return InputValidator.SANITIZE_PATTERNS.reduce((text, pattern) => text.replace(pattern, ""), input);
}
}
/**
* Output Filter
*/
export class OutputFilter {
config;
constructor(config) {
this.config = config ?? {};
}
/**
* Filter output
*/
async filter(output) {
const violations = [];
let filtered = output;
for (const pattern of this.config.blockedPatterns ?? []) {
if (!pattern.test(filtered))
continue;
violations.push({
type: "output",
reason: "Output contains blocked pattern",
content: filtered,
pattern,
});
filtered = filtered.replace(pattern, "[REDACTED]");
}
if (this.config.filterPII) {
filtered = this.filterPII(filtered);
}
const customFilter = this.config.filter;
if (customFilter) {
filtered = await customFilter(filtered);
}
return { filtered, violations };
}
/**
* Filter PII from output
*/
filterPII(output) {
return PII_REPLACEMENTS.reduce((text, { pattern, label }) => text.replace(pattern, label), output);
}
}
/**
* Report violations to the configured handler
*/
function reportViolations(violations, onViolation) {
if (!onViolation)
return;
for (const violation of violations)
onViolation(violation);
}
/**
* Create security middleware for agents
*/
export function securityMiddleware(config) {
const inputValidator = new InputValidator(config.input);
const outputFilter = new OutputFilter(config.output);
return async (context, next) => {
const inputString = typeof context.input === "string"
? context.input
: JSON.stringify(context.input);
const inputValidation = await inputValidator.validate(inputString);
if (!inputValidation.valid) {
reportViolations(inputValidation.violations, config.onViolation);
const firstViolation = inputValidation.violations[0];
throw toError(createError({
type: "agent",
message: `Input validation failed: ${firstViolation?.reason ?? "Unknown reason"}`,
}));
}
if (inputValidation.sanitized != null) {
context.input = inputValidation.sanitized;
}
const result = await next();
const outputFiltering = await outputFilter.filter(result.text);
reportViolations(outputFiltering.violations, config.onViolation);
return { ...result, text: outputFiltering.filtered };
};
}
import type { Agent, AgentMessage as Message, AgentResponse } from "../agent/index.js";
import type { HandlerContext } from "../types/index.js";
import { z } from "zod";
export declare const ChannelInvokeRequestSchema: z.ZodObject<{
dispatchId: z.ZodString;
conversationId: z.ZodString;
projectId: z.ZodString;
agentConfigId: z.ZodString;
platform: z.ZodLiteral<"slack">;
inboundMessage: z.ZodObject<{
text: z.ZodString;
userId: z.ZodString;
userName: z.ZodString;
isDirectMessage: z.ZodBoolean;
attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
id: z.ZodString;
kind: z.ZodEnum<["image", "file"]>;
filename: z.ZodOptional<z.ZodString>;
mediaType: z.ZodOptional<z.ZodString>;
privateUrl: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
kind: "file" | "image";
id: string;
filename?: string | undefined;
mediaType?: string | undefined;
privateUrl?: string | undefined;
}, {
kind: "file" | "image";
id: string;
filename?: string | undefined;
mediaType?: string | undefined;
privateUrl?: string | undefined;
}>, "many">>;
}, "strip", z.ZodTypeAny, {
text: string;
userId: string;
userName: string;
isDirectMessage: boolean;
attachments?: {
kind: "file" | "image";
id: string;
filename?: string | undefined;
mediaType?: string | undefined;
privateUrl?: string | undefined;
}[] | undefined;
}, {
text: string;
userId: string;
userName: string;
isDirectMessage: boolean;
attachments?: {
kind: "file" | "image";
id: string;
filename?: string | undefined;
mediaType?: string | undefined;
privateUrl?: string | undefined;
}[] | undefined;
}>;
conversationHistory: z.ZodArray<z.ZodObject<{
id: z.ZodString;
role: z.ZodEnum<["user", "assistant", "system", "tool"]>;
parts: z.ZodArray<z.ZodObject<{
type: z.ZodString;
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
type: z.ZodString;
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
type: z.ZodString;
}, z.ZodTypeAny, "passthrough">>, "many">;
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
createdAt: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
id: string;
role: "tool" | "user" | "assistant" | "system";
parts: z.objectOutputType<{
type: z.ZodString;
}, z.ZodTypeAny, "passthrough">[];
metadata?: Record<string, unknown> | undefined;
createdAt?: string | undefined;
}, {
id: string;
role: "tool" | "user" | "assistant" | "system";
parts: z.objectInputType<{
type: z.ZodString;
}, z.ZodTypeAny, "passthrough">[];
metadata?: Record<string, unknown> | undefined;
createdAt?: string | undefined;
}>, "many">;
generation: z.ZodOptional<z.ZodObject<{
maxResponseTokens: z.ZodOptional<z.ZodNumber>;
}, "strip", z.ZodTypeAny, {
maxResponseTokens?: number | undefined;
}, {
maxResponseTokens?: number | undefined;
}>>;
}, "strip", z.ZodTypeAny, {
platform: "slack";
projectId: string;
dispatchId: string;
conversationId: string;
agentConfigId: string;
inboundMessage: {
text: string;
userId: string;
userName: string;
isDirectMessage: boolean;
attachments?: {
kind: "file" | "image";
id: string;
filename?: string | undefined;
mediaType?: string | undefined;
privateUrl?: string | undefined;
}[] | undefined;
};
conversationHistory: {
id: string;
role: "tool" | "user" | "assistant" | "system";
parts: z.objectOutputType<{
type: z.ZodString;
}, z.ZodTypeAny, "passthrough">[];
metadata?: Record<string, unknown> | undefined;
createdAt?: string | undefined;
}[];
generation?: {
maxResponseTokens?: number | undefined;
} | undefined;
}, {
platform: "slack";
projectId: string;
dispatchId: string;
conversationId: string;
agentConfigId: string;
inboundMessage: {
text: string;
userId: string;
userName: string;
isDirectMessage: boolean;
attachments?: {
kind: "file" | "image";
id: string;
filename?: string | undefined;
mediaType?: string | undefined;
privateUrl?: string | undefined;
}[] | undefined;
};
conversationHistory: {
id: string;
role: "tool" | "user" | "assistant" | "system";
parts: z.objectInputType<{
type: z.ZodString;
}, z.ZodTypeAny, "passthrough">[];
metadata?: Record<string, unknown> | undefined;
createdAt?: string | undefined;
}[];
generation?: {
maxResponseTokens?: number | undefined;
} | undefined;
}>;
export declare const ChannelResponsePartSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
type: z.ZodLiteral<"text">;
text: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "text";
text: string;
}, {
type: "text";
text: string;
}>, z.ZodObject<{
type: z.ZodLiteral<"tool_call">;
id: z.ZodString;
name: z.ZodString;
input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
state: z.ZodEnum<["streaming", "pending", "completed", "error"]>;
}, "strip", z.ZodTypeAny, {
type: "tool_call";
name: string;
input: Record<string, unknown>;
id: string;
state: "error" | "streaming" | "completed" | "pending";
}, {
type: "tool_call";
name: string;
input: Record<string, unknown>;
id: string;
state: "error" | "streaming" | "completed" | "pending";
}>, z.ZodObject<{
type: z.ZodLiteral<"tool_result">;
tool_call_id: z.ZodString;
output: z.ZodUnknown;
is_error: z.ZodOptional<z.ZodBoolean>;
}, "strip", z.ZodTypeAny, {
type: "tool_result";
tool_call_id: string;
output?: unknown;
is_error?: boolean | undefined;
}, {
type: "tool_result";
tool_call_id: string;
output?: unknown;
is_error?: boolean | undefined;
}>, z.ZodObject<{
type: z.ZodLiteral<"reasoning">;
text: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "reasoning";
text: string;
}, {
type: "reasoning";
text: string;
}>, z.ZodObject<{
type: z.ZodLiteral<"error">;
code: z.ZodString;
message: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "error";
message: string;
code: string;
}, {
type: "error";
message: string;
code: string;
}>]>;
export declare const ChannelInvokeResponseSchema: z.ZodObject<{
ignored: z.ZodBoolean;
responseParts: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
type: z.ZodLiteral<"text">;
text: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "text";
text: string;
}, {
type: "text";
text: string;
}>, z.ZodObject<{
type: z.ZodLiteral<"tool_call">;
id: z.ZodString;
name: z.ZodString;
input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
state: z.ZodEnum<["streaming", "pending", "completed", "error"]>;
}, "strip", z.ZodTypeAny, {
type: "tool_call";
name: string;
input: Record<string, unknown>;
id: string;
state: "error" | "streaming" | "completed" | "pending";
}, {
type: "tool_call";
name: string;
input: Record<string, unknown>;
id: string;
state: "error" | "streaming" | "completed" | "pending";
}>, z.ZodObject<{
type: z.ZodLiteral<"tool_result">;
tool_call_id: z.ZodString;
output: z.ZodUnknown;
is_error: z.ZodOptional<z.ZodBoolean>;
}, "strip", z.ZodTypeAny, {
type: "tool_result";
tool_call_id: string;
output?: unknown;
is_error?: boolean | undefined;
}, {
type: "tool_result";
tool_call_id: string;
output?: unknown;
is_error?: boolean | undefined;
}>, z.ZodObject<{
type: z.ZodLiteral<"reasoning">;
text: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "reasoning";
text: string;
}, {
type: "reasoning";
text: string;
}>, z.ZodObject<{
type: z.ZodLiteral<"error">;
code: z.ZodString;
message: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "error";
message: string;
code: string;
}, {
type: "error";
message: string;
code: string;
}>]>, "many">>;
tokenUsage: z.ZodOptional<z.ZodObject<{
inputTokens: z.ZodOptional<z.ZodNumber>;
outputTokens: z.ZodOptional<z.ZodNumber>;
totalTokens: z.ZodOptional<z.ZodNumber>;
}, "strip", z.ZodTypeAny, {
totalTokens?: number | undefined;
inputTokens?: number | undefined;
outputTokens?: number | undefined;
}, {
totalTokens?: number | undefined;
inputTokens?: number | undefined;
outputTokens?: number | undefined;
}>>;
error: z.ZodOptional<z.ZodObject<{
code: z.ZodEnum<["provider_error", "internal_error"]>;
retryable: z.ZodBoolean;
}, "strip", z.ZodTypeAny, {
code: "provider_error" | "internal_error";
retryable: boolean;
}, {
code: "provider_error" | "internal_error";
retryable: boolean;
}>>;
}, "strip", z.ZodTypeAny, {
ignored: boolean;
error?: {
code: "provider_error" | "internal_error";
retryable: boolean;
} | undefined;
responseParts?: ({
type: "text";
text: string;
} | {
type: "tool_call";
name: string;
input: Record<string, unknown>;
id: string;
state: "error" | "streaming" | "completed" | "pending";
} | {
type: "tool_result";
tool_call_id: string;
output?: unknown;
is_error?: boolean | undefined;
} | {
type: "reasoning";
text: string;
} | {
type: "error";
message: string;
code: string;
})[] | undefined;
tokenUsage?: {
totalTokens?: number | undefined;
inputTokens?: number | undefined;
outputTokens?: number | undefined;
} | undefined;
}, {
ignored: boolean;
error?: {
code: "provider_error" | "internal_error";
retryable: boolean;
} | undefined;
responseParts?: ({
type: "text";
text: string;
} | {
type: "tool_call";
name: string;
input: Record<string, unknown>;
id: string;
state: "error" | "streaming" | "completed" | "pending";
} | {
type: "tool_result";
tool_call_id: string;
output?: unknown;
is_error?: boolean | undefined;
} | {
type: "reasoning";
text: string;
} | {
type: "error";
message: string;
code: string;
})[] | undefined;
tokenUsage?: {
totalTokens?: number | undefined;
inputTokens?: number | undefined;
outputTokens?: number | undefined;
} | undefined;
}>;
declare const dispatchClaimsSchema: z.ZodObject<{
iss: z.ZodString;
aud: z.ZodString;
sub: z.ZodString;
project_id: z.ZodString;
platform: z.ZodString;
body_sha256: z.ZodString;
iat: z.ZodNumber;
exp: z.ZodNumber;
}, "strip", z.ZodTypeAny, {
project_id: string;
platform: string;
sub: string;
iss: string;
aud: string;
body_sha256: string;
iat: number;
exp: number;
}, {
project_id: string;
platform: string;
sub: string;
iss: string;
aud: string;
body_sha256: string;
iat: number;
exp: number;
}>;
export type ChannelInvokeRequest = z.infer<typeof ChannelInvokeRequestSchema>;
export type ChannelInvokeResponse = z.infer<typeof ChannelInvokeResponseSchema>;
type ChannelResponsePart = z.infer<typeof ChannelResponsePartSchema>;
type DispatchClaims = z.infer<typeof dispatchClaimsSchema>;
export interface ChannelInvokeDeps {
ensureProjectDiscovery: (ctx: HandlerContext) => Promise<void>;
getAgent: (id: string) => Agent | undefined;
getAllAgentIds: () => string[];
}
export declare const defaultChannelInvokeDeps: ChannelInvokeDeps;
export declare function verifyDispatchJws(jws: string, body: string, options: {
audience: string;
publicKeyPem: string;
maxAgeSeconds: number;
expectedProjectId?: string;
}): Promise<DispatchClaims>;
export declare function normalizeConversationHistoryForRuntime(messages: ChannelInvokeRequest["conversationHistory"]): Message[];
export declare function resolveChannelInvokeAgent(agentConfigId: string, deps: Pick<ChannelInvokeDeps, "getAgent" | "getAllAgentIds">): Agent | undefined;
export declare function buildChannelResponseParts(response: AgentResponse): ChannelResponsePart[];
export declare function executeChannelInvoke(payload: ChannelInvokeRequest, ctx: HandlerContext, deps: ChannelInvokeDeps): Promise<ChannelInvokeResponse>;
export {};
//# sourceMappingURL=invoke.d.ts.map
{"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../../src/src/channels/invoke.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,IAAI,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA8BxB,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBrC,CAAC;AAiCH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAMpC,CAAC;AAEH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYtC,CAAC;AAQH,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;EASxB,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF,KAAK,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AACrE,KAAK,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,sBAAsB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,KAAK,GAAG,SAAS,CAAC;IAC5C,cAAc,EAAE,MAAM,MAAM,EAAE,CAAC;CAChC;AAED,eAAO,MAAM,wBAAwB,EAAE,iBAItC,CAAC;AAyCF,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GACA,OAAO,CAAC,cAAc,CAAC,CA2DzB;AAqCD,wBAAgB,sCAAsC,CACpD,QAAQ,EAAE,oBAAoB,CAAC,qBAAqB,CAAC,GACpD,OAAO,EAAE,CAUX;AAED,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,GAAG,gBAAgB,CAAC,GAC3D,KAAK,GAAG,SAAS,CA2BnB;AAsDD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,aAAa,GAAG,mBAAmB,EAAE,CAiDxF;AAgBD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,oBAAoB,EAC7B,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,qBAAqB,CAAC,CAkEhC"}
import * as dntShim from "../../_dnt.shims.js";
import { fromError } from "../errors/veryfront-error.js";
import { serverLogger } from "../utils/index.js";
import { base64urlEncodeBytes } from "../utils/base64url.js";
import { z } from "zod";
import { getAgent as getRegisteredAgent, getAllAgentIds as getRegisteredAgentIds, } from "../agent/composition/composition.js";
import { ensureProjectDiscovery as ensureProjectDiscoveryForProject } from "../server/handlers/request/api/project-discovery.js";
const logger = serverLogger.component("channels-invoke");
const SIGNATURE_SKEW_SECONDS = 5;
const rawHistoryPartSchema = z.object({
type: z.string(),
}).passthrough();
const channelAttachmentSchema = z.object({
id: z.string(),
kind: z.enum(["image", "file"]),
filename: z.string().optional(),
mediaType: z.string().optional(),
privateUrl: z.string().optional(),
});
const channelInvokeHistoryMessageSchema = z.object({
id: z.string(),
role: z.enum(["user", "assistant", "system", "tool"]),
parts: z.array(rawHistoryPartSchema),
metadata: z.record(z.unknown()).optional(),
createdAt: z.string().optional(),
});
export const ChannelInvokeRequestSchema = z.object({
dispatchId: z.string().min(1),
conversationId: z.string().min(1),
projectId: z.string().min(1),
agentConfigId: z.string().min(1),
platform: z.literal("slack"),
inboundMessage: z.object({
text: z.string(),
userId: z.string(),
userName: z.string(),
isDirectMessage: z.boolean(),
attachments: z.array(channelAttachmentSchema).optional(),
}),
conversationHistory: z.array(channelInvokeHistoryMessageSchema),
generation: z.object({
maxResponseTokens: z.number().int().positive().max(16384).optional(),
}).optional(),
});
const channelTextPartSchema = z.object({
type: z.literal("text"),
text: z.string(),
});
const channelToolCallPartSchema = z.object({
type: z.literal("tool_call"),
id: z.string(),
name: z.string(),
input: z.record(z.unknown()),
state: z.enum(["streaming", "pending", "completed", "error"]),
});
const channelToolResultPartSchema = z.object({
type: z.literal("tool_result"),
tool_call_id: z.string(),
output: z.unknown(),
is_error: z.boolean().optional(),
});
const channelReasoningPartSchema = z.object({
type: z.literal("reasoning"),
text: z.string(),
});
const channelErrorPartSchema = z.object({
type: z.literal("error"),
code: z.string(),
message: z.string(),
});
export const ChannelResponsePartSchema = z.discriminatedUnion("type", [
channelTextPartSchema,
channelToolCallPartSchema,
channelToolResultPartSchema,
channelReasoningPartSchema,
channelErrorPartSchema,
]);
export const ChannelInvokeResponseSchema = z.object({
ignored: z.boolean(),
responseParts: z.array(ChannelResponsePartSchema).optional(),
tokenUsage: z.object({
inputTokens: z.number().int().nonnegative().optional(),
outputTokens: z.number().int().nonnegative().optional(),
totalTokens: z.number().int().nonnegative().optional(),
}).optional(),
error: z.object({
code: z.enum(["provider_error", "internal_error"]),
retryable: z.boolean(),
}).optional(),
});
const dispatchHeaderSchema = z.object({
alg: z.literal("EdDSA"),
typ: z.string().optional(),
kid: z.string().optional(),
});
const dispatchClaimsSchema = z.object({
iss: z.string(),
aud: z.string(),
sub: z.string(),
project_id: z.string(),
platform: z.string(),
body_sha256: z.string(),
iat: z.number().int(),
exp: z.number().int(),
});
export const defaultChannelInvokeDeps = {
ensureProjectDiscovery: ensureProjectDiscoveryForProject,
getAgent: getRegisteredAgent,
getAllAgentIds: getRegisteredAgentIds,
};
function base64urlDecodeToBytes(input) {
const normalized = input
.replaceAll("-", "+")
.replaceAll("_", "/")
.padEnd(Math.ceil(input.length / 4) * 4, "=");
return toArrayBuffer(Uint8Array.from(atob(normalized), (char) => char.charCodeAt(0)));
}
function toArrayBuffer(bytes) {
const buffer = new ArrayBuffer(bytes.byteLength);
new Uint8Array(buffer).set(bytes);
return buffer;
}
function pemToDer(pem, label) {
const body = pem
.replace(`-----BEGIN ${label}-----`, "")
.replace(`-----END ${label}-----`, "")
.replace(/\s/g, "");
return toArrayBuffer(Uint8Array.from(atob(body), (char) => char.charCodeAt(0)));
}
async function importEd25519PublicKey(pem) {
return dntShim.crypto.subtle.importKey("spki", pemToDer(pem, "PUBLIC KEY"), "Ed25519", false, ["verify"]);
}
async function sha256Base64url(body) {
const hash = await dntShim.crypto.subtle.digest("SHA-256", new TextEncoder().encode(body));
return base64urlEncodeBytes(new Uint8Array(hash));
}
export async function verifyDispatchJws(jws, body, options) {
const parts = jws.split(".");
if (parts.length !== 3) {
throw new Error("Channel dispatch signature must be a compact JWS");
}
const encodedHeader = parts[0];
const encodedPayload = parts[1];
const encodedSignature = parts[2];
if (!encodedHeader || !encodedPayload || !encodedSignature) {
throw new Error("Channel dispatch signature must include header, payload, and signature");
}
const header = dispatchHeaderSchema.parse(JSON.parse(new TextDecoder().decode(base64urlDecodeToBytes(encodedHeader))));
const claims = dispatchClaimsSchema.parse(JSON.parse(new TextDecoder().decode(base64urlDecodeToBytes(encodedPayload))));
if (header.alg !== "EdDSA") {
throw new Error("Unsupported channel dispatch JWS algorithm");
}
const signingInput = new TextEncoder().encode(`${encodedHeader}.${encodedPayload}`);
const signature = base64urlDecodeToBytes(encodedSignature);
const publicKey = await importEd25519PublicKey(options.publicKeyPem);
const verified = await dntShim.crypto.subtle.verify("Ed25519", publicKey, signature, signingInput);
if (!verified) {
throw new Error("Channel dispatch signature verification failed");
}
if (claims.aud !== options.audience) {
throw new Error("Channel dispatch audience mismatch");
}
if (options.expectedProjectId && claims.project_id !== options.expectedProjectId) {
throw new Error("Channel dispatch project mismatch");
}
const now = Math.floor(Date.now() / 1000);
if (claims.exp <= now) {
throw new Error("Channel dispatch signature expired");
}
if (claims.iat > now + SIGNATURE_SKEW_SECONDS) {
throw new Error("Channel dispatch signature issued in the future");
}
if (now - claims.iat > options.maxAgeSeconds) {
throw new Error("Channel dispatch signature is too old");
}
const bodySha256 = await sha256Base64url(body);
if (claims.body_sha256 !== bodySha256) {
throw new Error("Channel dispatch body hash mismatch");
}
return claims;
}
function normalizeConversationPart(part) {
if (part.type === "text" && typeof part.text === "string") {
return { type: "text", text: part.text };
}
if (part.type === "tool_call" &&
typeof part.id === "string" &&
typeof part.name === "string" &&
part.input &&
typeof part.input === "object" &&
!Array.isArray(part.input)) {
return {
type: `tool-${part.name}`,
toolCallId: part.id,
toolName: part.name,
args: part.input,
};
}
if (part.type === "tool_result" && typeof part.tool_call_id === "string") {
return {
type: "tool-result",
toolCallId: part.tool_call_id,
toolName: typeof part.tool_name === "string" ? part.tool_name : "unknown",
result: "output" in part ? part.output : undefined,
};
}
return null;
}
export function normalizeConversationHistoryForRuntime(messages) {
return messages.map((message) => ({
id: message.id,
role: message.role,
parts: message.parts
.map((part) => normalizeConversationPart(part))
.filter((part) => part !== null),
...(message.createdAt ? { timestamp: Date.parse(message.createdAt) || undefined } : {}),
...(message.metadata ? { metadata: message.metadata } : {}),
}));
}
export function resolveChannelInvokeAgent(agentConfigId, deps) {
const exactAgent = deps.getAgent(agentConfigId);
if (exactAgent) {
return exactAgent;
}
const agentIds = deps.getAllAgentIds();
if (agentIds.length !== 1) {
return undefined;
}
const onlyAgentId = agentIds[0];
if (!onlyAgentId) {
return undefined;
}
const onlyAgent = deps.getAgent(onlyAgentId);
if (onlyAgent) {
logger.warn("Channel invoke fell back to the only discovered runtime agent because agentConfigId did not match a registry id", {
requestedAgentConfigId: agentConfigId,
resolvedAgentId: onlyAgentId,
});
}
return onlyAgent;
}
function normalizeToolCallState(status) {
switch (status) {
case "completed":
return "completed";
case "error":
return "error";
default:
return "pending";
}
}
function convertAssistantPartToChannelResponsePart(part, knownToolCallIds) {
if (part.type === "text" && "text" in part) {
return channelTextPartSchema.parse({
type: "text",
text: part.text,
});
}
const isToolCallPart = part.type === "tool-call" ||
(part.type.startsWith("tool-") && part.type !== "tool-result");
if (isToolCallPart &&
"toolCallId" in part &&
"toolName" in part &&
!knownToolCallIds.has(part.toolCallId)) {
return channelToolCallPartSchema.parse({
type: "tool_call",
id: part.toolCallId,
name: part.toolName,
input: "args" in part ? part.args : ("input" in part ? part.input : {}),
state: "pending",
});
}
return null;
}
function findLastAssistantMessage(messages) {
for (let index = messages.length - 1; index >= 0; index -= 1) {
if (messages[index]?.role === "assistant") {
return messages[index];
}
}
return undefined;
}
export function buildChannelResponseParts(response) {
const responseParts = [];
const knownToolCallIds = new Set();
if (response.thinking?.trim()) {
responseParts.push(channelReasoningPartSchema.parse({
type: "reasoning",
text: response.thinking,
}));
}
for (const toolCall of response.toolCalls) {
knownToolCallIds.add(toolCall.id);
responseParts.push(channelToolCallPartSchema.parse({
type: "tool_call",
id: toolCall.id,
name: toolCall.name,
input: toolCall.args,
state: normalizeToolCallState(toolCall.status),
}));
if (toolCall.status === "completed" || toolCall.status === "error") {
responseParts.push(channelToolResultPartSchema.parse({
type: "tool_result",
tool_call_id: toolCall.id,
output: toolCall.status === "error"
? { error: toolCall.error ?? "Tool execution failed" }
: toolCall.result,
...(toolCall.status === "error" ? { is_error: true } : {}),
}));
}
}
const lastAssistantMessage = findLastAssistantMessage(response.messages);
if (lastAssistantMessage) {
for (const part of lastAssistantMessage.parts) {
const converted = convertAssistantPartToChannelResponsePart(part, knownToolCallIds);
if (converted) {
responseParts.push(converted);
}
}
}
else if (response.text.trim()) {
responseParts.push(channelTextPartSchema.parse({
type: "text",
text: response.text,
}));
}
return responseParts;
}
function classifyRuntimeError(error) {
const veryfrontError = fromError(error);
if (veryfrontError?.type === "no_ai_available") {
return { code: "provider_error", retryable: false };
}
if (veryfrontError?.type === "api" || veryfrontError?.type === "network") {
return { code: "provider_error", retryable: true };
}
return { code: "internal_error", retryable: true };
}
export async function executeChannelInvoke(payload, ctx, deps) {
await deps.ensureProjectDiscovery(ctx);
const agent = resolveChannelInvokeAgent(payload.agentConfigId, deps);
if (!agent) {
logger.error("Channel invoke could not resolve a runtime agent for the request", {
requestedAgentConfigId: payload.agentConfigId,
discoveredAgentIds: deps.getAllAgentIds(),
projectSlug: ctx.projectSlug,
projectId: ctx.projectId,
});
return {
ignored: false,
error: {
code: "internal_error",
retryable: false,
},
};
}
const messages = normalizeConversationHistoryForRuntime(payload.conversationHistory);
await agent.clearMemory();
try {
const result = await agent.generate({
input: messages,
context: {
requestId: payload.dispatchId,
dispatchId: payload.dispatchId,
conversationId: payload.conversationId,
projectId: payload.projectId,
agentConfigId: payload.agentConfigId,
channel: payload.inboundMessage,
},
...(payload.generation?.maxResponseTokens
? {
maxOutputTokens: payload.generation.maxResponseTokens,
}
: {}),
});
return ChannelInvokeResponseSchema.parse({
ignored: false,
responseParts: buildChannelResponseParts(result),
tokenUsage: result.usage
? {
inputTokens: result.usage.promptTokens,
outputTokens: result.usage.completionTokens,
totalTokens: result.usage.totalTokens,
}
: undefined,
});
}
catch (error) {
logger.error("Channel invoke runtime execution failed", {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
projectSlug: ctx.projectSlug,
projectId: ctx.projectId,
dispatchId: payload.dispatchId,
});
return {
ignored: false,
error: classifyRuntimeError(error),
};
}
}
import type { HandlerContext } from "../../types.js";
/**
* Run AI discovery (agents, tools) for a project if not already done.
* Must be called within a runWithContext scope so the VFS can resolve
* the correct remote project files and the agent registry uses the
* correct project scope.
*/
export declare function ensureProjectDiscovery(ctx: HandlerContext): Promise<void>;
//# sourceMappingURL=project-discovery.d.ts.map
{"version":3,"file":"project-discovery.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/api/project-discovery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAsBrD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAwD/E"}
import { serverLogger } from "../../../../utils/index.js";
const logger = serverLogger.component("api-wrapper");
/**
* Tracks in-flight and completed AI discovery per project+release.
*
* Key: `{projectSlug}:{releaseId}` for production, `{projectSlug}:preview` for preview.
* This ensures a new deployment triggers re-discovery of agents/tools.
*
* Using a Map<string, Promise> deduplicates concurrent requests and
* allows retry on failure (the key is deleted if discovery rejects).
*/
const discoveredProjects = new Map();
/** Build a discovery cache key that incorporates the release/version. */
function discoveryKey(ctx) {
const slug = ctx.projectSlug ?? ctx.projectDir;
const version = ctx.releaseId ?? "preview";
return `${slug}:${version}`;
}
/**
* Run AI discovery (agents, tools) for a project if not already done.
* Must be called within a runWithContext scope so the VFS can resolve
* the correct remote project files and the agent registry uses the
* correct project scope.
*/
export async function ensureProjectDiscovery(ctx) {
const key = discoveryKey(ctx);
const existing = discoveredProjects.get(key);
if (existing)
return existing;
const promise = (async () => {
const { discoverAll } = await import("../../../../discovery/index.js");
const { agentRegistry } = await import("../../../../agent/composition/composition.js");
const { toolRegistry } = await import("../../../../tool/registry.js");
// Clear stale entries for this project scope before re-discovery.
// This prevents agents/tools removed in a new release from lingering.
agentRegistry.clear();
toolRegistry.clear();
const result = await discoverAll({
baseDir: ctx.projectDir,
fsAdapter: ctx.adapter.fs,
verbose: false,
});
const logData = {
projectSlug: ctx.projectSlug,
releaseId: ctx.releaseId,
agents: result.agents.size,
tools: result.tools.size,
errors: result.errors.length,
};
if (result.agents.size === 0 && result.tools.size === 0) {
logger.warn("AI discovery found 0 agents and 0 tools", {
...logData,
errorMessages: result.errors.map((e) => e.error.message).slice(0, 5),
baseDir: ctx.projectDir,
});
}
else {
logger.info("AI discovery completed", logData);
}
})();
discoveredProjects.set(key, promise);
try {
await promise;
}
catch (error) {
// Allow retry on next request
discoveredProjects.delete(key);
logger.warn("AI discovery failed (will retry)", {
projectSlug: ctx.projectSlug,
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
});
}
}
import * as dntShim from "../../../../_dnt.shims.js";
import { BaseHandler } from "../response/base.js";
import type { HandlerContext, HandlerMetadata, HandlerResult } from "../types.js";
import { type ChannelInvokeDeps } from "../../../channels/invoke.js";
export declare class ChannelInvokeHandler extends BaseHandler {
private readonly deps;
metadata: HandlerMetadata;
constructor(deps?: ChannelInvokeDeps);
handle(req: dntShim.Request, ctx: HandlerContext): Promise<HandlerResult>;
}
//# sourceMappingURL=channel-invoke.handler.d.ts.map
{"version":3,"file":"channel-invoke.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/channel-invoke.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,EACL,KAAK,iBAAiB,EAKvB,MAAM,6BAA6B,CAAC;AASrC,qBAAa,oBAAqB,SAAQ,WAAW;IAOvC,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,QAAQ,EAAE,eAAe,CAIvB;gBAE2B,IAAI,GAAE,iBAA4C;IAIzE,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CAiEhF"}
import { BaseHandler } from "../response/base.js";
import { ChannelInvokeRequestSchema, defaultChannelInvokeDeps, executeChannelInvoke, verifyDispatchJws, } from "../../../channels/invoke.js";
import { HTTP_INTERNAL_SERVER_ERROR, PRIORITY_MEDIUM_API, } from "../../../utils/constants/index.js";
const DISPATCH_JWS_HEADER = "x-veryfront-dispatch-jws";
const MAX_DISPATCH_SIGNATURE_AGE_SECONDS = 60;
export class ChannelInvokeHandler extends BaseHandler {
deps;
metadata = {
name: "ChannelInvokeHandler",
priority: PRIORITY_MEDIUM_API,
patterns: [{ pattern: "/channels/invoke", exact: true, method: "POST" }],
};
constructor(deps = defaultChannelInvokeDeps) {
super();
this.deps = deps;
}
async handle(req, ctx) {
if (!this.shouldHandle(req, ctx)) {
return this.continue();
}
return this.withProxyContext(ctx, async () => {
const builder = this.createResponseBuilder(ctx)
.withCORS(req, ctx.securityConfig?.cors)
.withSecurity(ctx.securityConfig ?? undefined, req);
const publicKeyPem = ctx.adapter.env.get("CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY");
if (!publicKeyPem) {
this.logWarn("Missing CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY for channel invoke endpoint");
return this.respond(builder.json({ error: "Channel dispatch verification is not configured" }, HTTP_INTERNAL_SERVER_ERROR));
}
const projectSlug = ctx.projectSlug;
if (!projectSlug) {
this.logWarn("Channel invoke request arrived without resolved project slug");
return this.respond(builder.json({ error: "Project context is unavailable" }, 400));
}
const dispatchJws = req.headers.get(DISPATCH_JWS_HEADER);
if (!dispatchJws) {
return this.respond(builder.json({ error: "Missing dispatch signature" }, 401));
}
const rawBody = await req.text();
try {
await verifyDispatchJws(dispatchJws, rawBody, {
audience: projectSlug,
expectedProjectId: ctx.projectId,
publicKeyPem,
maxAgeSeconds: MAX_DISPATCH_SIGNATURE_AGE_SECONDS,
});
}
catch (error) {
this.logWarn("Channel invoke signature verification failed", {
error: error instanceof Error ? error.message : String(error),
projectSlug,
projectId: ctx.projectId,
});
return this.respond(builder.json({ error: "Invalid dispatch signature" }, 401));
}
let payload;
try {
payload = ChannelInvokeRequestSchema.parse(JSON.parse(rawBody));
}
catch (error) {
this.logWarn("Channel invoke request validation failed", {
error: error instanceof Error ? error.message : String(error),
projectSlug,
projectId: ctx.projectId,
});
return this.respond(builder.json({ error: "Invalid channel invoke request" }, 400));
}
const response = await executeChannelInvoke(payload, ctx, this.deps);
return this.respond(builder.json(response, 200));
});
}
}
import type { AgentContext, AgentResponse } from "../../types.js";
import { createError, toError } from "../../../errors/veryfront-error.js";
export interface SecurityConfig {
/** Input validation rules */
input?: {
/** Maximum input length */
maxLength?: number;
/** Blocked patterns (regex) */
blockedPatterns?: RegExp[];
/** Sanitize input */
sanitize?: boolean;
/** Custom validator */
validate?: (input: string) => boolean | Promise<boolean>;
};
/** Output filtering rules */
output?: {
/** Blocked patterns in output */
blockedPatterns?: RegExp[];
/** Filter PII (Personal Identifiable Information) */
filterPII?: boolean;
/** Custom filter */
filter?: (output: string) => string | Promise<string>;
};
/** Action when violation detected */
onViolation?: (violation: SecurityViolation) => void;
}
export interface SecurityViolation {
/** Violation type */
type: "input" | "output";
/** Violation reason */
reason: string;
/** Original content */
content: string;
/** Matched pattern (if any) */
pattern?: RegExp;
}
/**
* Common blocked patterns
*/
export const COMMON_BLOCKED_PATTERNS = {
/** Prompt injection attempts */
promptInjection: [
/ignore\s+previous\s+instructions/i,
/ignore\s+all\s+previous\s+prompts/i,
/you\s+are\s+now\s+a/i,
/pretend\s+you\s+are/i,
/system:\s*/i,
/<\|im_start\|>/i,
/<\|im_end\|>/i,
],
/** Potential data exfiltration */
dataExfiltration: [
/password/i,
/api[_\s-]?key/i,
/secret/i,
/token/i,
/credit\s+card/i,
],
/** SQL injection patterns */
sqlInjection: [
/(\bUNION\b|\bSELECT\b).*\bFROM\b/i,
/;\s*(DROP|DELETE|UPDATE|INSERT)/i,
],
/** XSS patterns */
xss: [
/<script[^>]*>.*?<\/script>/gi,
/javascript:/i,
/on\w+\s*=/i, // Event handlers
],
};
/**
* PII patterns with replacement labels
*/
const PII_REPLACEMENTS: Array<{ pattern: RegExp; label: string }> = [
{ pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, label: "[EMAIL]" },
{
pattern: /\b(\+\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
label: "[PHONE]",
},
{ pattern: /\b\d{3}-\d{2}-\d{4}\b/g, label: "[SSN]" },
{ pattern: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g, label: "[CREDIT_CARD]" },
];
/**
* Input Validator
*/
export class InputValidator {
private config: NonNullable<SecurityConfig["input"]>;
constructor(config?: SecurityConfig["input"]) {
this.config = config ?? {};
}
/**
* Validate input
*/
async validate(input: string): Promise<{
valid: boolean;
sanitized?: string;
violations: SecurityViolation[];
}> {
const violations: SecurityViolation[] = [];
const maxLength = this.config.maxLength;
if (maxLength != null && input.length > maxLength) {
violations.push({
type: "input",
reason: `Input exceeds maximum length of ${maxLength}`,
content: `${input.substring(0, 100)}...`,
});
}
for (const pattern of this.config.blockedPatterns ?? []) {
if (!pattern.test(input)) continue;
violations.push({
type: "input",
reason: "Input matches blocked pattern",
content: input,
pattern,
});
}
const customValidate = this.config.validate;
if (customValidate) {
const customValid = await customValidate(input);
if (!customValid) {
violations.push({
type: "input",
reason: "Custom validation failed",
content: input,
});
}
}
const sanitized = this.config.sanitize ? this.sanitizeInput(input) : undefined;
return {
valid: violations.length === 0,
sanitized,
violations,
};
}
/** Sanitization patterns to remove harmful content */
private static readonly SANITIZE_PATTERNS: RegExp[] = [
/<script[^>]*>.*?<\/script>/gi, // Script tags
/on\w+\s*=\s*["'][^"']*["']/gi, // Event handlers
/javascript:/gi, // JavaScript protocol
];
/**
* Sanitize input (remove potentially harmful content)
*/
private sanitizeInput(input: string): string {
return InputValidator.SANITIZE_PATTERNS.reduce(
(text, pattern) => text.replace(pattern, ""),
input,
);
}
}
/**
* Output Filter
*/
export class OutputFilter {
private config: NonNullable<SecurityConfig["output"]>;
constructor(config?: SecurityConfig["output"]) {
this.config = config ?? {};
}
/**
* Filter output
*/
async filter(output: string): Promise<{
filtered: string;
violations: SecurityViolation[];
}> {
const violations: SecurityViolation[] = [];
let filtered = output;
for (const pattern of this.config.blockedPatterns ?? []) {
if (!pattern.test(filtered)) continue;
violations.push({
type: "output",
reason: "Output contains blocked pattern",
content: filtered,
pattern,
});
filtered = filtered.replace(pattern, "[REDACTED]");
}
if (this.config.filterPII) {
filtered = this.filterPII(filtered);
}
const customFilter = this.config.filter;
if (customFilter) {
filtered = await customFilter(filtered);
}
return { filtered, violations };
}
/**
* Filter PII from output
*/
private filterPII(output: string): string {
return PII_REPLACEMENTS.reduce(
(text, { pattern, label }) => text.replace(pattern, label),
output,
);
}
}
/**
* Report violations to the configured handler
*/
function reportViolations(
violations: SecurityViolation[],
onViolation?: (violation: SecurityViolation) => void,
): void {
if (!onViolation) return;
for (const violation of violations) onViolation(violation);
}
/**
* Create security middleware for agents
*/
export function securityMiddleware(
config: SecurityConfig,
): (context: AgentContext, next: () => Promise<AgentResponse>) => Promise<AgentResponse> {
const inputValidator = new InputValidator(config.input);
const outputFilter = new OutputFilter(config.output);
return async (
context: AgentContext,
next: () => Promise<AgentResponse>,
): Promise<AgentResponse> => {
const inputString = typeof context.input === "string"
? context.input
: JSON.stringify(context.input);
const inputValidation = await inputValidator.validate(inputString);
if (!inputValidation.valid) {
reportViolations(inputValidation.violations, config.onViolation);
const firstViolation = inputValidation.violations[0];
throw toError(
createError({
type: "agent",
message: `Input validation failed: ${firstViolation?.reason ?? "Unknown reason"}`,
}),
);
}
if (inputValidation.sanitized != null) {
context.input = inputValidation.sanitized;
}
const result = await next();
const outputFiltering = await outputFilter.filter(result.text);
reportViolations(outputFiltering.violations, config.onViolation);
return { ...result, text: outputFiltering.filtered };
};
}
import * as dntShim from "../../_dnt.shims.js";
import type { Agent, AgentMessage as Message, AgentResponse } from "../agent/index.js";
import { fromError } from "../errors/veryfront-error.js";
import type { HandlerContext } from "../types/index.js";
import { serverLogger } from "../utils/index.js";
import { base64urlEncodeBytes } from "../utils/base64url.js";
import { z } from "zod";
import {
getAgent as getRegisteredAgent,
getAllAgentIds as getRegisteredAgentIds,
} from "../agent/composition/composition.js";
import { ensureProjectDiscovery as ensureProjectDiscoveryForProject } from "../server/handlers/request/api/project-discovery.js";
const logger = serverLogger.component("channels-invoke");
const SIGNATURE_SKEW_SECONDS = 5;
const rawHistoryPartSchema = z.object({
type: z.string(),
}).passthrough();
const channelAttachmentSchema = z.object({
id: z.string(),
kind: z.enum(["image", "file"]),
filename: z.string().optional(),
mediaType: z.string().optional(),
privateUrl: z.string().optional(),
});
const channelInvokeHistoryMessageSchema = z.object({
id: z.string(),
role: z.enum(["user", "assistant", "system", "tool"]),
parts: z.array(rawHistoryPartSchema),
metadata: z.record(z.unknown()).optional(),
createdAt: z.string().optional(),
});
export const ChannelInvokeRequestSchema = z.object({
dispatchId: z.string().min(1),
conversationId: z.string().min(1),
projectId: z.string().min(1),
agentConfigId: z.string().min(1),
platform: z.literal("slack"),
inboundMessage: z.object({
text: z.string(),
userId: z.string(),
userName: z.string(),
isDirectMessage: z.boolean(),
attachments: z.array(channelAttachmentSchema).optional(),
}),
conversationHistory: z.array(channelInvokeHistoryMessageSchema),
generation: z.object({
maxResponseTokens: z.number().int().positive().max(16384).optional(),
}).optional(),
});
const channelTextPartSchema = z.object({
type: z.literal("text"),
text: z.string(),
});
const channelToolCallPartSchema = z.object({
type: z.literal("tool_call"),
id: z.string(),
name: z.string(),
input: z.record(z.unknown()),
state: z.enum(["streaming", "pending", "completed", "error"]),
});
const channelToolResultPartSchema = z.object({
type: z.literal("tool_result"),
tool_call_id: z.string(),
output: z.unknown(),
is_error: z.boolean().optional(),
});
const channelReasoningPartSchema = z.object({
type: z.literal("reasoning"),
text: z.string(),
});
const channelErrorPartSchema = z.object({
type: z.literal("error"),
code: z.string(),
message: z.string(),
});
export const ChannelResponsePartSchema = z.discriminatedUnion("type", [
channelTextPartSchema,
channelToolCallPartSchema,
channelToolResultPartSchema,
channelReasoningPartSchema,
channelErrorPartSchema,
]);
export const ChannelInvokeResponseSchema = z.object({
ignored: z.boolean(),
responseParts: z.array(ChannelResponsePartSchema).optional(),
tokenUsage: z.object({
inputTokens: z.number().int().nonnegative().optional(),
outputTokens: z.number().int().nonnegative().optional(),
totalTokens: z.number().int().nonnegative().optional(),
}).optional(),
error: z.object({
code: z.enum(["provider_error", "internal_error"]),
retryable: z.boolean(),
}).optional(),
});
const dispatchHeaderSchema = z.object({
alg: z.literal("EdDSA"),
typ: z.string().optional(),
kid: z.string().optional(),
});
const dispatchClaimsSchema = z.object({
iss: z.string(),
aud: z.string(),
sub: z.string(),
project_id: z.string(),
platform: z.string(),
body_sha256: z.string(),
iat: z.number().int(),
exp: z.number().int(),
});
export type ChannelInvokeRequest = z.infer<typeof ChannelInvokeRequestSchema>;
export type ChannelInvokeResponse = z.infer<typeof ChannelInvokeResponseSchema>;
type ChannelResponsePart = z.infer<typeof ChannelResponsePartSchema>;
type DispatchClaims = z.infer<typeof dispatchClaimsSchema>;
export interface ChannelInvokeDeps {
ensureProjectDiscovery: (ctx: HandlerContext) => Promise<void>;
getAgent: (id: string) => Agent | undefined;
getAllAgentIds: () => string[];
}
export const defaultChannelInvokeDeps: ChannelInvokeDeps = {
ensureProjectDiscovery: ensureProjectDiscoveryForProject,
getAgent: getRegisteredAgent,
getAllAgentIds: getRegisteredAgentIds,
};
function base64urlDecodeToBytes(input: string): ArrayBuffer {
const normalized = input
.replaceAll("-", "+")
.replaceAll("_", "/")
.padEnd(Math.ceil(input.length / 4) * 4, "=");
return toArrayBuffer(Uint8Array.from(atob(normalized), (char) => char.charCodeAt(0)));
}
function toArrayBuffer(bytes: Uint8Array): ArrayBuffer {
const buffer = new ArrayBuffer(bytes.byteLength);
new Uint8Array(buffer).set(bytes);
return buffer;
}
function pemToDer(pem: string, label: string): ArrayBuffer {
const body = pem
.replace(`-----BEGIN ${label}-----`, "")
.replace(`-----END ${label}-----`, "")
.replace(/\s/g, "");
return toArrayBuffer(Uint8Array.from(atob(body), (char) => char.charCodeAt(0)));
}
async function importEd25519PublicKey(pem: string): Promise<dntShim.CryptoKey> {
return dntShim.crypto.subtle.importKey(
"spki",
pemToDer(pem, "PUBLIC KEY"),
"Ed25519",
false,
["verify"],
);
}
async function sha256Base64url(body: string): Promise<string> {
const hash = await dntShim.crypto.subtle.digest("SHA-256", new TextEncoder().encode(body));
return base64urlEncodeBytes(new Uint8Array(hash));
}
export async function verifyDispatchJws(
jws: string,
body: string,
options: {
audience: string;
publicKeyPem: string;
maxAgeSeconds: number;
expectedProjectId?: string;
},
): Promise<DispatchClaims> {
const parts = jws.split(".");
if (parts.length !== 3) {
throw new Error("Channel dispatch signature must be a compact JWS");
}
const encodedHeader = parts[0];
const encodedPayload = parts[1];
const encodedSignature = parts[2];
if (!encodedHeader || !encodedPayload || !encodedSignature) {
throw new Error("Channel dispatch signature must include header, payload, and signature");
}
const header = dispatchHeaderSchema.parse(
JSON.parse(new TextDecoder().decode(base64urlDecodeToBytes(encodedHeader))),
);
const claims = dispatchClaimsSchema.parse(
JSON.parse(new TextDecoder().decode(base64urlDecodeToBytes(encodedPayload))),
);
if (header.alg !== "EdDSA") {
throw new Error("Unsupported channel dispatch JWS algorithm");
}
const signingInput = new TextEncoder().encode(`${encodedHeader}.${encodedPayload}`);
const signature = base64urlDecodeToBytes(encodedSignature);
const publicKey = await importEd25519PublicKey(options.publicKeyPem);
const verified = await dntShim.crypto.subtle.verify("Ed25519", publicKey, signature, signingInput);
if (!verified) {
throw new Error("Channel dispatch signature verification failed");
}
if (claims.aud !== options.audience) {
throw new Error("Channel dispatch audience mismatch");
}
if (options.expectedProjectId && claims.project_id !== options.expectedProjectId) {
throw new Error("Channel dispatch project mismatch");
}
const now = Math.floor(Date.now() / 1000);
if (claims.exp <= now) {
throw new Error("Channel dispatch signature expired");
}
if (claims.iat > now + SIGNATURE_SKEW_SECONDS) {
throw new Error("Channel dispatch signature issued in the future");
}
if (now - claims.iat > options.maxAgeSeconds) {
throw new Error("Channel dispatch signature is too old");
}
const bodySha256 = await sha256Base64url(body);
if (claims.body_sha256 !== bodySha256) {
throw new Error("Channel dispatch body hash mismatch");
}
return claims;
}
function normalizeConversationPart(
part: z.infer<typeof rawHistoryPartSchema>,
): Message["parts"][number] | null {
if (part.type === "text" && typeof part.text === "string") {
return { type: "text", text: part.text };
}
if (
part.type === "tool_call" &&
typeof part.id === "string" &&
typeof part.name === "string" &&
part.input &&
typeof part.input === "object" &&
!Array.isArray(part.input)
) {
return {
type: `tool-${part.name}`,
toolCallId: part.id,
toolName: part.name,
args: part.input as Record<string, unknown>,
};
}
if (part.type === "tool_result" && typeof part.tool_call_id === "string") {
return {
type: "tool-result",
toolCallId: part.tool_call_id,
toolName: typeof part.tool_name === "string" ? part.tool_name : "unknown",
result: "output" in part ? part.output : undefined,
};
}
return null;
}
export function normalizeConversationHistoryForRuntime(
messages: ChannelInvokeRequest["conversationHistory"],
): Message[] {
return messages.map((message) => ({
id: message.id,
role: message.role,
parts: message.parts
.map((part) => normalizeConversationPart(part))
.filter((part): part is NonNullable<typeof part> => part !== null),
...(message.createdAt ? { timestamp: Date.parse(message.createdAt) || undefined } : {}),
...(message.metadata ? { metadata: message.metadata } : {}),
}));
}
export function resolveChannelInvokeAgent(
agentConfigId: string,
deps: Pick<ChannelInvokeDeps, "getAgent" | "getAllAgentIds">,
): Agent | undefined {
const exactAgent = deps.getAgent(agentConfigId);
if (exactAgent) {
return exactAgent;
}
const agentIds = deps.getAllAgentIds();
if (agentIds.length !== 1) {
return undefined;
}
const onlyAgentId = agentIds[0];
if (!onlyAgentId) {
return undefined;
}
const onlyAgent = deps.getAgent(onlyAgentId);
if (onlyAgent) {
logger.warn(
"Channel invoke fell back to the only discovered runtime agent because agentConfigId did not match a registry id",
{
requestedAgentConfigId: agentConfigId,
resolvedAgentId: onlyAgentId,
},
);
}
return onlyAgent;
}
function normalizeToolCallState(status: string): "pending" | "completed" | "error" {
switch (status) {
case "completed":
return "completed";
case "error":
return "error";
default:
return "pending";
}
}
function convertAssistantPartToChannelResponsePart(
part: Message["parts"][number],
knownToolCallIds: Set<string>,
): ChannelResponsePart | null {
if (part.type === "text" && "text" in part) {
return channelTextPartSchema.parse({
type: "text",
text: part.text,
});
}
const isToolCallPart = part.type === "tool-call" ||
(part.type.startsWith("tool-") && part.type !== "tool-result");
if (
isToolCallPart &&
"toolCallId" in part &&
"toolName" in part &&
!knownToolCallIds.has(part.toolCallId)
) {
return channelToolCallPartSchema.parse({
type: "tool_call",
id: part.toolCallId,
name: part.toolName,
input: "args" in part ? part.args : ("input" in part ? part.input : {}),
state: "pending",
});
}
return null;
}
function findLastAssistantMessage(messages: Message[]): Message | undefined {
for (let index = messages.length - 1; index >= 0; index -= 1) {
if (messages[index]?.role === "assistant") {
return messages[index];
}
}
return undefined;
}
export function buildChannelResponseParts(response: AgentResponse): ChannelResponsePart[] {
const responseParts: ChannelResponsePart[] = [];
const knownToolCallIds = new Set<string>();
if (response.thinking?.trim()) {
responseParts.push(channelReasoningPartSchema.parse({
type: "reasoning",
text: response.thinking,
}));
}
for (const toolCall of response.toolCalls) {
knownToolCallIds.add(toolCall.id);
responseParts.push(channelToolCallPartSchema.parse({
type: "tool_call",
id: toolCall.id,
name: toolCall.name,
input: toolCall.args,
state: normalizeToolCallState(toolCall.status),
}));
if (toolCall.status === "completed" || toolCall.status === "error") {
responseParts.push(channelToolResultPartSchema.parse({
type: "tool_result",
tool_call_id: toolCall.id,
output: toolCall.status === "error"
? { error: toolCall.error ?? "Tool execution failed" }
: toolCall.result,
...(toolCall.status === "error" ? { is_error: true } : {}),
}));
}
}
const lastAssistantMessage = findLastAssistantMessage(response.messages);
if (lastAssistantMessage) {
for (const part of lastAssistantMessage.parts) {
const converted = convertAssistantPartToChannelResponsePart(part, knownToolCallIds);
if (converted) {
responseParts.push(converted);
}
}
} else if (response.text.trim()) {
responseParts.push(channelTextPartSchema.parse({
type: "text",
text: response.text,
}));
}
return responseParts;
}
function classifyRuntimeError(error: unknown): ChannelInvokeResponse["error"] {
const veryfrontError = fromError(error);
if (veryfrontError?.type === "no_ai_available") {
return { code: "provider_error", retryable: false };
}
if (veryfrontError?.type === "api" || veryfrontError?.type === "network") {
return { code: "provider_error", retryable: true };
}
return { code: "internal_error", retryable: true };
}
export async function executeChannelInvoke(
payload: ChannelInvokeRequest,
ctx: HandlerContext,
deps: ChannelInvokeDeps,
): Promise<ChannelInvokeResponse> {
await deps.ensureProjectDiscovery(ctx);
const agent = resolveChannelInvokeAgent(payload.agentConfigId, deps);
if (!agent) {
logger.error("Channel invoke could not resolve a runtime agent for the request", {
requestedAgentConfigId: payload.agentConfigId,
discoveredAgentIds: deps.getAllAgentIds(),
projectSlug: ctx.projectSlug,
projectId: ctx.projectId,
});
return {
ignored: false,
error: {
code: "internal_error",
retryable: false,
},
};
}
const messages = normalizeConversationHistoryForRuntime(payload.conversationHistory);
await agent.clearMemory();
try {
const result = await agent.generate({
input: messages,
context: {
requestId: payload.dispatchId,
dispatchId: payload.dispatchId,
conversationId: payload.conversationId,
projectId: payload.projectId,
agentConfigId: payload.agentConfigId,
channel: payload.inboundMessage,
},
...(payload.generation?.maxResponseTokens
? {
maxOutputTokens: payload.generation.maxResponseTokens,
}
: {}),
});
return ChannelInvokeResponseSchema.parse({
ignored: false,
responseParts: buildChannelResponseParts(result),
tokenUsage: result.usage
? {
inputTokens: result.usage.promptTokens,
outputTokens: result.usage.completionTokens,
totalTokens: result.usage.totalTokens,
}
: undefined,
});
} catch (error) {
logger.error("Channel invoke runtime execution failed", {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
projectSlug: ctx.projectSlug,
projectId: ctx.projectId,
dispatchId: payload.dispatchId,
});
return {
ignored: false,
error: classifyRuntimeError(error),
};
}
}
import { serverLogger } from "../../../../utils/index.js";
import type { HandlerContext } from "../../types.js";
const logger = serverLogger.component("api-wrapper");
/**
* Tracks in-flight and completed AI discovery per project+release.
*
* Key: `{projectSlug}:{releaseId}` for production, `{projectSlug}:preview` for preview.
* This ensures a new deployment triggers re-discovery of agents/tools.
*
* Using a Map<string, Promise> deduplicates concurrent requests and
* allows retry on failure (the key is deleted if discovery rejects).
*/
const discoveredProjects = new Map<string, Promise<void>>();
/** Build a discovery cache key that incorporates the release/version. */
function discoveryKey(ctx: HandlerContext): string {
const slug = ctx.projectSlug ?? ctx.projectDir;
const version = ctx.releaseId ?? "preview";
return `${slug}:${version}`;
}
/**
* Run AI discovery (agents, tools) for a project if not already done.
* Must be called within a runWithContext scope so the VFS can resolve
* the correct remote project files and the agent registry uses the
* correct project scope.
*/
export async function ensureProjectDiscovery(ctx: HandlerContext): Promise<void> {
const key = discoveryKey(ctx);
const existing = discoveredProjects.get(key);
if (existing) return existing;
const promise = (async () => {
const { discoverAll } = await import("../../../../discovery/index.js");
const { agentRegistry } = await import(
"../../../../agent/composition/composition.js"
);
const { toolRegistry } = await import("../../../../tool/registry.js");
// Clear stale entries for this project scope before re-discovery.
// This prevents agents/tools removed in a new release from lingering.
agentRegistry.clear();
toolRegistry.clear();
const result = await discoverAll({
baseDir: ctx.projectDir,
fsAdapter: ctx.adapter.fs,
verbose: false,
});
const logData = {
projectSlug: ctx.projectSlug,
releaseId: ctx.releaseId,
agents: result.agents.size,
tools: result.tools.size,
errors: result.errors.length,
};
if (result.agents.size === 0 && result.tools.size === 0) {
logger.warn("AI discovery found 0 agents and 0 tools", {
...logData,
errorMessages: result.errors.map((e) => e.error.message).slice(0, 5),
baseDir: ctx.projectDir,
});
} else {
logger.info("AI discovery completed", logData);
}
})();
discoveredProjects.set(key, promise);
try {
await promise;
} catch (error) {
// Allow retry on next request
discoveredProjects.delete(key);
logger.warn("AI discovery failed (will retry)", {
projectSlug: ctx.projectSlug,
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
});
}
}
import * as dntShim from "../../../../_dnt.shims.js";
import { BaseHandler } from "../response/base.js";
import type { HandlerContext, HandlerMetadata, HandlerPriority, HandlerResult } from "../types.js";
import {
type ChannelInvokeDeps,
ChannelInvokeRequestSchema,
defaultChannelInvokeDeps,
executeChannelInvoke,
verifyDispatchJws,
} from "../../../channels/invoke.js";
import {
HTTP_INTERNAL_SERVER_ERROR,
PRIORITY_MEDIUM_API,
} from "../../../utils/constants/index.js";
const DISPATCH_JWS_HEADER = "x-veryfront-dispatch-jws";
const MAX_DISPATCH_SIGNATURE_AGE_SECONDS = 60;
export class ChannelInvokeHandler extends BaseHandler {
metadata: HandlerMetadata = {
name: "ChannelInvokeHandler",
priority: PRIORITY_MEDIUM_API as HandlerPriority,
patterns: [{ pattern: "/channels/invoke", exact: true, method: "POST" }],
};
constructor(private readonly deps: ChannelInvokeDeps = defaultChannelInvokeDeps) {
super();
}
async handle(req: dntShim.Request, ctx: HandlerContext): Promise<HandlerResult> {
if (!this.shouldHandle(req, ctx)) {
return this.continue();
}
return this.withProxyContext(ctx, async () => {
const builder = this.createResponseBuilder(ctx)
.withCORS(req, ctx.securityConfig?.cors)
.withSecurity(ctx.securityConfig ?? undefined, req);
const publicKeyPem = ctx.adapter.env.get("CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY");
if (!publicKeyPem) {
this.logWarn("Missing CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY for channel invoke endpoint");
return this.respond(
builder.json(
{ error: "Channel dispatch verification is not configured" },
HTTP_INTERNAL_SERVER_ERROR,
),
);
}
const projectSlug = ctx.projectSlug;
if (!projectSlug) {
this.logWarn("Channel invoke request arrived without resolved project slug");
return this.respond(builder.json({ error: "Project context is unavailable" }, 400));
}
const dispatchJws = req.headers.get(DISPATCH_JWS_HEADER);
if (!dispatchJws) {
return this.respond(builder.json({ error: "Missing dispatch signature" }, 401));
}
const rawBody = await req.text();
try {
await verifyDispatchJws(dispatchJws, rawBody, {
audience: projectSlug,
expectedProjectId: ctx.projectId,
publicKeyPem,
maxAgeSeconds: MAX_DISPATCH_SIGNATURE_AGE_SECONDS,
});
} catch (error) {
this.logWarn("Channel invoke signature verification failed", {
error: error instanceof Error ? error.message : String(error),
projectSlug,
projectId: ctx.projectId,
});
return this.respond(builder.json({ error: "Invalid dispatch signature" }, 401));
}
let payload;
try {
payload = ChannelInvokeRequestSchema.parse(JSON.parse(rawBody));
} catch (error) {
this.logWarn("Channel invoke request validation failed", {
error: error instanceof Error ? error.message : String(error),
projectSlug,
projectId: ctx.projectId,
});
return this.respond(builder.json({ error: "Invalid channel invoke request" }, 400));
}
const response = await executeChannelInvoke(payload, ctx, this.deps);
return this.respond(builder.json(response, 200));
});
}
}
+3
-0

@@ -218,2 +218,4 @@ declare namespace _default {

"rehype-slug": string;
"rehype-raw": string;
"rehype-sanitize": string;
"rehype-stringify": string;

@@ -258,2 +260,3 @@ esbuild: string;

"@kreuzberg/wasm": string;
"#kreuzberg-wasm-glue": string;
};

@@ -260,0 +263,0 @@ namespace compilerOptions {

+6
-3
export default {
"name": "veryfront",
"version": "0.1.62",
"version": "0.1.63",
"license": "Apache-2.0",

@@ -228,2 +228,4 @@ "nodeModulesDir": "auto",

"rehype-slug": "npm:rehype-slug@6.0.0",
"rehype-raw": "npm:rehype-raw@7.0.0",
"rehype-sanitize": "npm:rehype-sanitize@6.0.0",
"rehype-stringify": "npm:rehype-stringify@10.0.1",

@@ -267,3 +269,4 @@ "esbuild": "npm:esbuild@0.20.2",

"tailwind-merge": "npm:tailwind-merge@2.6.0",
"@kreuzberg/wasm": "npm:@kreuzberg/wasm@4.4.2"
"@kreuzberg/wasm": "npm:@kreuzberg/wasm@4.4.2",
"#kreuzberg-wasm-glue": "npm:@kreuzberg/wasm@4.4.2/dist/pkg/kreuzberg_wasm.js"
},

@@ -296,3 +299,3 @@ "compilerOptions": {

"build:prepare": "deno run -A scripts/build/generate-integrations-module.ts && deno task generate && deno run -A scripts/build/prepare-framework-sources.ts",
"build": "deno task build:prepare && deno compile --allow-all --include src/platform/polyfills --include src/proxy/main.ts --include dist/framework-src --output ./bin/veryfront cli/main.ts",
"build": "deno task build:prepare && deno run -A scripts/build/compile-binary.ts --output ./bin/veryfront",
"build:npm": "deno run -A scripts/build/generate-integrations-module.ts && deno task generate && deno run -A scripts/build/build-npm-dnt.ts",

@@ -299,0 +302,0 @@ "release": "deno run -A scripts/release.ts",

@@ -1,1 +0,1 @@

{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/composition/composition.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CA2BnE;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzE;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,cAAc,GACrB;IAAE,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;CAAE,CAsDrD;AAID,cAAM,kBAAmB,SAAQ,oBAAoB,CAAC,KAAK,CAAC;CAAG;AAG/D,eAAO,MAAM,aAAa,oBAAuC,CAAC;AAElE,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAE5D;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAEtD;AAED,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AASD,wBAAgB,gBAAgB,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAQ5F"}
{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/composition/composition.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CA2BnE;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzE;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,cAAc,GACrB;IAAE,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;CAAE,CAsDrD;AAID,cAAM,kBAAmB,SAAQ,oBAAoB,CAAC,KAAK,CAAC;CAAG;AAG/D,eAAO,MAAM,aAAa,oBAAuC,CAAC;AAElE,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAE5D;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAEtD;AAED,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AAqBD,wBAAgB,gBAAgB,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAQ5F"}

@@ -94,5 +94,15 @@ /**

// binary FS, so they use globalThis bridges instead.
dntShim.dntGlobalThis.__vfGetAgent = getAgent;
dntShim.dntGlobalThis.__vfRegisterAgent = registerAgent;
dntShim.dntGlobalThis.__vfGetAllAgentIds = getAllAgentIds;
// Use Object.defineProperty to prevent accidental overwriting or enumeration.
for (const [key, value] of Object.entries({
__vfGetAgent: getAgent,
__vfRegisterAgent: registerAgent,
__vfGetAllAgentIds: getAllAgentIds,
})) {
Object.defineProperty(dntShim.dntGlobalThis, key, {
value,
writable: false,
enumerable: false,
configurable: false,
});
}
export function getAgentsAsTools(descriptions) {

@@ -99,0 +109,0 @@ const tools = {};

@@ -1,1 +0,1 @@

{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/src/agent/factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,KAAK,EACL,WAAW,EAKZ,MAAM,YAAY,CAAC;AA8CpB,wBAAgB,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,CA4LhD"}
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/src/agent/factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,KAAK,EACL,WAAW,EAKZ,MAAM,YAAY,CAAC;AA8CpB,wBAAgB,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,CAyMhD"}

@@ -121,3 +121,3 @@ import * as dntShim from "../../_dnt.shims.js";

generate(input) {
return withSpan("agent.factory.generate", () => runtime.generate(input.input, input.context, input.model), { "agent.id": id });
return withSpan("agent.factory.generate", () => runtime.generate(input.input, input.context, input.model, input.maxOutputTokens), { "agent.id": id });
},

@@ -138,3 +138,3 @@ stream(input) {

onChunk: input.onChunk,
}, input.model);
}, input.model, input.maxOutputTokens);
return createAgentStreamResult(stream);

@@ -156,3 +156,3 @@ }, { "agent.id": id, "agent.input_type": input.input ? "string" : "messages" });

const messages = body.messages ?? [];
const stream = await runtime.stream(messages, body.context, undefined, modelOverride);
const stream = await runtime.stream(messages, body.context, undefined, modelOverride, body.maxOutputTokens);
return new dntShim.Response(stream, { headers: STREAMING_HEADERS });

@@ -159,0 +159,0 @@ }, { "agent.id": id });

@@ -49,3 +49,3 @@ /**

*/
generate(input: string | Message[], context?: Record<string, unknown>, modelOverride?: string): Promise<AgentResponse>;
generate(input: string | Message[], context?: Record<string, unknown>, modelOverride?: string, maxOutputTokensOverride?: number): Promise<AgentResponse>;
/**

@@ -58,3 +58,3 @@ * Stream a response

onChunk?: (chunk: string) => void;
}, modelOverride?: string): Promise<ReadableStream<Uint8Array>>;
}, modelOverride?: string, maxOutputTokensOverride?: number): Promise<ReadableStream<Uint8Array>>;
/**

@@ -82,2 +82,3 @@ * Execute agent loop (with tool calling)

private computeMaxSteps;
private resolveMaxOutputTokens;
/**

@@ -84,0 +85,0 @@ * Get memory instance (for advanced use cases)

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/runtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,aAAa,EAGlB,KAAK,OAAO,EAEZ,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAKrB,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAe/D,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACpF,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAClG,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAqBxB;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CA6BxE;AAED,gEAAgE;AAChE,KAAK,iBAAiB,GAClB;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,MAAM,EAAE,GAAG,SAAS,EACvC,kBAAkB,EAAE,OAAO,GAC1B,iBAAiB,CAiBnB;AAkCD,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAS;IACnB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,MAAM,CAAuB;gBAEzB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW;IAS3C;;OAEG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,EACzB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,aAAa,CAAC;IAgCzB;;;OAGG;IACG,MAAM,CACV,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,CAAC,EAAE;QACV,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;QAC1C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;KACnC,EACD,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IAgFtC;;OAEG;YACW,gBAAgB;IAyN9B;;;;OAIG;YACW,yBAAyB;IAyNvC;;OAEG;YACW,eAAe;IAqC7B;;OAEG;YACW,mBAAmB;IAOjC;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;IACH,SAAS,IAAI,MAAM,CAAC,OAAO,CAAC;IAI5B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC;QAC9B,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IAIF;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CAGnC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/runtime/index.ts"],"names":[],"mappings":"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"}

@@ -135,3 +135,3 @@ /**

*/
async generate(input, context, modelOverride) {
async generate(input, context, modelOverride, maxOutputTokensOverride) {
const requestedModel = resolveConfiguredAgentModel(modelOverride || this.config.model);

@@ -157,3 +157,3 @@ const resolvedModelString = maybeUpgradeLocalModel(requestedModel);

const chain = new MiddlewareChain(this.config.middleware);
return chain.execute(agentContext, () => this.executeAgentLoop(systemPrompt, messages, resolvedModelString));
return chain.execute(agentContext, () => this.executeAgentLoop(systemPrompt, messages, resolvedModelString, maxOutputTokensOverride));
});

@@ -165,3 +165,3 @@ }

*/
async stream(messages, context, callbacks, modelOverride) {
async stream(messages, context, callbacks, modelOverride, maxOutputTokensOverride) {
const requestedModel = resolveConfiguredAgentModel(modelOverride || this.config.model);

@@ -210,3 +210,3 @@ // Auto-upgrade local/* to a cloud provider when API keys are available.

sendSSE(controller, encoder, { type: "text-start", id: textPartId });
await this.executeAgentLoopStreaming(systemPrompt, memoryMessages, controller, encoder, callbacks, textPartId, toolContext, resolvedModelString, languageModel);
await this.executeAgentLoopStreaming(systemPrompt, memoryMessages, controller, encoder, callbacks, textPartId, toolContext, resolvedModelString, languageModel, maxOutputTokensOverride);
sendSSE(controller, encoder, { type: "text-end", id: textPartId });

@@ -231,3 +231,3 @@ sendSSE(controller, encoder, { type: "message-finish" });

*/
async executeAgentLoop(systemPrompt, messages, modelString) {
async executeAgentLoop(systemPrompt, messages, modelString, maxOutputTokensOverride) {
return withSpan("agent.execution_loop", async (loopSpan) => {

@@ -271,3 +271,3 @@ const { maxAgentSteps } = getPlatformCapabilities();

tools: convertToolsToAISDK(tools),
maxOutputTokens: this.config.memory?.maxTokens ?? DEFAULT_MAX_TOKENS,
maxOutputTokens: this.resolveMaxOutputTokens(maxOutputTokensOverride),
temperature: DEFAULT_TEMPERATURE,

@@ -420,3 +420,3 @@ });

*/
async executeAgentLoopStreaming(systemPrompt, messages, controller, encoder, callbacks, textPartId, toolContext, modelString, resolvedModel) {
async executeAgentLoopStreaming(systemPrompt, messages, controller, encoder, callbacks, textPartId, toolContext, modelString, resolvedModel, maxOutputTokensOverride) {
const { maxAgentSteps } = getPlatformCapabilities();

@@ -453,3 +453,3 @@ const maxSteps = this.computeMaxSteps(maxAgentSteps);

tools: convertToolsToAISDK(tools),
maxOutputTokens: this.config.memory?.maxTokens ?? DEFAULT_MAX_TOKENS,
maxOutputTokens: this.resolveMaxOutputTokens(maxOutputTokensOverride),
temperature: DEFAULT_TEMPERATURE,

@@ -625,2 +625,10 @@ });

}
resolveMaxOutputTokens(maxOutputTokensOverride) {
if (typeof maxOutputTokensOverride === "number" &&
Number.isFinite(maxOutputTokensOverride) &&
maxOutputTokensOverride > 0) {
return Math.floor(maxOutputTokensOverride);
}
return this.config.memory?.maxTokens ?? DEFAULT_MAX_TOKENS;
}
/**

@@ -627,0 +635,0 @@ * Get memory instance (for advanced use cases)

@@ -73,2 +73,4 @@ /**************************

model?: ModelString;
/** Override the maximum model output tokens for this request. */
maxOutputTokens?: number;
}): Promise<AgentResponse>;

@@ -81,2 +83,4 @@ stream(input: {

model?: ModelString;
/** Override the maximum model output tokens for this request. */
maxOutputTokens?: number;
onToolCall?: (toolCall: ToolCall) => void;

@@ -83,0 +87,0 @@ onChunk?: (chunk: string) => void;

@@ -1,1 +0,1 @@

{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/src/agent/types.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/src/agent/types.ts"],"names":[],"mappings":"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"}

@@ -33,6 +33,6 @@ import { embed, embedMany } from "ai";

async embed(text) {
const value = queryPrefix + text;
if (!value.trim()) {
if (!text.trim()) {
throw new Error("Cannot embed an empty string");
}
const value = queryPrefix + text;
const result = await embed({ model, value });

@@ -39,0 +39,0 @@ return result.embedding;

@@ -461,3 +461,3 @@ import { z } from "zod";

}[];
name: "github" | "aws" | "twitter" | "zoom" | "anthropic" | "linear" | "gmail" | "calendar" | "sheets" | "drive" | "outlook" | "teams" | "sharepoint" | "onedrive" | "jira" | "confluence" | "bitbucket" | "slack" | "notion" | "figma" | "discord" | "gitlab" | "airtable" | "dropbox" | "hubspot" | "salesforce" | "asana" | "monday" | "intercom" | "freshdesk" | "mailchimp" | "shopify" | "quickbooks" | "xero" | "box" | "webex" | "trello" | "clickup" | "pipedrive" | "servicenow" | "supabase" | "neon" | "stripe" | "sentry" | "posthog" | "zendesk" | "docs-google" | "snowflake" | "mixpanel" | "twilio";
name: "github" | "aws" | "twitter" | "zoom" | "anthropic" | "linear" | "slack" | "gmail" | "calendar" | "sheets" | "drive" | "outlook" | "teams" | "sharepoint" | "onedrive" | "jira" | "confluence" | "bitbucket" | "notion" | "figma" | "discord" | "gitlab" | "airtable" | "dropbox" | "hubspot" | "salesforce" | "asana" | "monday" | "intercom" | "freshdesk" | "mailchimp" | "shopify" | "quickbooks" | "xero" | "box" | "webex" | "trello" | "clickup" | "pipedrive" | "servicenow" | "supabase" | "neon" | "stripe" | "sentry" | "posthog" | "zendesk" | "docs-google" | "snowflake" | "mixpanel" | "twilio";
displayName: string;

@@ -528,3 +528,3 @@ category?: string | undefined;

}[];
name: "github" | "aws" | "twitter" | "zoom" | "anthropic" | "linear" | "gmail" | "calendar" | "sheets" | "drive" | "outlook" | "teams" | "sharepoint" | "onedrive" | "jira" | "confluence" | "bitbucket" | "slack" | "notion" | "figma" | "discord" | "gitlab" | "airtable" | "dropbox" | "hubspot" | "salesforce" | "asana" | "monday" | "intercom" | "freshdesk" | "mailchimp" | "shopify" | "quickbooks" | "xero" | "box" | "webex" | "trello" | "clickup" | "pipedrive" | "servicenow" | "supabase" | "neon" | "stripe" | "sentry" | "posthog" | "zendesk" | "docs-google" | "snowflake" | "mixpanel" | "twilio";
name: "github" | "aws" | "twitter" | "zoom" | "anthropic" | "linear" | "slack" | "gmail" | "calendar" | "sheets" | "drive" | "outlook" | "teams" | "sharepoint" | "onedrive" | "jira" | "confluence" | "bitbucket" | "notion" | "figma" | "discord" | "gitlab" | "airtable" | "dropbox" | "hubspot" | "salesforce" | "asana" | "monday" | "intercom" | "freshdesk" | "mailchimp" | "shopify" | "quickbooks" | "xero" | "box" | "webex" | "trello" | "clickup" | "pipedrive" | "servicenow" | "supabase" | "neon" | "stripe" | "sentry" | "posthog" | "zendesk" | "docs-google" | "snowflake" | "mixpanel" | "twilio";
displayName: string;

@@ -531,0 +531,0 @@ category?: string | undefined;

@@ -23,7 +23,11 @@ import * as dntShim from "../../../_dnt.shims.js";

envReader?: EnvReader;
/** Optional authentication check — return true if the request is authenticated */
isAuthenticated?: (req: dntShim.Request) => boolean | Promise<boolean>;
}
export declare function createOAuthStatusHandler(config: OAuthServiceConfig, options?: OAuthStatusHandlerOptions): () => Promise<dntShim.Response>;
export declare function createOAuthStatusHandler(config: OAuthServiceConfig, options?: OAuthStatusHandlerOptions): (req: dntShim.Request) => Promise<dntShim.Response>;
export declare function createOAuthDisconnectHandler(config: OAuthServiceConfig, options?: {
tokenStore?: TokenStore;
}): () => Promise<dntShim.Response>;
/** Optional authentication check — return true if the request is authenticated */
isAuthenticated?: (req: dntShim.Request) => boolean | Promise<boolean>;
}): (req: dntShim.Request) => Promise<dntShim.Response>;
//# sourceMappingURL=init-handler.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"init-handler.d.ts","sourceRoot":"","sources":["../../../../src/src/oauth/handlers/init-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAElD,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK3F,MAAM,WAAW,uBAAuB;IACtC,oDAAoD;IACpD,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,uCAAuC;IACvC,WAAW,CAAC,EAAE,uBAAuB,CAAC;IAEtC,gFAAgF;IAChF,GAAG,CAAC,EAAE,iBAAiB,CAAC;IAExB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,uBAA4B,GACpC,MAAM,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAsCjC;AAED,MAAM,WAAW,yBAAyB;IACxC,oDAAoD;IACpD,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,yBAA8B,GACtC,MAAM,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAoBjC;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,UAAU,CAAA;CAAO,GACxC,MAAM,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAWjC"}
{"version":3,"file":"init-handler.d.ts","sourceRoot":"","sources":["../../../../src/src/oauth/handlers/init-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAElD,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK3F,MAAM,WAAW,uBAAuB;IACtC,oDAAoD;IACpD,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,uCAAuC;IACvC,WAAW,CAAC,EAAE,uBAAuB,CAAC;IAEtC,gFAAgF;IAChF,GAAG,CAAC,EAAE,iBAAiB,CAAC;IAExB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,uBAA4B,GACpC,MAAM,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAsCjC;AAED,MAAM,WAAW,yBAAyB;IACxC,oDAAoD;IACpD,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB,kFAAkF;IAClF,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxE;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,yBAA8B,GACtC,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAwBrD;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,kFAAkF;IAClF,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE,GACL,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAerD"}

@@ -40,3 +40,6 @@ import * as dntShim from "../../../_dnt.shims.js";

const envReader = options.envReader ?? getEnv;
return async function handler() {
return async function handler(req) {
if (options.isAuthenticated && !(await options.isAuthenticated(req))) {
return dntShim.Response.json({ error: "Unauthorized" }, { status: 401 });
}
const tokens = await tokenStore.getTokens(config.serviceId);

@@ -58,3 +61,6 @@ const isConnected = !!tokens?.accessToken;

const tokenStore = options.tokenStore ?? memoryTokenStore;
return async function handler() {
return async function handler(req) {
if (options.isAuthenticated && !(await options.isAuthenticated(req))) {
return dntShim.Response.json({ error: "Unauthorized" }, { status: 401 });
}
await tokenStore.clearTokens(config.serviceId);

@@ -61,0 +67,0 @@ return dntShim.Response.json({

@@ -1,1 +0,1 @@

{"version":3,"file":"opaque-deps.d.ts","sourceRoot":"","sources":["../../../../src/src/platform/compat/opaque-deps.ts"],"names":[],"mappings":"AA2BA,KAAK,YAAY,GAAG,GAAG,CAAC;AAExB,yEAAyE;AACzE,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC,CAE1D;AAED,8DAA8D;AAC9D,wBAAgB,oBAAoB,IAAI,OAAO,CAAC,YAAY,CAAC,CAK5D;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAC/C,YAAY,EAAE,CACZ,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnC,CAAC,CAWD"}
{"version":3,"file":"opaque-deps.d.ts","sourceRoot":"","sources":["../../../../src/src/platform/compat/opaque-deps.ts"],"names":[],"mappings":"AA2BA,KAAK,YAAY,GAAG,GAAG,CAAC;AAUxB,yEAAyE;AACzE,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC,CAE1D;AAED,8DAA8D;AAC9D,wBAAgB,oBAAoB,IAAI,OAAO,CAAC,YAAY,CAAC,CAK5D;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAC/C,YAAY,EAAE,CACZ,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnC,CAAC,CAkBD"}

@@ -18,3 +18,3 @@ /**

import * as dntShim from "../../../_dnt.shims.js";
import { isDeno } from "./runtime.js";
import { isDeno, isDenoCompiled } from "./runtime.js";
import { dynamicImport } from "./dynamic-import.js";

@@ -49,2 +49,11 @@ function resolve(pkg, version) {

const mod = await import("@kreuzberg/wasm");
if (isDenoCompiled) {
// Kreuzberg's initWasm() internally uses a computed dynamic import() to
// load the WASM glue module (kreuzberg_wasm.js). deno compile cannot
// trace computed import() paths, so the glue module is absent from the
// binary's embedded module graph. Pre-importing it here populates Deno's
// in-process module cache so the subsequent import() inside initWasm()
// resolves from cache instead of hitting the missing file.
await import("@kreuzberg/wasm/dist/pkg/kreuzberg_wasm.js");
}
await mod.initWasm?.();

@@ -51,0 +60,0 @@ return mod;

@@ -1,1 +0,1 @@

{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/src/prompt/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGvD,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA0BnD"}
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/src/prompt/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAIvD,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA0BnD"}
import { createError, toError } from "../errors/veryfront-error.js";
import { COMMON_BLOCKED_PATTERNS } from "../agent/middleware/security/validator.js";
export function prompt(config) {

@@ -27,7 +28,14 @@ const id = config.id ?? generatePromptId();

}
function sanitizeVariableValue(value) {
let sanitized = value;
for (const pattern of COMMON_BLOCKED_PATTERNS.promptInjection) {
sanitized = sanitized.replace(pattern, "");
}
return sanitized;
}
function interpolateVariables(template, variables) {
return template.replace(/\{(\w+)\}/g, (match, key) => {
const value = variables[key];
return value != null ? String(value) : match;
return value != null ? sanitizeVariableValue(String(value)) : match;
});
}

@@ -1,1 +0,1 @@

{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../../../src/src/react/components/ai/markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iCAAiC;IACjC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,KAAK,CAAC,SAAS,CAAC;CAC9D;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAkJD,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,SAAS,EACT,aAAoB,EACpB,eAAe,GAChB,EAAE,aAAa,GAAG,KAAK,CAAC,YAAY,CA+FpC"}
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../../../src/src/react/components/ai/markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iCAAiC;IACjC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,KAAK,CAAC,SAAS,CAAC;CAC9D;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAmJD,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,SAAS,EACT,aAAoB,EACpB,eAAe,GAChB,EAAE,aAAa,GAAG,KAAK,CAAC,YAAY,CA+FpC"}

@@ -6,6 +6,6 @@ import * as React from "react";

import { RichCodeBlock } from "./chat/components/code-block.js";
const ESM_REACT_MARKDOWN = "https://esm.sh/react-markdown@9?external=react&target=es2022";
const ESM_REMARK_GFM = "https://esm.sh/remark-gfm@4?target=es2022";
const ESM_REHYPE_HIGHLIGHT = "https://esm.sh/rehype-highlight@7?target=es2022";
const ESM_MERMAID = "https://esm.sh/mermaid@11";
const ESM_REACT_MARKDOWN = "https://esm.sh/react-markdown@9.0.3?external=react&target=es2022&pin=v135";
const ESM_REMARK_GFM = "https://esm.sh/remark-gfm@4.0.1?target=es2022&pin=v135";
const ESM_REHYPE_HIGHLIGHT = "https://esm.sh/rehype-highlight@7.0.2?target=es2022&pin=v135";
const ESM_MERMAID = "https://esm.sh/mermaid@11.4.1?pin=v135";
const dynamicImport = new Function("url", "return import(url)");

@@ -12,0 +12,0 @@ // deno-lint-ignore no-explicit-any

@@ -1,1 +0,1 @@

{"version":3,"file":"framework-candidates.generated.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/framework-candidates.generated.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAkkKjD,CAAC"}
{"version":3,"file":"framework-candidates.generated.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/framework-candidates.generated.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAmkKjD,CAAC"}

@@ -79,3 +79,3 @@ import { escapeHtml } from "../../../utils/html-escape.js";

<script type="module">
import mermaid from 'https://esm.sh/mermaid@11';
import mermaid from 'https://esm.sh/mermaid@11.4.1?pin=v135';

@@ -82,0 +82,0 @@ function getMermaidTheme() {

@@ -1,1 +0,1 @@

{"version":3,"file":"api-handler-wrapper.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/api/api-handler-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,aAAa,EACd,MAAM,gBAAgB,CAAC;AAqGxB,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAiE;IAChF,OAAO,CAAC,WAAW,CAA8B;IAEjD,QAAQ,EAAE,eAAe,CAGvB;gBAGA,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,uCAAuC,EAAE,cAAc;IAOnE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA6C/E,OAAO,CAAC,iBAAiB;CA4D1B"}
{"version":3,"file":"api-handler-wrapper.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/api/api-handler-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,aAAa,EACd,MAAM,gBAAgB,CAAC;AAiBxB,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAiE;IAChF,OAAO,CAAC,WAAW,CAA8B;IAEjD,QAAQ,EAAE,eAAe,CAGvB;gBAGA,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,uCAAuC,EAAE,cAAc;IAOnE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA6C/E,OAAO,CAAC,iBAAiB;CA4D1B"}

@@ -5,76 +5,3 @@ import { BaseHandler } from "../../response/base.js";

import { withSpan } from "../../../../observability/tracing/otlp-setup.js";
import { serverLogger } from "../../../../utils/index.js";
const logger = serverLogger.component("api-wrapper");
/**
* Tracks in-flight and completed AI discovery per project+release.
*
* Key: `{projectSlug}:{releaseId}` for production, `{projectSlug}:preview` for preview.
* This ensures a new deployment triggers re-discovery of agents/tools.
*
* Using a Map<string, Promise> deduplicates concurrent requests and
* allows retry on failure (the key is deleted if discovery rejects).
*/
const discoveredProjects = new Map();
/** Build a discovery cache key that incorporates the release/version. */
function discoveryKey(ctx) {
const slug = ctx.projectSlug ?? ctx.projectDir;
const version = ctx.releaseId ?? "preview";
return `${slug}:${version}`;
}
/**
* Run AI discovery (agents, tools) for a project if not already done.
* Must be called within a runWithContext scope so the VFS can resolve
* the correct remote project files and the agent registry uses the
* correct project scope.
*/
async function ensureProjectDiscovery(ctx) {
const key = discoveryKey(ctx);
const existing = discoveredProjects.get(key);
if (existing)
return existing;
const promise = (async () => {
const { discoverAll } = await import("../../../../discovery/index.js");
const { agentRegistry } = await import("../../../../agent/composition/composition.js");
const { toolRegistry } = await import("../../../../tool/registry.js");
// Clear stale entries for this project scope before re-discovery.
// This prevents agents/tools removed in a new release from lingering.
agentRegistry.clear();
toolRegistry.clear();
const result = await discoverAll({
baseDir: ctx.projectDir,
fsAdapter: ctx.adapter.fs,
verbose: false,
});
const logData = {
projectSlug: ctx.projectSlug,
releaseId: ctx.releaseId,
agents: result.agents.size,
tools: result.tools.size,
errors: result.errors.length,
};
if (result.agents.size === 0 && result.tools.size === 0) {
logger.warn("AI discovery found 0 agents and 0 tools", {
...logData,
errorMessages: result.errors.map((e) => e.error.message).slice(0, 5),
baseDir: ctx.projectDir,
});
}
else {
logger.info("AI discovery completed", logData);
}
})();
discoveredProjects.set(key, promise);
try {
await promise;
}
catch (error) {
// Allow retry on next request
discoveredProjects.delete(key);
logger.warn("AI discovery failed (will retry)", {
projectSlug: ctx.projectSlug,
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
});
}
}
import { ensureProjectDiscovery } from "./project-discovery.js";
export class ApiHandlerWrapper extends BaseHandler {

@@ -81,0 +8,0 @@ projectDir;

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAQlD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAwF7D,OAAO,EAAE,qBAAqB,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAMtF,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,oFAAoF;IACpF,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uGAAuG;IACvG,kBAAkB,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;CAC/C;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,IAAI,GAAE,qBAAsC,GAC3C,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAuanF;AAGD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAQlD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAyF7D,OAAO,EAAE,qBAAqB,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAMtF,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,oFAAoF;IACpF,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uGAAuG;IACvG,kBAAkB,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;CAC/C;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,IAAI,GAAE,qBAAsC,GAC3C,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAwanF;AAGD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC"}

@@ -45,2 +45,3 @@ /**

import { OpenAPIDocsHandler } from "../handlers/request/openapi-docs.handler.js";
import { ChannelInvokeHandler } from "../handlers/request/channel-invoke.handler.js";
import { DevDashboardHandler } from "../handlers/dev/dashboard/index.js";

@@ -127,2 +128,3 @@ import { ProjectsHandler } from "../handlers/dev/projects/index.js";

new OpenAPIDocsHandler(),
new ChannelInvokeHandler(),
new DevDashboardHandler(),

@@ -129,0 +131,0 @@ new ProjectsHandler(),

@@ -1,1 +0,1 @@

{"version":3,"file":"md-compiler.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/md/compiler/md-compiler.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,6BAA6B,CAAC;AAoDrC,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,GAAE,iBAA4B,EACrC,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,CAAC,gBAAgB,CAAC,CA2E3B"}
{"version":3,"file":"md-compiler.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/md/compiler/md-compiler.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,6BAA6B,CAAC;AAoDrC,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,GAAE,iBAA4B,EACrC,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,CAAC,gBAAgB,CAAC,CAmG3B"}

@@ -8,2 +8,4 @@ import { unified } from "unified";

import rehypeSlug from "rehype-slug";
import rehypeRaw from "rehype-raw";
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
import rehypeStringify from "rehype-stringify";

@@ -66,7 +68,29 @@ import { visit } from "unist-util-visit";

.use(rehypeSlug);
// Parse raw HTML nodes into proper elements before sanitizing.
pipeline.use(rehypeRaw);
// Add node positions after rehype-raw so attributes survive re-parsing.
if (studioEmbed && filePath) {
pipeline.use(rehypeNodePositions, { filePath });
}
// Extend the sanitize schema in studio embed mode to preserve
// data-node-* attributes used for element-to-source mapping.
const sanitizeSchema = studioEmbed
? {
...defaultSchema,
attributes: {
...defaultSchema.attributes,
"*": [
...(defaultSchema.attributes?.["*"] ?? []),
"data-node-file",
"data-node-name",
"data-node-line",
"data-node-column",
"data-node-source",
],
},
}
: defaultSchema;
const result = await pipeline
.use(rehypeStringify, { allowDangerousHtml: true })
.use(rehypeSanitize, sanitizeSchema)
.use(rehypeStringify)
.process(body);

@@ -73,0 +97,0 @@ const html = String(result);

{
"name": "veryfront",
"version": "0.1.62",
"version": "0.1.63",
"description": "The simplest way to build AI-powered apps",

@@ -154,2 +154,4 @@ "keywords": [

"rehype-highlight": "7.0.2",
"rehype-raw": "7.0.0",
"rehype-sanitize": "6.0.0",
"rehype-slug": "6.0.0",

@@ -156,0 +158,0 @@ "rehype-starry-night": "2.2.0",

export default {
"name": "veryfront",
"version": "0.1.62",
"version": "0.1.63",
"license": "Apache-2.0",

@@ -228,2 +228,4 @@ "nodeModulesDir": "auto",

"rehype-slug": "npm:rehype-slug@6.0.0",
"rehype-raw": "npm:rehype-raw@7.0.0",
"rehype-sanitize": "npm:rehype-sanitize@6.0.0",
"rehype-stringify": "npm:rehype-stringify@10.0.1",

@@ -267,3 +269,4 @@ "esbuild": "npm:esbuild@0.20.2",

"tailwind-merge": "npm:tailwind-merge@2.6.0",
"@kreuzberg/wasm": "npm:@kreuzberg/wasm@4.4.2"
"@kreuzberg/wasm": "npm:@kreuzberg/wasm@4.4.2",
"#kreuzberg-wasm-glue": "npm:@kreuzberg/wasm@4.4.2/dist/pkg/kreuzberg_wasm.js"
},

@@ -296,3 +299,3 @@ "compilerOptions": {

"build:prepare": "deno run -A scripts/build/generate-integrations-module.ts && deno task generate && deno run -A scripts/build/prepare-framework-sources.ts",
"build": "deno task build:prepare && deno compile --allow-all --include src/platform/polyfills --include src/proxy/main.ts --include dist/framework-src --output ./bin/veryfront cli/main.ts",
"build": "deno task build:prepare && deno run -A scripts/build/compile-binary.ts --output ./bin/veryfront",
"build:npm": "deno run -A scripts/build/generate-integrations-module.ts && deno task generate && deno run -A scripts/build/build-npm-dnt.ts",

@@ -299,0 +302,0 @@ "release": "deno run -A scripts/release.ts",

@@ -152,5 +152,17 @@ /**

// binary FS, so they use globalThis bridges instead.
(dntShim.dntGlobalThis as Record<string, unknown>).__vfGetAgent = getAgent;
(dntShim.dntGlobalThis as Record<string, unknown>).__vfRegisterAgent = registerAgent;
(dntShim.dntGlobalThis as Record<string, unknown>).__vfGetAllAgentIds = getAllAgentIds;
// Use Object.defineProperty to prevent accidental overwriting or enumeration.
for (
const [key, value] of Object.entries({
__vfGetAgent: getAgent,
__vfRegisterAgent: registerAgent,
__vfGetAllAgentIds: getAllAgentIds,
})
) {
Object.defineProperty(dntShim.dntGlobalThis, key, {
value,
writable: false,
enumerable: false,
configurable: false,
});
}

@@ -157,0 +169,0 @@ export function getAgentsAsTools(descriptions?: Record<string, string>): Record<string, Tool> {

@@ -163,3 +163,3 @@ import * as dntShim from "../../_dnt.shims.js";

"agent.factory.generate",
() => runtime.generate(input.input, input.context, input.model),
() => runtime.generate(input.input, input.context, input.model, input.maxOutputTokens),
{ "agent.id": id },

@@ -183,6 +183,12 @@ );

const stream = await runtime.stream(inputMessages, input.context, {
onToolCall: input.onToolCall,
onChunk: input.onChunk,
}, input.model);
const stream = await runtime.stream(
inputMessages,
input.context,
{
onToolCall: input.onToolCall,
onChunk: input.onChunk,
},
input.model,
input.maxOutputTokens,
);

@@ -203,2 +209,3 @@ return createAgentStreamResult(stream);

model?: string;
maxOutputTokens?: number;
} = await request.json();

@@ -222,3 +229,9 @@

const messages = body.messages ?? [];
const stream = await runtime.stream(messages, body.context, undefined, modelOverride);
const stream = await runtime.stream(
messages,
body.context,
undefined,
modelOverride,
body.maxOutputTokens,
);

@@ -225,0 +238,0 @@ return new dntShim.Response(stream, { headers: STREAMING_HEADERS });

@@ -197,2 +197,3 @@ /**

modelOverride?: string,
maxOutputTokensOverride?: number,
): Promise<AgentResponse> {

@@ -225,3 +226,9 @@ const requestedModel = resolveConfiguredAgentModel(modelOverride || this.config.model);

agentContext,
() => this.executeAgentLoop(systemPrompt, messages, resolvedModelString),
() =>
this.executeAgentLoop(
systemPrompt,
messages,
resolvedModelString,
maxOutputTokensOverride,
),
);

@@ -243,2 +250,3 @@ });

modelOverride?: string,
maxOutputTokensOverride?: number,
): Promise<ReadableStream<Uint8Array>> {

@@ -306,2 +314,3 @@ const requestedModel = resolveConfiguredAgentModel(modelOverride || this.config.model);

languageModel,
maxOutputTokensOverride,
);

@@ -332,2 +341,3 @@

modelString?: string,
maxOutputTokensOverride?: number,
): Promise<AgentResponse> {

@@ -381,3 +391,3 @@ return withSpan("agent.execution_loop", async (loopSpan) => {

tools: convertToolsToAISDK(tools),
maxOutputTokens: this.config.memory?.maxTokens ?? DEFAULT_MAX_TOKENS,
maxOutputTokens: this.resolveMaxOutputTokens(maxOutputTokensOverride),
temperature: DEFAULT_TEMPERATURE,

@@ -565,2 +575,3 @@ });

resolvedModel?: LanguageModel,
maxOutputTokensOverride?: number,
): Promise<AgentResponse> {

@@ -607,3 +618,3 @@ const { maxAgentSteps } = getPlatformCapabilities();

tools: convertToolsToAISDK(tools),
maxOutputTokens: this.config.memory?.maxTokens ?? DEFAULT_MAX_TOKENS,
maxOutputTokens: this.resolveMaxOutputTokens(maxOutputTokensOverride),
temperature: DEFAULT_TEMPERATURE,

@@ -829,2 +840,14 @@ });

private resolveMaxOutputTokens(maxOutputTokensOverride?: number): number {
if (
typeof maxOutputTokensOverride === "number" &&
Number.isFinite(maxOutputTokensOverride) &&
maxOutputTokensOverride > 0
) {
return Math.floor(maxOutputTokensOverride);
}
return this.config.memory?.maxTokens ?? DEFAULT_MAX_TOKENS;
}
/**

@@ -831,0 +854,0 @@ * Get memory instance (for advanced use cases)

@@ -137,2 +137,4 @@ /**************************

model?: ModelString;
/** Override the maximum model output tokens for this request. */
maxOutputTokens?: number;
}): Promise<AgentResponse>;

@@ -146,2 +148,4 @@

model?: ModelString;
/** Override the maximum model output tokens for this request. */
maxOutputTokens?: number;
onToolCall?: (toolCall: ToolCall) => void;

@@ -148,0 +152,0 @@ onChunk?: (chunk: string) => void;

@@ -38,6 +38,6 @@ import { embed, embedMany } from "ai";

async embed(text: string): Promise<number[]> {
const value = queryPrefix + text;
if (!value.trim()) {
if (!text.trim()) {
throw new Error("Cannot embed an empty string");
}
const value = queryPrefix + text;
const result = await embed({ model, value });

@@ -44,0 +44,0 @@ return result.embedding;

@@ -80,2 +80,5 @@ import * as dntShim from "../../../_dnt.shims.js";

envReader?: EnvReader;
/** Optional authentication check — return true if the request is authenticated */
isAuthenticated?: (req: dntShim.Request) => boolean | Promise<boolean>;
}

@@ -86,7 +89,11 @@

options: OAuthStatusHandlerOptions = {},
): () => Promise<dntShim.Response> {
): (req: dntShim.Request) => Promise<dntShim.Response> {
const tokenStore = options.tokenStore ?? memoryTokenStore;
const envReader = options.envReader ?? getEnv;
return async function handler(): Promise<dntShim.Response> {
return async function handler(req: dntShim.Request): Promise<dntShim.Response> {
if (options.isAuthenticated && !(await options.isAuthenticated(req))) {
return dntShim.Response.json({ error: "Unauthorized" }, { status: 401 });
}
const tokens = await tokenStore.getTokens(config.serviceId);

@@ -111,7 +118,15 @@

config: OAuthServiceConfig,
options: { tokenStore?: TokenStore } = {},
): () => Promise<dntShim.Response> {
options: {
tokenStore?: TokenStore;
/** Optional authentication check — return true if the request is authenticated */
isAuthenticated?: (req: dntShim.Request) => boolean | Promise<boolean>;
} = {},
): (req: dntShim.Request) => Promise<dntShim.Response> {
const tokenStore = options.tokenStore ?? memoryTokenStore;
return async function handler(): Promise<dntShim.Response> {
return async function handler(req: dntShim.Request): Promise<dntShim.Response> {
if (options.isAuthenticated && !(await options.isAuthenticated(req))) {
return dntShim.Response.json({ error: "Unauthorized" }, { status: 401 });
}
await tokenStore.clearTokens(config.serviceId);

@@ -118,0 +133,0 @@

@@ -20,3 +20,3 @@ /**

import { isDeno } from "./runtime.js";
import { isDeno, isDenoCompiled } from "./runtime.js";
import { dynamicImport } from "./dynamic-import.js";

@@ -31,2 +31,10 @@

type KreuzbergModule = {
initWasm?: () => Promise<void>;
extractBytes: (
data: Uint8Array,
mimeType: string,
) => Promise<{ content: string }>;
};
/** Lazily import `@huggingface/transformers` (+ onnxruntime, ~500MB). */

@@ -62,5 +70,12 @@ export function importTransformers(): Promise<OpaqueModule> {

// Regular import — visible to deno compile, resolved via deno.json import map
const mod = await import("@kreuzberg/wasm") as unknown as
& { initWasm?: () => Promise<void> }
& { extractBytes: (data: Uint8Array, mimeType: string) => Promise<{ content: string }> };
const mod = await import("@kreuzberg/wasm") as unknown as KreuzbergModule;
if (isDenoCompiled) {
// Kreuzberg's initWasm() internally uses a computed dynamic import() to
// load the WASM glue module (kreuzberg_wasm.js). deno compile cannot
// trace computed import() paths, so the glue module is absent from the
// binary's embedded module graph. Pre-importing it here populates Deno's
// in-process module cache so the subsequent import() inside initWasm()
// resolves from cache instead of hitting the missing file.
await import("@kreuzberg/wasm/dist/pkg/kreuzberg_wasm.js");
}
await mod.initWasm?.();

@@ -67,0 +82,0 @@ return mod;

import type { Prompt, PromptConfig } from "./types.js";
import { createError, toError } from "../errors/veryfront-error.js";
import { COMMON_BLOCKED_PATTERNS } from "../agent/middleware/security/validator.js";

@@ -38,2 +39,10 @@ export function prompt(config: PromptConfig): Prompt {

function sanitizeVariableValue(value: string): string {
let sanitized = value;
for (const pattern of COMMON_BLOCKED_PATTERNS.promptInjection) {
sanitized = sanitized.replace(pattern, "");
}
return sanitized;
}
function interpolateVariables(

@@ -45,4 +54,4 @@ template: string,

const value = variables[key];
return value != null ? String(value) : match;
return value != null ? sanitizeVariableValue(String(value)) : match;
});
}

@@ -24,6 +24,7 @@ import * as React from "react";

const ESM_REACT_MARKDOWN = "https://esm.sh/react-markdown@9?external=react&target=es2022";
const ESM_REMARK_GFM = "https://esm.sh/remark-gfm@4?target=es2022";
const ESM_REHYPE_HIGHLIGHT = "https://esm.sh/rehype-highlight@7?target=es2022";
const ESM_MERMAID = "https://esm.sh/mermaid@11";
const ESM_REACT_MARKDOWN =
"https://esm.sh/react-markdown@9.0.3?external=react&target=es2022&pin=v135";
const ESM_REMARK_GFM = "https://esm.sh/remark-gfm@4.0.1?target=es2022&pin=v135";
const ESM_REHYPE_HIGHLIGHT = "https://esm.sh/rehype-highlight@7.0.2?target=es2022&pin=v135";
const ESM_MERMAID = "https://esm.sh/mermaid@11.4.1?pin=v135";

@@ -30,0 +31,0 @@ const dynamicImport = new Function("url", "return import(url)") as (url: string) => Promise<any>;

@@ -121,3 +121,3 @@ /**

<script type="module">
import mermaid from 'https://esm.sh/mermaid@11';
import mermaid from 'https://esm.sh/mermaid@11.4.1?pin=v135';

@@ -124,0 +124,0 @@ function getMermaidTheme() {

@@ -12,6 +12,4 @@ import * as dntShim from "../../../../../_dnt.shims.js";

import { withSpan } from "../../../../observability/tracing/otlp-setup.js";
import { serverLogger } from "../../../../utils/index.js";
import { ensureProjectDiscovery } from "./project-discovery.js";
const logger = serverLogger.component("api-wrapper");
type FsWrapper = {

@@ -28,84 +26,2 @@ isMultiProjectMode?: () => boolean;

/**
* Tracks in-flight and completed AI discovery per project+release.
*
* Key: `{projectSlug}:{releaseId}` for production, `{projectSlug}:preview` for preview.
* This ensures a new deployment triggers re-discovery of agents/tools.
*
* Using a Map<string, Promise> deduplicates concurrent requests and
* allows retry on failure (the key is deleted if discovery rejects).
*/
const discoveredProjects = new Map<string, Promise<void>>();
/** Build a discovery cache key that incorporates the release/version. */
function discoveryKey(ctx: HandlerContext): string {
const slug = ctx.projectSlug ?? ctx.projectDir;
const version = ctx.releaseId ?? "preview";
return `${slug}:${version}`;
}
/**
* Run AI discovery (agents, tools) for a project if not already done.
* Must be called within a runWithContext scope so the VFS can resolve
* the correct remote project files and the agent registry uses the
* correct project scope.
*/
async function ensureProjectDiscovery(ctx: HandlerContext): Promise<void> {
const key = discoveryKey(ctx);
const existing = discoveredProjects.get(key);
if (existing) return existing;
const promise = (async () => {
const { discoverAll } = await import("../../../../discovery/index.js");
const { agentRegistry } = await import(
"../../../../agent/composition/composition.js"
);
const { toolRegistry } = await import("../../../../tool/registry.js");
// Clear stale entries for this project scope before re-discovery.
// This prevents agents/tools removed in a new release from lingering.
agentRegistry.clear();
toolRegistry.clear();
const result = await discoverAll({
baseDir: ctx.projectDir,
fsAdapter: ctx.adapter.fs,
verbose: false,
});
const logData = {
projectSlug: ctx.projectSlug,
releaseId: ctx.releaseId,
agents: result.agents.size,
tools: result.tools.size,
errors: result.errors.length,
};
if (result.agents.size === 0 && result.tools.size === 0) {
logger.warn("AI discovery found 0 agents and 0 tools", {
...logData,
errorMessages: result.errors.map((e) => e.error.message).slice(0, 5),
baseDir: ctx.projectDir,
});
} else {
logger.info("AI discovery completed", logData);
}
})();
discoveredProjects.set(key, promise);
try {
await promise;
} catch (error) {
// Allow retry on next request
discoveredProjects.delete(key);
logger.warn("AI discovery failed (will retry)", {
projectSlug: ctx.projectSlug,
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
});
}
}
export class ApiHandlerWrapper extends BaseHandler {

@@ -112,0 +28,0 @@ private projectDir: string;

@@ -56,2 +56,3 @@ /**

import { OpenAPIDocsHandler } from "../handlers/request/openapi-docs.handler.js";
import { ChannelInvokeHandler } from "../handlers/request/channel-invoke.handler.js";
import { DevDashboardHandler } from "../handlers/dev/dashboard/index.js";

@@ -208,2 +209,3 @@ import { ProjectsHandler } from "../handlers/dev/projects/index.js";

new OpenAPIDocsHandler(),
new ChannelInvokeHandler(),
new DevDashboardHandler(),

@@ -210,0 +212,0 @@ new ProjectsHandler(),

@@ -8,2 +8,4 @@ import { unified } from "unified";

import rehypeSlug from "rehype-slug";
import rehypeRaw from "rehype-raw";
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
import rehypeStringify from "rehype-stringify";

@@ -102,2 +104,6 @@ import { visit } from "unist-util-visit";

// Parse raw HTML nodes into proper elements before sanitizing.
pipeline.use(rehypeRaw);
// Add node positions after rehype-raw so attributes survive re-parsing.
if (studioEmbed && filePath) {

@@ -107,4 +113,24 @@ pipeline.use(rehypeNodePositions, { filePath });

// Extend the sanitize schema in studio embed mode to preserve
// data-node-* attributes used for element-to-source mapping.
const sanitizeSchema = studioEmbed
? {
...defaultSchema,
attributes: {
...defaultSchema.attributes,
"*": [
...(defaultSchema.attributes?.["*"] ?? []),
"data-node-file",
"data-node-name",
"data-node-line",
"data-node-column",
"data-node-source",
],
},
}
: defaultSchema;
const result = await pipeline
.use(rehypeStringify, { allowDangerousHtml: true })
.use(rehypeSanitize, sanitizeSchema)
.use(rehypeStringify)
.process(body);

@@ -111,0 +137,0 @@ const html = String(result);

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display