@treeseed/sdk 0.8.10 → 0.8.12

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.
@@ -0,0 +1,183 @@
1
+ const AGENT_OPERATION_NAMES = [
2
+ "switch",
3
+ "dev",
4
+ "verify",
5
+ "save",
6
+ "stage",
7
+ "merge_to_staging",
8
+ "close",
9
+ "release"
10
+ ];
11
+ const AGENT_OPERATION_MODES = ["dry_run", "read_only", "mutating"];
12
+ function normalizePath(value) {
13
+ return value.replace(/\\/gu, "/").replace(/^\.?\//u, "").replace(/\/+/gu, "/");
14
+ }
15
+ function matchesPattern(path, pattern) {
16
+ const normalizedPath = normalizePath(path);
17
+ const normalizedPattern = normalizePath(pattern);
18
+ if (normalizedPattern.endsWith("/**")) {
19
+ const prefix = normalizedPattern.slice(0, -3);
20
+ return normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`);
21
+ }
22
+ if (normalizedPattern.endsWith("/")) {
23
+ return normalizedPath.startsWith(normalizedPattern);
24
+ }
25
+ return normalizedPath === normalizedPattern || normalizedPath.startsWith(`${normalizedPattern}/`);
26
+ }
27
+ function listMatches(value, allowed) {
28
+ return !allowed?.length || value !== void 0 && allowed.includes(value);
29
+ }
30
+ function grantActive(grant, now) {
31
+ if ((grant.state ?? "active") !== "active") {
32
+ return "operation_grant_inactive";
33
+ }
34
+ if (grant.expiresAt && Date.parse(grant.expiresAt) <= now.valueOf()) {
35
+ return "operation_grant_expired";
36
+ }
37
+ return null;
38
+ }
39
+ function deny(code, summary, options = {}) {
40
+ return {
41
+ allowed: false,
42
+ status: options.status ?? "waiting",
43
+ code,
44
+ summary,
45
+ grant: options.grant,
46
+ metadata: options.metadata ?? {}
47
+ };
48
+ }
49
+ function allow(grant, metadata = {}) {
50
+ return {
51
+ allowed: true,
52
+ status: "completed",
53
+ code: "allowed",
54
+ summary: `Operation grant ${grant.id} allows this request.`,
55
+ grant,
56
+ metadata
57
+ };
58
+ }
59
+ function isAgentOperationName(value) {
60
+ return AGENT_OPERATION_NAMES.includes(value);
61
+ }
62
+ function resolveAgentOperationGrant(request, grants, now = /* @__PURE__ */ new Date()) {
63
+ return grants.find((grant) => {
64
+ if (request.permissionGrantId && grant.id !== request.permissionGrantId) return false;
65
+ if (grantActive(grant, now)) return false;
66
+ if (!grant.operations.includes(request.operation)) return false;
67
+ return true;
68
+ }) ?? null;
69
+ }
70
+ function decideAgentOperationPermission(input) {
71
+ const { request } = input;
72
+ const now = input.now ?? /* @__PURE__ */ new Date();
73
+ if (!isAgentOperationName(request.operation)) {
74
+ return deny("invalid_operation", `Unsupported agent operation "${String(request.operation)}".`, { status: "failed" });
75
+ }
76
+ if (request.operation === "release") {
77
+ const approval = request.approval;
78
+ if (!approval || approval.state !== "approved" || request.approvalId && approval.id !== request.approvalId) {
79
+ return deny("operation_release_approval_required", "Release requires an explicit approved release approval.");
80
+ }
81
+ }
82
+ const grant = resolveAgentOperationGrant(request, input.grants, now);
83
+ if (!grant) {
84
+ return deny("operation_permission_required", `No operation grant allows ${request.agentRole} to run ${request.operation}.`);
85
+ }
86
+ const activeCode = grantActive(grant, now);
87
+ if (activeCode) {
88
+ return deny(activeCode, `Operation grant ${grant.id} is not active.`, { grant });
89
+ }
90
+ if (!grant.modes.includes(request.mode)) {
91
+ return deny("operation_mode_not_granted", `Operation grant ${grant.id} does not allow ${request.mode} mode.`, { grant });
92
+ }
93
+ if (!listMatches(request.agentRole, grant.agentRoles)) {
94
+ return deny("operation_role_not_granted", `Operation grant ${grant.id} does not allow role ${request.agentRole}.`, { grant });
95
+ }
96
+ if (!listMatches(request.taskKind, grant.taskKinds)) {
97
+ return deny("operation_task_kind_not_granted", `Operation grant ${grant.id} does not allow task kind ${request.taskKind ?? "<none>"}.`, { grant });
98
+ }
99
+ if (!listMatches(request.projectId, grant.projectIds)) {
100
+ return deny("operation_project_not_granted", `Operation grant ${grant.id} does not allow project ${request.projectId}.`, { grant });
101
+ }
102
+ if (!listMatches(request.environment, grant.environments)) {
103
+ return deny("operation_environment_not_granted", `Operation grant ${grant.id} does not allow environment ${request.environment}.`, { grant });
104
+ }
105
+ if (grant.requiresApproval && (!request.approval || request.approval.state !== "approved")) {
106
+ return deny("operation_approval_required", `Operation grant ${grant.id} requires approval.`, { grant });
107
+ }
108
+ if (grant.approvalIds?.length && (!request.approval || !grant.approvalIds.includes(request.approval.id) || request.approval.state !== "approved")) {
109
+ return deny("operation_approval_required", `Operation grant ${grant.id} requires one of its approved approval ids.`, { grant });
110
+ }
111
+ if (request.mode === "mutating" && !request.worktreeRoot && request.operation !== "release") {
112
+ return deny("operation_worktree_required", `Mutating operation ${request.operation} requires an assigned worktree root.`, { grant });
113
+ }
114
+ const allowedPaths = request.allowedPaths?.length ? request.allowedPaths : grant.allowedPaths ?? [];
115
+ const forbiddenPaths = [...grant.forbiddenPaths ?? [], ...request.forbiddenPaths ?? []];
116
+ const changedPaths = request.changedPaths ?? [];
117
+ if ((request.operation === "stage" || request.operation === "merge_to_staging") && allowedPaths.length === 0) {
118
+ return deny("operation_allowed_paths_required", `${request.operation} requires allowed paths.`, { grant });
119
+ }
120
+ for (const changedPath of changedPaths) {
121
+ if (forbiddenPaths.some((pattern) => matchesPattern(changedPath, pattern))) {
122
+ return deny("operation_path_forbidden", `${changedPath} is forbidden.`, {
123
+ grant,
124
+ status: "failed",
125
+ metadata: { changedPath, forbiddenPaths }
126
+ });
127
+ }
128
+ if (allowedPaths.length > 0 && !allowedPaths.some((pattern) => matchesPattern(changedPath, pattern))) {
129
+ return deny("operation_path_not_allowed", `${changedPath} is outside allowed paths.`, {
130
+ grant,
131
+ status: "failed",
132
+ metadata: { changedPath, allowedPaths }
133
+ });
134
+ }
135
+ }
136
+ return allow(grant, { allowedPaths, forbiddenPaths });
137
+ }
138
+ function deniedAgentOperationResult(request, decision) {
139
+ return {
140
+ operation: request.operation,
141
+ status: decision.status,
142
+ summary: decision.summary,
143
+ changedPaths: request.changedPaths ?? [],
144
+ stagedPaths: [],
145
+ commandsRun: [],
146
+ artifacts: [],
147
+ error: {
148
+ code: decision.code,
149
+ message: decision.summary,
150
+ retryable: decision.status === "waiting"
151
+ },
152
+ metadata: {
153
+ permission: decision
154
+ }
155
+ };
156
+ }
157
+ function createAgentOperationEvent(input) {
158
+ return {
159
+ operation: input.request.operation,
160
+ mode: input.request.mode,
161
+ agentRole: input.request.agentRole,
162
+ taskId: input.request.taskId,
163
+ permissionGrantId: input.request.permissionGrantId,
164
+ inputSummary: {
165
+ projectId: input.request.projectId,
166
+ environment: input.request.environment,
167
+ allowedPaths: input.request.allowedPaths ?? [],
168
+ forbiddenPaths: input.request.forbiddenPaths ?? [],
169
+ changedPaths: input.request.changedPaths ?? []
170
+ },
171
+ result: input.result,
172
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
173
+ };
174
+ }
175
+ export {
176
+ AGENT_OPERATION_MODES,
177
+ AGENT_OPERATION_NAMES,
178
+ createAgentOperationEvent,
179
+ decideAgentOperationPermission,
180
+ deniedAgentOperationResult,
181
+ isAgentOperationName,
182
+ resolveAgentOperationGrant
183
+ };
@@ -29,7 +29,7 @@ const danglingSubjectEndings = /* @__PURE__ */ new Set([
29
29
  "update",
30
30
  "with"
31
31
  ]);
32
- const defaultTimeoutMs = 3e4;
32
+ const defaultTimeoutMs = 6e4;
33
33
  const defaultMaxDiffChars = 12e3;
34
34
  const subjectMaxLength = 72;
35
35
  function envValue(env, key) {
@@ -1,4 +1,5 @@
1
1
  import { existsSync, readdirSync } from "node:fs";
2
+ import { readFileSync } from "node:fs";
2
3
  import { resolve } from "node:path";
3
4
  import { spawnSync } from "node:child_process";
4
5
  import { resolveWranglerBin } from "./runtime-tools.js";
@@ -98,6 +99,28 @@ function markMigrationApplied({ cwd, wranglerConfig, persistTo, migration }) {
98
99
  command: `INSERT OR REPLACE INTO treeseed_schema_migrations (name, applied_at) VALUES ('${migration.replace(/'/g, "''")}', datetime('now'));`
99
100
  });
100
101
  }
102
+ function tableColumns({ cwd, wranglerConfig, persistTo, tableName }) {
103
+ const result = executeSqlCommand({
104
+ cwd,
105
+ wranglerConfig,
106
+ persistTo,
107
+ capture: true,
108
+ command: `PRAGMA table_info(${tableName.replace(/[^A-Za-z0-9_]/g, "")});`
109
+ });
110
+ const parsed = parseWranglerJsonOutput(result.stdout);
111
+ const rows = (Array.isArray(parsed) ? parsed : [parsed]).flatMap((entry) => entry.results ?? []);
112
+ return new Set(rows.map((row) => row.name).filter(Boolean));
113
+ }
114
+ function migrationAlreadySatisfied({ cwd, wranglerConfig, persistTo, filePath }) {
115
+ const sql = readFileSync(filePath, "utf8");
116
+ if (!sql.includes("execution_profile_id") || !sql.includes("task_estimate_profiles")) {
117
+ return false;
118
+ }
119
+ const estimateColumns = tableColumns({ cwd, wranglerConfig, persistTo, tableName: "task_estimates" });
120
+ const actualColumns = tableColumns({ cwd, wranglerConfig, persistTo, tableName: "task_usage_actuals" });
121
+ const profileColumns = tableColumns({ cwd, wranglerConfig, persistTo, tableName: "task_estimate_profiles" });
122
+ return estimateColumns.has("execution_profile_id") && actualColumns.has("execution_profile_id") && profileColumns.has("execution_profile_id") && profileColumns.has("completed_sample_count") && profileColumns.has("interrupted_sample_count") && profileColumns.has("confidence_score");
123
+ }
101
124
  function runLocalD1Migrations({ cwd, wranglerConfig, migrationsRoot, persistTo }) {
102
125
  ensureSchemaMigrationsTable({ cwd, wranglerConfig, persistTo });
103
126
  const appliedMigrations = loadAppliedMigrations({ cwd, wranglerConfig, persistTo });
@@ -111,6 +134,10 @@ function runLocalD1Migrations({ cwd, wranglerConfig, migrationsRoot, persistTo }
111
134
  console.error(`Unable to find migration file at ${filePath}.`);
112
135
  process.exit(1);
113
136
  }
137
+ if (migrationAlreadySatisfied({ cwd, wranglerConfig, persistTo, filePath })) {
138
+ markMigrationApplied({ cwd, wranglerConfig, persistTo, migration });
139
+ continue;
140
+ }
114
141
  executeSqlFile({ cwd, wranglerConfig, filePath, persistTo });
115
142
  markMigrationApplied({ cwd, wranglerConfig, persistTo, migration });
116
143
  }
@@ -352,7 +352,7 @@ function applyManagedProjectDefaults(projectRoot, input) {
352
352
  ...config.providers ?? {},
353
353
  forms: config.providers?.forms ?? "store_only",
354
354
  agents: {
355
- execution: "copilot",
355
+ execution: "codex",
356
356
  mutation: "local_branch",
357
357
  repository: "git",
358
358
  verification: "local",
@@ -37,7 +37,7 @@ const TREESEED_DEFAULT_PLUGIN_REFERENCES = [
37
37
  const TREESEED_DEFAULT_PROVIDER_SELECTIONS = {
38
38
  forms: "store_only",
39
39
  agents: {
40
- execution: "copilot",
40
+ execution: "codex",
41
41
  mutation: "local_branch",
42
42
  repository: "git",
43
43
  verification: "local",
@@ -5,5 +5,6 @@ export { TreeseedOperationsSdk } from './operations/runtime.ts';
5
5
  export type { HubContentResolutionPolicy, KnowledgeHubLaunchIntent, KnowledgeHubLaunchPhase, KnowledgeHubLaunchPlan, KnowledgeHubLaunchResult, KnowledgeHubRepositoryPlan, RepositoryHost, } from './operations/services/hub-launch.ts';
6
6
  export type { TreeseedOperationContext, TreeseedOperationImplementation, TreeseedOperationId, TreeseedOperationMetadata, TreeseedOperationProvider, TreeseedOperationProviderId, TreeseedOperationRequest, TreeseedOperationResult, TreeseedOperationGroup, } from './operations-types.ts';
7
7
  export { TreeseedOperationError } from './operations-types.ts';
8
+ export { AGENT_OPERATION_MODES, AGENT_OPERATION_NAMES, createAgentOperationEvent, decideAgentOperationPermission, deniedAgentOperationResult, isAgentOperationName, resolveAgentOperationGrant, type AgentDeterministicOperationStep, type AgentOperationApprovalRef, type AgentOperationEvent, type AgentOperationGrant, type AgentOperationMergeFailure, type AgentOperationMode, type AgentOperationName, type AgentOperationPermissionCode, type AgentOperationPermissionDecision, type AgentOperationRequest, type AgentOperationResult, type AgentOperationStatus, } from './operations/agent-tools.ts';
8
9
  export { TreeseedWorkflowSdk } from './workflow.ts';
9
10
  export type * from './workflow.ts';
@@ -15,20 +15,36 @@ import {
15
15
  } from "./operations/services/hub-launch.js";
16
16
  import { TreeseedOperationsSdk } from "./operations/runtime.js";
17
17
  import { TreeseedOperationError } from "./operations-types.js";
18
+ import {
19
+ AGENT_OPERATION_MODES,
20
+ AGENT_OPERATION_NAMES,
21
+ createAgentOperationEvent,
22
+ decideAgentOperationPermission,
23
+ deniedAgentOperationResult,
24
+ isAgentOperationName,
25
+ resolveAgentOperationGrant
26
+ } from "./operations/agent-tools.js";
18
27
  import { TreeseedWorkflowSdk } from "./workflow.js";
19
28
  export {
29
+ AGENT_OPERATION_MODES,
30
+ AGENT_OPERATION_NAMES,
20
31
  TRESEED_OPERATION_SPECS,
21
32
  TreeseedOperationError,
22
33
  TreeseedOperationsSdk,
23
34
  TreeseedWorkflowSdk,
24
35
  collectTreeseedConfigSeedValues,
36
+ createAgentOperationEvent,
25
37
  createKnowledgeHubRepositories,
38
+ decideAgentOperationPermission,
26
39
  defaultHubContentResolutionPolicy,
40
+ deniedAgentOperationResult,
27
41
  executeKnowledgeHubLaunch,
28
42
  findTreeseedOperation,
43
+ isAgentOperationName,
29
44
  listTreeseedOperationNames,
30
45
  normalizeKnowledgeHubLaunchIntent,
31
46
  planKnowledgeHubLaunch,
32
47
  planKnowledgeHubRepositories,
48
+ resolveAgentOperationGrant,
33
49
  validateRepositoryHost
34
50
  };
@@ -106,6 +106,10 @@ function processingPlaneEnabled(context) {
106
106
  function formsEnabled(context) {
107
107
  return webSurfaceEnabled(context) && (context.deployConfig.providers?.forms ?? "store_only") !== "none";
108
108
  }
109
+ function codexExecutionSelected(context) {
110
+ const execution = context.deployConfig.providers?.agents?.execution ?? "codex";
111
+ return execution === "codex" || execution === "codex_subscription";
112
+ }
109
113
  function railwayManagedEnabled(context) {
110
114
  if (!workflowPlaneAllows("processing")) {
111
115
  return false;
@@ -333,6 +337,7 @@ const PREDICATES = {
333
337
  apiSurfaceEnabled: (context) => apiSurfaceEnabled(context),
334
338
  processingPlaneEnabled: (context) => processingPlaneEnabled(context),
335
339
  formsEnabled: (context) => formsEnabled(context),
340
+ codexExecutionSelected: (context) => codexExecutionSelected(context),
336
341
  railwayManagedEnabled: (context) => railwayManagedEnabled(context),
337
342
  hubTreeseedHosted: (context) => resolveHubMode(context) === "treeseed_hosted",
338
343
  hubCustomerHosted: (context) => resolveHubMode(context) === "customer_hosted",
@@ -3,7 +3,7 @@ const TREESEED_DEFAULT_PROVIDER_SELECTIONS = {
3
3
  forms: "store_only",
4
4
  operations: "default",
5
5
  agents: {
6
- execution: "copilot",
6
+ execution: "codex",
7
7
  mutation: "local_branch",
8
8
  repository: "git",
9
9
  verification: "local",
@@ -6,7 +6,7 @@ var plugin_default_default = defineTreeseedPlugin({
6
6
  forms: ["store_only", "notify_admin", "full_email"],
7
7
  operations: ["default"],
8
8
  agents: {
9
- execution: ["stub", "manual", "copilot"],
9
+ execution: ["stub", "manual", "copilot", "codex", "codex_subscription"],
10
10
  mutation: ["local_branch"],
11
11
  repository: ["stub", "git"],
12
12
  verification: ["stub", "local"],
@@ -412,6 +412,224 @@ export type CapacityReservationState = 'reserved' | 'consuming' | 'consumed' | '
412
412
  export type CapacityEstimatePhase = 'intent' | 'discovery' | 'plan' | 'execution' | 'actual';
413
413
  export type CapacityEstimateConfidence = 'low' | 'medium' | 'high';
414
414
  export type CapacityApprovalState = 'pending' | 'approved' | 'rejected' | 'expired' | 'superseded';
415
+ export type TaskRiskClass = 'low' | 'medium' | 'high';
416
+ export type TaskMutationScope = 'none' | 'repository_read' | 'repository_write' | 'production';
417
+ export type TaskConcurrencyClass = 'read_only' | 'repository_claim' | 'exclusive_project' | 'human_attention';
418
+ export type TaskAdmissionOutcome = 'admitted' | 'planning_required' | 'approval_required' | 'budget_blocked' | 'deferred' | 'rejected';
419
+ export type CanonicalTaskState = 'pending' | 'queued' | 'claimed' | 'running' | 'completed' | 'failed' | 'waiting' | 'paused_for_approval' | 'checkpointing' | 'checkpointed' | 'continuation_required' | 'rollback_required' | 'rollback_complete' | 'provider_exhausted' | 'reservation_exhausted';
420
+ export type RepositoryWorkState = 'clean' | 'claimed_dirty' | 'checkpointed_dirty' | 'parked_dirty' | 'rollback_required';
421
+ export interface TaskClassification {
422
+ taskSignature: string;
423
+ risk: TaskRiskClass;
424
+ mutationScope: TaskMutationScope;
425
+ concurrencyClass: TaskConcurrencyClass;
426
+ expectedFanout: number;
427
+ confidence: CapacityEstimateConfidence;
428
+ requiresPlanning: boolean;
429
+ requiresApproval: boolean;
430
+ features?: Record<string, unknown>;
431
+ }
432
+ export interface ExecutionProfile {
433
+ id: string;
434
+ providerId?: string | null;
435
+ laneId?: string | null;
436
+ modelFamily?: string | null;
437
+ modelClass?: string | null;
438
+ contextWindowTokens?: number | null;
439
+ qualityWeight: number;
440
+ costMultiplier: number;
441
+ latencyClass: 'low' | 'medium' | 'high' | string;
442
+ concurrencyClass?: TaskConcurrencyClass | null;
443
+ quotaBehavior?: 'api_metered' | 'subscription_limited' | 'compute_bound' | 'attention_bound' | string | null;
444
+ metadata?: Record<string, unknown>;
445
+ }
446
+ export interface AttentionEstimate {
447
+ attentionWeight: number;
448
+ coordinationWeight: number;
449
+ totalAttentionWeight: number;
450
+ estimatedContextTokens: number;
451
+ requiredContextTokens: number;
452
+ source: string;
453
+ metadata?: Record<string, unknown>;
454
+ }
455
+ export interface AttentionPolicy {
456
+ maxAttentionLoad: number | null;
457
+ reserveAttentionPercent: number;
458
+ maxContextTokens: number | null;
459
+ maxContextSaturationPercent: number;
460
+ coordinationOverheadFactor: number;
461
+ }
462
+ export interface UtilityEstimate {
463
+ utilityValue: number;
464
+ maintenanceValue: number;
465
+ deadlinePressure: number;
466
+ successProbability: number;
467
+ qualityScore: number;
468
+ riskPenalty: number;
469
+ utilityScore: number;
470
+ utilityPerCredit: number;
471
+ source: string;
472
+ metadata?: Record<string, unknown>;
473
+ }
474
+ export interface UtilityPolicy {
475
+ minimumUtilityScore: number | null;
476
+ minimumUtilityPerCredit: number | null;
477
+ riskPenaltyFactor: number;
478
+ deadlineWindowHours: number;
479
+ maintenanceWeight: number;
480
+ priorityWeight: number;
481
+ }
482
+ export interface PredictiveReservePolicy {
483
+ enabled: boolean;
484
+ baseReservePercent: number;
485
+ maxReservePercent: number;
486
+ incidentReservePercent: number;
487
+ triggerBurstReservePercent: number;
488
+ deploymentWindowReservePercent: number;
489
+ providerDegradationReservePercent: number;
490
+ quotaPressureReservePercent: number;
491
+ }
492
+ export interface ReservePrediction {
493
+ reservePercent: number;
494
+ reserveCredits: number;
495
+ activelyAllocatableCredits: number;
496
+ reasons: string[];
497
+ signals: Record<string, unknown>;
498
+ }
499
+ export interface HybridExecutionPhase {
500
+ id: string;
501
+ kind: 'planning' | 'implementation' | 'review' | 'human_escalation' | string;
502
+ executionProfileId: string;
503
+ taskSignature?: string | null;
504
+ required: boolean;
505
+ admissionRequired: boolean;
506
+ mutationAllowed: boolean;
507
+ metadata?: Record<string, unknown>;
508
+ }
509
+ export interface HybridExecutionPlan {
510
+ schemaVersion: 1;
511
+ planId: string;
512
+ phases: HybridExecutionPhase[];
513
+ escalationPolicy?: Record<string, unknown>;
514
+ metadata?: Record<string, unknown>;
515
+ }
516
+ export interface WorkdayBudgetEnvelope {
517
+ dailyCreditBudget: number;
518
+ usedCredits: number;
519
+ queuedCredits: number;
520
+ reserveBufferCredits: number;
521
+ recoveryBudgetCredits: number;
522
+ activelyAllocatableCredits: number;
523
+ remainingCredits: number;
524
+ }
525
+ export interface TaskAdmissionPolicy {
526
+ planningThresholdCredits: number;
527
+ approvalThresholdCredits: number;
528
+ reserveBufferPercent: number;
529
+ recoveryBudgetCredits: number;
530
+ maxDownstreamTasks: number;
531
+ maxPlanningDepth: number;
532
+ maxAdmittedPlanTasksPerCycle: number;
533
+ planningTaskSignature: string;
534
+ allowBackfill?: boolean;
535
+ maxAttentionLoad?: number | null;
536
+ reserveAttentionPercent?: number | null;
537
+ maxContextTokens?: number | null;
538
+ maxContextSaturationPercent?: number | null;
539
+ coordinationOverheadFactor?: number | null;
540
+ predictiveReservePolicy?: Partial<PredictiveReservePolicy> | null;
541
+ utilityPolicy?: Partial<UtilityPolicy> | null;
542
+ }
543
+ export interface TaskAdmissionDecision {
544
+ outcome: TaskAdmissionOutcome;
545
+ taskSignature: string;
546
+ estimatedCreditsP50: number;
547
+ estimatedCreditsP90: number;
548
+ reservedCredits: number;
549
+ baseReservedCredits?: number;
550
+ executionProfileId?: string | null;
551
+ costMultiplier?: number | null;
552
+ reasons: string[];
553
+ requiresApproval: boolean;
554
+ requiresPlanning: boolean;
555
+ budget: WorkdayBudgetEnvelope;
556
+ policySnapshot: TaskAdmissionPolicy;
557
+ metadata?: Record<string, unknown>;
558
+ }
559
+ export interface TaskCheckpointArtifact {
560
+ id?: string;
561
+ taskId: string;
562
+ checkpointId?: string;
563
+ branch?: string | null;
564
+ baseCommit?: string | null;
565
+ currentCommit?: string | null;
566
+ currentGoal?: string | null;
567
+ currentPhase?: string | null;
568
+ filesChanged: string[];
569
+ commandsRun: string[];
570
+ testStatus?: 'not_run' | 'passing' | 'failing' | 'unknown' | string;
571
+ knownFailures: string[];
572
+ completedWork: string[];
573
+ remainingWorkEstimate?: {
574
+ p50: number;
575
+ p90: number;
576
+ } | null;
577
+ rollbackStrategy?: string | null;
578
+ continuationStrategy?: string | null;
579
+ repositoryState: RepositoryWorkState;
580
+ createdAt: string;
581
+ metadata?: Record<string, unknown>;
582
+ }
583
+ export interface PlannedTaskNode {
584
+ id?: string;
585
+ type: string;
586
+ agentId?: string | null;
587
+ title?: string | null;
588
+ priority?: number | null;
589
+ taskSignature?: string | null;
590
+ payload?: Record<string, unknown>;
591
+ estimatedCreditsP50?: number | null;
592
+ estimatedCreditsP90?: number | null;
593
+ risk?: TaskRiskClass | null;
594
+ mutationScope?: TaskMutationScope | null;
595
+ confidence?: CapacityEstimateConfidence | null;
596
+ expectedFanout?: number | null;
597
+ requiresApproval?: boolean | null;
598
+ requiresPlanning?: boolean | null;
599
+ dependsOn?: string[];
600
+ metadata?: Record<string, unknown>;
601
+ }
602
+ export interface TaskPlanProposal {
603
+ schemaVersion: 1;
604
+ planId: string;
605
+ sourceTaskId?: string | null;
606
+ parentTaskId?: string | null;
607
+ planningDepth: number;
608
+ tasks: PlannedTaskNode[];
609
+ totalEstimatedCreditsP50: number;
610
+ totalEstimatedCreditsP90: number;
611
+ createdAt?: string | null;
612
+ metadata?: Record<string, unknown>;
613
+ }
614
+ export interface PlanningPolicy {
615
+ maxDownstreamTasks: number;
616
+ maxPlanningDepth: number;
617
+ maxAdmittedPlanTasksPerCycle: number;
618
+ planningTaskSignature: string;
619
+ }
620
+ export interface PlanningAdmissionResult {
621
+ proposal: TaskPlanProposal;
622
+ admitted: PlannedTaskNode[];
623
+ deferred: PlannedTaskNode[];
624
+ rejected: Array<{
625
+ node: PlannedTaskNode;
626
+ reasons: string[];
627
+ }>;
628
+ totalEstimatedCreditsP50: number;
629
+ totalEstimatedCreditsP90: number;
630
+ admittedCreditsP90: number;
631
+ reasons: string[];
632
+ }
415
633
  export interface CapacityProvider {
416
634
  id: string;
417
635
  teamId: string | null;
@@ -536,6 +754,7 @@ export interface TaskEstimate {
536
754
  projectId: string;
537
755
  estimatePhase: CapacityEstimatePhase;
538
756
  taskSignature: string;
757
+ executionProfileId: string;
539
758
  confidence: CapacityEstimateConfidence;
540
759
  estimatedCreditsP50: number;
541
760
  estimatedCreditsP90: number;
@@ -555,6 +774,7 @@ export interface TaskUsageActual {
555
774
  workDayId: string | null;
556
775
  projectId: string;
557
776
  taskSignature: string;
777
+ executionProfileId: string;
558
778
  capacityProviderId: string | null;
559
779
  laneId: string | null;
560
780
  businessModel: CapacityBusinessModel | string;
@@ -577,7 +797,10 @@ export interface TaskUsageActual {
577
797
  }
578
798
  export interface TaskEstimateProfile {
579
799
  taskSignature: string;
800
+ executionProfileId: string;
580
801
  sampleCount: number;
802
+ completedSampleCount?: number;
803
+ interruptedSampleCount?: number;
581
804
  inputTokensP50: number | null;
582
805
  inputTokensP90: number | null;
583
806
  outputTokensP50: number | null;
@@ -588,6 +811,12 @@ export interface TaskEstimateProfile {
588
811
  filesChangedP90: number | null;
589
812
  creditsP50: number | null;
590
813
  creditsP90: number | null;
814
+ creditsVariance?: number | null;
815
+ confidenceScore?: number | null;
816
+ outlierCount?: number;
817
+ partialCredits?: number | null;
818
+ firstSampleAt?: string | null;
819
+ lastSampleAt?: string | null;
591
820
  updatedAt: string;
592
821
  }
593
822
  export interface ApprovalRequest {
@@ -1690,6 +1919,7 @@ export interface CreateTaskEstimateRequest {
1690
1919
  projectId: string;
1691
1920
  estimatePhase: CapacityEstimatePhase;
1692
1921
  taskSignature: string;
1922
+ executionProfileId?: string | null;
1693
1923
  confidence: CapacityEstimateConfidence;
1694
1924
  estimatedCreditsP50: number;
1695
1925
  estimatedCreditsP90: number;
@@ -1708,6 +1938,7 @@ export interface CreateTaskUsageActualRequest {
1708
1938
  workDayId?: string | null;
1709
1939
  projectId: string;
1710
1940
  taskSignature: string;
1941
+ executionProfileId?: string | null;
1711
1942
  capacityProviderId?: string | null;
1712
1943
  laneId?: string | null;
1713
1944
  businessModel: CapacityBusinessModel | string;
@@ -72,7 +72,7 @@ plugins:
72
72
  providers:
73
73
  forms: store_only
74
74
  agents:
75
- execution: copilot
75
+ execution: codex
76
76
  mutation: local_branch
77
77
  repository: git
78
78
  verification: local
@@ -27,6 +27,18 @@ export interface AgentOutputContract {
27
27
  modelMutations: string[];
28
28
  }
29
29
  export interface AgentExecutionConfig {
30
+ provider?: string;
31
+ model?: string;
32
+ approvalPolicy?: 'never' | 'on_request' | 'always' | string;
33
+ sandboxMode?: 'read_only' | 'workspace_write' | string;
34
+ reasoningEffort?: 'low' | 'medium' | 'high' | string;
35
+ allowedPaths?: string[];
36
+ forbiddenPaths?: string[];
37
+ worktree?: {
38
+ enabled?: boolean;
39
+ root?: string;
40
+ branchPrefix?: string;
41
+ };
30
42
  maxConcurrency: number;
31
43
  timeoutSeconds: number;
32
44
  cooldownSeconds: number;