@xfey/tutti 0.1.50 → 0.1.52
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/approvals/projections.d.ts +0 -1
- package/dist/approvals/projections.js +0 -1
- package/dist/artifacts/candidate-validator.js +3 -3
- package/dist/artifacts/manifest.d.ts +2 -0
- package/dist/artifacts/manifest.js +9 -2
- package/dist/artifacts/preview-runtime.js +1 -0
- package/dist/collaboration-state/index.d.ts +1 -1
- package/dist/collaboration-state/index.js +1 -1
- package/dist/collaboration-state/recovery.d.ts +2 -1
- package/dist/collaboration-state/recovery.js +16 -0
- package/dist/collaboration-state/types.d.ts +5 -0
- package/dist/control-plane/follow-up-run.js +0 -3
- package/dist/control-plane/formatters.d.ts +2 -4
- package/dist/control-plane/formatters.js +5 -11
- package/dist/control-plane/index.d.ts +1 -1
- package/dist/control-plane/run-result-recording.d.ts +2 -9
- package/dist/control-plane/run-result-recording.js +6 -24
- package/dist/control-plane/run-scheduler.js +0 -3
- package/dist/control-plane/run-start.js +1 -8
- package/dist/control-plane/startup-recovery.d.ts +1 -1
- package/dist/control-plane/startup-recovery.js +90 -23
- package/dist/control-plane/types.d.ts +4 -17
- package/dist/providers/openai/app-server/workspace-write-run.d.ts +0 -2
- package/dist/providers/openai/app-server/workspace-write-run.js +0 -1
- package/dist/run-pipeline/candidate-diff.js +1 -7
- package/dist/run-pipeline/openai.d.ts +0 -4
- package/dist/run-pipeline/openai.js +42 -148
- package/dist/run-pipeline/task-run-invocation.d.ts +1 -2
- package/dist/run-pipeline/task-run-invocation.js +2 -4
- package/dist/server-shell/cli/host-server-runtime.js +3 -10
- package/dist/store/lifecycle.d.ts +1 -0
- package/dist/store/lifecycle.js +8 -6
- package/dist/workspace-ops/index.d.ts +3 -2
- package/dist/workspace-ops/index.js +2 -1
- package/dist/workspace-ops/mainline.d.ts +1 -1
- package/dist/workspace-ops/mainline.js +1 -5
- package/dist/workspace-ops/project-docs.js +26 -15
- package/dist/workspace-ops/project-workspace.d.ts +12 -0
- package/dist/workspace-ops/project-workspace.js +257 -0
- package/dist/workspace-ops/reference-files.js +9 -9
- package/dist/workspace-ops/reference-summaries.js +9 -7
- package/dist/workspace-ops/system-commits.d.ts +7 -0
- package/dist/workspace-ops/system-commits.js +43 -0
- package/dist/workspace-ops/types.d.ts +22 -41
- package/migrations/README.md +2 -2
- package/package.json +1 -1
- package/prompts/README.md +2 -2
- package/prompts/prompt-flow-experiment-plan.md +19 -19
- package/prompts/prompt-flow-map.md +20 -19
- package/prompts/runs/README.md +5 -3
- package/prompts/runs/task-continuation.md +6 -4
- package/prompts/runs/task-retry.md +11 -10
- package/prompts/runs/task-run.md +6 -4
- package/web/assets/{homepage-motion-scene-DYV3iw6y.js → homepage-motion-scene-ENUzAyyp.js} +1 -1
- package/web/assets/{index-DNnf10zz.css → index-CWWkCr48.css} +1 -1
- package/web/assets/{index-D_bRz29f.js → index-DK8LBgHp.js} +3 -3
- package/web/index.html +2 -2
- package/dist/run-pipeline/promotion-reconcile.d.ts +0 -21
- package/dist/run-pipeline/promotion-reconcile.js +0 -195
- package/dist/workspace-ops/run-workspaces.d.ts +0 -19
- package/dist/workspace-ops/run-workspaces.js +0 -302
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
1
|
import { createNodeNpmCheckPlan, executeCheckPlan } from "../checks/index.js";
|
|
3
2
|
import { candidateChangedAfterChecks } from "./candidate-diff.js";
|
|
4
3
|
import { runArtifactCandidateGate, } from "./artifact-applicability.js";
|
|
5
4
|
import { checkSkipFields, checkStatus, checksSummary, completedWithoutRepoChanges, docsStatusForChangedPaths, failedBeforeChecks, failedFinishedResult, promotionNotAttempted, promotionNotPromoted, successfulRunSummary, } from "./result-projections.js";
|
|
6
|
-
import { runPromotionReconcile } from "./promotion-reconcile.js";
|
|
7
5
|
import { runWorkspaceWriteTask } from "./task-run-invocation.js";
|
|
8
6
|
import { readOpenAiApiKeyCredential } from "../providers/openai/credential-store.js";
|
|
9
7
|
import { resolveOpenAiProviderConfig, } from "../providers/openai/provider-config.js";
|
|
10
8
|
import { CodexAppServerWorkspaceWriteRunError } from "../providers/openai/app-server/workspace-write-run.js";
|
|
11
9
|
import { getProjectUserSkillsRoot } from "../skills/index.js";
|
|
12
|
-
import {
|
|
10
|
+
import { collectProjectSourceDiff, createAcceptedCheckpoint, inspectProjectWorkspace, WorkspaceOpsError, } from "../workspace-ops/index.js";
|
|
13
11
|
export { TASK_RUN_OUTPUT_SCHEMA } from "./task-run-output.js";
|
|
14
12
|
function isRecord(value) {
|
|
15
13
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -55,12 +53,6 @@ function summarizeWorkspaceOpsError(error, changedPaths) {
|
|
|
55
53
|
: promotionNotAttempted("workspace_ops_failed", error.message),
|
|
56
54
|
});
|
|
57
55
|
}
|
|
58
|
-
function isMainlineMovedPromotionConflict(error) {
|
|
59
|
-
return (error instanceof WorkspaceOpsError &&
|
|
60
|
-
error.code === "conflict" &&
|
|
61
|
-
isRecord(error.details) &&
|
|
62
|
-
error.details.reason_code === "mainline_not_at_base");
|
|
63
|
-
}
|
|
64
56
|
function runLineageFields(input) {
|
|
65
57
|
return {
|
|
66
58
|
run_lineage_kind: input.run_lineage.kind,
|
|
@@ -89,8 +81,8 @@ function logRunPipelineStage(options, input, fields, message) {
|
|
|
89
81
|
}
|
|
90
82
|
function stageProgressSummary(stage) {
|
|
91
83
|
switch (stage) {
|
|
92
|
-
case "
|
|
93
|
-
return "
|
|
84
|
+
case "workspace_inspect":
|
|
85
|
+
return "inspecting the persistent project workspace.";
|
|
94
86
|
case "provider_task_run":
|
|
95
87
|
return "running Codex task implementation.";
|
|
96
88
|
case "provider_retry":
|
|
@@ -103,16 +95,10 @@ function stageProgressSummary(stage) {
|
|
|
103
95
|
return "running project checks.";
|
|
104
96
|
case "post_checks_diff_collect":
|
|
105
97
|
return "rechecking candidate changes after checks.";
|
|
106
|
-
case "
|
|
107
|
-
return "
|
|
108
|
-
case "promotion":
|
|
109
|
-
return "promoting candidate changes to mainline.";
|
|
110
|
-
case "promotion_reconcile":
|
|
111
|
-
return "reconciling promotion after mainline changed.";
|
|
98
|
+
case "accepted_checkpoint":
|
|
99
|
+
return "saving the validated project state as an accepted checkpoint.";
|
|
112
100
|
case "self_correction":
|
|
113
101
|
return "retrying task implementation after pipeline feedback.";
|
|
114
|
-
case "run_workspace_cleanup":
|
|
115
|
-
return "cleaning up the run workspace.";
|
|
116
102
|
default:
|
|
117
103
|
return `running stage ${stage}.`;
|
|
118
104
|
}
|
|
@@ -176,31 +162,6 @@ function loggedSyncStage(options, input, stage, run, completedFields = () => ({}
|
|
|
176
162
|
throw error;
|
|
177
163
|
}
|
|
178
164
|
}
|
|
179
|
-
function runResultLogFields(result) {
|
|
180
|
-
if (result.result_kind === "finished") {
|
|
181
|
-
return {
|
|
182
|
-
result_kind: result.result_kind,
|
|
183
|
-
outcome: result.outcome,
|
|
184
|
-
checks: result.checks,
|
|
185
|
-
docs: result.docs,
|
|
186
|
-
changed_path_count: result.changed_paths?.length ?? 0,
|
|
187
|
-
promotion_status: result.promotion?.status,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
if (result.result_kind === "failed") {
|
|
191
|
-
return {
|
|
192
|
-
result_kind: result.result_kind,
|
|
193
|
-
error_code: result.error_code,
|
|
194
|
-
retryable: result.retryable,
|
|
195
|
-
changed_path_count: result.changed_paths?.length ?? 0,
|
|
196
|
-
promotion_status: result.promotion?.status,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
return {
|
|
200
|
-
result_kind: result.result_kind,
|
|
201
|
-
changed_path_count: result.changed_paths?.length ?? 0,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
165
|
const MAX_SELF_CORRECTION_RETRIES = 1;
|
|
205
166
|
const MAX_PROVIDER_TRANSIENT_RETRIES = 1;
|
|
206
167
|
function runPipelineResultPromotion(result) {
|
|
@@ -264,18 +225,6 @@ function appendSelfCorrectionExhaustedSummary(input) {
|
|
|
264
225
|
summary: `${input.result.summary} Self-correction retry limit reached after ${input.attemptedRetries} retry attempt${input.attemptedRetries === 1 ? "" : "s"}.`,
|
|
265
226
|
};
|
|
266
227
|
}
|
|
267
|
-
function resultTimelineEvents(result) {
|
|
268
|
-
return result.timeline_events ?? [];
|
|
269
|
-
}
|
|
270
|
-
function withCarriedTimelineEvents(result, carriedTimelineEvents) {
|
|
271
|
-
if (carriedTimelineEvents.length === 0) {
|
|
272
|
-
return result;
|
|
273
|
-
}
|
|
274
|
-
return {
|
|
275
|
-
...result,
|
|
276
|
-
timeline_events: [...carriedTimelineEvents, ...resultTimelineEvents(result)],
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
228
|
function isSelfCorrectionRequest(result) {
|
|
280
229
|
return "request_kind" in result && result.request_kind === "self_correction";
|
|
281
230
|
}
|
|
@@ -285,35 +234,12 @@ function isProviderTransientRunFailure(result) {
|
|
|
285
234
|
result.retryable &&
|
|
286
235
|
result.error_code.startsWith("codex_app_server_"));
|
|
287
236
|
}
|
|
288
|
-
function reconcileSelfCorrectionReason(result) {
|
|
289
|
-
const promotion = runPipelineResultPromotion(result);
|
|
290
|
-
if (promotion?.status !== "not_promoted") {
|
|
291
|
-
return undefined;
|
|
292
|
-
}
|
|
293
|
-
if (promotion.reason_code !== undefined) {
|
|
294
|
-
return promotion.reason_code;
|
|
295
|
-
}
|
|
296
|
-
if (result.result_kind === "failed") {
|
|
297
|
-
return result.error_code;
|
|
298
|
-
}
|
|
299
|
-
if (result.result_kind === "finished" && result.outcome === "failed") {
|
|
300
|
-
return "promotion_reconcile_failed";
|
|
301
|
-
}
|
|
302
|
-
return undefined;
|
|
303
|
-
}
|
|
304
237
|
async function runTaskAttempt(options, input) {
|
|
305
|
-
let handle;
|
|
306
238
|
let changedPaths;
|
|
307
239
|
try {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
taskId: input.task.id,
|
|
312
|
-
activityRef: input.activity_ref,
|
|
313
|
-
}));
|
|
314
|
-
handle = runHandle;
|
|
315
|
-
const taskOutput = await loggedAsyncStage(options, input, "provider_task_run", () => runWorkspaceWriteTask(options, input, runHandle), (output) => ({ decision: output.decision }));
|
|
316
|
-
const diff = loggedSyncStage(options, input, "candidate_diff_collect", () => collectRunDiff(runHandle), (result) => ({ changed_path_count: result.changed_paths.length }));
|
|
240
|
+
loggedSyncStage(options, input, "workspace_inspect", () => inspectProjectWorkspace(options.workspaceRoot), (inspection) => ({ head_oid: inspection.head_ref }));
|
|
241
|
+
const taskOutput = await loggedAsyncStage(options, input, "provider_task_run", () => runWorkspaceWriteTask(options, input), (output) => ({ decision: output.decision }));
|
|
242
|
+
const diff = loggedSyncStage(options, input, "candidate_diff_collect", () => collectProjectSourceDiff(options.workspaceRoot), (result) => ({ changed_path_count: result.changed_paths.length }));
|
|
317
243
|
changedPaths = diff.changed_paths;
|
|
318
244
|
if (taskOutput.decision === "needs_human") {
|
|
319
245
|
logRunPipelineStage(options, input, {
|
|
@@ -338,7 +264,7 @@ async function runTaskAttempt(options, input) {
|
|
|
338
264
|
promotion: promotionNotAttempted(errorCode, taskOutput.summary),
|
|
339
265
|
});
|
|
340
266
|
}
|
|
341
|
-
if (
|
|
267
|
+
if (!diff.has_changes && taskOutput.decision === "completed_no_repo_changes") {
|
|
342
268
|
return completedWithoutRepoChanges({
|
|
343
269
|
summary: taskOutput.summary,
|
|
344
270
|
...(taskOutput.user_note_candidate === undefined
|
|
@@ -346,7 +272,7 @@ async function runTaskAttempt(options, input) {
|
|
|
346
272
|
: { userNoteCandidate: taskOutput.user_note_candidate }),
|
|
347
273
|
});
|
|
348
274
|
}
|
|
349
|
-
if (
|
|
275
|
+
if (!diff.has_changes) {
|
|
350
276
|
const result = failedBeforeChecks({
|
|
351
277
|
summary: "Codex completed the Run but produced no candidate changes.",
|
|
352
278
|
errorCode: "no_candidate_changes",
|
|
@@ -361,7 +287,7 @@ async function runTaskAttempt(options, input) {
|
|
|
361
287
|
});
|
|
362
288
|
}
|
|
363
289
|
const artifactGate = await loggedAsyncStage(options, input, "artifact_candidate_validate", () => runArtifactCandidateGate({
|
|
364
|
-
repoRoot:
|
|
290
|
+
repoRoot: options.workspaceRoot,
|
|
365
291
|
task: {
|
|
366
292
|
title: input.task.title,
|
|
367
293
|
goal: input.task_detail.detail.contract.goal,
|
|
@@ -409,9 +335,9 @@ async function runTaskAttempt(options, input) {
|
|
|
409
335
|
});
|
|
410
336
|
}
|
|
411
337
|
let docs = docsStatusForChangedPaths(changedPaths);
|
|
412
|
-
const checkPlan = createNodeNpmCheckPlan({ workspaceRoot:
|
|
338
|
+
const checkPlan = createNodeNpmCheckPlan({ workspaceRoot: options.workspaceRoot });
|
|
413
339
|
const checks = await loggedAsyncStage(options, input, "checks", () => executeCheckPlan({
|
|
414
|
-
workspaceRoot:
|
|
340
|
+
workspaceRoot: options.workspaceRoot,
|
|
415
341
|
plan: checkPlan,
|
|
416
342
|
...(options.checkTimeoutMs === undefined ? {} : { timeoutMs: options.checkTimeoutMs }),
|
|
417
343
|
}), (result) => ({ checks_status: result.status, command_count: result.commands.length }));
|
|
@@ -429,7 +355,7 @@ async function runTaskAttempt(options, input) {
|
|
|
429
355
|
guidance: "Use the failed checks summary to fix the candidate so the task can pass Tutti checks.",
|
|
430
356
|
});
|
|
431
357
|
}
|
|
432
|
-
const postChecksDiff = loggedSyncStage(options, input, "post_checks_diff_collect", () =>
|
|
358
|
+
const postChecksDiff = loggedSyncStage(options, input, "post_checks_diff_collect", () => collectProjectSourceDiff(options.workspaceRoot), (result) => ({ changed_path_count: result.changed_paths.length }));
|
|
433
359
|
changedPaths = postChecksDiff.changed_paths;
|
|
434
360
|
docs = docsStatusForChangedPaths(changedPaths);
|
|
435
361
|
if (candidateChangedAfterChecks(diff, postChecksDiff)) {
|
|
@@ -449,53 +375,23 @@ async function runTaskAttempt(options, input) {
|
|
|
449
375
|
guidance: "Prevent checks from mutating tracked files, or intentionally produce those generated files before checks run.",
|
|
450
376
|
});
|
|
451
377
|
}
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
runHandle,
|
|
463
|
-
taskOutput,
|
|
464
|
-
sourceCommit: commit,
|
|
465
|
-
changedPaths: postChecksDiff.changed_paths,
|
|
466
|
-
}), (outcome) => runResultLogFields(outcome.result));
|
|
467
|
-
const reconcileResult = reconcileOutcome.result;
|
|
468
|
-
if (reconcileOutcome.artifact_gate_failure !== undefined) {
|
|
469
|
-
if (!reconcileOutcome.artifact_gate_failure.correctable) {
|
|
470
|
-
return reconcileResult;
|
|
471
|
-
}
|
|
472
|
-
const artifactFailure = reconcileOutcome.artifact_gate_failure.failure;
|
|
473
|
-
return requestSelfCorrection({
|
|
474
|
-
result: reconcileResult,
|
|
475
|
-
reasonCode: artifactFailure.reason_code,
|
|
476
|
-
guidance: artifactFailure.guidance,
|
|
477
|
-
diagnostic: artifactFailure.diagnostic,
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
const reasonCode = reconcileSelfCorrectionReason(reconcileResult);
|
|
481
|
-
if (reasonCode !== undefined) {
|
|
482
|
-
return requestSelfCorrection({
|
|
483
|
-
result: reconcileResult,
|
|
484
|
-
reasonCode,
|
|
485
|
-
guidance: "Rebuild the candidate from the latest mainline and resolve the promotion reconcile failure.",
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
return reconcileResult;
|
|
489
|
-
}
|
|
490
|
-
throw error;
|
|
491
|
-
}
|
|
378
|
+
const checkpoint = loggedSyncStage(options, input, "accepted_checkpoint", () => createAcceptedCheckpoint({
|
|
379
|
+
workspaceRoot: options.workspaceRoot,
|
|
380
|
+
taskId: input.task.id,
|
|
381
|
+
activityRef: input.activity_ref,
|
|
382
|
+
summary: `Complete Tutti task: ${input.task.title}`,
|
|
383
|
+
expectedSourceFingerprint: postChecksDiff.fingerprint,
|
|
384
|
+
}), (result) => ({
|
|
385
|
+
promoted_commit_oid: result.promoted_commit_oid,
|
|
386
|
+
checkpoint_disposition: result.disposition,
|
|
387
|
+
}));
|
|
492
388
|
return {
|
|
493
389
|
result_kind: "finished",
|
|
494
390
|
outcome: "completed",
|
|
495
391
|
summary: successfulRunSummary({
|
|
496
392
|
implementation: taskOutput.summary,
|
|
497
393
|
checks,
|
|
498
|
-
completion: "
|
|
394
|
+
completion: "Accepted checkpoint complete.",
|
|
499
395
|
}),
|
|
500
396
|
...(taskOutput.user_note_candidate === undefined
|
|
501
397
|
? {}
|
|
@@ -506,14 +402,25 @@ async function runTaskAttempt(options, input) {
|
|
|
506
402
|
changed_paths: changedPaths,
|
|
507
403
|
promotion: {
|
|
508
404
|
status: "promoted",
|
|
509
|
-
commit_oid:
|
|
510
|
-
summary: "
|
|
405
|
+
commit_oid: checkpoint.promoted_commit_oid,
|
|
406
|
+
summary: "Accepted checkpoint created after validation and checks.",
|
|
511
407
|
},
|
|
512
408
|
};
|
|
513
409
|
}
|
|
514
410
|
catch (error) {
|
|
411
|
+
if (changedPaths === undefined) {
|
|
412
|
+
try {
|
|
413
|
+
changedPaths = collectProjectSourceDiff(options.workspaceRoot).changed_paths;
|
|
414
|
+
}
|
|
415
|
+
catch {
|
|
416
|
+
// Preserve the original pipeline error when the best-effort diff cannot be collected.
|
|
417
|
+
}
|
|
418
|
+
}
|
|
515
419
|
if (error instanceof CodexAppServerWorkspaceWriteRunError) {
|
|
516
|
-
return
|
|
420
|
+
return {
|
|
421
|
+
...summarizeProviderError(error),
|
|
422
|
+
...(changedPaths === undefined ? {} : { changed_paths: changedPaths }),
|
|
423
|
+
};
|
|
517
424
|
}
|
|
518
425
|
if (error instanceof WorkspaceOpsError) {
|
|
519
426
|
return summarizeWorkspaceOpsError(error, changedPaths);
|
|
@@ -525,12 +432,6 @@ async function runTaskAttempt(options, input) {
|
|
|
525
432
|
...(changedPaths === undefined ? {} : { changedPaths }),
|
|
526
433
|
});
|
|
527
434
|
}
|
|
528
|
-
finally {
|
|
529
|
-
const cleanupHandle = handle;
|
|
530
|
-
if (cleanupHandle !== undefined) {
|
|
531
|
-
loggedSyncStage(options, input, "run_workspace_cleanup", () => cleanupRunWorkspace(cleanupHandle));
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
435
|
}
|
|
535
436
|
export function createOpenAiRunPipelineRunner(options) {
|
|
536
437
|
return {
|
|
@@ -538,7 +439,6 @@ export function createOpenAiRunPipelineRunner(options) {
|
|
|
538
439
|
let currentInput = input;
|
|
539
440
|
let selfCorrectionRetries = 0;
|
|
540
441
|
let providerTransientRetries = 0;
|
|
541
|
-
let carriedTimelineEvents = [];
|
|
542
442
|
for (;;) {
|
|
543
443
|
const result = await runTaskAttempt(options, currentInput);
|
|
544
444
|
if (isProviderTransientRunFailure(result) &&
|
|
@@ -558,18 +458,14 @@ export function createOpenAiRunPipelineRunner(options) {
|
|
|
558
458
|
}
|
|
559
459
|
providerTransientRetries = 0;
|
|
560
460
|
if (!isSelfCorrectionRequest(result)) {
|
|
561
|
-
return
|
|
461
|
+
return result;
|
|
562
462
|
}
|
|
563
463
|
if (selfCorrectionRetries >= MAX_SELF_CORRECTION_RETRIES) {
|
|
564
|
-
return
|
|
464
|
+
return appendSelfCorrectionExhaustedSummary({
|
|
565
465
|
result: result.fallback,
|
|
566
466
|
attemptedRetries: selfCorrectionRetries,
|
|
567
|
-
})
|
|
467
|
+
});
|
|
568
468
|
}
|
|
569
|
-
carriedTimelineEvents = [
|
|
570
|
-
...carriedTimelineEvents,
|
|
571
|
-
...resultTimelineEvents(result.fallback),
|
|
572
|
-
];
|
|
573
469
|
selfCorrectionRetries += 1;
|
|
574
470
|
const correctionContext = result.correction_context;
|
|
575
471
|
const nextAttempt = currentInput.run_lineage.attempt + 1;
|
|
@@ -609,8 +505,6 @@ export function resolveProjectOpenAiRunPipelineRunner(options) {
|
|
|
609
505
|
config,
|
|
610
506
|
apiKey: credential.secret,
|
|
611
507
|
workspaceRoot: options.workspaceRoot,
|
|
612
|
-
runWorkspaceRoot: join(options.runWorkspaceRoot, "run-workspaces"),
|
|
613
|
-
mergeWorkspaceRoot: join(options.mergeWorkspaceRoot ?? options.runWorkspaceRoot, "merge-workspaces"),
|
|
614
508
|
tuttiHome: options.tuttiHome,
|
|
615
509
|
userSkillsRoot: getProjectUserSkillsRoot(options.tuttiHome, options.projectId),
|
|
616
510
|
...(options.agentContext === undefined ? {} : { agentContext: options.agentContext }),
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { RunPipelineInput } from "../control-plane/index.js";
|
|
2
|
-
import type { RunWorkspaceHandle } from "../workspace-ops/index.js";
|
|
3
2
|
import type { OpenAiRunPipelineRunnerOptions } from "./openai.js";
|
|
4
3
|
import { type TaskRunOutput } from "./task-run-output.js";
|
|
5
|
-
export declare function runWorkspaceWriteTask(options: OpenAiRunPipelineRunnerOptions, input: RunPipelineInput
|
|
4
|
+
export declare function runWorkspaceWriteTask(options: OpenAiRunPipelineRunnerOptions, input: RunPipelineInput): Promise<TaskRunOutput>;
|
|
6
5
|
//# sourceMappingURL=task-run-invocation.d.ts.map
|
|
@@ -56,7 +56,7 @@ function usageSource(input) {
|
|
|
56
56
|
return "workspace_write_run";
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
export async function runWorkspaceWriteTask(options, input
|
|
59
|
+
export async function runWorkspaceWriteTask(options, input) {
|
|
60
60
|
const manager = options.approvalManager;
|
|
61
61
|
const approvalHandler = manager === undefined
|
|
62
62
|
? undefined
|
|
@@ -66,7 +66,6 @@ export async function runWorkspaceWriteTask(options, input, handle) {
|
|
|
66
66
|
taskId: input.task.id,
|
|
67
67
|
activityRef: input.activity_ref,
|
|
68
68
|
workspaceRoot: approvalInput.context.workspaceRoot,
|
|
69
|
-
runWorkspaceRoot: approvalInput.context.runWorkspaceRoot,
|
|
70
69
|
codexHome: approvalInput.context.codexHome,
|
|
71
70
|
...(approvalInput.context.tuttiHome === undefined
|
|
72
71
|
? {}
|
|
@@ -82,7 +81,7 @@ export async function runWorkspaceWriteTask(options, input, handle) {
|
|
|
82
81
|
try {
|
|
83
82
|
result = await runCodexAppServerWorkspaceWriteRun({
|
|
84
83
|
apiKey: options.apiKey,
|
|
85
|
-
workspaceRoot:
|
|
84
|
+
workspaceRoot: options.workspaceRoot,
|
|
86
85
|
prompt: createRunPrompt(options, input),
|
|
87
86
|
outputSchema: TASK_RUN_OUTPUT_SCHEMA,
|
|
88
87
|
validateOutput: (value) => isInitialTaskRunOutput(value),
|
|
@@ -90,7 +89,6 @@ export async function runWorkspaceWriteTask(options, input, handle) {
|
|
|
90
89
|
defaultModel: options.config.default_model,
|
|
91
90
|
approvalContext: {
|
|
92
91
|
workspaceRoot: options.workspaceRoot,
|
|
93
|
-
runWorkspaceRoot: handle.repo_root,
|
|
94
92
|
taskId: input.task.id,
|
|
95
93
|
activityRef: input.activity_ref,
|
|
96
94
|
},
|
|
@@ -4,7 +4,6 @@ import { completeRelayUpload, resolveRelayUpload } from "@tutti/relay-client";
|
|
|
4
4
|
import { ApprovalRequestManager } from "../../approvals/index.js";
|
|
5
5
|
import { ReadOnlyChatAssistant } from "../../chat-assistant/index.js";
|
|
6
6
|
import { ensureInitialProjectWelcomeMessage } from "../../collaboration-state/index.js";
|
|
7
|
-
import { resolveControlPlaneCommandRecovery } from "../../control-plane/command-recovery.js";
|
|
8
7
|
import { Phase5ControlPlane } from "../../control-plane/index.js";
|
|
9
8
|
import { resolveProjectOpenAiProcedureWorkflowRunner } from "../../control-plane/workflows/index.js";
|
|
10
9
|
import { resolveProjectOpenAiReadOnlyChatAssistantModel } from "../../providers/openai/chat-assistant.js";
|
|
@@ -12,7 +11,6 @@ import { configureProjectOpenAiProvider, readOpenAiProviderConfigProjection, } f
|
|
|
12
11
|
import { readProviderUsageProjection, recordProviderUsageEvent, } from "../../provider-usage/index.js";
|
|
13
12
|
import { resolveProjectOpenAiRunPipelineRunner } from "../../run-pipeline/openai.js";
|
|
14
13
|
import { openHostProjectStore } from "../../store/index.js";
|
|
15
|
-
import { cleanupStaleRunWorkspaces } from "../../workspace-ops/index.js";
|
|
16
14
|
import { resolvePackagedWebStaticRoot } from "../http/static-web.js";
|
|
17
15
|
import { AGENT_CONTEXT_BASE_PATH, AgentContextTokenRegistry, } from "../http/routes/agent-context.js";
|
|
18
16
|
import { readHostRepoProjection } from "../http/routes/project-api.js";
|
|
@@ -133,7 +131,7 @@ export async function startForegroundHostServer(options) {
|
|
|
133
131
|
const store = openHostProjectStore({
|
|
134
132
|
localStoreRoot,
|
|
135
133
|
projectId: options.preparation.project_id,
|
|
136
|
-
|
|
134
|
+
deferCommandRecovery: true,
|
|
137
135
|
now,
|
|
138
136
|
});
|
|
139
137
|
const workspaceEvents = new WorkspaceEventBus({ now });
|
|
@@ -224,7 +222,6 @@ export async function startForegroundHostServer(options) {
|
|
|
224
222
|
tuttiHome: options.preparation.tutti_home,
|
|
225
223
|
projectId: options.preparation.project_id,
|
|
226
224
|
workspaceRoot: options.preparation.workspace_root,
|
|
227
|
-
runWorkspaceRoot: localStoreRoot,
|
|
228
225
|
agentContext: agentContextRuntime,
|
|
229
226
|
approvalManager,
|
|
230
227
|
logger: runPipelineLogger,
|
|
@@ -428,12 +425,8 @@ export async function startForegroundHostServer(options) {
|
|
|
428
425
|
throw error;
|
|
429
426
|
}
|
|
430
427
|
controlPlane.runStartupRecovery({
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
workspaceRoot: options.preparation.workspace_root,
|
|
434
|
-
runWorkspaceRoot: join(localStoreRoot, "run-workspaces"),
|
|
435
|
-
mergeWorkspaceRoot: join(localStoreRoot, "merge-workspaces"),
|
|
436
|
-
}),
|
|
428
|
+
workspaceRoot: options.preparation.workspace_root,
|
|
429
|
+
recoverInProgressCommands: true,
|
|
437
430
|
});
|
|
438
431
|
let resolveClosed;
|
|
439
432
|
const closed = new Promise((resolve) => {
|
|
@@ -17,6 +17,7 @@ export type OpenHostProjectStoreOptions = {
|
|
|
17
17
|
projectId: ProjectId;
|
|
18
18
|
migrationsDirectory?: string;
|
|
19
19
|
commandRecoveryResolver?: CommandRecoveryResolver;
|
|
20
|
+
deferCommandRecovery?: boolean;
|
|
20
21
|
now?: () => Date;
|
|
21
22
|
};
|
|
22
23
|
export declare function openHostProjectStore(options: OpenHostProjectStoreOptions): HostProjectStore;
|
package/dist/store/lifecycle.js
CHANGED
|
@@ -22,12 +22,14 @@ export function openHostProjectStore(options) {
|
|
|
22
22
|
projectId: options.projectId,
|
|
23
23
|
now,
|
|
24
24
|
});
|
|
25
|
-
const commandRecovery =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
const commandRecovery = options.deferCommandRecovery
|
|
26
|
+
? { total_count: 0, completed_count: 0, failed_count: 0, interrupted_count: 0 }
|
|
27
|
+
: recoverInProgressCommandIdempotencyRecords(db, {
|
|
28
|
+
...(options.commandRecoveryResolver === undefined
|
|
29
|
+
? {}
|
|
30
|
+
: { resolve: options.commandRecoveryResolver }),
|
|
31
|
+
now,
|
|
32
|
+
});
|
|
31
33
|
return {
|
|
32
34
|
db,
|
|
33
35
|
database_path: databasePath,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export { DEFAULT_VIEWER_MAX_BYTES, MAX_REFERENCE_UPLOAD_BYTES, MAX_VIEWER_MAX_BYTES, PROJECT_DOC_PATHS, REFERENCE_IMAGE_ASSET_CACHE_VERSION, REFERENCE_IMAGE_PREVIEW_MAX_EDGE_PX, REFERENCE_IMAGE_THUMBNAIL_MAX_EDGE_PX, REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, REFERENCE_TUTTI_DIRECTORY_PATH, VIEWER_ASSET_READ_LIMIT_BYTES, VIEWER_HARD_READ_LIMIT_BYTES, WORKSPACE_OPS_MAINLINE_BRANCH, } from "./constants.js";
|
|
2
2
|
export { WorkspaceOpsError } from "./errors.js";
|
|
3
|
+
export { commitOwnedPaths, requireOwnedPathsWriteReady } from "./system-commits.js";
|
|
4
|
+
export { collectProjectSourceDiff, createAcceptedCheckpoint, fingerprintProjectSourceState, inspectProjectWorkspace, recoverAcceptedCheckpoint, } from "./project-workspace.js";
|
|
3
5
|
export { initializeProjectDocs, readProjectDocsInitializationStatus, syncProjectDocs, } from "./project-docs.js";
|
|
4
6
|
export { uploadReferenceFile } from "./reference-files.js";
|
|
5
7
|
export { referenceFileCategoryForPath, referenceFileSourceForPath, referenceMediaTypeForPath, referenceTuttiGroupForPath, } from "./reference-metadata.js";
|
|
6
8
|
export { readReferenceSummaryIndex, readReferenceSummaryTargets, referenceSummaryProjectionForEntry, updateReferenceSummaries, } from "./reference-summaries.js";
|
|
7
|
-
export { applyRunResultToMergeWorkspace, cleanupMergeWorkspace, cleanupRunWorkspace, cleanupStaleRunWorkspaces, collectMergeDiff, collectRunDiff, createMergeCandidateCommit, createResultCommit, fingerprintWorkspaceCandidate, prepareMergeWorkspace, prepareRunWorkspace, promoteMergeCandidate, promoteRunResult, } from "./run-workspaces.js";
|
|
8
9
|
export { readViewerAsset, readViewerFile, readViewerSnapshot, readViewerTree } from "./viewer.js";
|
|
9
10
|
export type { WorkspaceOpsErrorCode } from "./errors.js";
|
|
10
|
-
export type {
|
|
11
|
+
export type { AcceptedCheckpointRecoveryResult, AcceptedCheckpointResult, CreateAcceptedCheckpointOptions, InitializeProjectDocsOptions, InitializeProjectDocsResult, ProjectDocsInitializationContext, ProjectDocsInitializationDocument, ProjectDocsInitializationStatus, ProjectWorkspaceInspection, ReadViewerAssetOptions, ReadViewerFileOptions, ReadViewerTreeOptions, ReferenceFileCategory, ReferenceSummaryProjection, ReferenceSummaryRecord, ReferenceSummaryStatus, ReferenceSummaryTarget, RunDiffSummary, SyncProjectDocsResult, SyncProjectDocsUpdate, UpdateReferenceSummariesOptions, UpdateReferenceSummariesResult, UploadReferenceFileOptions, UploadReferenceFileResult, ViewerAssetResult, ViewerAssetVariant, ViewerEntryKind, ViewerFileContent, ViewerFileResult, ViewerRepoSnapshot, ViewerTreeEntry, ViewerTreeResult, WorkspaceViewerOptions, } from "./types.js";
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { DEFAULT_VIEWER_MAX_BYTES, MAX_REFERENCE_UPLOAD_BYTES, MAX_VIEWER_MAX_BYTES, PROJECT_DOC_PATHS, REFERENCE_IMAGE_ASSET_CACHE_VERSION, REFERENCE_IMAGE_PREVIEW_MAX_EDGE_PX, REFERENCE_IMAGE_THUMBNAIL_MAX_EDGE_PX, REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, REFERENCE_TUTTI_DIRECTORY_PATH, VIEWER_ASSET_READ_LIMIT_BYTES, VIEWER_HARD_READ_LIMIT_BYTES, WORKSPACE_OPS_MAINLINE_BRANCH, } from "./constants.js";
|
|
2
2
|
export { WorkspaceOpsError } from "./errors.js";
|
|
3
|
+
export { commitOwnedPaths, requireOwnedPathsWriteReady } from "./system-commits.js";
|
|
4
|
+
export { collectProjectSourceDiff, createAcceptedCheckpoint, fingerprintProjectSourceState, inspectProjectWorkspace, recoverAcceptedCheckpoint, } from "./project-workspace.js";
|
|
3
5
|
export { initializeProjectDocs, readProjectDocsInitializationStatus, syncProjectDocs, } from "./project-docs.js";
|
|
4
6
|
export { uploadReferenceFile } from "./reference-files.js";
|
|
5
7
|
export { referenceFileCategoryForPath, referenceFileSourceForPath, referenceMediaTypeForPath, referenceTuttiGroupForPath, } from "./reference-metadata.js";
|
|
6
8
|
export { readReferenceSummaryIndex, readReferenceSummaryTargets, referenceSummaryProjectionForEntry, updateReferenceSummaries, } from "./reference-summaries.js";
|
|
7
|
-
export { applyRunResultToMergeWorkspace, cleanupMergeWorkspace, cleanupRunWorkspace, cleanupStaleRunWorkspaces, collectMergeDiff, collectRunDiff, createMergeCandidateCommit, createResultCommit, fingerprintWorkspaceCandidate, prepareMergeWorkspace, prepareRunWorkspace, promoteMergeCandidate, promoteRunResult, } from "./run-workspaces.js";
|
|
8
9
|
export { readViewerAsset, readViewerFile, readViewerSnapshot, readViewerTree } from "./viewer.js";
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function
|
|
1
|
+
export declare function requireMainlineBranch(workspaceRoot: string): void;
|
|
2
2
|
//# sourceMappingURL=mainline.d.ts.map
|
|
@@ -2,15 +2,11 @@ import { WORKSPACE_OPS_MAINLINE_BRANCH } from "./constants.js";
|
|
|
2
2
|
import { WorkspaceOpsError } from "./errors.js";
|
|
3
3
|
import { tryGitText } from "./git.js";
|
|
4
4
|
import { readViewerSnapshot } from "./viewer.js";
|
|
5
|
-
export function
|
|
5
|
+
export function requireMainlineBranch(workspaceRoot) {
|
|
6
6
|
readViewerSnapshot({ workspaceRoot });
|
|
7
7
|
const currentBranch = tryGitText(["branch", "--show-current"], workspaceRoot);
|
|
8
8
|
if (currentBranch !== WORKSPACE_OPS_MAINLINE_BRANCH) {
|
|
9
9
|
throw new WorkspaceOpsError("conflict", "Mainline write requires the mainline branch to be checked out");
|
|
10
10
|
}
|
|
11
|
-
const status = tryGitText(["status", "--porcelain"], workspaceRoot);
|
|
12
|
-
if (status !== "") {
|
|
13
|
-
throw new WorkspaceOpsError("conflict", "Mainline write requires a clean mainline working tree");
|
|
14
|
-
}
|
|
15
11
|
}
|
|
16
12
|
//# sourceMappingURL=mainline.js.map
|
|
@@ -2,8 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { extname, join, posix } from "node:path";
|
|
3
3
|
import { PROJECT_DOC_PATHS } from "./constants.js";
|
|
4
4
|
import { WorkspaceOpsError } from "./errors.js";
|
|
5
|
-
import {
|
|
6
|
-
import { requireMainlineWriteReady } from "./mainline.js";
|
|
5
|
+
import { commitOwnedPaths, requireOwnedPathsWriteReady } from "./system-commits.js";
|
|
7
6
|
import { projectDocTemplate, repoFactHints } from "./project-doc-templates.js";
|
|
8
7
|
import { entryKind, lsPath, readViewerSnapshot, requireNormalizedViewerPath, } from "./viewer.js";
|
|
9
8
|
const MAX_PROJECT_DOC_SYNC_BYTES = 512 * 1024;
|
|
@@ -61,7 +60,6 @@ export function readProjectDocsInitializationStatus(options) {
|
|
|
61
60
|
};
|
|
62
61
|
}
|
|
63
62
|
export function initializeProjectDocs(options) {
|
|
64
|
-
requireMainlineWriteReady(options.workspaceRoot);
|
|
65
63
|
const createdPaths = [];
|
|
66
64
|
const repoHints = repoFactHints(options.workspaceRoot);
|
|
67
65
|
const providedDocuments = new Map();
|
|
@@ -81,6 +79,11 @@ export function initializeProjectDocs(options) {
|
|
|
81
79
|
providedDocuments.set(path, document.content);
|
|
82
80
|
}
|
|
83
81
|
}
|
|
82
|
+
const missingPaths = PROJECT_DOC_PATHS.filter((path) => !projectDocExists(options.workspaceRoot, path));
|
|
83
|
+
if (missingPaths.length === 0) {
|
|
84
|
+
return { created_paths: [] };
|
|
85
|
+
}
|
|
86
|
+
requireOwnedPathsWriteReady(options.workspaceRoot, missingPaths);
|
|
84
87
|
for (const path of PROJECT_DOC_PATHS) {
|
|
85
88
|
if (projectDocExists(options.workspaceRoot, path)) {
|
|
86
89
|
continue;
|
|
@@ -89,20 +92,20 @@ export function initializeProjectDocs(options) {
|
|
|
89
92
|
writeFileSync(join(options.workspaceRoot, path), `${content.trimEnd()}\n`, "utf8");
|
|
90
93
|
createdPaths.push(path);
|
|
91
94
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
const commitOid = commitOwnedPaths({
|
|
96
|
+
workspaceRoot: options.workspaceRoot,
|
|
97
|
+
paths: createdPaths,
|
|
98
|
+
message: "Initialize project docs",
|
|
99
|
+
});
|
|
100
|
+
if (commitOid === null) {
|
|
101
|
+
throw new WorkspaceOpsError("git_failed", "Project docs write produced no owned-path commit");
|
|
98
102
|
}
|
|
99
103
|
return {
|
|
100
104
|
created_paths: createdPaths,
|
|
101
|
-
commit_oid:
|
|
105
|
+
commit_oid: commitOid,
|
|
102
106
|
};
|
|
103
107
|
}
|
|
104
108
|
export function syncProjectDocs(options) {
|
|
105
|
-
requireMainlineWriteReady(options.workspaceRoot);
|
|
106
109
|
const normalizedUpdates = options.updates.map((update) => {
|
|
107
110
|
const normalizedPath = requireProjectDocsSyncPath({
|
|
108
111
|
workspaceRoot: options.workspaceRoot,
|
|
@@ -127,6 +130,10 @@ export function syncProjectDocs(options) {
|
|
|
127
130
|
}
|
|
128
131
|
seenPaths.add(update.path);
|
|
129
132
|
}
|
|
133
|
+
if (normalizedUpdates.length === 0) {
|
|
134
|
+
return { updated_paths: [] };
|
|
135
|
+
}
|
|
136
|
+
requireOwnedPathsWriteReady(options.workspaceRoot, normalizedUpdates.map((update) => update.path));
|
|
130
137
|
const updatedPaths = [];
|
|
131
138
|
for (const update of normalizedUpdates) {
|
|
132
139
|
const filesystemPath = join(options.workspaceRoot, ...update.path.split("/"));
|
|
@@ -144,13 +151,17 @@ export function syncProjectDocs(options) {
|
|
|
144
151
|
if (updatedPaths.length === 0) {
|
|
145
152
|
return { updated_paths: [] };
|
|
146
153
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
const commitOid = commitOwnedPaths({
|
|
155
|
+
workspaceRoot: options.workspaceRoot,
|
|
156
|
+
paths: updatedPaths,
|
|
157
|
+
message: "Sync project docs",
|
|
158
|
+
});
|
|
159
|
+
if (commitOid === null) {
|
|
160
|
+
throw new WorkspaceOpsError("git_failed", "Project docs sync produced no owned-path commit");
|
|
150
161
|
}
|
|
151
162
|
return {
|
|
152
163
|
updated_paths: updatedPaths,
|
|
153
|
-
commit_oid:
|
|
164
|
+
commit_oid: commitOid,
|
|
154
165
|
};
|
|
155
166
|
}
|
|
156
167
|
//# sourceMappingURL=project-docs.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ActivityRef, TaskId } from "@tutti/shared/ids";
|
|
2
|
+
import type { AcceptedCheckpointRecoveryResult, AcceptedCheckpointResult, CreateAcceptedCheckpointOptions, ProjectWorkspaceInspection, RunDiffSummary } from "./types.js";
|
|
3
|
+
export declare function fingerprintProjectSourceState(workspaceRoot: string): string;
|
|
4
|
+
export declare function collectProjectSourceDiff(workspaceRoot: string): RunDiffSummary;
|
|
5
|
+
export declare function inspectProjectWorkspace(workspaceRoot: string): ProjectWorkspaceInspection;
|
|
6
|
+
export declare function recoverAcceptedCheckpoint(options: {
|
|
7
|
+
workspaceRoot: string;
|
|
8
|
+
taskId: TaskId;
|
|
9
|
+
activityRef: ActivityRef;
|
|
10
|
+
}): AcceptedCheckpointRecoveryResult;
|
|
11
|
+
export declare function createAcceptedCheckpoint(options: CreateAcceptedCheckpointOptions): AcceptedCheckpointResult;
|
|
12
|
+
//# sourceMappingURL=project-workspace.d.ts.map
|