@treeseed/sdk 0.12.40 → 0.12.42
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/dist/agent-capacity.d.ts +184 -0
- package/dist/agent-capacity.js +225 -3
- package/dist/guarantees/index.d.ts +2 -0
- package/dist/guarantees/index.js +14 -1
- package/dist/hosting/graph.js +1 -1
- package/dist/local-dev/managed-dev.js +45 -4
- package/dist/operations/repository-operations.js +18 -7
- package/dist/operations/services/deployment-readiness.js +10 -2
- package/dist/operations/services/railway-deploy.js +1 -1
- package/dist/operations/services/template-registry.js +1 -2
- package/dist/platform/desired-state.js +1 -1
- package/dist/reconcile/builtin-adapters.js +10 -2
- package/dist/treeseed/template-catalog/catalog.fixture.json +0 -184
- package/dist/types/agents.d.ts +101 -3
- package/dist/types/agents.js +21 -7
- package/dist/workflow/operations.js +5 -3
- package/drizzle/market/0015_agent_decision_assignment_graphs.sql +60 -0
- package/package.json +1 -1
package/dist/agent-capacity.d.ts
CHANGED
|
@@ -1,6 +1,176 @@
|
|
|
1
1
|
import type { CapacityGrant, CapacityLedgerEntry, CapacityPlan as LegacyCapacityPlan, CapacityProvider, CapacityReservation, ExecutionProvider, TaskUsageActual } from './sdk-types.js';
|
|
2
|
+
import type { TreeseedContentRef } from './content-operations.js';
|
|
2
3
|
import type { AgentRuntimeSpec, AgentWorkPackage, ExecutionCapabilityDemand, ExecutionCapabilitySupply, ExecutionProviderDescriptor, ExecutionResourceNeed } from './types/agents.js';
|
|
3
4
|
export type AgentExecutionMode = 'planning' | 'acting';
|
|
5
|
+
export type AgentPlanningActivityType = 'planning' | 'estimating' | 'reviewing' | 'reporting';
|
|
6
|
+
export type ContentRef = TreeseedContentRef;
|
|
7
|
+
export type AgentEstimateConfidence = 'low' | 'medium' | 'high';
|
|
8
|
+
export type AgentEstimateRiskLevel = 'low' | 'medium' | 'high';
|
|
9
|
+
export type DecisionDependencyType = 'artifact' | 'capability' | 'decision' | 'external-resource' | 'human-input';
|
|
10
|
+
export interface AgentOutputRequirement {
|
|
11
|
+
id?: string;
|
|
12
|
+
outputType: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
contentModel?: string;
|
|
15
|
+
required?: boolean;
|
|
16
|
+
metadata?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
export interface DecisionDependencySpec {
|
|
19
|
+
id: string;
|
|
20
|
+
type: DecisionDependencyType;
|
|
21
|
+
requiredBefore: 'start' | 'complete' | 'review' | 'release';
|
|
22
|
+
optional?: boolean;
|
|
23
|
+
deliverableType?: string;
|
|
24
|
+
capability?: string;
|
|
25
|
+
agentClass?: string;
|
|
26
|
+
contentRefs?: string[];
|
|
27
|
+
humanInputPolicy?: {
|
|
28
|
+
requiredFrom: 'team-human' | 'any-human' | 'any-human-or-agent';
|
|
29
|
+
teamId?: string | null;
|
|
30
|
+
};
|
|
31
|
+
summary?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface StructuredAgentEstimate {
|
|
34
|
+
id: string;
|
|
35
|
+
teamId: string;
|
|
36
|
+
projectId: string;
|
|
37
|
+
decisionId?: string | null;
|
|
38
|
+
proposalId?: string | null;
|
|
39
|
+
workUnitId?: string | null;
|
|
40
|
+
agentClass: string;
|
|
41
|
+
agentId?: string | null;
|
|
42
|
+
minCredits: number;
|
|
43
|
+
expectedCredits: number;
|
|
44
|
+
maxCredits: number;
|
|
45
|
+
confidence: AgentEstimateConfidence;
|
|
46
|
+
riskLevel: AgentEstimateRiskLevel;
|
|
47
|
+
assumptions: string[];
|
|
48
|
+
blockers: string[];
|
|
49
|
+
dependencies: DecisionDependencySpec[];
|
|
50
|
+
expectedOutputs: AgentOutputRequirement[];
|
|
51
|
+
acceptanceCriteria: string[];
|
|
52
|
+
completionEvidence: string[];
|
|
53
|
+
createdAt?: string | null;
|
|
54
|
+
metadata?: Record<string, unknown>;
|
|
55
|
+
}
|
|
56
|
+
export interface DeliverableContract {
|
|
57
|
+
id: string;
|
|
58
|
+
teamId: string;
|
|
59
|
+
projectId: string;
|
|
60
|
+
decisionId: string;
|
|
61
|
+
deliverableType: string;
|
|
62
|
+
producerAgentClasses: string[];
|
|
63
|
+
reviewerAgentClasses?: string[];
|
|
64
|
+
requiredSections?: string[];
|
|
65
|
+
acceptanceCriteria: string[];
|
|
66
|
+
status: 'required' | 'draft' | 'submitted' | 'approved' | 'rejected';
|
|
67
|
+
metadata?: Record<string, unknown>;
|
|
68
|
+
}
|
|
69
|
+
export interface DeliverableManifest {
|
|
70
|
+
id: string;
|
|
71
|
+
deliverableContractId: string;
|
|
72
|
+
projectId: string;
|
|
73
|
+
decisionId: string;
|
|
74
|
+
producedRefs: ContentRef[];
|
|
75
|
+
coverage?: Record<string, ContentRef[]>;
|
|
76
|
+
summary: string;
|
|
77
|
+
readyForReview: boolean;
|
|
78
|
+
submittedByAgentId?: string | null;
|
|
79
|
+
submittedAt?: string | null;
|
|
80
|
+
metadata?: Record<string, unknown>;
|
|
81
|
+
}
|
|
82
|
+
export interface DecisionAssignmentGraphNode {
|
|
83
|
+
id: string;
|
|
84
|
+
decisionId: string;
|
|
85
|
+
projectId: string;
|
|
86
|
+
targetAgentClass: string;
|
|
87
|
+
activityType: AgentPlanningActivityType | 'acting';
|
|
88
|
+
handler?: string | null;
|
|
89
|
+
requiredCapabilities: string[];
|
|
90
|
+
requiredDeliverableContractIds: string[];
|
|
91
|
+
inputRefs: ContentRef[];
|
|
92
|
+
outputRequirements: AgentOutputRequirement[];
|
|
93
|
+
capacity: {
|
|
94
|
+
expectedCredits: number;
|
|
95
|
+
maxCredits: number;
|
|
96
|
+
};
|
|
97
|
+
status: 'pending' | 'ready' | 'leased' | 'running' | 'blocked' | 'completed' | 'failed' | 'cancelled';
|
|
98
|
+
metadata?: Record<string, unknown>;
|
|
99
|
+
}
|
|
100
|
+
export interface DecisionAssignmentGraphEdge {
|
|
101
|
+
fromNodeId: string;
|
|
102
|
+
toNodeId: string;
|
|
103
|
+
edgeType: 'blocks-start' | 'blocks-completion' | 'blocks-release';
|
|
104
|
+
reason?: string;
|
|
105
|
+
}
|
|
106
|
+
export interface DecisionAssignmentGraph {
|
|
107
|
+
id: string;
|
|
108
|
+
teamId: string;
|
|
109
|
+
projectId: string;
|
|
110
|
+
decisionId: string;
|
|
111
|
+
version: number;
|
|
112
|
+
status: 'draft' | 'compiled' | 'ready' | 'executing' | 'completed' | 'blocked';
|
|
113
|
+
estimateIds: string[];
|
|
114
|
+
deliverableContracts: DeliverableContract[];
|
|
115
|
+
nodes: DecisionAssignmentGraphNode[];
|
|
116
|
+
edges: DecisionAssignmentGraphEdge[];
|
|
117
|
+
compiledAt?: string | null;
|
|
118
|
+
compiledBy: 'api-control-plane';
|
|
119
|
+
metadata?: Record<string, unknown>;
|
|
120
|
+
}
|
|
121
|
+
export interface AgentCapacityContractDiagnostic {
|
|
122
|
+
severity: 'info' | 'warning' | 'error';
|
|
123
|
+
code: string;
|
|
124
|
+
message: string;
|
|
125
|
+
path?: string;
|
|
126
|
+
}
|
|
127
|
+
export interface AgentCapacityContractValidationResult {
|
|
128
|
+
ok: boolean;
|
|
129
|
+
diagnostics: AgentCapacityContractDiagnostic[];
|
|
130
|
+
}
|
|
131
|
+
export interface DecisionAssignmentGraphCompileResult {
|
|
132
|
+
graph: DecisionAssignmentGraph;
|
|
133
|
+
diagnostics: AgentCapacityContractDiagnostic[];
|
|
134
|
+
}
|
|
135
|
+
export interface WorkdayModeSplitPolicy {
|
|
136
|
+
targetPercent: number;
|
|
137
|
+
minPercent?: number;
|
|
138
|
+
maxPercent?: number;
|
|
139
|
+
hardCapPercent?: number;
|
|
140
|
+
}
|
|
141
|
+
export interface WorkdayModeSplits {
|
|
142
|
+
planning?: WorkdayModeSplitPolicy;
|
|
143
|
+
acting?: WorkdayModeSplitPolicy;
|
|
144
|
+
}
|
|
145
|
+
export interface PlanningReservationMetadata {
|
|
146
|
+
source: 'provider_assignment_synthesis' | string;
|
|
147
|
+
planningSource: 'live_workday' | 'planning_input_request' | 'decision_estimation' | 'decision_review' | 'fallback_planning' | string;
|
|
148
|
+
activityType: AgentPlanningActivityType;
|
|
149
|
+
projectAgentClassId?: string | null;
|
|
150
|
+
agentSlug?: string | null;
|
|
151
|
+
workdayId?: string | null;
|
|
152
|
+
allocationSetId?: string | null;
|
|
153
|
+
modeSplitVersion?: string | null;
|
|
154
|
+
}
|
|
155
|
+
export interface ModeCapacityReservationAvailabilityInput {
|
|
156
|
+
teamId: string;
|
|
157
|
+
capacityProviderId: string;
|
|
158
|
+
projectId: string;
|
|
159
|
+
workDayId?: string | null;
|
|
160
|
+
allocationSetId?: string | null;
|
|
161
|
+
projectAgentClassId?: string | null;
|
|
162
|
+
agentSlug?: string | null;
|
|
163
|
+
mode: AgentExecutionMode;
|
|
164
|
+
activityType?: AgentPlanningActivityType | 'acting' | string | null;
|
|
165
|
+
reservedCredits: number;
|
|
166
|
+
}
|
|
167
|
+
export interface ModeCapacityReservationAvailabilityResult {
|
|
168
|
+
ok: boolean;
|
|
169
|
+
hold?: boolean;
|
|
170
|
+
reason?: string | null;
|
|
171
|
+
gates: Record<string, unknown>;
|
|
172
|
+
}
|
|
173
|
+
export declare const DEFAULT_WORKDAY_MODE_SPLITS: Required<WorkdayModeSplits>;
|
|
4
174
|
export type AllocationSetStatus = 'draft' | 'active' | 'superseded' | 'archived';
|
|
5
175
|
export type ProjectAgentClassStatus = 'active' | 'paused' | 'archived';
|
|
6
176
|
export type ProviderAvailabilitySessionStatus = 'open' | 'draining' | 'closed' | 'expired';
|
|
@@ -728,6 +898,20 @@ export interface CapacitySettlementSummary {
|
|
|
728
898
|
metadata?: Record<string, unknown>;
|
|
729
899
|
createdAt?: string;
|
|
730
900
|
}
|
|
901
|
+
export declare function validateDecisionDependencySpec(dependency: DecisionDependencySpec, path?: string): AgentCapacityContractValidationResult;
|
|
902
|
+
export declare function validateStructuredAgentEstimate(estimate: StructuredAgentEstimate): AgentCapacityContractValidationResult;
|
|
903
|
+
export declare function validateDeliverableContract(contract: DeliverableContract): AgentCapacityContractValidationResult;
|
|
904
|
+
export declare function validateDeliverableManifest(manifest: DeliverableManifest): AgentCapacityContractValidationResult;
|
|
905
|
+
export declare function validateDecisionAssignmentGraph(graph: DecisionAssignmentGraph): AgentCapacityContractValidationResult;
|
|
906
|
+
export declare function compileDecisionAssignmentGraphFromEstimates(input: {
|
|
907
|
+
id?: string;
|
|
908
|
+
teamId: string;
|
|
909
|
+
projectId: string;
|
|
910
|
+
decisionId: string;
|
|
911
|
+
version?: number;
|
|
912
|
+
estimates: StructuredAgentEstimate[];
|
|
913
|
+
compiledAt?: string | null;
|
|
914
|
+
}): DecisionAssignmentGraphCompileResult;
|
|
731
915
|
export declare function computeDecisionScopeHash(scope: unknown): string;
|
|
732
916
|
export declare function isDecisionReadyForActing(status?: Pick<DecisionPlanningStatus, 'executionReadiness' | 'planningInputsStatus'> | null): boolean;
|
|
733
917
|
export declare function isPlanningInputOpen(request: Pick<PlanningInputRequest, 'status'>): boolean;
|
package/dist/agent-capacity.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const DEFAULT_WORKDAY_MODE_SPLITS = {
|
|
2
|
+
planning: { targetPercent: 25, minPercent: 10, maxPercent: 40 },
|
|
3
|
+
acting: { targetPercent: 75, minPercent: 60, maxPercent: 90 }
|
|
4
|
+
};
|
|
1
5
|
const AGENT_ASSIGNMENT_WORKSPACE_ACCESS_MODES = [
|
|
2
6
|
"context_only",
|
|
3
7
|
"workspace_write",
|
|
@@ -44,6 +48,208 @@ function firstArray(...values) {
|
|
|
44
48
|
}
|
|
45
49
|
return [];
|
|
46
50
|
}
|
|
51
|
+
function diagnostic(diagnostics, code, message, path, severity = "error") {
|
|
52
|
+
diagnostics.push({ severity, code, message, path });
|
|
53
|
+
}
|
|
54
|
+
function validateNonEmptyString(diagnostics, value, field, path = field) {
|
|
55
|
+
if (typeof value !== "string" || !value.trim()) diagnostic(diagnostics, "required_string_missing", `${field} is required.`, path);
|
|
56
|
+
}
|
|
57
|
+
function validateNonNegativeNumber(diagnostics, value, field, path = field) {
|
|
58
|
+
if (!Number.isFinite(Number(value)) || Number(value) < 0) {
|
|
59
|
+
diagnostic(diagnostics, "non_negative_number_required", `${field} must be a non-negative number.`, path);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function validationResult(diagnostics) {
|
|
63
|
+
return { ok: diagnostics.every((entry) => entry.severity !== "error"), diagnostics };
|
|
64
|
+
}
|
|
65
|
+
function dependencyToContractId(projectId, decisionId, dependency) {
|
|
66
|
+
const deliverable = dependency.deliverableType || dependency.capability || dependency.id;
|
|
67
|
+
return `${projectId}:${decisionId}:deliverable:${deliverable}`.replace(/[^a-zA-Z0-9:_-]+/gu, "-");
|
|
68
|
+
}
|
|
69
|
+
function edgeTypeForDependency(dependency) {
|
|
70
|
+
if (dependency.requiredBefore === "complete" || dependency.requiredBefore === "review") return "blocks-completion";
|
|
71
|
+
if (dependency.requiredBefore === "release") return "blocks-release";
|
|
72
|
+
return "blocks-start";
|
|
73
|
+
}
|
|
74
|
+
function validateDecisionDependencySpec(dependency, path = "dependency") {
|
|
75
|
+
const diagnostics = [];
|
|
76
|
+
validateNonEmptyString(diagnostics, dependency.id, "id", `${path}.id`);
|
|
77
|
+
if (!["artifact", "capability", "decision", "external-resource", "human-input"].includes(dependency.type)) {
|
|
78
|
+
diagnostic(diagnostics, "invalid_dependency_type", `Dependency ${dependency.id || "<unknown>"} has an invalid type.`, `${path}.type`);
|
|
79
|
+
}
|
|
80
|
+
if (!["start", "complete", "review", "release"].includes(dependency.requiredBefore)) {
|
|
81
|
+
diagnostic(diagnostics, "invalid_dependency_required_before", `Dependency ${dependency.id || "<unknown>"} has an invalid requiredBefore value.`, `${path}.requiredBefore`);
|
|
82
|
+
}
|
|
83
|
+
if (dependency.type === "artifact" && !dependency.deliverableType) diagnostic(diagnostics, "artifact_dependency_missing_deliverable_type", "Artifact dependencies must declare deliverableType.", `${path}.deliverableType`);
|
|
84
|
+
if (dependency.type === "capability" && !dependency.capability && !dependency.agentClass) diagnostic(diagnostics, "capability_dependency_missing_capability", "Capability dependencies must declare capability or agentClass.", `${path}.capability`);
|
|
85
|
+
if (dependency.type === "human-input") {
|
|
86
|
+
const policy = dependency.humanInputPolicy;
|
|
87
|
+
if (!policy || !["team-human", "any-human", "any-human-or-agent"].includes(policy.requiredFrom)) {
|
|
88
|
+
diagnostic(diagnostics, "human_input_policy_missing", "Human-input dependencies must declare a valid humanInputPolicy.requiredFrom.", `${path}.humanInputPolicy.requiredFrom`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return validationResult(diagnostics);
|
|
92
|
+
}
|
|
93
|
+
function validateStructuredAgentEstimate(estimate) {
|
|
94
|
+
const diagnostics = [];
|
|
95
|
+
validateNonEmptyString(diagnostics, estimate.id, "id");
|
|
96
|
+
validateNonEmptyString(diagnostics, estimate.teamId, "teamId");
|
|
97
|
+
validateNonEmptyString(diagnostics, estimate.projectId, "projectId");
|
|
98
|
+
validateNonEmptyString(diagnostics, estimate.agentClass, "agentClass");
|
|
99
|
+
if (!estimate.decisionId && !estimate.proposalId) diagnostic(diagnostics, "estimate_missing_subject", "Structured estimates must reference a decisionId or proposalId.", "decisionId");
|
|
100
|
+
validateNonNegativeNumber(diagnostics, estimate.minCredits, "minCredits");
|
|
101
|
+
validateNonNegativeNumber(diagnostics, estimate.expectedCredits, "expectedCredits");
|
|
102
|
+
validateNonNegativeNumber(diagnostics, estimate.maxCredits, "maxCredits");
|
|
103
|
+
if (Number(estimate.minCredits) > Number(estimate.expectedCredits) || Number(estimate.expectedCredits) > Number(estimate.maxCredits)) {
|
|
104
|
+
diagnostic(diagnostics, "estimate_credit_bounds_invalid", "Estimate credit bounds must satisfy min <= expected <= max.", "expectedCredits");
|
|
105
|
+
}
|
|
106
|
+
if (!["low", "medium", "high"].includes(estimate.confidence)) diagnostic(diagnostics, "estimate_confidence_invalid", "Estimate confidence must be low, medium, or high.", "confidence");
|
|
107
|
+
if (!["low", "medium", "high"].includes(estimate.riskLevel)) diagnostic(diagnostics, "estimate_risk_level_invalid", "Estimate riskLevel must be low, medium, or high.", "riskLevel");
|
|
108
|
+
for (const [index, dependency] of (estimate.dependencies ?? []).entries()) {
|
|
109
|
+
diagnostics.push(...validateDecisionDependencySpec(dependency, `dependencies.${index}`).diagnostics);
|
|
110
|
+
}
|
|
111
|
+
return validationResult(diagnostics);
|
|
112
|
+
}
|
|
113
|
+
function validateDeliverableContract(contract) {
|
|
114
|
+
const diagnostics = [];
|
|
115
|
+
validateNonEmptyString(diagnostics, contract.id, "id");
|
|
116
|
+
validateNonEmptyString(diagnostics, contract.teamId, "teamId");
|
|
117
|
+
validateNonEmptyString(diagnostics, contract.projectId, "projectId");
|
|
118
|
+
validateNonEmptyString(diagnostics, contract.decisionId, "decisionId");
|
|
119
|
+
validateNonEmptyString(diagnostics, contract.deliverableType, "deliverableType");
|
|
120
|
+
if (!Array.isArray(contract.producerAgentClasses) || contract.producerAgentClasses.length === 0) diagnostic(diagnostics, "deliverable_contract_missing_producer", "Deliverable contracts must declare at least one producerAgentClass.", "producerAgentClasses");
|
|
121
|
+
if (!["required", "draft", "submitted", "approved", "rejected"].includes(contract.status)) diagnostic(diagnostics, "deliverable_contract_status_invalid", "Deliverable contract has an invalid status.", "status");
|
|
122
|
+
return validationResult(diagnostics);
|
|
123
|
+
}
|
|
124
|
+
function validateDeliverableManifest(manifest) {
|
|
125
|
+
const diagnostics = [];
|
|
126
|
+
validateNonEmptyString(diagnostics, manifest.id, "id");
|
|
127
|
+
validateNonEmptyString(diagnostics, manifest.deliverableContractId, "deliverableContractId");
|
|
128
|
+
validateNonEmptyString(diagnostics, manifest.projectId, "projectId");
|
|
129
|
+
validateNonEmptyString(diagnostics, manifest.decisionId, "decisionId");
|
|
130
|
+
if (!Array.isArray(manifest.producedRefs) || manifest.producedRefs.length === 0) diagnostic(diagnostics, "deliverable_manifest_missing_refs", "Deliverable manifests must map the contract to at least one produced content ref.", "producedRefs");
|
|
131
|
+
validateNonEmptyString(diagnostics, manifest.summary, "summary");
|
|
132
|
+
return validationResult(diagnostics);
|
|
133
|
+
}
|
|
134
|
+
function validateDecisionAssignmentGraph(graph) {
|
|
135
|
+
const diagnostics = [];
|
|
136
|
+
validateNonEmptyString(diagnostics, graph.id, "id");
|
|
137
|
+
validateNonEmptyString(diagnostics, graph.teamId, "teamId");
|
|
138
|
+
validateNonEmptyString(diagnostics, graph.projectId, "projectId");
|
|
139
|
+
validateNonEmptyString(diagnostics, graph.decisionId, "decisionId");
|
|
140
|
+
if (!Number.isInteger(graph.version) || graph.version < 1) diagnostic(diagnostics, "graph_version_invalid", "Decision assignment graph version must be a positive integer.", "version");
|
|
141
|
+
if (graph.compiledBy !== "api-control-plane") diagnostic(diagnostics, "graph_compiler_invalid", "Decision assignment graphs must be compiled by api-control-plane.", "compiledBy");
|
|
142
|
+
const nodeIds = new Set(graph.nodes.map((node) => node.id));
|
|
143
|
+
for (const [index, node] of graph.nodes.entries()) {
|
|
144
|
+
validateNonEmptyString(diagnostics, node.id, "node.id", `nodes.${index}.id`);
|
|
145
|
+
validateNonEmptyString(diagnostics, node.targetAgentClass, "node.targetAgentClass", `nodes.${index}.targetAgentClass`);
|
|
146
|
+
validateNonNegativeNumber(diagnostics, node.capacity.expectedCredits, "node.capacity.expectedCredits", `nodes.${index}.capacity.expectedCredits`);
|
|
147
|
+
validateNonNegativeNumber(diagnostics, node.capacity.maxCredits, "node.capacity.maxCredits", `nodes.${index}.capacity.maxCredits`);
|
|
148
|
+
}
|
|
149
|
+
for (const [index, edge] of graph.edges.entries()) {
|
|
150
|
+
if (!nodeIds.has(edge.fromNodeId)) diagnostic(diagnostics, "graph_edge_from_missing", `Edge ${index} references missing fromNodeId.`, `edges.${index}.fromNodeId`);
|
|
151
|
+
if (!nodeIds.has(edge.toNodeId)) diagnostic(diagnostics, "graph_edge_to_missing", `Edge ${index} references missing toNodeId.`, `edges.${index}.toNodeId`);
|
|
152
|
+
}
|
|
153
|
+
for (const [index, contract] of graph.deliverableContracts.entries()) {
|
|
154
|
+
diagnostics.push(...validateDeliverableContract(contract).diagnostics.map((entry) => ({ ...entry, path: `deliverableContracts.${index}${entry.path ? `.${entry.path}` : ""}` })));
|
|
155
|
+
}
|
|
156
|
+
return validationResult(diagnostics);
|
|
157
|
+
}
|
|
158
|
+
function compileDecisionAssignmentGraphFromEstimates(input) {
|
|
159
|
+
const diagnostics = [];
|
|
160
|
+
const estimates = [...input.estimates ?? []].sort((left, right) => left.agentClass.localeCompare(right.agentClass) || String(left.agentId ?? "").localeCompare(String(right.agentId ?? "")) || left.id.localeCompare(right.id));
|
|
161
|
+
for (const [index, estimate] of estimates.entries()) {
|
|
162
|
+
diagnostics.push(...validateStructuredAgentEstimate(estimate).diagnostics.map((entry) => ({ ...entry, path: `estimates.${index}${entry.path ? `.${entry.path}` : ""}` })));
|
|
163
|
+
}
|
|
164
|
+
const contractMap = /* @__PURE__ */ new Map();
|
|
165
|
+
const deliverableProducerNodes = /* @__PURE__ */ new Map();
|
|
166
|
+
const nodes = [];
|
|
167
|
+
const edges = [];
|
|
168
|
+
for (const estimate of estimates) {
|
|
169
|
+
for (const dependency of estimate.dependencies.filter((entry) => entry.type === "artifact" && entry.deliverableType)) {
|
|
170
|
+
const contractId = dependencyToContractId(input.projectId, input.decisionId, dependency);
|
|
171
|
+
if (!contractMap.has(contractId)) {
|
|
172
|
+
const producerClass = dependency.agentClass || dependency.capability || dependency.deliverableType || "producer";
|
|
173
|
+
contractMap.set(contractId, {
|
|
174
|
+
id: contractId,
|
|
175
|
+
teamId: input.teamId,
|
|
176
|
+
projectId: input.projectId,
|
|
177
|
+
decisionId: input.decisionId,
|
|
178
|
+
deliverableType: dependency.deliverableType ?? dependency.id,
|
|
179
|
+
producerAgentClasses: [producerClass],
|
|
180
|
+
acceptanceCriteria: dependency.summary ? [dependency.summary] : [],
|
|
181
|
+
status: "required",
|
|
182
|
+
metadata: { sourceDependencyId: dependency.id }
|
|
183
|
+
});
|
|
184
|
+
const producerNodeId = `${contractId}:produce`;
|
|
185
|
+
deliverableProducerNodes.set(contractId, producerNodeId);
|
|
186
|
+
nodes.push({
|
|
187
|
+
id: producerNodeId,
|
|
188
|
+
decisionId: input.decisionId,
|
|
189
|
+
projectId: input.projectId,
|
|
190
|
+
targetAgentClass: producerClass,
|
|
191
|
+
activityType: "acting",
|
|
192
|
+
handler: null,
|
|
193
|
+
requiredCapabilities: uniqueStrings([dependency.capability ?? ""].filter(Boolean)),
|
|
194
|
+
requiredDeliverableContractIds: [],
|
|
195
|
+
inputRefs: [],
|
|
196
|
+
outputRequirements: [{ id: `${contractId}:output`, outputType: dependency.deliverableType ?? dependency.id, description: dependency.summary, required: true }],
|
|
197
|
+
capacity: { expectedCredits: 1, maxCredits: 1 },
|
|
198
|
+
status: "pending",
|
|
199
|
+
metadata: { deliverableContractId: contractId, generatedFromDependency: dependency.id }
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
for (const estimate of estimates) {
|
|
205
|
+
const nodeId = estimate.workUnitId || `estimate:${estimate.id}:work`;
|
|
206
|
+
const artifactDependencies = estimate.dependencies.filter((dependency) => dependency.type === "artifact" && dependency.deliverableType);
|
|
207
|
+
const requiredDeliverableContractIds = artifactDependencies.map((dependency) => dependencyToContractId(input.projectId, input.decisionId, dependency));
|
|
208
|
+
const inputRefs = estimate.dependencies.flatMap((dependency) => (dependency.contentRefs ?? []).map((ref) => ({ model: "note", collection: "notes", slug: ref, id: ref })));
|
|
209
|
+
nodes.push({
|
|
210
|
+
id: nodeId,
|
|
211
|
+
decisionId: input.decisionId,
|
|
212
|
+
projectId: input.projectId,
|
|
213
|
+
targetAgentClass: estimate.agentClass,
|
|
214
|
+
activityType: "acting",
|
|
215
|
+
handler: null,
|
|
216
|
+
requiredCapabilities: uniqueStrings(estimate.dependencies.map((dependency) => dependency.capability ?? dependency.agentClass ?? "").filter(Boolean)),
|
|
217
|
+
requiredDeliverableContractIds,
|
|
218
|
+
inputRefs,
|
|
219
|
+
outputRequirements: estimate.expectedOutputs,
|
|
220
|
+
capacity: { expectedCredits: estimate.expectedCredits, maxCredits: estimate.maxCredits },
|
|
221
|
+
status: "pending",
|
|
222
|
+
metadata: {
|
|
223
|
+
estimateId: estimate.id,
|
|
224
|
+
confidence: estimate.confidence,
|
|
225
|
+
riskLevel: estimate.riskLevel,
|
|
226
|
+
humanInputDependencies: estimate.dependencies.filter((dependency) => dependency.type === "human-input")
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
for (const dependency of artifactDependencies) {
|
|
230
|
+
const contractId = dependencyToContractId(input.projectId, input.decisionId, dependency);
|
|
231
|
+
const producerNodeId = deliverableProducerNodes.get(contractId);
|
|
232
|
+
if (producerNodeId) edges.push({ fromNodeId: producerNodeId, toNodeId: nodeId, edgeType: edgeTypeForDependency(dependency), reason: dependency.summary ?? dependency.deliverableType });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const graph = {
|
|
236
|
+
id: input.id ?? `${input.projectId}:${input.decisionId}:graph:v${input.version ?? 1}`,
|
|
237
|
+
teamId: input.teamId,
|
|
238
|
+
projectId: input.projectId,
|
|
239
|
+
decisionId: input.decisionId,
|
|
240
|
+
version: input.version ?? 1,
|
|
241
|
+
status: diagnostics.some((entry) => entry.severity === "error") ? "blocked" : "compiled",
|
|
242
|
+
estimateIds: estimates.map((estimate) => estimate.id),
|
|
243
|
+
deliverableContracts: [...contractMap.values()].sort((left, right) => left.id.localeCompare(right.id)),
|
|
244
|
+
nodes: nodes.sort((left, right) => left.id.localeCompare(right.id)),
|
|
245
|
+
edges: edges.sort((left, right) => left.fromNodeId.localeCompare(right.fromNodeId) || left.toNodeId.localeCompare(right.toNodeId) || left.edgeType.localeCompare(right.edgeType)),
|
|
246
|
+
compiledAt: input.compiledAt ?? null,
|
|
247
|
+
compiledBy: "api-control-plane",
|
|
248
|
+
metadata: { compiler: "compileDecisionAssignmentGraphFromEstimates" }
|
|
249
|
+
};
|
|
250
|
+
diagnostics.push(...validateDecisionAssignmentGraph(graph).diagnostics);
|
|
251
|
+
return { graph, diagnostics };
|
|
252
|
+
}
|
|
47
253
|
function booleanOrNull(value) {
|
|
48
254
|
return typeof value === "boolean" ? value : null;
|
|
49
255
|
}
|
|
@@ -1122,6 +1328,15 @@ function validateAgentKernelModeExecutionInput(input) {
|
|
|
1122
1328
|
{ retryable: true, metadata: { reservationId: capacity.reservationId ?? null, reservedCredits: capacity.reservedCredits ?? null } }
|
|
1123
1329
|
);
|
|
1124
1330
|
}
|
|
1331
|
+
const activityType = String(record(decision.metadata).activityType ?? record(capacity.metadata).activityType ?? "");
|
|
1332
|
+
const deterministicSystemReport = mode === "planning" && activityType === "reporting" && record(capacity.metadata).deterministicSystemReport === true;
|
|
1333
|
+
if (mode === "planning" && !deterministicSystemReport && (!capacity.reservationId || Number(capacity.reservedCredits ?? 0) <= 0)) {
|
|
1334
|
+
return createAgentKernelModeFallback(
|
|
1335
|
+
"assignment_capacity_not_reserved",
|
|
1336
|
+
`Assignment ${assignment.id} is missing reserved capacity for planning execution.`,
|
|
1337
|
+
{ retryable: true, metadata: { reservationId: capacity.reservationId ?? null, reservedCredits: capacity.reservedCredits ?? null, activityType: activityType || null } }
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1125
1340
|
if (mode === "acting" && !hasAcceptedCapacityPlanProvenance({ assignment, decisionInput: decision, capacityEnvelope: capacity })) {
|
|
1126
1341
|
return createAgentKernelModeFallback(
|
|
1127
1342
|
"assignment_capacity_plan_not_accepted",
|
|
@@ -1490,9 +1705,9 @@ function summarizeCapacityRuntimeDiagnostics(input) {
|
|
|
1490
1705
|
}
|
|
1491
1706
|
}
|
|
1492
1707
|
}
|
|
1493
|
-
const uniqueDiagnostics = Array.from(new Map(diagnostics.map((
|
|
1494
|
-
`${
|
|
1495
|
-
|
|
1708
|
+
const uniqueDiagnostics = Array.from(new Map(diagnostics.map((diagnostic2) => [
|
|
1709
|
+
`${diagnostic2.assignmentId ?? "global"}:${diagnostic2.code}`,
|
|
1710
|
+
diagnostic2
|
|
1496
1711
|
])).values());
|
|
1497
1712
|
return {
|
|
1498
1713
|
projectId: input.projectId,
|
|
@@ -1555,9 +1770,11 @@ function validateCapacitySettlementInvariant(input) {
|
|
|
1555
1770
|
}
|
|
1556
1771
|
export {
|
|
1557
1772
|
AGENT_ASSIGNMENT_WORKSPACE_ACCESS_MODES,
|
|
1773
|
+
DEFAULT_WORKDAY_MODE_SPLITS,
|
|
1558
1774
|
buildAgentCapacityPlanDraft,
|
|
1559
1775
|
buildExecutionProviderAssignmentExplanation,
|
|
1560
1776
|
buildProviderAssignmentExplanation,
|
|
1777
|
+
compileDecisionAssignmentGraphFromEstimates,
|
|
1561
1778
|
compileExecutionCapabilityDemand,
|
|
1562
1779
|
compileExecutionCapabilitySupply,
|
|
1563
1780
|
computeDecisionScopeHash,
|
|
@@ -1595,6 +1812,11 @@ export {
|
|
|
1595
1812
|
validateAgentKernelModeExecutionInput,
|
|
1596
1813
|
validateAgentKernelOutputs,
|
|
1597
1814
|
validateCapacitySettlementInvariant,
|
|
1815
|
+
validateDecisionAssignmentGraph,
|
|
1816
|
+
validateDecisionDependencySpec,
|
|
1817
|
+
validateDeliverableContract,
|
|
1818
|
+
validateDeliverableManifest,
|
|
1598
1819
|
validateProviderAssignmentCapabilityHandles,
|
|
1820
|
+
validateStructuredAgentEstimate,
|
|
1599
1821
|
validateTreeDxProxyHandle
|
|
1600
1822
|
};
|
|
@@ -82,6 +82,7 @@ export type TreeseedGuaranteeManifest = {
|
|
|
82
82
|
optional?: string[];
|
|
83
83
|
};
|
|
84
84
|
notes?: string[];
|
|
85
|
+
dependsOnGuarantees?: string[];
|
|
85
86
|
};
|
|
86
87
|
export type TreeseedLoadedGuarantee = {
|
|
87
88
|
sourcePath: string;
|
|
@@ -362,6 +363,7 @@ export declare function exportTreeseedGuaranteesJson(input: {
|
|
|
362
363
|
optional?: string[];
|
|
363
364
|
};
|
|
364
365
|
notes?: string[];
|
|
366
|
+
dependsOnGuarantees?: string[];
|
|
365
367
|
sourcePath: string;
|
|
366
368
|
}[];
|
|
367
369
|
};
|
package/dist/guarantees/index.js
CHANGED
|
@@ -155,6 +155,11 @@ function parseGuaranteeManifest(value, diagnostics, sourcePath) {
|
|
|
155
155
|
if (subtype && !TAXONOMY_PATTERN.test(subtype)) diagnostics.push(diagnostic("error", "guarantee.invalid_subtype", `Guarantee subtype must be lowercase kebab-case: ${subtype}.`, "subtype", sourcePath));
|
|
156
156
|
if (status && !KNOWN_STATUSES.has(status)) diagnostics.push(diagnostic("error", "guarantee.invalid_status", `Unsupported guarantee status "${status}".`, "status", sourcePath));
|
|
157
157
|
if (surface && !KNOWN_SURFACES.has(surface)) diagnostics.push(diagnostic("error", "guarantee.invalid_surface", `Unsupported guarantee surface "${surface}".`, "surface", sourcePath));
|
|
158
|
+
const dependsOnGuarantees = Array.isArray(value.dependsOnGuarantees) ? value.dependsOnGuarantees.map((entry) => {
|
|
159
|
+
if (typeof entry === "string") return entry;
|
|
160
|
+
if (!isRecord(entry)) return "";
|
|
161
|
+
return typeof entry.ref === "string" && typeof entry.ownerPackage === "string" ? `${entry.ownerPackage}:${entry.ref}` : stringValue(entry.ref);
|
|
162
|
+
}).filter(Boolean) : [];
|
|
158
163
|
const dependencies = isRecord(value.dependencies) ? value.dependencies : {};
|
|
159
164
|
const actors = isRecord(value.actors) ? value.actors : {};
|
|
160
165
|
const devices = isRecord(value.devices) ? value.devices : {};
|
|
@@ -212,7 +217,8 @@ function parseGuaranteeManifest(value, diagnostics, sourcePath) {
|
|
|
212
217
|
required: stringArray(evidence.required),
|
|
213
218
|
optional: stringArray(evidence.optional)
|
|
214
219
|
},
|
|
215
|
-
notes: stringArray(value.notes)
|
|
220
|
+
notes: stringArray(value.notes),
|
|
221
|
+
dependsOnGuarantees
|
|
216
222
|
};
|
|
217
223
|
if (manifest.status === "active") {
|
|
218
224
|
const hasContract = Boolean(manifest.scene?.required || manifest.api?.required || manifest.content?.required || manifest.audit?.required);
|
|
@@ -400,6 +406,13 @@ function validateTreeseedGuaranteeRegistry(input) {
|
|
|
400
406
|
if (entry.manifest.journeyIndex && dep >= entry.manifest.journeyIndex) diagnostics.push(diagnostic("error", "guarantee.forward_journey_dependency", `Journey dependency ${dep} must be lower than ${entry.manifest.journeyIndex}.`, "dependencies.journeys", entry.sourcePath));
|
|
401
407
|
}
|
|
402
408
|
}
|
|
409
|
+
for (const entry of valid) {
|
|
410
|
+
for (const dep of entry.manifest.dependsOnGuarantees ?? []) {
|
|
411
|
+
const [ownerPackage, ref] = dep.includes(":") ? dep.split(/:(.+)/u).filter(Boolean) : ["", dep];
|
|
412
|
+
const match = valid.find((candidate) => (!ownerPackage || candidate.manifest.ownerPackage === ownerPackage) && candidate.manifest.status === "active" && (candidate.manifest.id === ref || allVerifierRefs(candidate.manifest).includes(ref)));
|
|
413
|
+
if (!match) diagnostics.push(diagnostic("error", "guarantee.missing_depends_on_guarantee", `Missing active guarantee dependency "${dep}".`, "dependsOnGuarantees", entry.sourcePath));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
403
416
|
const visiting = /* @__PURE__ */ new Set();
|
|
404
417
|
const visited = /* @__PURE__ */ new Set();
|
|
405
418
|
const visit = (id, chain) => {
|
package/dist/hosting/graph.js
CHANGED
|
@@ -359,7 +359,7 @@ function buildProfileFromDeployConfig(input) {
|
|
|
359
359
|
sourceCommit: sourcePolicy.sourceCommit,
|
|
360
360
|
sourceRootDirectory: sourcePolicy.sourceRootDirectory,
|
|
361
361
|
dockerfilePath: sourcePolicy.sourceMode === "git" ? service.railway?.dockerfilePath ?? null : null,
|
|
362
|
-
buildCommand: sourcePolicy.imageRef ? null : service.railway?.buildCommand ?? null,
|
|
362
|
+
buildCommand: sourcePolicy.imageRef || sourcePolicy.sourceMode === "git" && service.railway?.dockerfilePath ? null : service.railway?.buildCommand ?? null,
|
|
363
363
|
startCommand: sourcePolicy.imageRef ? null : service.railway?.startCommand ?? null,
|
|
364
364
|
healthcheckPath: service.railway?.healthcheckPath ?? null,
|
|
365
365
|
runtimeMode: service.railway?.runtimeMode ?? null,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
2
2
|
import { closeSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync, appendFileSync, openSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
function splitSurfaces(value) {
|
|
@@ -13,6 +13,44 @@ function pidAlive(pid) {
|
|
|
13
13
|
return false;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
function portListenerPids(port) {
|
|
17
|
+
if (!port || !Number.isFinite(port)) return [];
|
|
18
|
+
try {
|
|
19
|
+
const output = execFileSync("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN", "-n", "-P"], {
|
|
20
|
+
encoding: "utf8",
|
|
21
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
22
|
+
});
|
|
23
|
+
return output.split(/\r?\n/u).map((entry) => Number(entry.trim())).filter((pid) => Number.isInteger(pid) && pid > 0);
|
|
24
|
+
} catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function waitForPidsToExit(pids, timeoutMs = 3e3) {
|
|
29
|
+
const startedAt = Date.now();
|
|
30
|
+
while (pids.some((pid) => pidAlive(pid)) && Date.now() - startedAt < timeoutMs) {
|
|
31
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 100));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function stopConflictingPortListeners(spec, allowedPid = null) {
|
|
35
|
+
const conflicts = portListenerPids(spec.port).filter((pid) => pid !== process.pid && pid !== allowedPid);
|
|
36
|
+
if (conflicts.length === 0) return [];
|
|
37
|
+
for (const pid of conflicts) {
|
|
38
|
+
try {
|
|
39
|
+
process.kill(pid, "SIGTERM");
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
await waitForPidsToExit(conflicts);
|
|
44
|
+
const remaining = conflicts.filter((pid) => pidAlive(pid));
|
|
45
|
+
for (const pid of remaining) {
|
|
46
|
+
try {
|
|
47
|
+
process.kill(pid, "SIGKILL");
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
await waitForPidsToExit(remaining, 1e3);
|
|
52
|
+
return conflicts;
|
|
53
|
+
}
|
|
16
54
|
function scopeId(tenantRoot) {
|
|
17
55
|
return Buffer.from(tenantRoot).toString("base64url").slice(0, 32);
|
|
18
56
|
}
|
|
@@ -240,7 +278,7 @@ async function instanceFromSpecWithHealth(spec) {
|
|
|
240
278
|
const instance = instanceFromSpec(spec);
|
|
241
279
|
const healthStatus = await checkHealth(spec);
|
|
242
280
|
const healthy = healthStatus.length === 0 || healthStatus.every((entry) => entry.ok);
|
|
243
|
-
const running = healthStatus.length > 0 ? healthy : instance.running;
|
|
281
|
+
const running = healthStatus.length > 0 ? instance.running && healthy : instance.running;
|
|
244
282
|
return {
|
|
245
283
|
...instance,
|
|
246
284
|
healthStatus,
|
|
@@ -270,7 +308,7 @@ function stopSpec(spec) {
|
|
|
270
308
|
rmSync(spec.instancePath, { force: true });
|
|
271
309
|
return instance;
|
|
272
310
|
}
|
|
273
|
-
async function startSpec(spec, force = false) {
|
|
311
|
+
async function startSpec(spec, force = false, forceConflicts = false) {
|
|
274
312
|
const existing = instanceFromSpec(spec);
|
|
275
313
|
if (existing.running && !force) {
|
|
276
314
|
return existing;
|
|
@@ -278,6 +316,9 @@ async function startSpec(spec, force = false) {
|
|
|
278
316
|
if (existing.running && force) {
|
|
279
317
|
stopSpec(spec);
|
|
280
318
|
}
|
|
319
|
+
if (forceConflicts) {
|
|
320
|
+
await stopConflictingPortListeners(spec, existing.pid);
|
|
321
|
+
}
|
|
281
322
|
mkdirSync(resolve(spec.pidPath, ".."), { recursive: true });
|
|
282
323
|
mkdirSync(resolve(spec.instancePath, ".."), { recursive: true });
|
|
283
324
|
mkdirSync(resolve(spec.logPath, ".."), { recursive: true });
|
|
@@ -311,7 +352,7 @@ async function startTreeseedManagedDev(options = {}) {
|
|
|
311
352
|
const plan = createTreeseedIntegratedDevPlan(options);
|
|
312
353
|
const instances = [];
|
|
313
354
|
for (const spec of plan.processes) {
|
|
314
|
-
await startSpec(spec, options.force === true);
|
|
355
|
+
await startSpec(spec, options.force === true, options.forceConflicts === true);
|
|
315
356
|
instances.push(await waitForHealthySpec(spec));
|
|
316
357
|
}
|
|
317
358
|
return { ok: instances.every((entry) => entry.running), action: "start", plan, instances };
|
|
@@ -50,19 +50,30 @@ const CONTENT_DEFAULTS = {
|
|
|
50
50
|
idPrefix: "agent",
|
|
51
51
|
extension: "mdx",
|
|
52
52
|
fields: {
|
|
53
|
+
slug: "",
|
|
54
|
+
title: "",
|
|
53
55
|
name: "",
|
|
54
|
-
|
|
56
|
+
agentClass: "general",
|
|
55
57
|
enabled: true,
|
|
56
58
|
operator: "TreeSeed platform",
|
|
57
59
|
runtimeStatus: "active",
|
|
58
60
|
capabilities: [],
|
|
59
61
|
tags: ["agent"],
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
identity: {
|
|
63
|
+
purpose: "",
|
|
64
|
+
responsibilities: [],
|
|
65
|
+
durableInstructions: "Keep work observable, governed, and grounded in project content."
|
|
66
|
+
},
|
|
67
|
+
activityProfiles: {
|
|
68
|
+
planning: {
|
|
69
|
+
enabled: true,
|
|
70
|
+
handler: "writer",
|
|
71
|
+
prompt: { system: "Use TreeDX-backed content tools and stay scoped to this project." },
|
|
72
|
+
branchPolicy: { kind: "read-only", base: "main" },
|
|
73
|
+
tools: { allowed: ["treeseed.content.query", "treeseed.content.read"] },
|
|
74
|
+
outputs: { messageTypes: [], modelMutations: [] }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
66
77
|
},
|
|
67
78
|
body: "Describe this agent role, operating boundaries, and expected outputs."
|
|
68
79
|
}
|
|
@@ -127,7 +127,11 @@ function collectTreeseedDeploymentReadiness(options) {
|
|
|
127
127
|
checks.push(check("hosting:api:projectGroup", api.projectGroupId, "treeseed-control-plane", "API project group targets the Treeseed control plane.", "Bind services.api to the treeseed-control-plane project group.", "projectGroupId"));
|
|
128
128
|
checks.push(check("hosting:api:rootDir", api.config?.rootDir, expectedApiUnitRoot, "API effective rootDir points at the API package.", "Set services.api.rootDir and services.api.railway.rootDir relative to the owning API manifest.", "rootDir"));
|
|
129
129
|
if (environment === "local") {
|
|
130
|
-
|
|
130
|
+
if (api.config?.dockerfilePath) {
|
|
131
|
+
checks.push(check("hosting:api:dockerfilePath", api.config.dockerfilePath, "/Dockerfile.api", "API local deploy uses the same Dockerfile build contract as staging.", "Set services.api.railway.dockerfilePath to /Dockerfile.api.", "dockerfilePath"));
|
|
132
|
+
} else {
|
|
133
|
+
checks.push(check("hosting:api:buildCommand", api.config?.buildCommand, "npm run build", "API build command is package-local.", 'Set services.api.railway.buildCommand to "npm run build".', "buildCommand"));
|
|
134
|
+
}
|
|
131
135
|
checks.push(check("hosting:api:startCommand", api.config?.startCommand, "npm run start:api", "API start command is package-local.", 'Set services.api.railway.startCommand to "npm run start:api".', "startCommand"));
|
|
132
136
|
} else if (environment === "staging") {
|
|
133
137
|
checks.push(check("hosting:api:sourceMode", api.config?.sourceMode, "git", "API staging deploy uses Railway Git source builds.", "Set services.api.railway.sourceMode to git.", "sourceMode"));
|
|
@@ -154,7 +158,11 @@ function collectTreeseedDeploymentReadiness(options) {
|
|
|
154
158
|
checks.push(check("hosting:operationsRunner:projectGroup", runner.projectGroupId, "treeseed-control-plane", "Runner project group targets the Treeseed control plane.", "Bind services.operationsRunner to the treeseed-control-plane project group.", "projectGroupId"));
|
|
155
159
|
checks.push(check("hosting:operationsRunner:rootDir", runner.config?.rootDir, expectedRunnerUnitRoot, "Runner effective rootDir points at the API package.", "Set services.operationsRunner.rootDir and services.operationsRunner.railway.rootDir relative to the owning API manifest.", "rootDir"));
|
|
156
160
|
if (environment === "local") {
|
|
157
|
-
|
|
161
|
+
if (runner.config?.dockerfilePath) {
|
|
162
|
+
checks.push(check("hosting:operationsRunner:dockerfilePath", runner.config.dockerfilePath, "/Dockerfile.operations-runner", "Runner local deploy uses the same Dockerfile build contract as staging.", "Set services.operationsRunner.railway.dockerfilePath to /Dockerfile.operations-runner.", "dockerfilePath"));
|
|
163
|
+
} else {
|
|
164
|
+
checks.push(check("hosting:operationsRunner:buildCommand", runner.config?.buildCommand, "npm run build", "Runner build command is package-local.", 'Set services.operationsRunner.railway.buildCommand to "npm run build".', "buildCommand"));
|
|
165
|
+
}
|
|
158
166
|
checks.push(check("hosting:operationsRunner:startCommand", runner.config?.startCommand, "npm run start:runner", "Runner start command is package-local.", 'Set services.operationsRunner.railway.startCommand to "npm run start:runner".', "startCommand"));
|
|
159
167
|
} else if (environment === "staging") {
|
|
160
168
|
checks.push(check("hosting:operationsRunner:sourceMode", runner.config?.sourceMode, "git", "Runner staging deploy uses Railway Git source builds.", "Set services.operationsRunner.railway.sourceMode to git.", "sourceMode"));
|
|
@@ -713,7 +713,7 @@ function configuredRailwayServicesForConfig(tenantRoot, scope, deployConfig, app
|
|
|
713
713
|
rootDir: serviceRoot,
|
|
714
714
|
publicBaseUrl,
|
|
715
715
|
railwayEnvironment,
|
|
716
|
-
buildCommand: resolvedImageRef ? null : service.railway?.buildCommand ?? null,
|
|
716
|
+
buildCommand: resolvedImageRef || sourcePolicy.sourceMode === "git" && service.railway?.dockerfilePath ? null : service.railway?.buildCommand ?? null,
|
|
717
717
|
startCommand: isCapacityProviderService ? null : resolvedImageRef ? null : service.railway?.startCommand ?? null,
|
|
718
718
|
imageRef: resolvedImageRef,
|
|
719
719
|
sourceMode: sourcePolicy.sourceMode,
|
|
@@ -70,8 +70,7 @@ function listTemplateArtifactIds() {
|
|
|
70
70
|
}
|
|
71
71
|
const LOCAL_STARTER_ID_TO_DIRECTORY = {
|
|
72
72
|
"research": "research",
|
|
73
|
-
"engineering": "engineering"
|
|
74
|
-
"information-hub": "information-hub"
|
|
73
|
+
"engineering": "engineering"
|
|
75
74
|
};
|
|
76
75
|
function localStartersRoot() {
|
|
77
76
|
return resolve(cliPackageRoot, "..", "..", "starters");
|
|
@@ -636,7 +636,7 @@ function localDevelopmentResources(tenantRoot, environment, localContent) {
|
|
|
636
636
|
},
|
|
637
637
|
volumes: [{ name: "treeseed-local-treedx-data", mountPath: "/data", sharedLocalOnly: true }],
|
|
638
638
|
healthChecks: [
|
|
639
|
-
{ id: "treedx-api", kind: "http", url: "http://127.0.0.1:4000/api/v1/health" }
|
|
639
|
+
{ id: "treedx-api", kind: "http", url: "http://127.0.0.1:4000/api/v1/health", attempts: 240, intervalMs: 2e3 }
|
|
640
640
|
]
|
|
641
641
|
},
|
|
642
642
|
source: { type: "package-adapter", id: "treedx" }
|
|
@@ -782,6 +782,9 @@ function buildLocalDockerComposeAdapter() {
|
|
|
782
782
|
},
|
|
783
783
|
diff(input) {
|
|
784
784
|
if (!input.observed.exists) return { action: "blocked", reasons: input.observed.warnings, before: input.observed.live, after: input.unit.spec };
|
|
785
|
+
if (input.unit.spec.forceRecreate === true) {
|
|
786
|
+
return { action: "update", reasons: ["compose force recreate requested"], before: input.observed.live, after: input.unit.spec };
|
|
787
|
+
}
|
|
785
788
|
return input.observed.status === "ready" ? noopDiff() : { action: "create", reasons: ["compose services are not running"], before: input.observed.live, after: input.unit.spec };
|
|
786
789
|
},
|
|
787
790
|
apply(input) {
|
|
@@ -799,7 +802,7 @@ function buildLocalDockerComposeAdapter() {
|
|
|
799
802
|
env,
|
|
800
803
|
profiles: localComposeProfiles(input),
|
|
801
804
|
buildPolicy: localComposeBuildPolicy(input),
|
|
802
|
-
action: "up"
|
|
805
|
+
action: input.unit.spec.forceRecreate === true ? "restart" : "up"
|
|
803
806
|
});
|
|
804
807
|
if (!result.ok) {
|
|
805
808
|
throw new Error(result.stderr.trim() || result.stdout.trim() || "docker compose up failed");
|
|
@@ -811,7 +814,9 @@ function buildLocalDockerComposeAdapter() {
|
|
|
811
814
|
const checks = [];
|
|
812
815
|
for (const healthCheck of healthChecks) {
|
|
813
816
|
if (healthCheck.kind === "http" && typeof healthCheck.url === "string") {
|
|
814
|
-
const
|
|
817
|
+
const attempts = typeof healthCheck.attempts === "number" && Number.isFinite(healthCheck.attempts) ? Math.max(1, Math.floor(healthCheck.attempts)) : void 0;
|
|
818
|
+
const intervalMs = typeof healthCheck.intervalMs === "number" && Number.isFinite(healthCheck.intervalMs) ? Math.max(100, Math.floor(healthCheck.intervalMs)) : void 0;
|
|
819
|
+
const health = await checkHttpHealthWithRetry(healthCheck.url, attempts, intervalMs);
|
|
815
820
|
checks.push(verificationCheck(String(healthCheck.id ?? healthCheck.url), `HTTP health ${healthCheck.url}`, "api", {
|
|
816
821
|
exists: health.ok,
|
|
817
822
|
configured: true,
|
|
@@ -1235,6 +1240,9 @@ function buildLocalProcessAdapter() {
|
|
|
1235
1240
|
};
|
|
1236
1241
|
},
|
|
1237
1242
|
diff(input) {
|
|
1243
|
+
if (input.unit.spec.action === "restart") {
|
|
1244
|
+
return { action: "update", reasons: ["local process restart requested"], before: input.observed.live, after: input.unit.spec };
|
|
1245
|
+
}
|
|
1238
1246
|
return input.observed.status === "ready" ? noopDiff() : { action: "create", reasons: ["local process is not reported ready"], before: input.observed.live, after: input.unit.spec };
|
|
1239
1247
|
},
|
|
1240
1248
|
async apply(input) {
|
|
@@ -368,190 +368,6 @@
|
|
|
368
368
|
"relatedKnowledge": [],
|
|
369
369
|
"relatedObjectives": []
|
|
370
370
|
},
|
|
371
|
-
{
|
|
372
|
-
"id": "information-hub",
|
|
373
|
-
"displayName": "TreeSeed Information Hub",
|
|
374
|
-
"description": "First-party TreeSeed starter for recurring information retrieval, distillation, and downstream distribution.",
|
|
375
|
-
"summary": "An information distribution starter for retrieving updates over time, distilling derived knowledge, and packaging it for downstream projects.",
|
|
376
|
-
"status": "live",
|
|
377
|
-
"featured": true,
|
|
378
|
-
"category": "starter",
|
|
379
|
-
"audience": [
|
|
380
|
-
"curators",
|
|
381
|
-
"maintainers"
|
|
382
|
-
],
|
|
383
|
-
"tags": [
|
|
384
|
-
"starter",
|
|
385
|
-
"information-hub",
|
|
386
|
-
"distribution",
|
|
387
|
-
"knowledge-packs",
|
|
388
|
-
"agents"
|
|
389
|
-
],
|
|
390
|
-
"publisher": {
|
|
391
|
-
"id": "treeseed",
|
|
392
|
-
"name": "TreeSeed",
|
|
393
|
-
"url": "https://treeseed.dev"
|
|
394
|
-
},
|
|
395
|
-
"publisherVerified": true,
|
|
396
|
-
"templateVersion": "1.0.0",
|
|
397
|
-
"templateApiVersion": 1,
|
|
398
|
-
"minCliVersion": "0.1.1",
|
|
399
|
-
"minCoreVersion": "0.1.2",
|
|
400
|
-
"fulfillment": {
|
|
401
|
-
"mode": "git",
|
|
402
|
-
"source": {
|
|
403
|
-
"kind": "git",
|
|
404
|
-
"repoUrl": "https://github.com/treeseed-templates/information-hub.git",
|
|
405
|
-
"directory": ".",
|
|
406
|
-
"ref": "staging",
|
|
407
|
-
"integrity": "pending-external-repo"
|
|
408
|
-
},
|
|
409
|
-
"hooksPolicy": "builtin_only",
|
|
410
|
-
"supportsReconcile": true
|
|
411
|
-
},
|
|
412
|
-
"offer": {
|
|
413
|
-
"priceModel": "free",
|
|
414
|
-
"license": "AGPL-3.0-only",
|
|
415
|
-
"support": "community"
|
|
416
|
-
},
|
|
417
|
-
"launchRequirements": {
|
|
418
|
-
"version": 1,
|
|
419
|
-
"hosts": [
|
|
420
|
-
{
|
|
421
|
-
"kind": "host",
|
|
422
|
-
"key": "sourceRepository",
|
|
423
|
-
"type": "repository",
|
|
424
|
-
"required": true,
|
|
425
|
-
"compatibleProviders": [
|
|
426
|
-
"github"
|
|
427
|
-
],
|
|
428
|
-
"displayName": "Source repository",
|
|
429
|
-
"purpose": "Create and push the generated information hub project repository.",
|
|
430
|
-
"defaultSelection": "team-default",
|
|
431
|
-
"configWrites": [
|
|
432
|
-
{
|
|
433
|
-
"target": "treeseed.site.yaml",
|
|
434
|
-
"path": "hosting.hostBindings.sourceRepository.provider",
|
|
435
|
-
"valueFrom": "selectedHost.provider"
|
|
436
|
-
},
|
|
437
|
-
{
|
|
438
|
-
"target": "treeseed.site.yaml",
|
|
439
|
-
"path": "hosting.hostBindings.sourceRepository.owner",
|
|
440
|
-
"valueFrom": "selectedHost.github.owner"
|
|
441
|
-
},
|
|
442
|
-
{
|
|
443
|
-
"target": "treeseed.site.yaml",
|
|
444
|
-
"path": "hosting.hostBindings.sourceRepository.repository",
|
|
445
|
-
"valueFrom": "derived.repositoryName"
|
|
446
|
-
}
|
|
447
|
-
],
|
|
448
|
-
"environmentWrites": [
|
|
449
|
-
{
|
|
450
|
-
"env": "GITHUB_TOKEN",
|
|
451
|
-
"valueFrom": "selectedHost.token",
|
|
452
|
-
"targets": [
|
|
453
|
-
"github-secret"
|
|
454
|
-
],
|
|
455
|
-
"scopes": [
|
|
456
|
-
"staging",
|
|
457
|
-
"prod"
|
|
458
|
-
],
|
|
459
|
-
"sensitivity": "secret"
|
|
460
|
-
}
|
|
461
|
-
]
|
|
462
|
-
},
|
|
463
|
-
{
|
|
464
|
-
"kind": "host",
|
|
465
|
-
"key": "publicWeb",
|
|
466
|
-
"type": "web",
|
|
467
|
-
"required": true,
|
|
468
|
-
"compatibleProviders": [
|
|
469
|
-
"cloudflare"
|
|
470
|
-
],
|
|
471
|
-
"displayName": "Public web host",
|
|
472
|
-
"purpose": "Deploy the information hub site, previews, content storage, and web runtime resources.",
|
|
473
|
-
"defaultSelection": "managed",
|
|
474
|
-
"configWrites": [
|
|
475
|
-
{
|
|
476
|
-
"target": "treeseed.site.yaml",
|
|
477
|
-
"path": "hosting.hostBindings.publicWeb.provider",
|
|
478
|
-
"valueFrom": "selectedHost.provider"
|
|
479
|
-
},
|
|
480
|
-
{
|
|
481
|
-
"target": "treeseed.site.yaml",
|
|
482
|
-
"path": "surfaces.web.provider",
|
|
483
|
-
"valueFrom": "selectedHost.provider"
|
|
484
|
-
},
|
|
485
|
-
{
|
|
486
|
-
"target": "treeseed.site.yaml",
|
|
487
|
-
"path": "surfaces.web.environments.prod.domain",
|
|
488
|
-
"valueFrom": "launchInput.domains.productionDomain",
|
|
489
|
-
"writeWhen": "host-selected"
|
|
490
|
-
},
|
|
491
|
-
{
|
|
492
|
-
"target": "treeseed.site.yaml",
|
|
493
|
-
"path": "surfaces.web.environments.staging.domain",
|
|
494
|
-
"valueFrom": "launchInput.domains.stagingDomain",
|
|
495
|
-
"writeWhen": "host-selected"
|
|
496
|
-
}
|
|
497
|
-
],
|
|
498
|
-
"environmentWrites": [
|
|
499
|
-
{
|
|
500
|
-
"env": "TREESEED_PUBLIC_WEB_PROVIDER",
|
|
501
|
-
"valueFrom": "selectedHost.provider",
|
|
502
|
-
"targets": [
|
|
503
|
-
"github-variable",
|
|
504
|
-
"cloudflare-var"
|
|
505
|
-
],
|
|
506
|
-
"scopes": [
|
|
507
|
-
"staging",
|
|
508
|
-
"prod"
|
|
509
|
-
],
|
|
510
|
-
"sensitivity": "plain"
|
|
511
|
-
}
|
|
512
|
-
]
|
|
513
|
-
},
|
|
514
|
-
{
|
|
515
|
-
"kind": "host",
|
|
516
|
-
"key": "transactionalEmail",
|
|
517
|
-
"type": "email",
|
|
518
|
-
"required": false,
|
|
519
|
-
"compatibleProviders": [
|
|
520
|
-
"smtp"
|
|
521
|
-
],
|
|
522
|
-
"displayName": "Transactional email",
|
|
523
|
-
"purpose": "Send form, account, and information hub project notification email.",
|
|
524
|
-
"defaultSelection": "managed",
|
|
525
|
-
"configWrites": [
|
|
526
|
-
{
|
|
527
|
-
"target": "treeseed.site.yaml",
|
|
528
|
-
"path": "hosting.hostBindings.transactionalEmail.provider",
|
|
529
|
-
"valueFrom": "selectedHost.provider",
|
|
530
|
-
"writeWhen": "host-selected"
|
|
531
|
-
}
|
|
532
|
-
],
|
|
533
|
-
"environmentWrites": [
|
|
534
|
-
{
|
|
535
|
-
"env": "SMTP_HOST",
|
|
536
|
-
"valueFrom": "selectedHost.smtpHost",
|
|
537
|
-
"targets": [
|
|
538
|
-
"github-secret",
|
|
539
|
-
"railway-secret"
|
|
540
|
-
],
|
|
541
|
-
"scopes": [
|
|
542
|
-
"staging",
|
|
543
|
-
"prod"
|
|
544
|
-
],
|
|
545
|
-
"sensitivity": "secret"
|
|
546
|
-
}
|
|
547
|
-
]
|
|
548
|
-
}
|
|
549
|
-
]
|
|
550
|
-
},
|
|
551
|
-
"relatedBooks": [],
|
|
552
|
-
"relatedKnowledge": [],
|
|
553
|
-
"relatedObjectives": []
|
|
554
|
-
},
|
|
555
371
|
{
|
|
556
372
|
"id": "market-control-plane",
|
|
557
373
|
"displayName": "TreeSeed Market Control Plane",
|
package/dist/types/agents.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ export declare const AGENT_TRIGGER_KINDS: readonly ["schedule", "message", "foll
|
|
|
2
2
|
export declare const AGENT_PERMISSION_OPERATIONS: readonly ["get", "read", "search", "follow", "pick", "create", "update"];
|
|
3
3
|
export declare const AGENT_MESSAGE_STATUSES: readonly ["pending", "claimed", "completed", "failed", "dead_letter"];
|
|
4
4
|
export declare const AGENT_RUN_STATUSES: readonly ["running", "completed", "failed", "waiting"];
|
|
5
|
-
export declare const AGENT_HANDLER_KINDS: readonly ["
|
|
5
|
+
export declare const AGENT_HANDLER_KINDS: readonly ["writer", "actor", "estimate", "releaser", "reporter"];
|
|
6
|
+
export declare const AGENT_ACTIVITY_TYPES: readonly ["planning", "estimating", "acting", "reviewing", "reporting"];
|
|
7
|
+
export declare const ENGINEERING_HANDLER_KINDS: readonly ["writer", "actor", "estimate", "releaser", "reporter"];
|
|
6
8
|
export declare const AGENT_CLI_ALLOW_TOOLS: readonly ["shell(git)", "shell(npm)", "web"];
|
|
7
9
|
export declare const EXECUTION_RESOURCE_NEED_KINDS: readonly ["repository", "treedx_workspace", "workflow", "secret", "external_issue", "external_job"];
|
|
8
10
|
export declare const EXECUTION_PROVIDER_KINDS: readonly ["ai_model", "human_issue_queue", "deterministic_workflow", "local_process"];
|
|
@@ -14,6 +16,8 @@ export type AgentPermissionOperation = (typeof AGENT_PERMISSION_OPERATIONS)[numb
|
|
|
14
16
|
export type AgentMessageStatus = (typeof AGENT_MESSAGE_STATUSES)[number];
|
|
15
17
|
export type AgentRunStatus = (typeof AGENT_RUN_STATUSES)[number];
|
|
16
18
|
export type AgentHandlerKind = string;
|
|
19
|
+
export type AgentActivityType = (typeof AGENT_ACTIVITY_TYPES)[number];
|
|
20
|
+
export type EngineeringHandlerKind = (typeof ENGINEERING_HANDLER_KINDS)[number];
|
|
17
21
|
export type AgentCliAllowTool = (typeof AGENT_CLI_ALLOW_TOOLS)[number];
|
|
18
22
|
export type ExecutionResourceNeedKind = (typeof EXECUTION_RESOURCE_NEED_KINDS)[number] | string;
|
|
19
23
|
export type ExecutionProviderKind = (typeof EXECUTION_PROVIDER_KINDS)[number] | string;
|
|
@@ -69,6 +73,7 @@ export interface AgentOutputContract {
|
|
|
69
73
|
}
|
|
70
74
|
export interface AgentToolPolicy {
|
|
71
75
|
allowed: string[];
|
|
76
|
+
denied?: string[];
|
|
72
77
|
}
|
|
73
78
|
export interface AgentContentScope {
|
|
74
79
|
models: string[];
|
|
@@ -84,6 +89,94 @@ export interface AgentContentAccessPolicy {
|
|
|
84
89
|
allowed: boolean;
|
|
85
90
|
};
|
|
86
91
|
}
|
|
92
|
+
export type AgentBranchPolicy = {
|
|
93
|
+
kind: 'read-only';
|
|
94
|
+
base: 'main' | 'staging';
|
|
95
|
+
} | {
|
|
96
|
+
kind: 'main-planning-content';
|
|
97
|
+
base: 'main';
|
|
98
|
+
} | {
|
|
99
|
+
kind: 'staging-content';
|
|
100
|
+
base: 'staging';
|
|
101
|
+
} | {
|
|
102
|
+
kind: 'assignment-feature';
|
|
103
|
+
base: 'staging';
|
|
104
|
+
target: 'staging';
|
|
105
|
+
prefix?: string;
|
|
106
|
+
branchNameTemplate?: string;
|
|
107
|
+
worktree?: 'new' | 'reuse';
|
|
108
|
+
updateBaseBeforeRun?: boolean;
|
|
109
|
+
mergeTargetBeforeSave?: boolean;
|
|
110
|
+
} | {
|
|
111
|
+
kind: 'staging-release';
|
|
112
|
+
base: 'staging';
|
|
113
|
+
target: 'main';
|
|
114
|
+
};
|
|
115
|
+
export type AgentQuestionAnswerPolicy = {
|
|
116
|
+
kind: 'team-human';
|
|
117
|
+
teamId?: string;
|
|
118
|
+
requiredRoles?: string[];
|
|
119
|
+
} | {
|
|
120
|
+
kind: 'human-or-agent';
|
|
121
|
+
teamId?: string;
|
|
122
|
+
allowedRoles?: string[];
|
|
123
|
+
allowedAgentClasses?: string[];
|
|
124
|
+
} | {
|
|
125
|
+
kind: 'specific-human';
|
|
126
|
+
teamMemberId: string;
|
|
127
|
+
} | {
|
|
128
|
+
kind: 'specific-agent';
|
|
129
|
+
projectId: string;
|
|
130
|
+
agentSlug: string;
|
|
131
|
+
};
|
|
132
|
+
export interface AgentQuestionPolicy {
|
|
133
|
+
defaultAnswerPolicy?: AgentQuestionAnswerPolicy;
|
|
134
|
+
blockExecutionWhenCreated?: boolean;
|
|
135
|
+
}
|
|
136
|
+
export interface AgentActivityPromptConfig {
|
|
137
|
+
system: string;
|
|
138
|
+
task?: string;
|
|
139
|
+
templates?: Record<string, string>;
|
|
140
|
+
}
|
|
141
|
+
export interface AgentActivityExecutionConfig {
|
|
142
|
+
providerPreference?: string[];
|
|
143
|
+
maxRuntimeSeconds?: number;
|
|
144
|
+
maxRetries?: number;
|
|
145
|
+
verificationRequired?: boolean;
|
|
146
|
+
}
|
|
147
|
+
export interface AgentActivityProfile {
|
|
148
|
+
enabled: boolean;
|
|
149
|
+
handler: EngineeringHandlerKind;
|
|
150
|
+
prompt: AgentActivityPromptConfig;
|
|
151
|
+
branchPolicy: AgentBranchPolicy;
|
|
152
|
+
contentAccess?: AgentContentAccessPolicy;
|
|
153
|
+
tools: AgentToolPolicy;
|
|
154
|
+
outputs: AgentOutputContract;
|
|
155
|
+
questionPolicy?: AgentQuestionPolicy;
|
|
156
|
+
execution?: AgentActivityExecutionConfig;
|
|
157
|
+
}
|
|
158
|
+
export interface AgentCapability {
|
|
159
|
+
id: string;
|
|
160
|
+
description?: string;
|
|
161
|
+
produces?: string[];
|
|
162
|
+
requires?: string[];
|
|
163
|
+
reviews?: string[];
|
|
164
|
+
metadata?: Record<string, unknown>;
|
|
165
|
+
}
|
|
166
|
+
export interface AgentDefinitionIdentity {
|
|
167
|
+
purpose: string;
|
|
168
|
+
responsibilities: string[];
|
|
169
|
+
durableInstructions: string;
|
|
170
|
+
}
|
|
171
|
+
export interface AgentDefinition {
|
|
172
|
+
slug: string;
|
|
173
|
+
title: string;
|
|
174
|
+
agentClass: string;
|
|
175
|
+
template?: string;
|
|
176
|
+
identity: AgentDefinitionIdentity;
|
|
177
|
+
capabilities: AgentCapability[];
|
|
178
|
+
activityProfiles: Partial<Record<AgentActivityType, AgentActivityProfile>>;
|
|
179
|
+
}
|
|
87
180
|
export interface AgentExecutionConfig {
|
|
88
181
|
provider?: string;
|
|
89
182
|
model?: string;
|
|
@@ -176,7 +269,7 @@ export interface AgentWorkPackageConstraints {
|
|
|
176
269
|
maxAttempts?: number | null;
|
|
177
270
|
metadata?: Record<string, unknown>;
|
|
178
271
|
}
|
|
179
|
-
export type AgentHandlerAlgorithmKind =
|
|
272
|
+
export type AgentHandlerAlgorithmKind = EngineeringHandlerKind;
|
|
180
273
|
export type AgentWorkPackageKind = AgentHandlerAlgorithmKind | string;
|
|
181
274
|
export interface AgentInputSelector {
|
|
182
275
|
source: string;
|
|
@@ -332,9 +425,14 @@ export interface AgentCliOptions {
|
|
|
332
425
|
export interface AgentRuntimeSpec {
|
|
333
426
|
slug: string;
|
|
334
427
|
handler: AgentHandlerKind;
|
|
428
|
+
activityType?: AgentActivityType;
|
|
429
|
+
activityProfiles?: Partial<Record<AgentActivityType, AgentActivityProfile>>;
|
|
430
|
+
branchPolicy?: AgentBranchPolicy;
|
|
431
|
+
questionPolicy?: AgentQuestionPolicy;
|
|
432
|
+
identity?: AgentDefinitionIdentity;
|
|
335
433
|
projectAgentClassId?: string;
|
|
336
434
|
projectAgentClassSlug?: string;
|
|
337
|
-
|
|
435
|
+
activityConfig?: AgentHandlerConfig;
|
|
338
436
|
enabled: boolean;
|
|
339
437
|
systemPrompt: string;
|
|
340
438
|
persona: string;
|
package/dist/types/agents.js
CHANGED
|
@@ -17,13 +17,25 @@ const AGENT_MESSAGE_STATUSES = [
|
|
|
17
17
|
];
|
|
18
18
|
const AGENT_RUN_STATUSES = ["running", "completed", "failed", "waiting"];
|
|
19
19
|
const AGENT_HANDLER_KINDS = [
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
"writer",
|
|
21
|
+
"actor",
|
|
22
|
+
"estimate",
|
|
23
|
+
"releaser",
|
|
24
|
+
"reporter"
|
|
25
|
+
];
|
|
26
|
+
const AGENT_ACTIVITY_TYPES = [
|
|
27
|
+
"planning",
|
|
28
|
+
"estimating",
|
|
29
|
+
"acting",
|
|
30
|
+
"reviewing",
|
|
31
|
+
"reporting"
|
|
32
|
+
];
|
|
33
|
+
const ENGINEERING_HANDLER_KINDS = [
|
|
34
|
+
"writer",
|
|
35
|
+
"actor",
|
|
36
|
+
"estimate",
|
|
37
|
+
"releaser",
|
|
38
|
+
"reporter"
|
|
27
39
|
];
|
|
28
40
|
const AGENT_CLI_ALLOW_TOOLS = [
|
|
29
41
|
"shell(git)",
|
|
@@ -67,12 +79,14 @@ const EXECUTION_PROVIDER_QUOTA_VISIBILITIES = [
|
|
|
67
79
|
"exact"
|
|
68
80
|
];
|
|
69
81
|
export {
|
|
82
|
+
AGENT_ACTIVITY_TYPES,
|
|
70
83
|
AGENT_CLI_ALLOW_TOOLS,
|
|
71
84
|
AGENT_HANDLER_KINDS,
|
|
72
85
|
AGENT_MESSAGE_STATUSES,
|
|
73
86
|
AGENT_PERMISSION_OPERATIONS,
|
|
74
87
|
AGENT_RUN_STATUSES,
|
|
75
88
|
AGENT_TRIGGER_KINDS,
|
|
89
|
+
ENGINEERING_HANDLER_KINDS,
|
|
76
90
|
EXECUTION_PROVIDER_KINDS,
|
|
77
91
|
EXECUTION_PROVIDER_PRESSURE_STATES,
|
|
78
92
|
EXECUTION_PROVIDER_QUOTA_VISIBILITIES,
|
|
@@ -1717,11 +1717,13 @@ function hostedWorkflowForSavedRepository(root, repo) {
|
|
|
1717
1717
|
if (repo.branch === STAGING_BRANCH && existsSync(resolve(repo.path, "treeseed.site.yaml")) && workflowFileExists(repo.path, "deploy.yml")) {
|
|
1718
1718
|
return "deploy.yml";
|
|
1719
1719
|
}
|
|
1720
|
-
return "verify.yml";
|
|
1720
|
+
if (workflowFileExists(repo.path, "verify.yml")) return "verify.yml";
|
|
1721
|
+
return null;
|
|
1721
1722
|
}
|
|
1722
1723
|
function gatesForSavedRepositoryReports(root, reports) {
|
|
1723
|
-
return reports.filter((repo) => repo.pushed && repo.commitSha && repo.branch && (repo.committed || repo.tagName)).
|
|
1724
|
+
return reports.filter((repo) => repo.pushed && repo.commitSha && repo.branch && (repo.committed || repo.tagName)).flatMap((repo) => {
|
|
1724
1725
|
const workflow = hostedWorkflowForSavedRepository(root, repo);
|
|
1726
|
+
if (!workflow) return [];
|
|
1725
1727
|
const gate = {
|
|
1726
1728
|
name: repo.name,
|
|
1727
1729
|
repoPath: repo.path,
|
|
@@ -1729,7 +1731,7 @@ function gatesForSavedRepositoryReports(root, reports) {
|
|
|
1729
1731
|
branch: String(repo.branch),
|
|
1730
1732
|
headSha: String(repo.commitSha)
|
|
1731
1733
|
};
|
|
1732
|
-
return /^deploy(?:[-.]|$)/u.test(workflow) ? hostedDeployGate(gate) : gate;
|
|
1734
|
+
return [/^deploy(?:[-.]|$)/u.test(workflow) ? hostedDeployGate(gate) : gate];
|
|
1733
1735
|
});
|
|
1734
1736
|
}
|
|
1735
1737
|
function packageHostedVerifyWorkflow(adapter) {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS "structured_agent_estimates" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"team_id" text NOT NULL,
|
|
4
|
+
"project_id" text NOT NULL,
|
|
5
|
+
"decision_id" text,
|
|
6
|
+
"proposal_id" text,
|
|
7
|
+
"work_unit_id" text,
|
|
8
|
+
"agent_class" text NOT NULL,
|
|
9
|
+
"agent_id" text,
|
|
10
|
+
"status" text DEFAULT 'submitted' NOT NULL,
|
|
11
|
+
"estimate_json" text NOT NULL,
|
|
12
|
+
"metadata_json" text DEFAULT '{}' NOT NULL,
|
|
13
|
+
"created_at" text NOT NULL,
|
|
14
|
+
"accepted_at" text,
|
|
15
|
+
"rejected_at" text
|
|
16
|
+
);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS "idx_structured_agent_estimates_decision" ON "structured_agent_estimates" ("decision_id","status","created_at");
|
|
18
|
+
|
|
19
|
+
CREATE TABLE IF NOT EXISTS "decision_assignment_graphs" (
|
|
20
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
21
|
+
"team_id" text NOT NULL,
|
|
22
|
+
"project_id" text NOT NULL,
|
|
23
|
+
"decision_id" text NOT NULL,
|
|
24
|
+
"version" integer NOT NULL,
|
|
25
|
+
"status" text NOT NULL,
|
|
26
|
+
"active" integer DEFAULT 0 NOT NULL,
|
|
27
|
+
"graph_json" text NOT NULL,
|
|
28
|
+
"metadata_json" text DEFAULT '{}' NOT NULL,
|
|
29
|
+
"compiled_at" text,
|
|
30
|
+
"created_at" text NOT NULL,
|
|
31
|
+
"updated_at" text NOT NULL
|
|
32
|
+
);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS "idx_decision_assignment_graphs_decision" ON "decision_assignment_graphs" ("decision_id","active","version");
|
|
34
|
+
|
|
35
|
+
CREATE TABLE IF NOT EXISTS "deliverable_contracts" (
|
|
36
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
37
|
+
"team_id" text NOT NULL,
|
|
38
|
+
"project_id" text NOT NULL,
|
|
39
|
+
"decision_id" text NOT NULL,
|
|
40
|
+
"deliverable_type" text NOT NULL,
|
|
41
|
+
"status" text NOT NULL,
|
|
42
|
+
"contract_json" text NOT NULL,
|
|
43
|
+
"metadata_json" text DEFAULT '{}' NOT NULL,
|
|
44
|
+
"created_at" text NOT NULL,
|
|
45
|
+
"updated_at" text NOT NULL
|
|
46
|
+
);
|
|
47
|
+
CREATE INDEX IF NOT EXISTS "idx_deliverable_contracts_decision" ON "deliverable_contracts" ("decision_id","status","deliverable_type");
|
|
48
|
+
|
|
49
|
+
CREATE TABLE IF NOT EXISTS "deliverable_manifests" (
|
|
50
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
51
|
+
"deliverable_contract_id" text NOT NULL,
|
|
52
|
+
"project_id" text NOT NULL,
|
|
53
|
+
"decision_id" text NOT NULL,
|
|
54
|
+
"ready_for_review" integer DEFAULT 0 NOT NULL,
|
|
55
|
+
"manifest_json" text NOT NULL,
|
|
56
|
+
"metadata_json" text DEFAULT '{}' NOT NULL,
|
|
57
|
+
"submitted_at" text,
|
|
58
|
+
"created_at" text NOT NULL
|
|
59
|
+
);
|
|
60
|
+
CREATE INDEX IF NOT EXISTS "idx_deliverable_manifests_contract" ON "deliverable_manifests" ("deliverable_contract_id","submitted_at");
|