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.
Files changed (180) hide show
  1. package/AGENTS.md +38 -3
  2. package/CHANGELOG.md +29 -0
  3. package/README.md +25 -10
  4. package/package.json +8 -5
  5. package/src/forge/_generated/actionSubscriptions.json +2 -2
  6. package/src/forge/_generated/actionSubscriptions.ts +3 -3
  7. package/src/forge/_generated/agentAdapterManifest.json +2 -2
  8. package/src/forge/_generated/agentAdapterManifest.ts +3 -3
  9. package/src/forge/_generated/agentContract.json +2 -2
  10. package/src/forge/_generated/agentContract.ts +183 -50
  11. package/src/forge/_generated/agentQuickstart.md +3 -1
  12. package/src/forge/_generated/agentTools.json +2 -0
  13. package/src/forge/_generated/agentTools.md +16 -0
  14. package/src/forge/_generated/agentTools.ts +12 -0
  15. package/src/forge/_generated/aiContext.ts +67 -1
  16. package/src/forge/_generated/aiModels.json +2 -2
  17. package/src/forge/_generated/aiModels.ts +17 -1
  18. package/src/forge/_generated/aiProviders.json +1 -1
  19. package/src/forge/_generated/aiProviders.ts +1 -1
  20. package/src/forge/_generated/aiRegistry.json +2 -2
  21. package/src/forge/_generated/aiRegistry.ts +7 -5
  22. package/src/forge/_generated/api.json +2 -2
  23. package/src/forge/_generated/api.ts +1 -1
  24. package/src/forge/_generated/appGraph.json +2 -2
  25. package/src/forge/_generated/appGraph.ts +512 -260
  26. package/src/forge/_generated/appMap.md +21 -1
  27. package/src/forge/_generated/artifactManifest.json +2 -2
  28. package/src/forge/_generated/artifactManifest.ts +2 -2
  29. package/src/forge/_generated/authClaims.json +1 -1
  30. package/src/forge/_generated/authClaims.ts +1 -1
  31. package/src/forge/_generated/authConfig.json +1 -1
  32. package/src/forge/_generated/authConfig.ts +1 -1
  33. package/src/forge/_generated/authContext.ts +1 -1
  34. package/src/forge/_generated/authRegistry.json +1 -1
  35. package/src/forge/_generated/authRegistry.ts +1 -1
  36. package/src/forge/_generated/buildInfo.json +2 -2
  37. package/src/forge/_generated/buildInfo.ts +4 -4
  38. package/src/forge/_generated/capabilityMap.json +2 -2
  39. package/src/forge/_generated/capabilityMap.md +1 -1
  40. package/src/forge/_generated/capabilityMap.ts +2 -2
  41. package/src/forge/_generated/client.ts +1 -1
  42. package/src/forge/_generated/clientApi.ts +1 -1
  43. package/src/forge/_generated/clientManifest.json +2 -2
  44. package/src/forge/_generated/clientManifest.ts +3 -3
  45. package/src/forge/_generated/clientTypes.ts +1 -1
  46. package/src/forge/_generated/configRegistry.json +1 -1
  47. package/src/forge/_generated/configRegistry.ts +1 -1
  48. package/src/forge/_generated/dataGraph.json +2 -2
  49. package/src/forge/_generated/dataGraph.ts +3 -3
  50. package/src/forge/_generated/db.json +1 -1
  51. package/src/forge/_generated/db.ts +1 -1
  52. package/src/forge/_generated/dbSecurityManifest.json +1 -1
  53. package/src/forge/_generated/dbSecurityManifest.ts +1 -1
  54. package/src/forge/_generated/dbSessionContext.json +1 -1
  55. package/src/forge/_generated/dbSessionContext.ts +1 -1
  56. package/src/forge/_generated/deployManifest.json +2 -2
  57. package/src/forge/_generated/deployManifest.ts +7 -7
  58. package/src/forge/_generated/devManifest.json +2 -2
  59. package/src/forge/_generated/devManifest.ts +18 -3
  60. package/src/forge/_generated/envSchema.json +1 -1
  61. package/src/forge/_generated/envSchema.ts +1 -1
  62. package/src/forge/_generated/frontendGraph.json +1 -1
  63. package/src/forge/_generated/frontendGraph.ts +1 -1
  64. package/src/forge/_generated/importGuards.json +1 -1
  65. package/src/forge/_generated/importGuards.ts +1 -1
  66. package/src/forge/_generated/index.ts +2 -1
  67. package/src/forge/_generated/liveProductionManifest.json +1 -1
  68. package/src/forge/_generated/liveProductionManifest.ts +1 -1
  69. package/src/forge/_generated/liveProtocol.json +1 -1
  70. package/src/forge/_generated/liveProtocol.ts +1 -1
  71. package/src/forge/_generated/liveQueryRegistry.json +2 -2
  72. package/src/forge/_generated/liveQueryRegistry.ts +3 -3
  73. package/src/forge/_generated/liveTransportConfig.json +1 -1
  74. package/src/forge/_generated/liveTransportConfig.ts +1 -1
  75. package/src/forge/_generated/makeRegistry.json +2 -2
  76. package/src/forge/_generated/makeRegistry.ts +16 -2
  77. package/src/forge/_generated/makeTemplates.json +2 -2
  78. package/src/forge/_generated/makeTemplates.ts +6 -1
  79. package/src/forge/_generated/mockMap.json +1 -1
  80. package/src/forge/_generated/mockMap.ts +1 -1
  81. package/src/forge/_generated/operationPlaybooks.md +34 -14
  82. package/src/forge/_generated/packageGraph.json +2 -2
  83. package/src/forge/_generated/packageGraph.ts +8808 -4723
  84. package/src/forge/_generated/packageUpgradeRegistry.json +2 -2
  85. package/src/forge/_generated/packageUpgradeRegistry.ts +2 -2
  86. package/src/forge/_generated/permissionMatrix.json +2 -2
  87. package/src/forge/_generated/permissionMatrix.ts +3 -3
  88. package/src/forge/_generated/policyRegistry.json +2 -2
  89. package/src/forge/_generated/policyRegistry.ts +3 -3
  90. package/src/forge/_generated/queryRegistry.json +2 -2
  91. package/src/forge/_generated/queryRegistry.ts +3 -3
  92. package/src/forge/_generated/react.d.ts +1 -1
  93. package/src/forge/_generated/react.ts +1 -1
  94. package/src/forge/_generated/reactManifest.json +2 -2
  95. package/src/forge/_generated/reactManifest.ts +3 -3
  96. package/src/forge/_generated/releaseManifest.json +2 -2
  97. package/src/forge/_generated/releaseManifest.ts +3 -3
  98. package/src/forge/_generated/rlsPolicies.json +1 -1
  99. package/src/forge/_generated/rlsPolicies.sql +1 -1
  100. package/src/forge/_generated/rlsPolicies.ts +1 -1
  101. package/src/forge/_generated/runtimeGraph.json +2 -2
  102. package/src/forge/_generated/runtimeGraph.ts +3 -3
  103. package/src/forge/_generated/runtimeMatrix.json +2 -2
  104. package/src/forge/_generated/runtimeMatrix.ts +8684 -1939
  105. package/src/forge/_generated/runtimeRegistry.ts +1 -1
  106. package/src/forge/_generated/runtimeRules.md +13 -1
  107. package/src/forge/_generated/secretRegistry.json +1 -1
  108. package/src/forge/_generated/secretRegistry.ts +1 -1
  109. package/src/forge/_generated/secretsContext.ts +1 -1
  110. package/src/forge/_generated/serverApi.ts +1 -1
  111. package/src/forge/_generated/sourceMapManifest.json +2 -2
  112. package/src/forge/_generated/sourceMapManifest.ts +2 -2
  113. package/src/forge/_generated/sqlPlan.json +1 -1
  114. package/src/forge/_generated/sqlPlan.ts +1 -1
  115. package/src/forge/_generated/subscriptionManifest.json +2 -2
  116. package/src/forge/_generated/subscriptionManifest.ts +3 -3
  117. package/src/forge/_generated/symbolicationManifest.json +2 -2
  118. package/src/forge/_generated/symbolicationManifest.ts +2 -2
  119. package/src/forge/_generated/telemetryRegistry.json +2 -2
  120. package/src/forge/_generated/telemetryRegistry.ts +3 -3
  121. package/src/forge/_generated/telemetrySinks.json +2 -2
  122. package/src/forge/_generated/telemetrySinks.ts +2 -2
  123. package/src/forge/_generated/tenantScope.json +2 -2
  124. package/src/forge/_generated/tenantScope.ts +3 -3
  125. package/src/forge/_generated/testGraph.json +2 -2
  126. package/src/forge/_generated/testGraph.ts +339 -17
  127. package/src/forge/_generated/testPlanRegistry.json +2 -2
  128. package/src/forge/_generated/testPlanRegistry.ts +2 -2
  129. package/src/forge/_generated/uiRoutes.json +1 -1
  130. package/src/forge/_generated/uiRoutes.ts +1 -1
  131. package/src/forge/_generated/uiScenarios.json +1 -1
  132. package/src/forge/_generated/uiScenarios.ts +1 -1
  133. package/src/forge/_generated/uiTestManifest.json +2 -2
  134. package/src/forge/_generated/uiTestManifest.ts +2 -2
  135. package/src/forge/_generated/workflowRegistry.json +2 -2
  136. package/src/forge/_generated/workflowRegistry.ts +3 -3
  137. package/src/forge/_generated/workflowSubscriptions.json +2 -2
  138. package/src/forge/_generated/workflowSubscriptions.ts +3 -3
  139. package/src/forge/cli/ai.ts +351 -1
  140. package/src/forge/cli/auth.ts +36 -1
  141. package/src/forge/cli/commands.ts +19 -0
  142. package/src/forge/cli/parse.ts +67 -8
  143. package/src/forge/cli/rls.ts +529 -17
  144. package/src/forge/cli/secrets.ts +46 -1
  145. package/src/forge/cli/security.ts +269 -0
  146. package/src/forge/compiler/agent-contract/build.ts +289 -8
  147. package/src/forge/compiler/agent-contract/types.ts +43 -0
  148. package/src/forge/compiler/ai-registry/build.ts +62 -1
  149. package/src/forge/compiler/ai-registry/constants.ts +1 -1
  150. package/src/forge/compiler/ai-registry/parse.ts +98 -4
  151. package/src/forge/compiler/app-graph/forge-apis.ts +1 -0
  152. package/src/forge/compiler/dev-manifest/build.ts +3 -0
  153. package/src/forge/compiler/diagnostics/codes.ts +15 -0
  154. package/src/forge/compiler/diagnostics/create.ts +1 -1
  155. package/src/forge/compiler/make-registry/build.ts +13 -0
  156. package/src/forge/compiler/orchestrator/plan.ts +11 -0
  157. package/src/forge/compiler/orchestrator/serialize.ts +68 -0
  158. package/src/forge/compiler/package-graph/compiler.ts +13 -3
  159. package/src/forge/compiler/types/ai-registry.ts +25 -1
  160. package/src/forge/compiler/types/app-graph.ts +1 -0
  161. package/src/forge/compiler/types/cli.ts +1 -0
  162. package/src/forge/compiler/types/dev-manifest.ts +3 -0
  163. package/src/forge/dev/server.ts +508 -1
  164. package/src/forge/make/index.ts +126 -3
  165. package/src/forge/make/templates.ts +188 -0
  166. package/src/forge/make/types.ts +1 -0
  167. package/src/forge/runtime/ai/context.ts +210 -5
  168. package/src/forge/runtime/ai/types.ts +70 -0
  169. package/src/forge/runtime/auth/claims.ts +32 -0
  170. package/src/forge/runtime/auth/errors.ts +2 -0
  171. package/src/forge/runtime/context/create-context.ts +30 -6
  172. package/src/forge/runtime/db/memory-adapter.ts +2 -2
  173. package/src/forge/runtime/telemetry/scrubber.ts +56 -5
  174. package/src/forge/runtime/webhooks/security.ts +184 -0
  175. package/src/forge/server.ts +93 -0
  176. package/src/forge/version.ts +1 -1
  177. package/templates/b2b-support-web/package.json +1 -0
  178. package/templates/b2b-support-web/tsconfig.json +4 -1
  179. package/templates/minimal-web/package.json +1 -0
  180. 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
+ }
@@ -13,6 +13,7 @@ export const FORGE_BUILDER_APIS: Readonly<Record<string, ForgeKind>> = {
13
13
  definePolicies: "policy",
14
14
  workflow: "workflow",
15
15
  agent: "agent",
16
+ aiTool: "aiTool",
16
17
  telemetryEvent: "telemetryEvent",
17
18
  };
18
19
 
@@ -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 print --json"],
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
- diagnostics.push(forgePkgNoTypes(dep.name, subpath));
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
- diagnostics.push(forgePkgNoTypes(dep.name, subpath));
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
- diagnostics.push(forgePkgNoTypes(dep.name, subpath));
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
  }
@@ -12,6 +12,7 @@ export type ForgeKind =
12
12
  | "policy"
13
13
  | "workflow"
14
14
  | "agent"
15
+ | "aiTool"
15
16
  | "telemetryEvent";
16
17
 
17
18
  export interface ForgeSymbol {
@@ -79,6 +79,7 @@ export type InspectTarget =
79
79
  | "test-graph"
80
80
  | "test-plans"
81
81
  | "agent-contract"
82
+ | "agent-tools"
82
83
  | "agent-adapters"
83
84
  | "capability-map"
84
85
  | "framework"
@@ -11,6 +11,9 @@ export interface DevRoute {
11
11
  | "workflows"
12
12
  | "workflow-runs"
13
13
  | "workflow-process"
14
+ | "ai-agent-chat"
15
+ | "ai-agent-run"
16
+ | "ai-providers"
14
17
  | "queries"
15
18
  | "query";
16
19
  entryName?: string;