@cassiomc1/forgeloop 1.6.4 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/AGENT_COMPATIBILITY.md +11 -0
  2. package/DOCS_INDEX.md +1 -0
  3. package/GUIDE_ROUTER.md +26 -1
  4. package/LOOP_ENGINEERING.md +48 -0
  5. package/ORCHESTRATOR_INTEGRATION.md +38 -0
  6. package/PROTOCOL_INTEGRATION.md +62 -0
  7. package/README.md +25 -0
  8. package/benchmarks/execution-profiles/README.md +44 -0
  9. package/benchmarks/execution-profiles/api-feature.json +18 -0
  10. package/benchmarks/execution-profiles/authentication-change.json +18 -0
  11. package/benchmarks/execution-profiles/documentation-correction.json +18 -0
  12. package/benchmarks/execution-profiles/infrastructure-release.json +18 -0
  13. package/benchmarks/execution-profiles/novatask-saas-landing-page.json +36 -0
  14. package/benchmarks/execution-profiles/small-bug-fix.json +18 -0
  15. package/benchmarks/execution-profiles/static-landing-page.json +18 -0
  16. package/completions/_forgeloop +6 -4
  17. package/completions/forgeloop.bash +8 -4
  18. package/completions/forgeloop.fish +24 -1
  19. package/docs/AGENT_PROTOCOL_SUMMARY.md +45 -1
  20. package/docs/ARTIFACT_REFERENCE.md +53 -7
  21. package/docs/CLI_REFERENCE.md +83 -1
  22. package/docs/EXECUTION_PROFILE_BENCHMARKS.md +208 -0
  23. package/docs/GETTING_STARTED.md +28 -0
  24. package/docs/MCP.md +6 -0
  25. package/docs/RELEASE_CHECKLIST.md +4 -0
  26. package/docs/TROUBLESHOOTING.md +6 -0
  27. package/docs/UNIVERSAL_INTEGRATION.md +58 -0
  28. package/package.json +14 -2
  29. package/schemas/config.schema.json +1 -0
  30. package/schemas/execution-profile-benchmark-aggregate.schema.json +43 -0
  31. package/schemas/execution-profile-benchmark-run.schema.json +106 -0
  32. package/schemas/execution-profile-benchmark-scenario.schema.json +66 -0
  33. package/schemas/routing-result.schema.json +12 -0
  34. package/schemas/usage.schema.json +30 -0
  35. package/scripts/check-efficiency-regression.mjs +99 -0
  36. package/scripts/generate-agent-protocol-summary.mjs +35 -0
  37. package/scripts/lib/execution-profile-benchmark-io.mjs +67 -0
  38. package/scripts/run-execution-profile-benchmarks.mjs +265 -0
  39. package/scripts/summarize-execution-profile-benchmarks.mjs +84 -0
  40. package/scripts/validate-execution-profile-benchmarks.mjs +120 -0
  41. package/src/cli.js +16 -4
  42. package/src/commands/efficiency.js +12 -0
  43. package/src/commands/eval.js +8 -2
  44. package/src/commands/metrics.js +2 -2
  45. package/src/commands/next.js +26 -2
  46. package/src/commands/route.js +20 -2
  47. package/src/commands/task-show.js +31 -2
  48. package/src/commands/usage-record.js +61 -0
  49. package/src/core/artifact-registry.js +12 -0
  50. package/src/core/cli-command-definitions.js +27 -0
  51. package/src/core/command-executors.js +29 -5
  52. package/src/core/command-input.js +34 -0
  53. package/src/core/config.js +9 -0
  54. package/src/core/efficiency.js +197 -0
  55. package/src/core/error-codes.js +18 -0
  56. package/src/core/execution-profile-benchmarks.js +674 -0
  57. package/src/core/execution-profile-context.js +177 -0
  58. package/src/core/execution-profile.js +248 -0
  59. package/src/core/integration-invocation-policy.js +43 -0
  60. package/src/core/integration-resources.js +22 -1
  61. package/src/core/protocol-info.js +42 -0
  62. package/src/core/resumability.js +27 -1
  63. package/src/core/router.js +23 -1
  64. package/src/core/runtime-context.js +11 -0
  65. package/src/core/schema-validation.js +4 -0
  66. package/src/core/task-paths.js +2 -0
  67. package/src/core/templates.js +4 -0
  68. package/src/core/trace.js +1 -0
  69. package/src/core/trajectory-evaluation.js +2 -2
  70. package/src/core/trajectory-metrics.js +18 -2
  71. package/src/core/usage.js +137 -0
  72. package/src/integration.d.ts +84 -0
  73. package/src/integration.js +14 -0
@@ -1,4 +1,5 @@
1
1
  import { GUIDE_IDS, PROTOCOL_VERSION } from "./protocol.js";
2
+ import { assertExecutionProfile, resolveExecutionProfile } from "./execution-profile.js";
2
3
 
3
4
  export const ROUTING_SCHEMA_VERSION = 1;
4
5
 
@@ -50,6 +51,12 @@ const SIGNALS = Object.freeze({
50
51
  "critical-path",
51
52
  "performance",
52
53
  "accessibility",
54
+ "destructive",
55
+ "irreversible",
56
+ "production-deployment",
57
+ "credentials",
58
+ "payment",
59
+ "migration",
53
60
  ]),
54
61
  platforms: new Set(["web", "mobile", "desktop", "server", "ci", "cross-platform"]),
55
62
  });
@@ -167,7 +174,7 @@ export function normalizeRouteInput(input = {}) {
167
174
  };
168
175
  }
169
176
 
170
- export function evaluateRoute(input = {}) {
177
+ export function evaluateRoute(input = {}, profileOptions = {}) {
171
178
  const normalized = normalizeRouteInput(input);
172
179
  const selected = new Map();
173
180
  const excluded = {};
@@ -259,6 +266,14 @@ export function evaluateRoute(input = {}) {
259
266
  guides,
260
267
  reasons: Object.fromEntries(guides.map((guide) => [guide, selected.get(guide)])),
261
268
  excluded,
269
+ executionProfile: resolveExecutionProfile({
270
+ routeInput: normalized,
271
+ contract: profileOptions.contract ?? input.contract ?? null,
272
+ taskDescriptor: profileOptions.taskDescriptor ?? input.taskDescriptor ?? null,
273
+ configuredProfile: profileOptions.configuredProfile ?? input.configuredProfile ?? "auto",
274
+ requestedProfile: profileOptions.requestedProfile
275
+ ?? (Object.prototype.hasOwnProperty.call(input, "executionProfile") ? input.executionProfile : null),
276
+ }),
262
277
  };
263
278
  return assertRouteInvariants(result);
264
279
  }
@@ -278,6 +293,13 @@ export function assertRouteInvariants(result) {
278
293
  if (result.contractFingerprint !== undefined && !/^[a-f0-9]{64}$/.test(result.contractFingerprint)) {
279
294
  throw invariantError("E_ROUTE_INVALID", "Route contractFingerprint must be a lowercase SHA-256 fingerprint");
280
295
  }
296
+ if (result.executionProfile !== undefined) {
297
+ try {
298
+ assertExecutionProfile(result.executionProfile);
299
+ } catch (error) {
300
+ throw invariantError(error.code ?? "E_EXECUTION_PROFILE_INCONSISTENT", error.message);
301
+ }
302
+ }
281
303
  for (const guide of result.guides) {
282
304
  if (!GUIDE_IDS.includes(guide)) throw invariantError("E_ROUTE_INVALID", `Route result contains unknown guide: ${guide}`);
283
305
  if (!Array.isArray(result.reasons?.[guide]) || result.reasons[guide].length === 0) {
@@ -94,5 +94,16 @@ export function createForgeLoopContext(options = {}) {
94
94
  if (options?.verificationExecutionPolicy !== undefined) {
95
95
  context.verificationExecutionPolicy = normalizeVerificationExecutionPolicy(options.verificationExecutionPolicy);
96
96
  }
97
+ if (options?.usageProvider !== undefined) {
98
+ if (!options.usageProvider
99
+ || typeof options.usageProvider !== "object"
100
+ || Array.isArray(options.usageProvider)
101
+ || typeof options.usageProvider.getTaskUsage !== "function") {
102
+ const error = new Error("Usage provider must expose getTaskUsage({ projectPath, taskId })");
103
+ error.code = "E_USAGE_INVALID";
104
+ throw error;
105
+ }
106
+ context.usageProvider = options.usageProvider;
107
+ }
97
108
  return Object.freeze(context);
98
109
  }
@@ -50,6 +50,10 @@ export const SHIPPED_SCHEMA_NAMES = Object.freeze([
50
50
  "code-attestation",
51
51
  "attestation-verification-result",
52
52
  "in-toto-statement",
53
+ "usage",
54
+ "execution-profile-benchmark-scenario",
55
+ "execution-profile-benchmark-run",
56
+ "execution-profile-benchmark-aggregate",
53
57
  ]);
54
58
 
55
59
  export class SchemaValidationError extends Error {
@@ -18,6 +18,7 @@ export const TASK_ARTIFACT_FILES = Object.freeze({
18
18
  actions: "actions",
19
19
  approvals: "approvals",
20
20
  evaluations: "evaluations",
21
+ usage: "usage.json",
21
22
  policySnapshot: "policy-snapshot.json",
22
23
  recovery: "recovery.json",
23
24
  workspaceBinding: "workspace-binding.json",
@@ -193,6 +194,7 @@ export function buildTaskArtifactPaths(taskId) {
193
194
  actions: `${dir}/${TASK_ARTIFACT_FILES.actions}`,
194
195
  approvals: `${dir}/${TASK_ARTIFACT_FILES.approvals}`,
195
196
  evaluations: `${dir}/${TASK_ARTIFACT_FILES.evaluations}`,
197
+ usage: `${dir}/${TASK_ARTIFACT_FILES.usage}`,
196
198
  lock: taskLockPath(taskId),
197
199
  policySnapshot: `${dir}/${TASK_ARTIFACT_FILES.policySnapshot}`,
198
200
  recovery: `${dir}/${TASK_ARTIFACT_FILES.recovery}`,
@@ -83,6 +83,10 @@ export const TEMPLATE_PATHS = [
83
83
  "schemas/code-attestation.schema.json",
84
84
  "schemas/attestation-verification-result.schema.json",
85
85
  "schemas/in-toto-statement.schema.json",
86
+ "schemas/usage.schema.json",
87
+ "schemas/execution-profile-benchmark-scenario.schema.json",
88
+ "schemas/execution-profile-benchmark-run.schema.json",
89
+ "schemas/execution-profile-benchmark-aggregate.schema.json",
86
90
  ];
87
91
 
88
92
  export function getPackageRoot() {
package/src/core/trace.js CHANGED
@@ -68,6 +68,7 @@ const EVENT_CATEGORY_MAP = Object.freeze({
68
68
  APPROVAL_REQUESTED: "execution",
69
69
  APPROVAL_RESOLVED: "execution",
70
70
  TRAJECTORY_EVALUATED: "audit",
71
+ USAGE_RECORDED: "audit",
71
72
  });
72
73
 
73
74
  const LIFECYCLE_TRANSITIONS = Object.freeze([
@@ -12,7 +12,7 @@ import { withTaskTransaction } from "./transaction.js";
12
12
 
13
13
  function evaluationError(code, message) { const error = new Error(message); error.code = code; return error; }
14
14
 
15
- export async function evaluateTrajectory({ target, packageRoot, taskId, scenarioPath, evaluationId = `eval-${randomUUID()}` }) {
15
+ export async function evaluateTrajectory({ target, packageRoot, taskId, scenarioPath, evaluationId = `eval-${randomUUID()}`, runtimeContext = null }) {
16
16
  if (typeof scenarioPath !== "string" || !scenarioPath || path.isAbsolute(scenarioPath) || scenarioPath.includes("..")) {
17
17
  throw evaluationError("E_TRAJECTORY_SCENARIO_INVALID", "scenario path must be a relative project-local file");
18
18
  }
@@ -26,7 +26,7 @@ export async function evaluateTrajectory({ target, packageRoot, taskId, scenario
26
26
  assertSchema(scenario, await readSchema("trajectory-scenario", packageRoot), "trajectory scenario");
27
27
  if (!/^eval-[A-Za-z0-9_-]+$/.test(evaluationId)) throw evaluationError("E_TRAJECTORY_SCENARIO_INVALID", "evaluationId is invalid");
28
28
  const trace = await buildTaskTrace({ target, packageRoot, taskId });
29
- const metrics = await buildTrajectoryMetrics({ target, packageRoot, taskId });
29
+ const metrics = await buildTrajectoryMetrics({ target, packageRoot, taskId, runtimeContext });
30
30
  const milestoneSet = new Set(trace.events.map((event) => event.type));
31
31
  const missingMilestones = (scenario.requiredMilestones ?? []).filter((milestone) => !milestoneSet.has(milestone));
32
32
  const completionEvent = trace.events.find((event) => event.type === "COMPLETION_VALIDATED");
@@ -1,12 +1,15 @@
1
1
  import { buildTaskTrace } from "./trace.js";
2
2
  import { buildTaskReflection } from "./reflection.js";
3
+ import { readPersistedRoute } from "./route-artifact.js";
4
+ import { providerUsage, readTaskUsage, unknownUsage } from "./usage.js";
5
+ import { projectExecutionProfile } from "./execution-profile.js";
3
6
 
4
7
  export const COMPARABLE_WORK_EVENTS = new Set([
5
8
  "EXECUTION_STARTED", "VERIFICATION_RECORDED", "DIAGNOSTIC_CASE_RECORDED",
6
9
  "INTERVENTION_RECORDED", "ACTION_STARTED", "ACTION_RECONCILED", "REVIEW_STARTED",
7
10
  ]);
8
11
 
9
- export async function buildTrajectoryMetrics({ target, packageRoot, taskId }) {
12
+ export async function buildTrajectoryMetrics({ target, packageRoot, taskId, runtimeContext = null }) {
10
13
  const trace = await buildTaskTrace({ target, packageRoot, taskId });
11
14
  const reflection = await buildTaskReflection({ target, packageRoot, taskId });
12
15
  const events = trace.events;
@@ -18,6 +21,18 @@ export async function buildTrajectoryMetrics({ target, packageRoot, taskId }) {
18
21
  const firstMs = firstEventAt ? Date.parse(firstEventAt) : NaN;
19
22
  const lastMs = lastEventAt ? Date.parse(lastEventAt) : NaN;
20
23
  const interventions = reflection.interventions ?? { count: 0, informative: 0, nonInformative: 0 };
24
+ let executionProfile = null;
25
+ try {
26
+ const route = await readPersistedRoute(target, packageRoot, { taskId });
27
+ executionProfile = projectExecutionProfile(route.value);
28
+ } catch {
29
+ // Legacy tasks and incomplete task namespaces may not have a route.
30
+ }
31
+ let usage = await readTaskUsage(target, packageRoot, taskId);
32
+ const usageProvider = runtimeContext?.usageProvider;
33
+ if (usageProvider && typeof usageProvider.getTaskUsage === "function") {
34
+ usage = providerUsage(await usageProvider.getTaskUsage({ projectPath: target, taskId }));
35
+ }
21
36
  return {
22
37
  schemaVersion: 1,
23
38
  taskId,
@@ -74,7 +89,8 @@ export async function buildTrajectoryMetrics({ target, packageRoot, taskId }) {
74
89
  lastEventAt,
75
90
  wallClockMs: Number.isFinite(firstMs) && Number.isFinite(lastMs) ? Math.max(0, lastMs - firstMs) : null,
76
91
  },
77
- usage: { tokens: null, costUsd: null, source: "UNKNOWN" },
92
+ executionProfile,
93
+ usage: usage ?? unknownUsage(),
78
94
  comparableSteps: events.filter((event) => COMPARABLE_WORK_EVENTS.has(event.type)).length,
79
95
  };
80
96
  }
@@ -0,0 +1,137 @@
1
+ import { readJsonArtifact, writeJsonArtifact } from "./artifacts.js";
2
+ import { taskArtifactPath } from "./task-paths.js";
3
+
4
+ export const USAGE_SOURCES = Object.freeze([
5
+ "PROVIDER_REPORTED",
6
+ "HOST_REPORTED",
7
+ "ACTOR_REPORTED",
8
+ "UNKNOWN",
9
+ ]);
10
+
11
+ export const USAGE_FIELDS = Object.freeze([
12
+ "inputTokens",
13
+ "outputTokens",
14
+ "cacheReadTokens",
15
+ "cacheWriteTokens",
16
+ "totalTokens",
17
+ "costUsd",
18
+ "model",
19
+ "provider",
20
+ "source",
21
+ ]);
22
+
23
+ function usageError(code, message) {
24
+ const error = new Error(message);
25
+ error.code = code;
26
+ return error;
27
+ }
28
+
29
+ function nullableNonNegativeInteger(value, label) {
30
+ if (value === null || value === undefined || value === "") return null;
31
+ if (!Number.isInteger(value) || value < 0) {
32
+ throw usageError("E_USAGE_INVALID", `${label} must be a non-negative integer or null`);
33
+ }
34
+ return value;
35
+ }
36
+
37
+ function nullableCost(value) {
38
+ if (value === null || value === undefined || value === "") return null;
39
+ const numeric = typeof value === "number" ? value : Number(value);
40
+ if (!Number.isFinite(numeric) || numeric < 0) {
41
+ throw usageError("E_USAGE_INVALID", "costUsd must be a non-negative finite number or null");
42
+ }
43
+ return numeric;
44
+ }
45
+
46
+ function nullableString(value, label) {
47
+ if (value === null || value === undefined || value === "") return null;
48
+ if (typeof value !== "string" || !value.trim()) {
49
+ throw usageError("E_USAGE_INVALID", `${label} must be a non-empty string or null`);
50
+ }
51
+ return value.trim();
52
+ }
53
+
54
+ export function unknownUsage() {
55
+ return {
56
+ inputTokens: null,
57
+ outputTokens: null,
58
+ cacheReadTokens: null,
59
+ cacheWriteTokens: null,
60
+ totalTokens: null,
61
+ costUsd: null,
62
+ model: null,
63
+ provider: null,
64
+ source: "UNKNOWN",
65
+ };
66
+ }
67
+
68
+ export function normalizeUsage(value = {}, { defaultSource = "UNKNOWN", allowedSources = USAGE_SOURCES } = {}) {
69
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
70
+ throw usageError("E_USAGE_INVALID", "usage must be an object");
71
+ }
72
+ const source = value.source ?? defaultSource;
73
+ if (!allowedSources.includes(source)) {
74
+ throw usageError("E_USAGE_SOURCE_INVALID", `usage.source must be one of ${allowedSources.join(", ")}`);
75
+ }
76
+ return {
77
+ inputTokens: nullableNonNegativeInteger(value.inputTokens, "inputTokens"),
78
+ outputTokens: nullableNonNegativeInteger(value.outputTokens, "outputTokens"),
79
+ cacheReadTokens: nullableNonNegativeInteger(value.cacheReadTokens, "cacheReadTokens"),
80
+ cacheWriteTokens: nullableNonNegativeInteger(value.cacheWriteTokens, "cacheWriteTokens"),
81
+ totalTokens: nullableNonNegativeInteger(value.totalTokens, "totalTokens"),
82
+ costUsd: nullableCost(value.costUsd),
83
+ model: nullableString(value.model, "model"),
84
+ provider: nullableString(value.provider, "provider"),
85
+ source,
86
+ };
87
+ }
88
+
89
+ export function createUsageArtifact({ taskId, usage = {}, recordedAt = new Date().toISOString() } = {}) {
90
+ if (typeof taskId !== "string" || !taskId) throw usageError("E_USAGE_INVALID", "taskId is required for usage telemetry");
91
+ if (typeof recordedAt !== "string" || !recordedAt.trim() || Number.isNaN(Date.parse(recordedAt))) {
92
+ throw usageError("E_USAGE_INVALID", "recordedAt must be a valid timestamp");
93
+ }
94
+ return {
95
+ schemaVersion: 1,
96
+ protocolVersion: 1,
97
+ taskId,
98
+ recordedAt,
99
+ usage: normalizeUsage(usage),
100
+ };
101
+ }
102
+
103
+ export function providerUsage(value) {
104
+ if (value === null || value === undefined) return unknownUsage();
105
+ const candidate = value?.usage && typeof value.usage === "object" && !Array.isArray(value.usage)
106
+ ? value.usage
107
+ : value;
108
+ const source = candidate?.source ?? "HOST_REPORTED";
109
+ if (!["PROVIDER_REPORTED", "HOST_REPORTED"].includes(source)) {
110
+ throw usageError("E_USAGE_SOURCE_INVALID", "host usage providers may report only PROVIDER_REPORTED or HOST_REPORTED");
111
+ }
112
+ return normalizeUsage(candidate, { defaultSource: source, allowedSources: ["PROVIDER_REPORTED", "HOST_REPORTED"] });
113
+ }
114
+
115
+ export async function readTaskUsage(target, packageRoot, taskId) {
116
+ try {
117
+ const artifact = await readJsonArtifact(target, taskArtifactPath(taskId, "usage"), "usage", packageRoot);
118
+ return artifact.value.usage;
119
+ } catch (error) {
120
+ if (error.code === "ARTIFACT_MISSING") return unknownUsage();
121
+ if (error.code === "E_USAGE_SOURCE_INVALID" || error.code === "E_USAGE_INVALID") throw error;
122
+ error.code = "E_USAGE_INVALID";
123
+ throw error;
124
+ }
125
+ }
126
+
127
+ export async function writeTaskUsage(target, packageRoot, artifact, options = {}) {
128
+ const value = createUsageArtifact(artifact);
129
+ return writeJsonArtifact(
130
+ target,
131
+ taskArtifactPath(value.taskId, "usage"),
132
+ value,
133
+ "usage",
134
+ packageRoot,
135
+ { ...options, taskId: value.taskId, operation: options.operation ?? "usage-record" },
136
+ );
137
+ }
@@ -59,8 +59,92 @@ export interface ForgeLoopContext {
59
59
  authorityContext: Record<string, unknown>;
60
60
  verificationExecutionAdapter?: Record<string, unknown>;
61
61
  verificationExecutionPolicy?: { requiredIsolation: string };
62
+ usageProvider?: ForgeLoopUsageProvider;
62
63
  }
63
64
 
65
+ export interface ForgeLoopUsageProvider {
66
+ getTaskUsage(input: { projectPath: string; taskId: string }): Promise<ForgeLoopUsage | Record<string, unknown>> | ForgeLoopUsage | Record<string, unknown>;
67
+ }
68
+
69
+ export interface ForgeLoopUsage {
70
+ inputTokens: number | null;
71
+ outputTokens: number | null;
72
+ cacheReadTokens: number | null;
73
+ cacheWriteTokens: number | null;
74
+ totalTokens: number | null;
75
+ costUsd: number | null;
76
+ model: string | null;
77
+ provider: string | null;
78
+ source: "PROVIDER_REPORTED" | "HOST_REPORTED";
79
+ }
80
+
81
+ export interface ForgeLoopContextUsage {
82
+ source: "HOST_REPORTED" | "UNKNOWN";
83
+ profile: "light" | "balanced" | "full" | null;
84
+ items: {
85
+ taskContext: number | null;
86
+ guides: number | null;
87
+ history: number | null;
88
+ protocolInstructions: number | null;
89
+ repositoryContext: number | null;
90
+ other: number | null;
91
+ };
92
+ }
93
+
94
+ export declare const EXECUTION_PROFILES: readonly ["light", "balanced", "full"];
95
+ export declare const EXECUTION_PROFILE_REQUESTS: readonly ["auto", "light", "balanced", "full"];
96
+ export declare const LEGACY_EXECUTION_PROFILE: "balanced";
97
+ export declare function projectExecutionProfile(route?: Record<string, unknown> | null): "light" | "balanced" | "full" | null;
98
+ export declare function resolveExecutionProfile(input?: {
99
+ routeInput?: Record<string, unknown>;
100
+ contract?: Record<string, unknown> | null;
101
+ taskDescriptor?: Record<string, unknown> | null;
102
+ configuredProfile?: "auto" | "light" | "balanced" | "full";
103
+ requestedProfile?: "auto" | "light" | "balanced" | "full" | null;
104
+ }): {
105
+ requested: "auto" | "light" | "balanced" | "full";
106
+ floor: "light" | "balanced" | "full";
107
+ resolved: "light" | "balanced" | "full";
108
+ reasons: readonly string[];
109
+ escalated: boolean;
110
+ };
111
+
112
+ export interface ForgeLoopExecutionProfileContext {
113
+ schemaVersion: 1;
114
+ protocolVersion: 1;
115
+ taskId: string;
116
+ executionProfile: {
117
+ requested: "auto" | "light" | "balanced" | "full";
118
+ floor: "light" | "balanced" | "full";
119
+ resolved: "light" | "balanced" | "full";
120
+ reasons: readonly string[];
121
+ escalated: boolean;
122
+ };
123
+ phase: string;
124
+ nextAction: string | null;
125
+ objective: string | null;
126
+ deliverables: readonly string[];
127
+ constraints: readonly string[];
128
+ selectedGuideIds: readonly string[];
129
+ verificationRequirements: readonly Record<string, unknown>[];
130
+ contextPolicy: Record<string, unknown>;
131
+ contextUsage?: ForgeLoopContextUsage;
132
+ optionalContext: { available: readonly string[]; loaded: readonly string[] };
133
+ invariants: Record<string, boolean>;
134
+ }
135
+
136
+ export declare const PROFILE_CONTEXT_POLICIES: Readonly<Record<string, Record<string, unknown>>>;
137
+ export declare function getExecutionProfileContextPolicy(profile: "light" | "balanced" | "full"): Record<string, unknown>;
138
+ export declare function legacyExecutionProfile(): ForgeLoopExecutionProfileContext["executionProfile"];
139
+ export declare function projectExecutionProfileContext(input: Record<string, unknown>): ForgeLoopExecutionProfileContext;
140
+ export declare function buildExecutionProfileContext(input: {
141
+ target: string;
142
+ packageRoot?: string;
143
+ taskId: string;
144
+ authorityContext?: Record<string, unknown>;
145
+ runtimeContext?: Record<string, unknown>;
146
+ }): Promise<ForgeLoopExecutionProfileContext>;
147
+
64
148
  export declare const FORGELOOP_INTEGRATION_API_VERSION: number;
65
149
  export declare const FORGELOOP_INTEGRATION_RUNTIME_VERSION: number;
66
150
  export declare const CLI_COMMAND_DEFINITIONS: Readonly<Record<string, ForgeLoopCommandDefinition>>;
@@ -45,6 +45,20 @@ export {
45
45
  };
46
46
 
47
47
  export { createForgeLoopContext } from "./core/runtime-context.js";
48
+ export {
49
+ PROFILE_CONTEXT_POLICIES,
50
+ buildExecutionProfileContext,
51
+ getExecutionProfileContextPolicy,
52
+ legacyExecutionProfile,
53
+ projectExecutionProfileContext,
54
+ } from "./core/execution-profile-context.js";
55
+ export {
56
+ EXECUTION_PROFILES,
57
+ EXECUTION_PROFILE_REQUESTS,
58
+ LEGACY_EXECUTION_PROFILE,
59
+ projectExecutionProfile,
60
+ resolveExecutionProfile,
61
+ } from "./core/execution-profile.js";
48
62
  export {
49
63
  E_VERIFICATION_EXECUTION_INVALID,
50
64
  E_VERIFICATION_ISOLATION_UNAVAILABLE,