@tiangong-ai/cli 0.0.20 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/README.md +148 -11
- package/dist/cli.js +1 -1
- package/dist/research/orchestration.js +166 -11
- package/dist/research/orchestration.js.map +1 -1
- package/dist/research/workspace/broker.js +407 -31
- package/dist/research/workspace/broker.js.map +1 -1
- package/dist/research/workspace/capabilities.js +65 -0
- package/dist/research/workspace/capabilities.js.map +1 -1
- package/dist/research/workspace/constants.d.ts +1 -1
- package/dist/research/workspace/constants.js +4 -0
- package/dist/research/workspace/constants.js.map +1 -1
- package/dist/research/workspace/evidence.d.ts +32 -0
- package/dist/research/workspace/evidence.js +235 -0
- package/dist/research/workspace/evidence.js.map +1 -0
- package/dist/research/workspace/executor.d.ts +11 -1
- package/dist/research/workspace/executor.js +678 -105
- package/dist/research/workspace/executor.js.map +1 -1
- package/dist/research/workspace/input-plan.d.ts +5 -0
- package/dist/research/workspace/input-plan.js +319 -0
- package/dist/research/workspace/input-plan.js.map +1 -0
- package/dist/research/workspace/journal.js +2 -1
- package/dist/research/workspace/journal.js.map +1 -1
- package/dist/research/workspace/preflight.d.ts +108 -0
- package/dist/research/workspace/preflight.js +261 -0
- package/dist/research/workspace/preflight.js.map +1 -0
- package/dist/research/workspace/projects.d.ts +5 -2
- package/dist/research/workspace/projects.js +290 -6
- package/dist/research/workspace/projects.js.map +1 -1
- package/dist/research/workspace/runtime.d.ts +5 -1
- package/dist/research/workspace/runtime.js +1314 -196
- package/dist/research/workspace/runtime.js.map +1 -1
- package/dist/research/workspace/sanitization.d.ts +5 -0
- package/dist/research/workspace/sanitization.js +72 -0
- package/dist/research/workspace/sanitization.js.map +1 -0
- package/dist/research/workspace/schemas.d.ts +17 -0
- package/dist/research/workspace/schemas.js +342 -0
- package/dist/research/workspace/schemas.js.map +1 -0
- package/dist/research/workspace/storage.js +4 -0
- package/dist/research/workspace/storage.js.map +1 -1
- package/dist/research/workspace/types.d.ts +160 -0
- package/dist/research/workspace/workspace.d.ts +14 -3
- package/dist/research/workspace/workspace.js +374 -12
- package/dist/research/workspace/workspace.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { loadCapabilityDeclarations, verifyCapabilities } from "./capabilities.js";
|
|
2
|
+
import { canonicalJson, sha256Text } from "./storage.js";
|
|
3
|
+
import { loadWorkspaceConfig, verifyDoctorAttestation } from "./workspace.js";
|
|
4
|
+
export const RESEARCH_AGENT_PROTOCOL_OVERHEAD_TOKENS = {
|
|
5
|
+
codex: 5_000,
|
|
6
|
+
claude: 12_000,
|
|
7
|
+
};
|
|
8
|
+
export const RESEARCH_ESTIMATED_BYTES_PER_TOKEN = 3;
|
|
9
|
+
export const RESEARCH_MAX_REPAIR_SOURCE_BYTES = 32_000;
|
|
10
|
+
export const RESEARCH_STRUCTURED_OUTPUT_MAX_TURNS = 2;
|
|
11
|
+
export const RESEARCH_BROKER_MAX_TURNS = 6;
|
|
12
|
+
export const RESEARCH_REPAIR_MAX_TURNS = 1;
|
|
13
|
+
export async function evaluateProjectPreflight(root, question, requirements, inputPlan) {
|
|
14
|
+
const config = await loadWorkspaceConfig(root);
|
|
15
|
+
const capabilities = await loadCapabilityDeclarations(root);
|
|
16
|
+
const capabilityVerification = await verifyCapabilities(root);
|
|
17
|
+
const doctorAttestation = config.mode === "production-research" ? await verifyDoctorAttestation(root) : null;
|
|
18
|
+
const networkCapabilities = capabilities.capabilities
|
|
19
|
+
.filter((capability) => capability.permissions.includes("brokered-network"))
|
|
20
|
+
.map((capability) => ({
|
|
21
|
+
id: capability.id,
|
|
22
|
+
allowedHosts: capability.allowedHosts,
|
|
23
|
+
accept: capability.http?.accept ?? null,
|
|
24
|
+
maxResponseBytes: capability.http?.maxResponseBytes ?? null,
|
|
25
|
+
maxItems: capability.http?.maxItems ?? null,
|
|
26
|
+
coverage: capability.coverage,
|
|
27
|
+
}));
|
|
28
|
+
const packageTokens = config.budget.packageMaxTokens;
|
|
29
|
+
const tokenReservation = Object.values(packageTokens).reduce((sum, value) => sum + value, 0);
|
|
30
|
+
const wallReservation = Object.values(config.budget.packageMaxWallSeconds).reduce((sum, value) => sum + value, 0);
|
|
31
|
+
const estimatedMaxCostUsd = config.producer.pricing && config.reviewer.pricing
|
|
32
|
+
? roundMoney(reservedAgentPackageCost(config.producer, packageTokens.discover, config) +
|
|
33
|
+
reservedAgentPackageCost(config.producer, packageTokens.analyze, config) +
|
|
34
|
+
reservedAgentPackageCost(config.producer, packageTokens.synthesize, config) +
|
|
35
|
+
reservedAgentPackageCost(config.reviewer, packageTokens.review, config))
|
|
36
|
+
: null;
|
|
37
|
+
const gaps = [];
|
|
38
|
+
if (config.mode === "production-research" && !requirements) {
|
|
39
|
+
gaps.push("explicit-evidence-requirements-missing");
|
|
40
|
+
}
|
|
41
|
+
if (config.mode === "production-research" && (!config.producer.model || !config.reviewer.model)) {
|
|
42
|
+
gaps.push("explicit-agent-models-missing");
|
|
43
|
+
}
|
|
44
|
+
if (config.mode === "production-research" &&
|
|
45
|
+
(!config.producer.effort || !config.reviewer.effort)) {
|
|
46
|
+
gaps.push("explicit-agent-effort-missing");
|
|
47
|
+
}
|
|
48
|
+
if (config.mode === "production-research" && !config.producer.verbosity) {
|
|
49
|
+
gaps.push("explicit-codex-verbosity-missing");
|
|
50
|
+
}
|
|
51
|
+
if (config.mode === "production-research" &&
|
|
52
|
+
(!config.producer.pricing || !config.reviewer.pricing)) {
|
|
53
|
+
gaps.push("explicit-agent-pricing-missing");
|
|
54
|
+
}
|
|
55
|
+
if (config.producer.agent === config.reviewer.agent) {
|
|
56
|
+
gaps.push("independent-review-route-missing");
|
|
57
|
+
}
|
|
58
|
+
if (!networkCapabilities.length && !inputPlan)
|
|
59
|
+
gaps.push("no-evidence-acquisition-plan");
|
|
60
|
+
if (capabilityVerification.status !== "verified") {
|
|
61
|
+
gaps.push("capability-lock-missing-or-drifted");
|
|
62
|
+
}
|
|
63
|
+
if (doctorAttestation && doctorAttestation.status !== "verified") {
|
|
64
|
+
gaps.push(`doctor-attestation-${doctorAttestation.status}`);
|
|
65
|
+
}
|
|
66
|
+
if (tokenReservation > config.budget.maxTokens) {
|
|
67
|
+
gaps.push(`package-token-reservations-exceed-total:${tokenReservation}/${config.budget.maxTokens}`);
|
|
68
|
+
}
|
|
69
|
+
if (wallReservation > config.budget.maxWallSeconds) {
|
|
70
|
+
gaps.push(`package-wall-reservations-exceed-total:${wallReservation}/${config.budget.maxWallSeconds}`);
|
|
71
|
+
}
|
|
72
|
+
if (estimatedMaxCostUsd !== null && estimatedMaxCostUsd > config.budget.maxCostUsd) {
|
|
73
|
+
gaps.push(`package-cost-reservations-exceed-total:${estimatedMaxCostUsd}/${config.budget.maxCostUsd}`);
|
|
74
|
+
}
|
|
75
|
+
const outputAndRepairReservation = config.budget.maxOutputTokens + config.budget.maxRepairTokens;
|
|
76
|
+
for (const [stage, tokens] of Object.entries(packageTokens)) {
|
|
77
|
+
if (tokens < outputAndRepairReservation) {
|
|
78
|
+
gaps.push(`package-output-repair-reservation-exceeds-${stage}:${outputAndRepairReservation}/${tokens}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const embeddedStageContextReservation = config.budget.maxOutputTokens * 2;
|
|
82
|
+
if (embeddedStageContextReservation > config.budget.maxInputContextTokens) {
|
|
83
|
+
gaps.push(`embedded-stage-context-reservation-exceeds-total:${embeddedStageContextReservation}/${config.budget.maxInputContextTokens}`);
|
|
84
|
+
}
|
|
85
|
+
const recommendedDiscoverOutputTokens = requirements ? 768 + requirements.minSources * 320 : null;
|
|
86
|
+
if (config.mode === "production-research" &&
|
|
87
|
+
recommendedDiscoverOutputTokens !== null &&
|
|
88
|
+
config.budget.maxOutputTokens < recommendedDiscoverOutputTokens) {
|
|
89
|
+
gaps.push(`discover-output-reservation-below-schema-recommendation:${config.budget.maxOutputTokens}/${recommendedDiscoverOutputTokens}`);
|
|
90
|
+
}
|
|
91
|
+
const estimatedInputContextTokens = inputPlan?.inputs.reduce((sum, input) => sum + Math.ceil((input.contextBytes ?? input.bytes) / 4), 0) ?? 0;
|
|
92
|
+
if (estimatedInputContextTokens > config.budget.maxInputContextTokens) {
|
|
93
|
+
gaps.push(`input-context-reservation-exceeds-total:${estimatedInputContextTokens}/${config.budget.maxInputContextTokens}`);
|
|
94
|
+
}
|
|
95
|
+
const maxTurns = {
|
|
96
|
+
discover: networkCapabilities.length
|
|
97
|
+
? RESEARCH_BROKER_MAX_TURNS
|
|
98
|
+
: RESEARCH_STRUCTURED_OUTPUT_MAX_TURNS,
|
|
99
|
+
analyze: RESEARCH_STRUCTURED_OUTPUT_MAX_TURNS,
|
|
100
|
+
synthesize: RESEARCH_STRUCTURED_OUTPUT_MAX_TURNS,
|
|
101
|
+
review: RESEARCH_STRUCTURED_OUTPUT_MAX_TURNS,
|
|
102
|
+
repair: RESEARCH_REPAIR_MAX_TURNS,
|
|
103
|
+
};
|
|
104
|
+
const preCallTokenReservations = {
|
|
105
|
+
discover: estimatedStageTokenReservation(config.producer, estimatedInputContextTokens, maxTurns.discover, config),
|
|
106
|
+
analyze: estimatedStageTokenReservation(config.producer, config.budget.maxOutputTokens, RESEARCH_STRUCTURED_OUTPUT_MAX_TURNS, config),
|
|
107
|
+
synthesize: estimatedStageTokenReservation(config.producer, config.budget.maxOutputTokens * 2, RESEARCH_STRUCTURED_OUTPUT_MAX_TURNS, config),
|
|
108
|
+
review: estimatedStageTokenReservation(config.reviewer, estimatedInputContextTokens + config.budget.maxOutputTokens * 3 + 2_000, RESEARCH_STRUCTURED_OUTPUT_MAX_TURNS, config),
|
|
109
|
+
};
|
|
110
|
+
for (const [stage, reservation] of Object.entries(preCallTokenReservations)) {
|
|
111
|
+
const configured = packageTokens[stage];
|
|
112
|
+
if (reservation > configured) {
|
|
113
|
+
gaps.push(`package-precall-reservation-exceeds-${stage}:${reservation}/${configured}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (requirements) {
|
|
117
|
+
appendEvidencePlanGaps(gaps, requirements, inputPlan, networkCapabilities);
|
|
118
|
+
}
|
|
119
|
+
const result = {
|
|
120
|
+
schemaVersion: 1,
|
|
121
|
+
mode: config.mode,
|
|
122
|
+
questionSha256: sha256Text(question),
|
|
123
|
+
evidenceRequirements: requirements,
|
|
124
|
+
capabilities: networkCapabilities,
|
|
125
|
+
inputPlan: inputPlan === null
|
|
126
|
+
? null
|
|
127
|
+
: {
|
|
128
|
+
sha256: inputPlan.sha256,
|
|
129
|
+
inputs: inputPlan.inputs.map((input) => ({
|
|
130
|
+
id: input.id,
|
|
131
|
+
role: input.role,
|
|
132
|
+
sha256: input.sha256,
|
|
133
|
+
bytes: input.bytes,
|
|
134
|
+
contextSha256: input.contextSha256,
|
|
135
|
+
contextBytes: input.contextBytes,
|
|
136
|
+
contextRanges: input.contextRanges ?? null,
|
|
137
|
+
dimensions: input.dimensions,
|
|
138
|
+
sourceType: input.sourceType,
|
|
139
|
+
fullText: input.fullText,
|
|
140
|
+
publicationDate: input.publicationDate,
|
|
141
|
+
})),
|
|
142
|
+
},
|
|
143
|
+
capabilityVerification,
|
|
144
|
+
doctorAttestation: doctorAttestation === null
|
|
145
|
+
? null
|
|
146
|
+
: {
|
|
147
|
+
status: doctorAttestation.status,
|
|
148
|
+
errors: doctorAttestation.errors,
|
|
149
|
+
attestationSha256: doctorAttestation.attestation?.attestationSha256 ?? null,
|
|
150
|
+
checkedAt: doctorAttestation.attestation?.checkedAt ?? null,
|
|
151
|
+
expiresAt: doctorAttestation.attestation?.expiresAt ?? null,
|
|
152
|
+
},
|
|
153
|
+
gaps,
|
|
154
|
+
budget: {
|
|
155
|
+
tokenReservation,
|
|
156
|
+
wallReservation,
|
|
157
|
+
maxTokens: config.budget.maxTokens,
|
|
158
|
+
maxCostUsd: config.budget.maxCostUsd,
|
|
159
|
+
maxWallSeconds: config.budget.maxWallSeconds,
|
|
160
|
+
confirmationCostUsd: config.budget.confirmationCostUsd,
|
|
161
|
+
confirmationRequired: config.budget.maxCostUsd > config.budget.confirmationCostUsd,
|
|
162
|
+
estimatedMaxCostUsd,
|
|
163
|
+
maxBrokerContextTokens: config.budget.maxBrokerContextTokens,
|
|
164
|
+
estimatedInputContextTokens,
|
|
165
|
+
maxInputContextTokens: config.budget.maxInputContextTokens,
|
|
166
|
+
embeddedStageContextReservation,
|
|
167
|
+
recommendedDiscoverOutputTokens,
|
|
168
|
+
outputTokenLimitEnforcement: {
|
|
169
|
+
producer: "post-execution",
|
|
170
|
+
reviewer: "post-execution",
|
|
171
|
+
},
|
|
172
|
+
preCallTokenReservations,
|
|
173
|
+
maxTurns,
|
|
174
|
+
},
|
|
175
|
+
executionPolicy: {
|
|
176
|
+
producer: {
|
|
177
|
+
agent: config.producer.agent,
|
|
178
|
+
model: config.producer.model,
|
|
179
|
+
effort: config.producer.effort ?? null,
|
|
180
|
+
verbosity: config.producer.verbosity ?? null,
|
|
181
|
+
wrapperTargetPinned: Boolean(config.producer.wrapperTargetBinary),
|
|
182
|
+
turnLimitEnforcement: config.producer.agent === "claude" ? "provider" : "reservation-and-post-execution",
|
|
183
|
+
},
|
|
184
|
+
reviewer: {
|
|
185
|
+
agent: config.reviewer.agent,
|
|
186
|
+
model: config.reviewer.model,
|
|
187
|
+
effort: config.reviewer.effort ?? null,
|
|
188
|
+
verbosity: config.reviewer.verbosity ?? null,
|
|
189
|
+
wrapperTargetPinned: Boolean(config.reviewer.wrapperTargetBinary),
|
|
190
|
+
turnLimitEnforcement: config.reviewer.agent === "claude" ? "provider" : "reservation-and-post-execution",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
readyToInitialize: capabilityVerification.status === "verified" &&
|
|
194
|
+
(config.mode === "smoke-test" || Boolean(requirements)) &&
|
|
195
|
+
gaps.length === 0,
|
|
196
|
+
};
|
|
197
|
+
return { ...result, preflightSha256: sha256Text(canonicalJson(result)) };
|
|
198
|
+
}
|
|
199
|
+
function estimatedStageTokenReservation(route, embeddedContextTokens, primaryMaxTurns, config) {
|
|
200
|
+
const protocolOverhead = RESEARCH_AGENT_PROTOCOL_OVERHEAD_TOKENS[route.agent];
|
|
201
|
+
const primaryPromptAndSchemaAllowance = 4_000;
|
|
202
|
+
const primaryInput = (protocolOverhead + embeddedContextTokens + primaryPromptAndSchemaAllowance) * primaryMaxTurns;
|
|
203
|
+
const repairInput = protocolOverhead +
|
|
204
|
+
Math.ceil((RESEARCH_MAX_REPAIR_SOURCE_BYTES + 2_048) / RESEARCH_ESTIMATED_BYTES_PER_TOKEN);
|
|
205
|
+
return (primaryInput +
|
|
206
|
+
config.budget.maxOutputTokens +
|
|
207
|
+
repairInput * RESEARCH_REPAIR_MAX_TURNS +
|
|
208
|
+
config.budget.maxRepairTokens);
|
|
209
|
+
}
|
|
210
|
+
export function reservedAgentPackageCost(route, tokens, config) {
|
|
211
|
+
if (!route.pricing)
|
|
212
|
+
return 0;
|
|
213
|
+
const maximumOutputTokens = Math.min(tokens, config.budget.maxOutputTokens + config.budget.maxRepairTokens);
|
|
214
|
+
const maximumInputTokens = Math.max(0, tokens - maximumOutputTokens);
|
|
215
|
+
const inputRate = Math.max(route.pricing.inputUsdPerMillionTokens, route.pricing.cachedInputUsdPerMillionTokens);
|
|
216
|
+
return ((maximumInputTokens * inputRate +
|
|
217
|
+
maximumOutputTokens * route.pricing.outputUsdPerMillionTokens) /
|
|
218
|
+
1_000_000);
|
|
219
|
+
}
|
|
220
|
+
function appendEvidencePlanGaps(gaps, requirements, inputPlan, networkCapabilities) {
|
|
221
|
+
const declaredCoverage = networkCapabilities.flatMap((capability) => capability.coverage ? [capability.coverage] : []);
|
|
222
|
+
const dimensions = new Set([
|
|
223
|
+
...declaredCoverage.flatMap((coverage) => coverage.dimensions),
|
|
224
|
+
...(inputPlan?.inputs.flatMap((input) => input.dimensions) ?? []),
|
|
225
|
+
]);
|
|
226
|
+
const sourceTypes = new Set([
|
|
227
|
+
...declaredCoverage.flatMap((coverage) => coverage.sourceTypes),
|
|
228
|
+
...(inputPlan?.inputs.map((input) => input.sourceType) ?? []),
|
|
229
|
+
]);
|
|
230
|
+
for (const dimension of requirements.dimensions) {
|
|
231
|
+
if (!dimensions.has(dimension))
|
|
232
|
+
gaps.push(`evidence-plan-dimension-uncovered:${dimension}`);
|
|
233
|
+
}
|
|
234
|
+
for (const sourceType of requirements.sourceTypes) {
|
|
235
|
+
if (!sourceTypes.has(sourceType)) {
|
|
236
|
+
gaps.push(`evidence-plan-source-type-uncovered:${sourceType}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const canDiscoverFullText = declaredCoverage.some((coverage) => coverage.fullText);
|
|
240
|
+
const canDiscoverDates = declaredCoverage.some((coverage) => coverage.publicationDates);
|
|
241
|
+
const plannedInputCount = inputPlan?.inputs.length ?? 0;
|
|
242
|
+
const plannedFullTextCount = inputPlan?.inputs.filter((input) => input.fullText).length ?? 0;
|
|
243
|
+
const plannedDatedCount = inputPlan?.inputs.filter((input) => input.publicationDate !== null &&
|
|
244
|
+
(requirements.publicationDateFrom === null ||
|
|
245
|
+
input.publicationDate >= requirements.publicationDateFrom) &&
|
|
246
|
+
(requirements.publicationDateTo === null ||
|
|
247
|
+
input.publicationDate <= requirements.publicationDateTo)).length ?? 0;
|
|
248
|
+
if (!networkCapabilities.length && plannedInputCount < requirements.minSources) {
|
|
249
|
+
gaps.push(`evidence-plan-min-sources-insufficient:${plannedInputCount}/${requirements.minSources}`);
|
|
250
|
+
}
|
|
251
|
+
if (requirements.minFullTextSources > plannedFullTextCount && !canDiscoverFullText) {
|
|
252
|
+
gaps.push(`evidence-plan-full-text-insufficient:${plannedFullTextCount}/${requirements.minFullTextSources}`);
|
|
253
|
+
}
|
|
254
|
+
if (requirements.minDatedSources > plannedDatedCount && !canDiscoverDates) {
|
|
255
|
+
gaps.push(`evidence-plan-dated-sources-insufficient:${plannedDatedCount}/${requirements.minDatedSources}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function roundMoney(value) {
|
|
259
|
+
return Math.round(value * 1_000_000) / 1_000_000;
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../../src/research/workspace/preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import type { ProjectInput, ProjectState, WorkPackage } from "./types.js";
|
|
2
|
-
export declare function initializeProject(root: string, projectId: string, question: string): Promise<ProjectState>;
|
|
1
|
+
import type { ProjectEvidenceRequirements, ProjectInput, ProjectState, WorkPackage, VerifiedProjectInputPlan } from "./types.js";
|
|
2
|
+
export declare function initializeProject(root: string, projectId: string, question: string, evidenceRequirements?: ProjectEvidenceRequirements, budgetConfirmed?: boolean, inputPlan?: VerifiedProjectInputPlan): Promise<ProjectState>;
|
|
3
3
|
export declare function addProjectInput(root: string, projectId: string, inputPath: string, role: ProjectInput["role"]): Promise<ProjectInput>;
|
|
4
4
|
export declare function loadProject(root: string, projectId: string): Promise<ProjectState>;
|
|
5
5
|
export declare function listProjects(root: string): Promise<ProjectState[]>;
|
|
6
6
|
export declare function saveProject(root: string, project: ProjectState): Promise<void>;
|
|
7
|
+
export declare function retryProjectPackage(root: string, projectId: string, packageId?: string): Promise<ProjectState>;
|
|
8
|
+
export declare function forkProject(root: string, sourceProjectId: string, targetProjectId: string, resumeThrough?: "discover" | "analyze" | "synthesize"): Promise<ProjectState>;
|
|
7
9
|
export declare function refreshProject(project: ProjectState): ProjectState;
|
|
8
10
|
export declare function nextReadyPackage(project: ProjectState): WorkPackage | undefined;
|
|
9
11
|
export declare function packageById(project: ProjectState, packageId: string): WorkPackage;
|
|
12
|
+
export declare function normalizeEvidenceRequirements(value: ProjectEvidenceRequirements): ProjectEvidenceRequirements;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { lstat, readdir } from "node:fs/promises";
|
|
3
|
-
import { basename, join, resolve, sep } from "node:path";
|
|
2
|
+
import { cp, lstat, readdir } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
4
4
|
import { CliError } from "../../errors.js";
|
|
5
5
|
import { RESEARCH_CONTROL_DIRECTORY } from "./constants.js";
|
|
6
6
|
import { appendJournalEvent } from "./journal.js";
|
|
7
|
-
import {
|
|
7
|
+
import { cloneProjectEvidenceReceipts } from "./evidence.js";
|
|
8
|
+
import { projectInputsFromPlan, reverifyProjectInputPlan } from "./input-plan.js";
|
|
9
|
+
import { evaluateProjectPreflight } from "./preflight.js";
|
|
10
|
+
import { ensureDirectory, fileRecord, fileSize, readJsonFile, sha256File, workspacePaths, writeJsonAtomic, } from "./storage.js";
|
|
8
11
|
import { loadWorkspaceConfig, withWorkspaceLock } from "./workspace.js";
|
|
9
12
|
const PROJECT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{2,63}$/;
|
|
10
|
-
export async function initializeProject(root, projectId, question) {
|
|
13
|
+
export async function initializeProject(root, projectId, question, evidenceRequirements, budgetConfirmed = false, inputPlan) {
|
|
11
14
|
validateProjectId(projectId);
|
|
12
15
|
const normalizedQuestion = question.trim();
|
|
13
16
|
if (normalizedQuestion.length < 8 || normalizedQuestion.length > 4000) {
|
|
@@ -28,6 +31,36 @@ export async function initializeProject(root, projectId, question) {
|
|
|
28
31
|
});
|
|
29
32
|
}
|
|
30
33
|
const config = await loadWorkspaceConfig(root);
|
|
34
|
+
if (config.mode === "production-research" && !evidenceRequirements) {
|
|
35
|
+
throw new CliError("Production research requires explicit evidence requirements.", {
|
|
36
|
+
code: "RESEARCH_EVIDENCE_REQUIREMENTS_REQUIRED",
|
|
37
|
+
exitCode: 2,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (config.mode === "production-research" &&
|
|
41
|
+
config.budget.maxCostUsd > config.budget.confirmationCostUsd &&
|
|
42
|
+
!budgetConfirmed) {
|
|
43
|
+
throw new CliError(`Production budget requires explicit confirmation above $${config.budget.confirmationCostUsd}.`, {
|
|
44
|
+
code: "RESEARCH_BUDGET_CONFIRMATION_REQUIRED",
|
|
45
|
+
exitCode: 2,
|
|
46
|
+
details: {
|
|
47
|
+
maxCostUsd: config.budget.maxCostUsd,
|
|
48
|
+
confirmationCostUsd: config.budget.confirmationCostUsd,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const requirements = normalizeEvidenceRequirements(evidenceRequirements ?? defaultEvidenceRequirements(config));
|
|
53
|
+
const admittedInputPlan = inputPlan ? await reverifyProjectInputPlan(inputPlan) : undefined;
|
|
54
|
+
if (config.mode === "production-research") {
|
|
55
|
+
const preflight = await evaluateProjectPreflight(root, normalizedQuestion, requirements, admittedInputPlan ?? null);
|
|
56
|
+
if (!preflight.readyToInitialize) {
|
|
57
|
+
throw new CliError("Production project initialization was blocked by preflight.", {
|
|
58
|
+
code: "RESEARCH_PREFLIGHT_BLOCKED",
|
|
59
|
+
exitCode: 3,
|
|
60
|
+
details: { gaps: preflight.gaps, preflightSha256: preflight.preflightSha256 },
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
31
64
|
const now = new Date().toISOString();
|
|
32
65
|
const project = {
|
|
33
66
|
schemaVersion: 1,
|
|
@@ -36,9 +69,18 @@ export async function initializeProject(root, projectId, question) {
|
|
|
36
69
|
status: "ready",
|
|
37
70
|
createdAt: now,
|
|
38
71
|
updatedAt: now,
|
|
39
|
-
|
|
72
|
+
budgetConfirmedAt: budgetConfirmed ? now : null,
|
|
73
|
+
inputs: admittedInputPlan ? projectInputsFromPlan(admittedInputPlan, now) : [],
|
|
74
|
+
evidenceRequirements: requirements,
|
|
40
75
|
packages: defaultWorkPackages(config),
|
|
41
|
-
usage: {
|
|
76
|
+
usage: {
|
|
77
|
+
tokens: 0,
|
|
78
|
+
inputTokens: 0,
|
|
79
|
+
cachedInputTokens: 0,
|
|
80
|
+
outputTokens: 0,
|
|
81
|
+
costUsd: 0,
|
|
82
|
+
wallSeconds: 0,
|
|
83
|
+
},
|
|
42
84
|
};
|
|
43
85
|
await Promise.all([
|
|
44
86
|
ensureDirectory(projectRoot),
|
|
@@ -49,6 +91,13 @@ export async function initializeProject(root, projectId, question) {
|
|
|
49
91
|
await appendJournalEvent(paths.journal, "project.initialized", projectId, {
|
|
50
92
|
projectId,
|
|
51
93
|
questionSha256: await hashQuestion(normalizedQuestion),
|
|
94
|
+
inputPlanSha256: admittedInputPlan?.sha256 ?? null,
|
|
95
|
+
inputs: project.inputs.map((input) => ({
|
|
96
|
+
id: input.id,
|
|
97
|
+
role: input.role,
|
|
98
|
+
sha256: input.sha256,
|
|
99
|
+
bytes: input.bytes,
|
|
100
|
+
})),
|
|
52
101
|
});
|
|
53
102
|
return project;
|
|
54
103
|
});
|
|
@@ -123,10 +172,156 @@ export async function saveProject(root, project) {
|
|
|
123
172
|
validateProjectShape(project, project.id);
|
|
124
173
|
await writeJsonAtomic(join(workspacePaths(root).projects, project.id, "project.json"), project);
|
|
125
174
|
}
|
|
175
|
+
export async function retryProjectPackage(root, projectId, packageId) {
|
|
176
|
+
validateProjectId(projectId);
|
|
177
|
+
return withWorkspaceLock(root, "project.retry", async () => {
|
|
178
|
+
const project = await loadProject(root, projectId);
|
|
179
|
+
const selected = packageId
|
|
180
|
+
? packageById(project, packageId)
|
|
181
|
+
: project.packages.find((item) => item.status === "failed" || item.status === "retry");
|
|
182
|
+
if (!selected || (selected.status !== "failed" && selected.status !== "retry")) {
|
|
183
|
+
throw new CliError("Project retry requires a failed or retryable package.", {
|
|
184
|
+
code: "RESEARCH_RETRY_NOT_AVAILABLE",
|
|
185
|
+
exitCode: 2,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
const selectedIndex = project.packages.indexOf(selected);
|
|
189
|
+
const previous = {
|
|
190
|
+
status: selected.status,
|
|
191
|
+
attempts: selected.attempts,
|
|
192
|
+
failureKind: selected.lastFailureKind,
|
|
193
|
+
};
|
|
194
|
+
for (const [index, workPackage] of project.packages.entries()) {
|
|
195
|
+
if (index < selectedIndex)
|
|
196
|
+
continue;
|
|
197
|
+
workPackage.status = index === selectedIndex ? "ready" : "pending";
|
|
198
|
+
workPackage.maxAttempts = Math.max(workPackage.maxAttempts, workPackage.attempts + 1);
|
|
199
|
+
workPackage.startedAt = null;
|
|
200
|
+
workPackage.completedAt = null;
|
|
201
|
+
workPackage.lastError = null;
|
|
202
|
+
workPackage.lastFailureKind = null;
|
|
203
|
+
workPackage.retryNotBefore = null;
|
|
204
|
+
}
|
|
205
|
+
project.status = "ready";
|
|
206
|
+
project.updatedAt = new Date().toISOString();
|
|
207
|
+
await saveProject(root, project);
|
|
208
|
+
await appendJournalEvent(workspacePaths(root).journal, "project.retry.requested", projectId, {
|
|
209
|
+
projectId,
|
|
210
|
+
packageId: selected.id,
|
|
211
|
+
previous,
|
|
212
|
+
preservedOutputs: true,
|
|
213
|
+
});
|
|
214
|
+
return project;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
export async function forkProject(root, sourceProjectId, targetProjectId, resumeThrough) {
|
|
218
|
+
validateProjectId(sourceProjectId);
|
|
219
|
+
validateProjectId(targetProjectId);
|
|
220
|
+
if (sourceProjectId === targetProjectId) {
|
|
221
|
+
throw new CliError("Fork target must use a different project ID.", {
|
|
222
|
+
code: "RESEARCH_PROJECT_FORK_INVALID",
|
|
223
|
+
exitCode: 2,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return withWorkspaceLock(root, "project.fork", async () => {
|
|
227
|
+
const source = await loadProject(root, sourceProjectId);
|
|
228
|
+
const targetRoot = join(workspacePaths(root).projects, targetProjectId);
|
|
229
|
+
if (await lstat(targetRoot).catch(() => undefined)) {
|
|
230
|
+
throw new CliError(`Research project already exists: ${targetProjectId}`, {
|
|
231
|
+
code: "RESEARCH_PROJECT_EXISTS",
|
|
232
|
+
exitCode: 2,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
const config = await loadWorkspaceConfig(root);
|
|
236
|
+
const packages = defaultWorkPackages(config);
|
|
237
|
+
const inheritedStages = resumeThrough
|
|
238
|
+
? ["discover", "analyze", "synthesize"].slice(0, ["discover", "analyze", "synthesize"].indexOf(resumeThrough) + 1)
|
|
239
|
+
: [];
|
|
240
|
+
for (const stage of inheritedStages) {
|
|
241
|
+
const sourcePackage = source.packages.find((item) => item.stage === stage);
|
|
242
|
+
if (sourcePackage?.status !== "complete") {
|
|
243
|
+
throw new CliError(`Cannot fork through incomplete package: ${stage}.`, {
|
|
244
|
+
code: "RESEARCH_PROJECT_FORK_INVALID",
|
|
245
|
+
exitCode: 2,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const now = new Date().toISOString();
|
|
250
|
+
for (const workPackage of packages) {
|
|
251
|
+
if (inheritedStages.includes(workPackage.stage)) {
|
|
252
|
+
workPackage.status = "complete";
|
|
253
|
+
workPackage.completedAt = now;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const project = {
|
|
257
|
+
schemaVersion: 1,
|
|
258
|
+
id: targetProjectId,
|
|
259
|
+
question: source.question,
|
|
260
|
+
status: "ready",
|
|
261
|
+
createdAt: now,
|
|
262
|
+
updatedAt: now,
|
|
263
|
+
budgetConfirmedAt: source.budgetConfirmedAt,
|
|
264
|
+
inputs: source.inputs.map((input) => ({ ...input })),
|
|
265
|
+
evidenceRequirements: {
|
|
266
|
+
...source.evidenceRequirements,
|
|
267
|
+
dimensions: [...source.evidenceRequirements.dimensions],
|
|
268
|
+
sourceTypes: [...source.evidenceRequirements.sourceTypes],
|
|
269
|
+
},
|
|
270
|
+
packages,
|
|
271
|
+
usage: {
|
|
272
|
+
tokens: 0,
|
|
273
|
+
inputTokens: 0,
|
|
274
|
+
cachedInputTokens: 0,
|
|
275
|
+
outputTokens: 0,
|
|
276
|
+
costUsd: 0,
|
|
277
|
+
wallSeconds: 0,
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
await Promise.all([
|
|
281
|
+
ensureDirectory(targetRoot),
|
|
282
|
+
ensureDirectory(join(targetRoot, "outputs")),
|
|
283
|
+
ensureDirectory(join(targetRoot, "runs")),
|
|
284
|
+
]);
|
|
285
|
+
const inheritedOutputs = [];
|
|
286
|
+
const stageOutput = {
|
|
287
|
+
discover: "outputs/evidence.json",
|
|
288
|
+
analyze: "outputs/analysis.json",
|
|
289
|
+
synthesize: "outputs/report.md",
|
|
290
|
+
};
|
|
291
|
+
for (const stage of inheritedStages) {
|
|
292
|
+
const logicalPath = stageOutput[stage];
|
|
293
|
+
const sourcePath = join(workspacePaths(root).projects, sourceProjectId, logicalPath);
|
|
294
|
+
const record = await fileRecord(sourcePath, logicalPath);
|
|
295
|
+
const destination = join(targetRoot, logicalPath);
|
|
296
|
+
await ensureDirectory(dirname(destination));
|
|
297
|
+
await cp(sourcePath, destination, { errorOnExist: true, force: false });
|
|
298
|
+
inheritedOutputs.push(record);
|
|
299
|
+
}
|
|
300
|
+
if (inheritedStages.includes("discover")) {
|
|
301
|
+
await cloneProjectEvidenceReceipts(root, sourceProjectId, targetProjectId);
|
|
302
|
+
}
|
|
303
|
+
refreshProject(project);
|
|
304
|
+
await writeJsonAtomic(join(targetRoot, "project.json"), project);
|
|
305
|
+
await appendJournalEvent(workspacePaths(root).journal, "project.forked", targetProjectId, {
|
|
306
|
+
sourceProjectId,
|
|
307
|
+
targetProjectId,
|
|
308
|
+
resumeThrough: resumeThrough ?? null,
|
|
309
|
+
inheritedOutputs,
|
|
310
|
+
inheritedUsage: false,
|
|
311
|
+
});
|
|
312
|
+
return project;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
126
315
|
export function refreshProject(project) {
|
|
316
|
+
const now = Date.now();
|
|
127
317
|
for (const workPackage of project.packages) {
|
|
128
318
|
if (workPackage.status !== "pending" && workPackage.status !== "retry")
|
|
129
319
|
continue;
|
|
320
|
+
if (workPackage.status === "retry" &&
|
|
321
|
+
workPackage.retryNotBefore &&
|
|
322
|
+
Date.parse(workPackage.retryNotBefore) > now) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
130
325
|
const dependenciesComplete = workPackage.dependencies.every((dependency) => project.packages.find((candidate) => candidate.id === dependency)?.status === "complete");
|
|
131
326
|
if (dependenciesComplete)
|
|
132
327
|
workPackage.status = "ready";
|
|
@@ -178,6 +373,8 @@ function workPackage(id, stage, kind, executor, dependencies, expectedOutputs, m
|
|
|
178
373
|
attempts: 0,
|
|
179
374
|
maxAttempts,
|
|
180
375
|
lastError: null,
|
|
376
|
+
lastFailureKind: null,
|
|
377
|
+
retryNotBefore: null,
|
|
181
378
|
startedAt: null,
|
|
182
379
|
completedAt: null,
|
|
183
380
|
};
|
|
@@ -187,10 +384,15 @@ function validateProjectShape(project, expectedId) {
|
|
|
187
384
|
project.id !== expectedId ||
|
|
188
385
|
!PROJECT_ID_PATTERN.test(project.id) ||
|
|
189
386
|
typeof project.question !== "string" ||
|
|
387
|
+
(project.budgetConfirmedAt !== null && typeof project.budgetConfirmedAt !== "string") ||
|
|
190
388
|
!Array.isArray(project.inputs) ||
|
|
389
|
+
!isEvidenceRequirements(project.evidenceRequirements) ||
|
|
191
390
|
!Array.isArray(project.packages) ||
|
|
192
391
|
!project.usage ||
|
|
193
392
|
typeof project.usage.tokens !== "number" ||
|
|
393
|
+
typeof project.usage.inputTokens !== "number" ||
|
|
394
|
+
typeof project.usage.cachedInputTokens !== "number" ||
|
|
395
|
+
typeof project.usage.outputTokens !== "number" ||
|
|
194
396
|
typeof project.usage.costUsd !== "number" ||
|
|
195
397
|
typeof project.usage.wallSeconds !== "number") {
|
|
196
398
|
throw new CliError(`Research project state is invalid: ${expectedId}`, {
|
|
@@ -210,6 +412,88 @@ function validateProjectShape(project, expectedId) {
|
|
|
210
412
|
});
|
|
211
413
|
}
|
|
212
414
|
}
|
|
415
|
+
function defaultEvidenceRequirements(config) {
|
|
416
|
+
return config.mode === "production-research"
|
|
417
|
+
? {
|
|
418
|
+
dimensions: ["research-question"],
|
|
419
|
+
sourceTypes: ["primary"],
|
|
420
|
+
minSources: 3,
|
|
421
|
+
minFullTextSources: 1,
|
|
422
|
+
minDatedSources: 1,
|
|
423
|
+
publicationDateFrom: null,
|
|
424
|
+
publicationDateTo: null,
|
|
425
|
+
}
|
|
426
|
+
: {
|
|
427
|
+
dimensions: ["research-question"],
|
|
428
|
+
sourceTypes: [],
|
|
429
|
+
minSources: 1,
|
|
430
|
+
minFullTextSources: 0,
|
|
431
|
+
minDatedSources: 0,
|
|
432
|
+
publicationDateFrom: null,
|
|
433
|
+
publicationDateTo: null,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
export function normalizeEvidenceRequirements(value) {
|
|
437
|
+
const normalized = {
|
|
438
|
+
dimensions: [...new Set(value.dimensions.map(normalizeRequirementId))].sort(),
|
|
439
|
+
sourceTypes: [...new Set(value.sourceTypes.map(normalizeRequirementId))].sort(),
|
|
440
|
+
minSources: value.minSources,
|
|
441
|
+
minFullTextSources: value.minFullTextSources,
|
|
442
|
+
minDatedSources: value.minDatedSources,
|
|
443
|
+
publicationDateFrom: value.publicationDateFrom,
|
|
444
|
+
publicationDateTo: value.publicationDateTo,
|
|
445
|
+
};
|
|
446
|
+
if (!isEvidenceRequirements(normalized)) {
|
|
447
|
+
throw new CliError("Evidence requirements are invalid.", {
|
|
448
|
+
code: "RESEARCH_EVIDENCE_REQUIREMENTS_INVALID",
|
|
449
|
+
exitCode: 2,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
return normalized;
|
|
453
|
+
}
|
|
454
|
+
function isEvidenceRequirements(value) {
|
|
455
|
+
return (Boolean(value) &&
|
|
456
|
+
typeof value === "object" &&
|
|
457
|
+
!Array.isArray(value) &&
|
|
458
|
+
Array.isArray(value.dimensions) &&
|
|
459
|
+
value.dimensions.length > 0 &&
|
|
460
|
+
value.dimensions.every(validRequirementId) &&
|
|
461
|
+
Array.isArray(value.sourceTypes) &&
|
|
462
|
+
value.sourceTypes.every(validRequirementId) &&
|
|
463
|
+
Number.isInteger(value.minSources) &&
|
|
464
|
+
value.minSources > 0 &&
|
|
465
|
+
Number.isInteger(value.minFullTextSources) &&
|
|
466
|
+
value.minFullTextSources >= 0 &&
|
|
467
|
+
value.minFullTextSources <=
|
|
468
|
+
value.minSources &&
|
|
469
|
+
Number.isInteger(value.minDatedSources) &&
|
|
470
|
+
value.minDatedSources >= 0 &&
|
|
471
|
+
value.minDatedSources <=
|
|
472
|
+
value.minSources &&
|
|
473
|
+
validDateBoundary(value.publicationDateFrom) &&
|
|
474
|
+
validDateBoundary(value.publicationDateTo) &&
|
|
475
|
+
dateRangeOrdered(value.publicationDateFrom, value.publicationDateTo));
|
|
476
|
+
}
|
|
477
|
+
function validDateBoundary(value) {
|
|
478
|
+
if (value === null)
|
|
479
|
+
return true;
|
|
480
|
+
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
481
|
+
return false;
|
|
482
|
+
const timestamp = Date.parse(`${value}T00:00:00.000Z`);
|
|
483
|
+
return Number.isFinite(timestamp) && new Date(timestamp).toISOString().slice(0, 10) === value;
|
|
484
|
+
}
|
|
485
|
+
function dateRangeOrdered(from, to) {
|
|
486
|
+
return from === null || to === null || from <= to;
|
|
487
|
+
}
|
|
488
|
+
function normalizeRequirementId(value) {
|
|
489
|
+
return value
|
|
490
|
+
.trim()
|
|
491
|
+
.toLowerCase()
|
|
492
|
+
.replace(/[^a-z0-9._:-]+/g, "-");
|
|
493
|
+
}
|
|
494
|
+
function validRequirementId(value) {
|
|
495
|
+
return typeof value === "string" && /^[a-z0-9][a-z0-9._:-]{0,127}$/.test(value);
|
|
496
|
+
}
|
|
213
497
|
function validateProjectId(projectId) {
|
|
214
498
|
if (!PROJECT_ID_PATTERN.test(projectId)) {
|
|
215
499
|
throw new CliError("Project ID must contain 3-64 lowercase letters, digits, or hyphens and start with a letter or digit.", { code: "RESEARCH_PROJECT_ID_INVALID", exitCode: 2 });
|