forgeos 0.1.0-alpha.2 → 0.1.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +38 -3
- package/CHANGELOG.md +29 -0
- package/README.md +25 -10
- package/package.json +8 -5
- package/src/forge/_generated/actionSubscriptions.json +2 -2
- package/src/forge/_generated/actionSubscriptions.ts +3 -3
- package/src/forge/_generated/agentAdapterManifest.json +2 -2
- package/src/forge/_generated/agentAdapterManifest.ts +3 -3
- package/src/forge/_generated/agentContract.json +2 -2
- package/src/forge/_generated/agentContract.ts +183 -50
- package/src/forge/_generated/agentQuickstart.md +3 -1
- package/src/forge/_generated/agentTools.json +2 -0
- package/src/forge/_generated/agentTools.md +16 -0
- package/src/forge/_generated/agentTools.ts +12 -0
- package/src/forge/_generated/aiContext.ts +67 -1
- package/src/forge/_generated/aiModels.json +2 -2
- package/src/forge/_generated/aiModels.ts +17 -1
- package/src/forge/_generated/aiProviders.json +1 -1
- package/src/forge/_generated/aiProviders.ts +1 -1
- package/src/forge/_generated/aiRegistry.json +2 -2
- package/src/forge/_generated/aiRegistry.ts +7 -5
- package/src/forge/_generated/api.json +2 -2
- package/src/forge/_generated/api.ts +1 -1
- package/src/forge/_generated/appGraph.json +2 -2
- package/src/forge/_generated/appGraph.ts +512 -260
- package/src/forge/_generated/appMap.md +21 -1
- package/src/forge/_generated/artifactManifest.json +2 -2
- package/src/forge/_generated/artifactManifest.ts +2 -2
- package/src/forge/_generated/authClaims.json +1 -1
- package/src/forge/_generated/authClaims.ts +1 -1
- package/src/forge/_generated/authConfig.json +1 -1
- package/src/forge/_generated/authConfig.ts +1 -1
- package/src/forge/_generated/authContext.ts +1 -1
- package/src/forge/_generated/authRegistry.json +1 -1
- package/src/forge/_generated/authRegistry.ts +1 -1
- package/src/forge/_generated/buildInfo.json +2 -2
- package/src/forge/_generated/buildInfo.ts +4 -4
- package/src/forge/_generated/capabilityMap.json +2 -2
- package/src/forge/_generated/capabilityMap.md +1 -1
- package/src/forge/_generated/capabilityMap.ts +2 -2
- package/src/forge/_generated/client.ts +1 -1
- package/src/forge/_generated/clientApi.ts +1 -1
- package/src/forge/_generated/clientManifest.json +2 -2
- package/src/forge/_generated/clientManifest.ts +3 -3
- package/src/forge/_generated/clientTypes.ts +1 -1
- package/src/forge/_generated/configRegistry.json +1 -1
- package/src/forge/_generated/configRegistry.ts +1 -1
- package/src/forge/_generated/dataGraph.json +2 -2
- package/src/forge/_generated/dataGraph.ts +3 -3
- package/src/forge/_generated/db.json +1 -1
- package/src/forge/_generated/db.ts +1 -1
- package/src/forge/_generated/dbSecurityManifest.json +1 -1
- package/src/forge/_generated/dbSecurityManifest.ts +1 -1
- package/src/forge/_generated/dbSessionContext.json +1 -1
- package/src/forge/_generated/dbSessionContext.ts +1 -1
- package/src/forge/_generated/deployManifest.json +2 -2
- package/src/forge/_generated/deployManifest.ts +7 -7
- package/src/forge/_generated/devManifest.json +2 -2
- package/src/forge/_generated/devManifest.ts +18 -3
- package/src/forge/_generated/envSchema.json +1 -1
- package/src/forge/_generated/envSchema.ts +1 -1
- package/src/forge/_generated/frontendGraph.json +1 -1
- package/src/forge/_generated/frontendGraph.ts +1 -1
- package/src/forge/_generated/importGuards.json +1 -1
- package/src/forge/_generated/importGuards.ts +1 -1
- package/src/forge/_generated/index.ts +2 -1
- package/src/forge/_generated/liveProductionManifest.json +1 -1
- package/src/forge/_generated/liveProductionManifest.ts +1 -1
- package/src/forge/_generated/liveProtocol.json +1 -1
- package/src/forge/_generated/liveProtocol.ts +1 -1
- package/src/forge/_generated/liveQueryRegistry.json +2 -2
- package/src/forge/_generated/liveQueryRegistry.ts +3 -3
- package/src/forge/_generated/liveTransportConfig.json +1 -1
- package/src/forge/_generated/liveTransportConfig.ts +1 -1
- package/src/forge/_generated/makeRegistry.json +2 -2
- package/src/forge/_generated/makeRegistry.ts +16 -2
- package/src/forge/_generated/makeTemplates.json +2 -2
- package/src/forge/_generated/makeTemplates.ts +6 -1
- package/src/forge/_generated/mockMap.json +1 -1
- package/src/forge/_generated/mockMap.ts +1 -1
- package/src/forge/_generated/operationPlaybooks.md +34 -14
- package/src/forge/_generated/packageGraph.json +2 -2
- package/src/forge/_generated/packageGraph.ts +8808 -4723
- package/src/forge/_generated/packageUpgradeRegistry.json +2 -2
- package/src/forge/_generated/packageUpgradeRegistry.ts +2 -2
- package/src/forge/_generated/permissionMatrix.json +2 -2
- package/src/forge/_generated/permissionMatrix.ts +3 -3
- package/src/forge/_generated/policyRegistry.json +2 -2
- package/src/forge/_generated/policyRegistry.ts +3 -3
- package/src/forge/_generated/queryRegistry.json +2 -2
- package/src/forge/_generated/queryRegistry.ts +3 -3
- package/src/forge/_generated/react.d.ts +1 -1
- package/src/forge/_generated/react.ts +1 -1
- package/src/forge/_generated/reactManifest.json +2 -2
- package/src/forge/_generated/reactManifest.ts +3 -3
- package/src/forge/_generated/releaseManifest.json +2 -2
- package/src/forge/_generated/releaseManifest.ts +3 -3
- package/src/forge/_generated/rlsPolicies.json +1 -1
- package/src/forge/_generated/rlsPolicies.sql +1 -1
- package/src/forge/_generated/rlsPolicies.ts +1 -1
- package/src/forge/_generated/runtimeGraph.json +2 -2
- package/src/forge/_generated/runtimeGraph.ts +3 -3
- package/src/forge/_generated/runtimeMatrix.json +2 -2
- package/src/forge/_generated/runtimeMatrix.ts +8684 -1939
- package/src/forge/_generated/runtimeRegistry.ts +1 -1
- package/src/forge/_generated/runtimeRules.md +13 -1
- package/src/forge/_generated/secretRegistry.json +1 -1
- package/src/forge/_generated/secretRegistry.ts +1 -1
- package/src/forge/_generated/secretsContext.ts +1 -1
- package/src/forge/_generated/serverApi.ts +1 -1
- package/src/forge/_generated/sourceMapManifest.json +2 -2
- package/src/forge/_generated/sourceMapManifest.ts +2 -2
- package/src/forge/_generated/sqlPlan.json +1 -1
- package/src/forge/_generated/sqlPlan.ts +1 -1
- package/src/forge/_generated/subscriptionManifest.json +2 -2
- package/src/forge/_generated/subscriptionManifest.ts +3 -3
- package/src/forge/_generated/symbolicationManifest.json +2 -2
- package/src/forge/_generated/symbolicationManifest.ts +2 -2
- package/src/forge/_generated/telemetryRegistry.json +2 -2
- package/src/forge/_generated/telemetryRegistry.ts +3 -3
- package/src/forge/_generated/telemetrySinks.json +2 -2
- package/src/forge/_generated/telemetrySinks.ts +2 -2
- package/src/forge/_generated/tenantScope.json +2 -2
- package/src/forge/_generated/tenantScope.ts +3 -3
- package/src/forge/_generated/testGraph.json +2 -2
- package/src/forge/_generated/testGraph.ts +339 -17
- package/src/forge/_generated/testPlanRegistry.json +2 -2
- package/src/forge/_generated/testPlanRegistry.ts +2 -2
- package/src/forge/_generated/uiRoutes.json +1 -1
- package/src/forge/_generated/uiRoutes.ts +1 -1
- package/src/forge/_generated/uiScenarios.json +1 -1
- package/src/forge/_generated/uiScenarios.ts +1 -1
- package/src/forge/_generated/uiTestManifest.json +2 -2
- package/src/forge/_generated/uiTestManifest.ts +2 -2
- package/src/forge/_generated/workflowRegistry.json +2 -2
- package/src/forge/_generated/workflowRegistry.ts +3 -3
- package/src/forge/_generated/workflowSubscriptions.json +2 -2
- package/src/forge/_generated/workflowSubscriptions.ts +3 -3
- package/src/forge/cli/ai.ts +351 -1
- package/src/forge/cli/auth.ts +36 -1
- package/src/forge/cli/commands.ts +19 -0
- package/src/forge/cli/parse.ts +67 -8
- package/src/forge/cli/rls.ts +529 -17
- package/src/forge/cli/secrets.ts +46 -1
- package/src/forge/cli/security.ts +269 -0
- package/src/forge/compiler/agent-contract/build.ts +289 -8
- package/src/forge/compiler/agent-contract/types.ts +43 -0
- package/src/forge/compiler/ai-registry/build.ts +62 -1
- package/src/forge/compiler/ai-registry/constants.ts +1 -1
- package/src/forge/compiler/ai-registry/parse.ts +98 -4
- package/src/forge/compiler/app-graph/forge-apis.ts +1 -0
- package/src/forge/compiler/dev-manifest/build.ts +3 -0
- package/src/forge/compiler/diagnostics/codes.ts +15 -0
- package/src/forge/compiler/diagnostics/create.ts +1 -1
- package/src/forge/compiler/make-registry/build.ts +13 -0
- package/src/forge/compiler/orchestrator/plan.ts +11 -0
- package/src/forge/compiler/orchestrator/serialize.ts +68 -0
- package/src/forge/compiler/package-graph/compiler.ts +13 -3
- package/src/forge/compiler/types/ai-registry.ts +25 -1
- package/src/forge/compiler/types/app-graph.ts +1 -0
- package/src/forge/compiler/types/cli.ts +1 -0
- package/src/forge/compiler/types/dev-manifest.ts +3 -0
- package/src/forge/dev/server.ts +508 -1
- package/src/forge/make/index.ts +126 -3
- package/src/forge/make/templates.ts +188 -0
- package/src/forge/make/types.ts +1 -0
- package/src/forge/runtime/ai/context.ts +210 -5
- package/src/forge/runtime/ai/types.ts +70 -0
- package/src/forge/runtime/auth/claims.ts +32 -0
- package/src/forge/runtime/auth/errors.ts +2 -0
- package/src/forge/runtime/context/create-context.ts +30 -6
- package/src/forge/runtime/db/memory-adapter.ts +2 -2
- package/src/forge/runtime/telemetry/scrubber.ts +56 -5
- package/src/forge/runtime/webhooks/security.ts +184 -0
- package/src/forge/server.ts +93 -0
- package/src/forge/version.ts +1 -1
- package/templates/b2b-support-web/package.json +1 -0
- package/templates/b2b-support-web/tsconfig.json +4 -1
- package/templates/minimal-web/package.json +1 -0
- package/templates/minimal-web/tsconfig.json +3 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ForgeAiProvider } from "../types/ai-registry.ts";
|
|
2
2
|
|
|
3
3
|
const AI_METHOD_PATTERN =
|
|
4
|
-
/(?:ctx\.ai|ai)\.(generateText|streamText|generateStructured)\s*\(/g;
|
|
4
|
+
/(?:(?:ctx\.ai|ai)\.(generateText|streamText|generateStructured|runAgent)|ctx\.agent\.(run))\s*\(/g;
|
|
5
5
|
|
|
6
6
|
const PROVIDER_PATTERN =
|
|
7
7
|
/provider\s*:\s*["'](openai|anthropic|gateway)["']/;
|
|
@@ -11,7 +11,7 @@ const MODEL_PATTERN = /model\s*:\s*["']([^"']+)["']/;
|
|
|
11
11
|
const PURPOSE_PATTERN = /purpose\s*:\s*["']([^"']+)["']/;
|
|
12
12
|
|
|
13
13
|
export interface ParsedAiCall {
|
|
14
|
-
method: "generateText" | "streamText" | "generateStructured";
|
|
14
|
+
method: "generateText" | "streamText" | "generateStructured" | "runAgent";
|
|
15
15
|
provider?: ForgeAiProvider;
|
|
16
16
|
model?: string;
|
|
17
17
|
purpose?: string;
|
|
@@ -21,7 +21,7 @@ export function parseAiCallsFromSlice(sourceSlice: string): ParsedAiCall[] {
|
|
|
21
21
|
const calls: ParsedAiCall[] = [];
|
|
22
22
|
|
|
23
23
|
for (const match of sourceSlice.matchAll(AI_METHOD_PATTERN)) {
|
|
24
|
-
const method = match[1] as ParsedAiCall["method"];
|
|
24
|
+
const method = (match[1] ?? (match[2] ? "runAgent" : "")) as ParsedAiCall["method"];
|
|
25
25
|
const start = match.index ?? 0;
|
|
26
26
|
const window = sourceSlice.slice(start, start + 600);
|
|
27
27
|
|
|
@@ -49,8 +49,102 @@ export function parseAiCallsFromSlice(sourceSlice: string): ParsedAiCall[] {
|
|
|
49
49
|
return calls;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const FORBIDDEN_AI_CONTEXT_PATTERN = /ctx\.ai
|
|
52
|
+
const FORBIDDEN_AI_CONTEXT_PATTERN = /ctx\.(?:ai\.|agent\.run\s*\()/;
|
|
53
53
|
|
|
54
54
|
export function detectCtxAiUsage(sourceSlice: string): boolean {
|
|
55
55
|
return FORBIDDEN_AI_CONTEXT_PATTERN.test(sourceSlice);
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
const DESCRIPTION_PATTERN = /description\s*:\s*["'`]([^"'`]+)["'`]/;
|
|
59
|
+
const RISK_PATTERN = /risk\s*:\s*["'](read|write|external|destructive)["']/;
|
|
60
|
+
const STRICT_PATTERN = /strict\s*:\s*(true|false)/;
|
|
61
|
+
const NEEDS_APPROVAL_PATTERN = /needsApproval\s*:\s*(true|false|async\s*\(|\([^)]*\)\s*=>|[A-Za-z_$][A-Za-z0-9_$]*)/;
|
|
62
|
+
const INSTRUCTIONS_PATTERN = /instructions\s*:\s*["'`]([^"'`]+)["'`]/;
|
|
63
|
+
const TOOL_ARRAY_PATTERN = /tools\s*:\s*\[([^\]]*)\]/s;
|
|
64
|
+
const TOOL_OBJECT_PATTERN = /tools\s*:\s*\{([^}]*)\}/s;
|
|
65
|
+
const STOP_TOOL_PATTERN = /stopWhen\s*:\s*\{[^}]*kind\s*:\s*["']toolCall["'][^}]*toolName\s*:\s*["']([^"']+)["'][^}]*\}/s;
|
|
66
|
+
const STOP_STEP_PATTERN = /stopWhen\s*:\s*\{[^}]*kind\s*:\s*["']stepCount["'][^}]*maxSteps\s*:\s*(\d+)[^}]*\}/s;
|
|
67
|
+
const MAX_STEPS_PATTERN = /maxSteps\s*:\s*(\d+)/;
|
|
68
|
+
|
|
69
|
+
export interface ParsedAiToolMeta {
|
|
70
|
+
description?: string;
|
|
71
|
+
risk: "read" | "write" | "external" | "destructive" | "unknown";
|
|
72
|
+
strict: boolean;
|
|
73
|
+
needsApproval: boolean | "dynamic";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ParsedAiAgentMeta {
|
|
77
|
+
provider?: ForgeAiProvider;
|
|
78
|
+
model?: string;
|
|
79
|
+
instructions?: string;
|
|
80
|
+
tools: string[];
|
|
81
|
+
stopWhen:
|
|
82
|
+
| { kind: "stepCount"; maxSteps: number }
|
|
83
|
+
| { kind: "toolCall"; toolName: string }
|
|
84
|
+
| { kind: "default" };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function parseBooleanOrDynamic(value: string | undefined): boolean | "dynamic" {
|
|
88
|
+
if (value === "true") return true;
|
|
89
|
+
if (value === "false") return false;
|
|
90
|
+
return "dynamic";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parseStringList(raw: string | undefined): string[] {
|
|
94
|
+
if (!raw) return [];
|
|
95
|
+
return [...raw.matchAll(/["'`]([^"'`]+)["'`]/g)]
|
|
96
|
+
.map((match) => match[1] ?? "")
|
|
97
|
+
.filter(Boolean)
|
|
98
|
+
.sort();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parseObjectToolKeys(raw: string | undefined): string[] {
|
|
102
|
+
if (!raw) return [];
|
|
103
|
+
const explicit = [...raw.matchAll(/([A-Za-z_$][A-Za-z0-9_$]*)\s*:/g)]
|
|
104
|
+
.map((match) => match[1] ?? "")
|
|
105
|
+
.filter(Boolean);
|
|
106
|
+
const shorthand = raw
|
|
107
|
+
.split(",")
|
|
108
|
+
.map((part) => part.trim())
|
|
109
|
+
.filter((part) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(part));
|
|
110
|
+
return [...new Set([...explicit, ...shorthand])].sort();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function parseAiToolMeta(sourceSlice: string): ParsedAiToolMeta {
|
|
114
|
+
const description = DESCRIPTION_PATTERN.exec(sourceSlice)?.[1];
|
|
115
|
+
const risk = RISK_PATTERN.exec(sourceSlice)?.[1] as ParsedAiToolMeta["risk"] | undefined;
|
|
116
|
+
const strict = STRICT_PATTERN.exec(sourceSlice)?.[1] === "true";
|
|
117
|
+
const needsApprovalMatch = NEEDS_APPROVAL_PATTERN.exec(sourceSlice)?.[1];
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
...(description ? { description } : {}),
|
|
121
|
+
risk: risk ?? "unknown",
|
|
122
|
+
strict,
|
|
123
|
+
needsApproval: needsApprovalMatch
|
|
124
|
+
? parseBooleanOrDynamic(needsApprovalMatch)
|
|
125
|
+
: false,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function parseAiAgentMeta(sourceSlice: string): ParsedAiAgentMeta {
|
|
130
|
+
const provider = PROVIDER_PATTERN.exec(sourceSlice)?.[1] as ForgeAiProvider | undefined;
|
|
131
|
+
const model = MODEL_PATTERN.exec(sourceSlice)?.[1];
|
|
132
|
+
const instructions = INSTRUCTIONS_PATTERN.exec(sourceSlice)?.[1];
|
|
133
|
+
const arrayTools = parseStringList(TOOL_ARRAY_PATTERN.exec(sourceSlice)?.[1]);
|
|
134
|
+
const objectTools = parseObjectToolKeys(TOOL_OBJECT_PATTERN.exec(sourceSlice)?.[1]);
|
|
135
|
+
const stopTool = STOP_TOOL_PATTERN.exec(sourceSlice)?.[1];
|
|
136
|
+
const stopStepsRaw =
|
|
137
|
+
STOP_STEP_PATTERN.exec(sourceSlice)?.[1] ?? MAX_STEPS_PATTERN.exec(sourceSlice)?.[1];
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
...(provider ? { provider } : {}),
|
|
141
|
+
...(model ? { model } : {}),
|
|
142
|
+
...(instructions ? { instructions } : {}),
|
|
143
|
+
tools: [...new Set([...arrayTools, ...objectTools])].sort(),
|
|
144
|
+
stopWhen: stopTool
|
|
145
|
+
? { kind: "toolCall", toolName: stopTool }
|
|
146
|
+
: stopStepsRaw
|
|
147
|
+
? { kind: "stepCount", maxSteps: Number(stopStepsRaw) }
|
|
148
|
+
: { kind: "default" },
|
|
149
|
+
};
|
|
150
|
+
}
|
|
@@ -61,6 +61,9 @@ function buildRoutes(
|
|
|
61
61
|
{ method: "GET", path: "/health", purpose: "health" },
|
|
62
62
|
{ method: "GET", path: "/entries", purpose: "entries" },
|
|
63
63
|
{ method: "GET", path: "/queries", purpose: "queries" },
|
|
64
|
+
{ method: "POST", path: "/ai/agents/chat", purpose: "ai-agent-chat" },
|
|
65
|
+
{ method: "POST", path: "/ai/agents/run", purpose: "ai-agent-run" },
|
|
66
|
+
{ method: "GET", path: "/ai/providers", purpose: "ai-providers" },
|
|
64
67
|
{ method: "GET", path: "/workflows", purpose: "workflows" },
|
|
65
68
|
{ method: "GET", path: "/workflows/runs", purpose: "workflow-runs" },
|
|
66
69
|
{ method: "POST", path: "/workflows/process", purpose: "workflow-process" },
|
|
@@ -14,6 +14,7 @@ export const FORGE_AUTH_INVALID_AUDIENCE = "FORGE_AUTH_INVALID_AUDIENCE" as cons
|
|
|
14
14
|
export const FORGE_AUTH_TOKEN_EXPIRED = "FORGE_AUTH_TOKEN_EXPIRED" as const;
|
|
15
15
|
export const FORGE_AUTH_JWKS_FAILED = "FORGE_AUTH_JWKS_FAILED" as const;
|
|
16
16
|
export const FORGE_AUTH_CLAIM_MISSING = "FORGE_AUTH_CLAIM_MISSING" as const;
|
|
17
|
+
export const FORGE_AUTH_CLAIM_INVALID = "FORGE_AUTH_CLAIM_INVALID" as const;
|
|
17
18
|
export const FORGE_AUTH_TENANT_MISSING = "FORGE_AUTH_TENANT_MISSING" as const;
|
|
18
19
|
export const FORGE_AUTH_DEV_HEADERS_IN_PRODUCTION =
|
|
19
20
|
"FORGE_AUTH_DEV_HEADERS_IN_PRODUCTION" as const;
|
|
@@ -47,6 +48,7 @@ export const FORGE_RLS_UNSUPPORTED_TENANT_TYPE =
|
|
|
47
48
|
export const FORGE_RLS_FORCE_DISABLED = "FORGE_RLS_FORCE_DISABLED" as const;
|
|
48
49
|
export const FORGE_RLS_APPLY_FAILED = "FORGE_RLS_APPLY_FAILED" as const;
|
|
49
50
|
export const FORGE_RLS_TEST_FAILED = "FORGE_RLS_TEST_FAILED" as const;
|
|
51
|
+
export const FORGE_RLS_MUTATION_FAILED = "FORGE_RLS_MUTATION_FAILED" as const;
|
|
50
52
|
export const FORGE_RLS_SESSION_CONTEXT_MISSING =
|
|
51
53
|
"FORGE_RLS_SESSION_CONTEXT_MISSING" as const;
|
|
52
54
|
export const FORGE_RLS_SESSION_CONTEXT_UNSAFE =
|
|
@@ -315,6 +317,12 @@ export const FORGE_TELEMETRY_PAYLOAD_TRUNCATED =
|
|
|
315
317
|
"FORGE_TELEMETRY_PAYLOAD_TRUNCATED" as const;
|
|
316
318
|
export const FORGE_TELEMETRY_SINK_FAILED = "FORGE_TELEMETRY_SINK_FAILED" as const;
|
|
317
319
|
export const FORGE_TELEMETRY_UNKNOWN_SINK = "FORGE_TELEMETRY_UNKNOWN_SINK" as const;
|
|
320
|
+
export const FORGE_WEBHOOK_SIGNATURE_INVALID =
|
|
321
|
+
"FORGE_WEBHOOK_SIGNATURE_INVALID" as const;
|
|
322
|
+
export const FORGE_WEBHOOK_TIMESTAMP_INVALID =
|
|
323
|
+
"FORGE_WEBHOOK_TIMESTAMP_INVALID" as const;
|
|
324
|
+
export const FORGE_WEBHOOK_REPLAY_DETECTED =
|
|
325
|
+
"FORGE_WEBHOOK_REPLAY_DETECTED" as const;
|
|
318
326
|
export const FORGE_POLICY_DENIED = "FORGE_POLICY_DENIED" as const;
|
|
319
327
|
export const FORGE_POLICY_MISSING = "FORGE_POLICY_MISSING" as const;
|
|
320
328
|
export const FORGE_POLICY_UNKNOWN = "FORGE_POLICY_UNKNOWN" as const;
|
|
@@ -338,6 +346,7 @@ export const FORGE_AI_SECRET_MISSING = "FORGE_AI_SECRET_MISSING" as const;
|
|
|
338
346
|
export const FORGE_AI_DYNAMIC_PROVIDER = "FORGE_AI_DYNAMIC_PROVIDER" as const;
|
|
339
347
|
export const FORGE_AI_GENERATION_FAILED = "FORGE_AI_GENERATION_FAILED" as const;
|
|
340
348
|
export const FORGE_AI_USAGE_UNAVAILABLE = "FORGE_AI_USAGE_UNAVAILABLE" as const;
|
|
349
|
+
export const FORGE_AI_REDTEAM_FAILED = "FORGE_AI_REDTEAM_FAILED" as const;
|
|
341
350
|
export const FORGE_QUERY_WRITE_FORBIDDEN = "FORGE_QUERY_WRITE_FORBIDDEN" as const;
|
|
342
351
|
export const FORGE_QUERY_EMIT_FORBIDDEN = "FORGE_QUERY_EMIT_FORBIDDEN" as const;
|
|
343
352
|
export const FORGE_QUERY_SECRET_FORBIDDEN = "FORGE_QUERY_SECRET_FORBIDDEN" as const;
|
|
@@ -379,6 +388,7 @@ export const DIAGNOSTIC_CODES = [
|
|
|
379
388
|
FORGE_AUTH_TOKEN_EXPIRED,
|
|
380
389
|
FORGE_AUTH_JWKS_FAILED,
|
|
381
390
|
FORGE_AUTH_CLAIM_MISSING,
|
|
391
|
+
FORGE_AUTH_CLAIM_INVALID,
|
|
382
392
|
FORGE_AUTH_TENANT_MISSING,
|
|
383
393
|
FORGE_AUTH_DEV_HEADERS_IN_PRODUCTION,
|
|
384
394
|
FORGE_AUTH_MODE_INVALID,
|
|
@@ -406,6 +416,7 @@ export const DIAGNOSTIC_CODES = [
|
|
|
406
416
|
FORGE_RLS_FORCE_DISABLED,
|
|
407
417
|
FORGE_RLS_APPLY_FAILED,
|
|
408
418
|
FORGE_RLS_TEST_FAILED,
|
|
419
|
+
FORGE_RLS_MUTATION_FAILED,
|
|
409
420
|
FORGE_RLS_SESSION_CONTEXT_MISSING,
|
|
410
421
|
FORGE_RLS_SESSION_CONTEXT_UNSAFE,
|
|
411
422
|
FORGE_DB_SUPERUSER_RUNTIME,
|
|
@@ -575,6 +586,9 @@ export const DIAGNOSTIC_CODES = [
|
|
|
575
586
|
FORGE_TELEMETRY_PAYLOAD_TRUNCATED,
|
|
576
587
|
FORGE_TELEMETRY_SINK_FAILED,
|
|
577
588
|
FORGE_TELEMETRY_UNKNOWN_SINK,
|
|
589
|
+
FORGE_WEBHOOK_SIGNATURE_INVALID,
|
|
590
|
+
FORGE_WEBHOOK_TIMESTAMP_INVALID,
|
|
591
|
+
FORGE_WEBHOOK_REPLAY_DETECTED,
|
|
578
592
|
FORGE_POLICY_DENIED,
|
|
579
593
|
FORGE_POLICY_MISSING,
|
|
580
594
|
FORGE_POLICY_UNKNOWN,
|
|
@@ -595,6 +609,7 @@ export const DIAGNOSTIC_CODES = [
|
|
|
595
609
|
FORGE_AI_DYNAMIC_PROVIDER,
|
|
596
610
|
FORGE_AI_GENERATION_FAILED,
|
|
597
611
|
FORGE_AI_USAGE_UNAVAILABLE,
|
|
612
|
+
FORGE_AI_REDTEAM_FAILED,
|
|
598
613
|
FORGE_QUERY_WRITE_FORBIDDEN,
|
|
599
614
|
FORGE_QUERY_EMIT_FORBIDDEN,
|
|
600
615
|
FORGE_QUERY_SECRET_FORBIDDEN,
|
|
@@ -71,7 +71,7 @@ function defaultGuidanceForCode(code: string): DiagnosticGuidance | null {
|
|
|
71
71
|
if (code.startsWith("FORGE_RLS_")) {
|
|
72
72
|
return {
|
|
73
73
|
fixHint: "Inspect generated RLS SQL and validate tenant isolation before applying migrations.",
|
|
74
|
-
suggestedCommands: ["forge rls check --json", "forge rls
|
|
74
|
+
suggestedCommands: ["forge rls check --json", "forge rls test --db postgres --json"],
|
|
75
75
|
docs: ["src/forge/_generated/agentContract.json"],
|
|
76
76
|
};
|
|
77
77
|
}
|
|
@@ -34,6 +34,7 @@ export function buildMakeRegistry(generatorVersion: string): MakeRegistryArtifac
|
|
|
34
34
|
"forge make list --json",
|
|
35
35
|
"forge make explain <primitive> --json",
|
|
36
36
|
"forge make ui --framework vite --dry-run --json",
|
|
37
|
+
"forge make ai-chat support --dry-run --json",
|
|
37
38
|
"forge make resource <name> --fields title:text,status:enum(open,closed) --dry-run --json",
|
|
38
39
|
"forge make resource <name> --fields title:text --with-ui --yes",
|
|
39
40
|
"forge make apply <planId>",
|
|
@@ -123,6 +124,17 @@ export function buildMakeRegistry(generatorVersion: string): MakeRegistryArtifac
|
|
|
123
124
|
modifies: [],
|
|
124
125
|
examples: ["forge make ui --framework vite --yes"],
|
|
125
126
|
},
|
|
127
|
+
{
|
|
128
|
+
name: "ai-chat",
|
|
129
|
+
summary: "Add a Forge AI agent and React chat component backed by /ai/agents/run.",
|
|
130
|
+
creates: [
|
|
131
|
+
"src/ai/<name>Agent.ts",
|
|
132
|
+
"web/components/<Name>AiChat.tsx",
|
|
133
|
+
"web/app/<name>-ai/page.tsx when web/app exists",
|
|
134
|
+
],
|
|
135
|
+
modifies: [],
|
|
136
|
+
examples: ["forge make ai-chat support --yes"],
|
|
137
|
+
},
|
|
126
138
|
{
|
|
127
139
|
name: "resource",
|
|
128
140
|
summary: "Add schema, policies, CRUD, queries, liveQuery, optional UI, and tests.",
|
|
@@ -155,6 +167,7 @@ export function buildMakeTemplates(): MakeTemplateArtifact {
|
|
|
155
167
|
{ name: "component", sourceKind: "frontend", outputPattern: "web/components/<name>.tsx" },
|
|
156
168
|
{ name: "page", sourceKind: "frontend", outputPattern: "web/app/<route>/page.tsx" },
|
|
157
169
|
{ name: "ui", sourceKind: "frontend", outputPattern: "web/src/App.tsx" },
|
|
170
|
+
{ name: "ai-chat", sourceKind: "frontend", outputPattern: "web/components/<name>AiChat.tsx" },
|
|
158
171
|
{ name: "placeholder-test", sourceKind: "test", outputPattern: "tests/make-generated/<name>.test.ts" },
|
|
159
172
|
],
|
|
160
173
|
};
|
|
@@ -54,6 +54,8 @@ import { buildDevManifest } from "../dev-manifest/build.ts";
|
|
|
54
54
|
import { buildRuntimeGraph } from "../runtime-graph/build.ts";
|
|
55
55
|
import {
|
|
56
56
|
buildAgentContractArtifacts,
|
|
57
|
+
serializeAgentToolRegistryJson,
|
|
58
|
+
serializeAgentToolRegistryTs,
|
|
57
59
|
serializeCapabilityMapJson,
|
|
58
60
|
serializeCapabilityMapTs,
|
|
59
61
|
serializeAgentContractJson,
|
|
@@ -399,6 +401,15 @@ export function plan(input: PlanInput): EmitPlan {
|
|
|
399
401
|
serializeAgentContractJson(agentArtifacts.contract),
|
|
400
402
|
),
|
|
401
403
|
makeEmitFile(`${GENERATED_DIR}/appMap.md`, agentArtifacts.appMapMd),
|
|
404
|
+
makeEmitFile(
|
|
405
|
+
`${GENERATED_DIR}/agentTools.ts`,
|
|
406
|
+
serializeAgentToolRegistryTs(agentArtifacts.toolRegistry),
|
|
407
|
+
),
|
|
408
|
+
makeEmitFile(
|
|
409
|
+
`${GENERATED_DIR}/agentTools.json`,
|
|
410
|
+
serializeAgentToolRegistryJson(agentArtifacts.toolRegistry),
|
|
411
|
+
),
|
|
412
|
+
makeEmitFile(`${GENERATED_DIR}/agentTools.md`, agentArtifacts.agentToolsMd),
|
|
402
413
|
makeEmitFile(
|
|
403
414
|
`${GENERATED_DIR}/capabilityMap.ts`,
|
|
404
415
|
serializeCapabilityMapTs(agentArtifacts.capabilityMap),
|
|
@@ -680,6 +680,8 @@ export function serializeAiRegistryJson(registry: import("../types/ai-registry.t
|
|
|
680
680
|
inputHash: registry.inputHash,
|
|
681
681
|
providers: registry.providers,
|
|
682
682
|
generations: registry.generations,
|
|
683
|
+
tools: registry.tools,
|
|
684
|
+
agents: registry.agents,
|
|
683
685
|
diagnostics: registry.diagnostics,
|
|
684
686
|
};
|
|
685
687
|
return serializeCanonical(payload);
|
|
@@ -762,10 +764,76 @@ export interface ForgeGenerateStructuredInput<T> {
|
|
|
762
764
|
schema: ForgeFlexibleSchema<T>;
|
|
763
765
|
}
|
|
764
766
|
|
|
767
|
+
export type ForgeAiToolRisk = "read" | "write" | "external" | "destructive";
|
|
768
|
+
|
|
769
|
+
export interface ForgeAiToolRuntimeContext {
|
|
770
|
+
secrets: {
|
|
771
|
+
get(name: string): string;
|
|
772
|
+
optional(name: string): string | undefined;
|
|
773
|
+
has(name: string): boolean;
|
|
774
|
+
};
|
|
775
|
+
env: Record<string, string | undefined>;
|
|
776
|
+
telemetry?: {
|
|
777
|
+
traceId?: string;
|
|
778
|
+
capture(name: string, properties?: Record<string, unknown>): Promise<void>;
|
|
779
|
+
};
|
|
780
|
+
auth?: unknown;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
export interface ForgeAiToolDefinition<TArgs = unknown, TResult = unknown> {
|
|
784
|
+
description: string;
|
|
785
|
+
inputSchema: unknown;
|
|
786
|
+
outputSchema?: unknown;
|
|
787
|
+
strict?: boolean;
|
|
788
|
+
needsApproval?: boolean | ((args: TArgs) => boolean | Promise<boolean>);
|
|
789
|
+
risk?: ForgeAiToolRisk;
|
|
790
|
+
handler: (
|
|
791
|
+
ctx: ForgeAiToolRuntimeContext,
|
|
792
|
+
args: TArgs,
|
|
793
|
+
) => TResult | Promise<TResult>;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
export type ForgeAgentStopWhen =
|
|
797
|
+
| { kind: "stepCount"; maxSteps: number }
|
|
798
|
+
| { kind: "toolCall"; toolName: string };
|
|
799
|
+
|
|
800
|
+
export interface ForgeRunAgentInput {
|
|
801
|
+
provider?: ForgeAiProvider;
|
|
802
|
+
model: string;
|
|
803
|
+
prompt: string;
|
|
804
|
+
instructions: string;
|
|
805
|
+
purpose?: string;
|
|
806
|
+
tools?: Record<string, ForgeAiToolDefinition>;
|
|
807
|
+
stopWhen?: ForgeAgentStopWhen;
|
|
808
|
+
maxSteps?: number;
|
|
809
|
+
temperature?: number;
|
|
810
|
+
maxTokens?: number;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
export interface ForgeRunAgentResult {
|
|
814
|
+
text: string;
|
|
815
|
+
provider: ForgeAiProvider;
|
|
816
|
+
model: string;
|
|
817
|
+
purpose?: string;
|
|
818
|
+
usage: ForgeAiUsage;
|
|
819
|
+
latencyMs: number;
|
|
820
|
+
toolCalls: Array<{
|
|
821
|
+
toolName: string;
|
|
822
|
+
input: unknown;
|
|
823
|
+
}>;
|
|
824
|
+
toolResults: Array<{
|
|
825
|
+
toolName: string;
|
|
826
|
+
output: unknown;
|
|
827
|
+
}>;
|
|
828
|
+
steps: number;
|
|
829
|
+
estimatedCostUsd?: number;
|
|
830
|
+
}
|
|
831
|
+
|
|
765
832
|
export interface AiContext {
|
|
766
833
|
generateText(input: ForgeGenerateTextInput): Promise<ForgeGenerateTextResult>;
|
|
767
834
|
streamText(input: ForgeStreamTextInput): Promise<ForgeStreamTextResult>;
|
|
768
835
|
generateStructured<T>(input: ForgeGenerateStructuredInput<T>): Promise<T>;
|
|
836
|
+
runAgent(input: ForgeRunAgentInput): Promise<ForgeRunAgentResult>;
|
|
769
837
|
}
|
|
770
838
|
`;
|
|
771
839
|
}
|
|
@@ -73,6 +73,10 @@ export interface BuildResult {
|
|
|
73
73
|
diagnostics: Diagnostic[];
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
function shouldWarnNoTypes(packageName: string): boolean {
|
|
77
|
+
return !packageName.startsWith("@types/");
|
|
78
|
+
}
|
|
79
|
+
|
|
76
80
|
export class PackageGraphCompiler {
|
|
77
81
|
async build(
|
|
78
82
|
deps: Dependency[],
|
|
@@ -363,7 +367,9 @@ export class PackageGraphCompiler {
|
|
|
363
367
|
}
|
|
364
368
|
|
|
365
369
|
if (resolved.dtsPath == null) {
|
|
366
|
-
|
|
370
|
+
if (shouldWarnNoTypes(dep.name)) {
|
|
371
|
+
diagnostics.push(forgePkgNoTypes(dep.name, subpath));
|
|
372
|
+
}
|
|
367
373
|
return {
|
|
368
374
|
subpath,
|
|
369
375
|
conditions: resolved.conditions,
|
|
@@ -382,7 +388,9 @@ export class PackageGraphCompiler {
|
|
|
382
388
|
subpath,
|
|
383
389
|
);
|
|
384
390
|
} catch {
|
|
385
|
-
|
|
391
|
+
if (shouldWarnNoTypes(dep.name)) {
|
|
392
|
+
diagnostics.push(forgePkgNoTypes(dep.name, subpath));
|
|
393
|
+
}
|
|
386
394
|
return {
|
|
387
395
|
subpath,
|
|
388
396
|
conditions: resolved.conditions,
|
|
@@ -401,7 +409,9 @@ export class PackageGraphCompiler {
|
|
|
401
409
|
}
|
|
402
410
|
|
|
403
411
|
if (exports.length === 0) {
|
|
404
|
-
|
|
412
|
+
if (shouldWarnNoTypes(dep.name)) {
|
|
413
|
+
diagnostics.push(forgePkgNoTypes(dep.name, subpath));
|
|
414
|
+
}
|
|
405
415
|
}
|
|
406
416
|
|
|
407
417
|
return {
|
|
@@ -18,10 +18,32 @@ export interface AiGenerationCall {
|
|
|
18
18
|
provider: ForgeAiProvider;
|
|
19
19
|
model: string;
|
|
20
20
|
purpose?: string;
|
|
21
|
-
method: "generateText" | "streamText" | "generateStructured";
|
|
21
|
+
method: "generateText" | "streamText" | "generateStructured" | "runAgent";
|
|
22
22
|
file: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export interface AiToolDefinition {
|
|
26
|
+
name: string;
|
|
27
|
+
file: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
risk: "read" | "write" | "external" | "destructive" | "unknown";
|
|
30
|
+
strict: boolean;
|
|
31
|
+
needsApproval: boolean | "dynamic";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AiAgentDefinition {
|
|
35
|
+
name: string;
|
|
36
|
+
file: string;
|
|
37
|
+
provider: ForgeAiProvider;
|
|
38
|
+
model: string;
|
|
39
|
+
instructions?: string;
|
|
40
|
+
tools: string[];
|
|
41
|
+
stopWhen:
|
|
42
|
+
| { kind: "stepCount"; maxSteps: number }
|
|
43
|
+
| { kind: "toolCall"; toolName: string }
|
|
44
|
+
| { kind: "default" };
|
|
45
|
+
}
|
|
46
|
+
|
|
25
47
|
export interface AiRegistry {
|
|
26
48
|
schemaVersion: string;
|
|
27
49
|
generatorVersion: string;
|
|
@@ -29,5 +51,7 @@ export interface AiRegistry {
|
|
|
29
51
|
inputHash: string;
|
|
30
52
|
providers: AiProviderDefinition[];
|
|
31
53
|
generations: AiGenerationCall[];
|
|
54
|
+
tools: AiToolDefinition[];
|
|
55
|
+
agents: AiAgentDefinition[];
|
|
32
56
|
diagnostics: unknown[];
|
|
33
57
|
}
|