pi-crew 0.9.33 → 0.9.35
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/CHANGELOG.md +211 -0
- package/dist/build-meta.json +257 -261
- package/dist/index.mjs +2127 -1937
- package/dist/index.mjs.map +4 -4
- package/docs/performance-optimization-execution-plan.md +520 -0
- package/package.json +1 -1
- package/src/extension/team-tool/api.ts +8 -8
- package/src/extension/team-tool/goal-wrap.ts +2 -2
- package/src/extension/team-tool/goal.ts +2 -2
- package/src/extension/team-tool/handle-schedule.ts +102 -0
- package/src/extension/team-tool/run.ts +0 -42
- package/src/extension/team-tool.ts +13 -8
- package/src/observability/exporters/otlp-exporter.ts +24 -2
- package/src/runtime/adaptive-plan.ts +3 -3
- package/src/runtime/background-runner.ts +4 -4
- package/src/runtime/child-pi.ts +191 -56
- package/src/runtime/live-session-runtime.ts +39 -18
- package/src/runtime/parallel-utils.ts +5 -0
- package/src/runtime/skill-instructions.ts +61 -2
- package/src/runtime/stale-reconciler.ts +5 -0
- package/src/runtime/task-graph-scheduler.ts +48 -17
- package/src/runtime/task-runner/prompt-builder.ts +55 -1
- package/src/runtime/task-runner/state-helpers.ts +18 -1
- package/src/runtime/task-runner/tail-read.ts +12 -0
- package/src/runtime/task-runner.ts +40 -19
- package/src/runtime/team-runner.ts +204 -84
- package/src/schema/team-tool-schema.ts +4 -0
- package/src/state/artifact-store.ts +5 -0
- package/src/state/atomic-write.ts +69 -9
- package/src/state/state-store.ts +107 -30
- package/src/utils/token-counter.ts +168 -35
- package/workflows/pipeline.workflow.md +13 -12
|
@@ -10,6 +10,11 @@ const CREW_SCHEDULER_KEY = Symbol.for("pi-crew:scheduler");
|
|
|
10
10
|
type SchedulerRef = {
|
|
11
11
|
add(job: import("../../runtime/scheduler.ts").ScheduledJob): void;
|
|
12
12
|
list(): import("../../runtime/scheduler.ts").ScheduledJob[];
|
|
13
|
+
remove(id: string): boolean;
|
|
14
|
+
update(
|
|
15
|
+
id: string,
|
|
16
|
+
patch: Partial<import("../../runtime/scheduler.ts").ScheduledJob>,
|
|
17
|
+
): import("../../runtime/scheduler.ts").ScheduledJob | undefined;
|
|
13
18
|
};
|
|
14
19
|
|
|
15
20
|
function getCrewScheduler(): SchedulerRef | undefined {
|
|
@@ -65,7 +70,27 @@ function buildScheduleSpec(params: ScheduleParams): {
|
|
|
65
70
|
throw new Error("schedule requires one of: cron, interval, or once.");
|
|
66
71
|
}
|
|
67
72
|
|
|
73
|
+
function getSubAction(params: TeamToolParamsValue): string | undefined {
|
|
74
|
+
const raw = params.subAction ?? (params.config as { subAction?: unknown } | undefined)?.subAction;
|
|
75
|
+
return typeof raw === "string" ? raw.toLowerCase() : undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getJobIdParam(params: TeamToolParamsValue): string {
|
|
79
|
+
const fromTop = typeof params.jobId === "string" ? params.jobId : "";
|
|
80
|
+
const fromConfig = ((params.config as { jobId?: unknown } | undefined)?.jobId as string | undefined) ?? "";
|
|
81
|
+
return (fromTop || fromConfig).trim();
|
|
82
|
+
}
|
|
83
|
+
|
|
68
84
|
export function handleSchedule(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
|
|
85
|
+
// Route subactions FIRST so remove/disable/enable don't require a goal.
|
|
86
|
+
const subAction = getSubAction(params);
|
|
87
|
+
if (subAction === "remove" || subAction === "delete") {
|
|
88
|
+
return handleRemoveScheduled(params, ctx);
|
|
89
|
+
}
|
|
90
|
+
if (subAction === "disable" || subAction === "enable" || subAction === "update") {
|
|
91
|
+
return handleUpdateScheduled(params, ctx);
|
|
92
|
+
}
|
|
93
|
+
|
|
69
94
|
const team = params.team ?? "default";
|
|
70
95
|
const goal = params.goal ?? params.task ?? "";
|
|
71
96
|
if (!goal) return result("Schedule requires goal or task.", { action: "schedule", status: "error" }, true);
|
|
@@ -239,3 +264,80 @@ export function handleListScheduled(_params: TeamToolParamsValue, ctx: TeamConte
|
|
|
239
264
|
}
|
|
240
265
|
return result(lines.join("\n"), { action: "scheduled", status: "ok" });
|
|
241
266
|
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Remove a scheduled job. Mirrors the `cancel` action for runs.
|
|
270
|
+
*
|
|
271
|
+
* Usage:
|
|
272
|
+
* team action='schedule' subAction='remove' jobId='<uuid>'
|
|
273
|
+
* team action='schedule' subAction='delete' jobId='<uuid>'
|
|
274
|
+
*/
|
|
275
|
+
export function handleRemoveScheduled(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
|
|
276
|
+
const jobId = getJobIdParam(params);
|
|
277
|
+
if (!jobId) {
|
|
278
|
+
return result(
|
|
279
|
+
"subAction=remove requires jobId. Usage: team action='schedule' subAction='remove' jobId='<uuid>'",
|
|
280
|
+
{ action: "schedule", status: "error" },
|
|
281
|
+
true,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
const scheduler = getCrewScheduler();
|
|
285
|
+
if (!scheduler) {
|
|
286
|
+
return result("Scheduler not running.", { action: "schedule", status: "error" }, true);
|
|
287
|
+
}
|
|
288
|
+
const removed = scheduler.remove(jobId);
|
|
289
|
+
persistScheduledJobRemove(ctx.cwd, jobId);
|
|
290
|
+
if (!removed) {
|
|
291
|
+
return result(`No scheduled job with id '${jobId}'.`, { action: "schedule", status: "error" }, true);
|
|
292
|
+
}
|
|
293
|
+
return result([`Scheduled job removed.`, ` Job ID: ${jobId}`].join("\n"), {
|
|
294
|
+
action: "schedule",
|
|
295
|
+
status: "ok",
|
|
296
|
+
data: { jobId, removed: true },
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Update a scheduled job — toggle enabled flag or apply patches.
|
|
302
|
+
*
|
|
303
|
+
* Usage:
|
|
304
|
+
* team action='schedule' subAction='disable' jobId='<uuid>'
|
|
305
|
+
* team action='schedule' subAction='enable' jobId='<uuid>'
|
|
306
|
+
* team action='schedule' subAction='update' jobId='<uuid>' cron='0 9 * * *'
|
|
307
|
+
*/
|
|
308
|
+
export function handleUpdateScheduled(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
|
|
309
|
+
const jobId = getJobIdParam(params);
|
|
310
|
+
if (!jobId) {
|
|
311
|
+
return result(
|
|
312
|
+
"subAction=update requires jobId. Usage: team action='schedule' subAction='update|enable|disable' jobId='<uuid>'",
|
|
313
|
+
{ action: "schedule", status: "error" },
|
|
314
|
+
true,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
const subAction = typeof params.subAction === "string" ? params.subAction.toLowerCase() : "update";
|
|
318
|
+
const scheduler = getCrewScheduler();
|
|
319
|
+
if (!scheduler) {
|
|
320
|
+
return result("Scheduler not running.", { action: "schedule", status: "error" }, true);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const patch: Partial<import("../../runtime/scheduler.ts").ScheduledJob> = {};
|
|
324
|
+
if (subAction === "disable") patch.enabled = false;
|
|
325
|
+
if (subAction === "enable") patch.enabled = true;
|
|
326
|
+
// For generic "update", allow patch of cron / interval / goal.
|
|
327
|
+
if (subAction === "update") {
|
|
328
|
+
if (typeof params.cron === "string") patch.schedule = params.cron;
|
|
329
|
+
if (typeof params.goal === "string" || typeof params.task === "string") {
|
|
330
|
+
patch.description = typeof params.task === "string" ? params.task : params.goal;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const updated = scheduler.update(jobId, patch);
|
|
335
|
+
if (!updated) {
|
|
336
|
+
return result(`No scheduled job with id '${jobId}'.`, { action: "schedule", status: "error" }, true);
|
|
337
|
+
}
|
|
338
|
+
persistScheduledJobUpdate(ctx.cwd, updated);
|
|
339
|
+
return result(
|
|
340
|
+
[`Scheduled job updated.`, ` Job ID: ${jobId}`, ` Enabled: ${updated.enabled}`, ` Schedule: ${updated.schedule}`].join("\n"),
|
|
341
|
+
{ action: "schedule", status: "ok", data: { jobId, enabled: updated.enabled, schedule: updated.schedule } },
|
|
342
|
+
);
|
|
343
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { allAgents, discoverAgents } from "../../agents/discover-agents.ts";
|
|
2
2
|
import { loadConfig } from "../../config/config.ts";
|
|
3
|
-
import { PipelineRunner, type PipelineWorkflow } from "../../runtime/pipeline-runner.ts";
|
|
4
3
|
import { sanitizeTaskText } from "../../runtime/task-packet.ts";
|
|
5
4
|
// Heavy runtime — lazy-loaded to avoid 1.4s import cost at extension registration.
|
|
6
5
|
import type { executeTeamRun as ExecuteTeamRunFn } from "../../runtime/team-runner.ts";
|
|
@@ -374,47 +373,6 @@ export async function handleRun(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
374
373
|
}
|
|
375
374
|
}
|
|
376
375
|
|
|
377
|
-
// Check if this is a pipeline workflow - special handling for multi-stage execution
|
|
378
|
-
const isPipelineWorkflow = workflowName === "pipeline" && !directAgent;
|
|
379
|
-
if (isPipelineWorkflow) {
|
|
380
|
-
// For pipeline workflows, use PipelineRunner for execution
|
|
381
|
-
const pipelineRunner = new PipelineRunner();
|
|
382
|
-
const pipelineWorkflow: PipelineWorkflow = {
|
|
383
|
-
name: workflow.name,
|
|
384
|
-
description: workflow.description,
|
|
385
|
-
goal,
|
|
386
|
-
stages: workflow.steps.map((step) => ({
|
|
387
|
-
name: step.id,
|
|
388
|
-
team: step.role,
|
|
389
|
-
inputs: step.task,
|
|
390
|
-
usePreviousResults: step.dependsOn && step.dependsOn.length > 0,
|
|
391
|
-
})),
|
|
392
|
-
stopOnError: true,
|
|
393
|
-
defaultMaxConcurrency: workflow.maxConcurrency ?? 5,
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
// For now, show pipeline workflow info - full integration would require
|
|
397
|
-
// connecting PipelineRunner to the actual team execution system
|
|
398
|
-
const stageInfo = pipelineWorkflow.stages.map((s) => `- ${s.name} (${s.team})`).join("\n");
|
|
399
|
-
return result(
|
|
400
|
-
[
|
|
401
|
-
`Pipeline workflow '${workflow.name}' is not yet wired into the team execution system.`,
|
|
402
|
-
`Goal: ${goal}`,
|
|
403
|
-
`Defined stages (${pipelineWorkflow.stages.length}):`,
|
|
404
|
-
stageInfo,
|
|
405
|
-
"",
|
|
406
|
-
"To actually run work right now, use a supported workflow instead:",
|
|
407
|
-
" - action='run' workflow='default' (explore → plan → execute → verify)",
|
|
408
|
-
" - action='run' workflow='implementation' (adaptive, parallel specialists)",
|
|
409
|
-
" - action='run' workflow='research' (explore → analyze → write)",
|
|
410
|
-
"",
|
|
411
|
-
"Run action='list' resource='workflow' to see all available workflows.",
|
|
412
|
-
].join("\n"),
|
|
413
|
-
{ action: "run", status: "ok" },
|
|
414
|
-
false,
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
376
|
// LAZY: dodge the jiti ESM/CJS interop TDZ race on the static `import { validateWorkflowForTeam }` above (issue #28, RFC 17).
|
|
419
377
|
const { validateWorkflowForTeam: validateWorkflow } = await import("../../workflows/validate-workflow.ts");
|
|
420
378
|
const validationErrors = validateWorkflow(workflow, team);
|
|
@@ -11,7 +11,7 @@ import { writeArtifact } from "../state/artifact-store.ts";
|
|
|
11
11
|
import { appendEvent, appendEventFireAndForget } from "../state/event-log.ts";
|
|
12
12
|
import { withRunLock } from "../state/locks.ts";
|
|
13
13
|
import { replayPendingMailboxMessages } from "../state/mailbox.ts";
|
|
14
|
-
import { loadRunManifestById,
|
|
14
|
+
import { loadRunManifestById, saveRunManifestAsync, saveRunTasks, updateRunStatus } from "../state/state-store.ts";
|
|
15
15
|
import type { ArtifactDescriptor, TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
16
16
|
import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
|
|
17
17
|
import { assertSafePathId } from "../utils/safe-paths.ts";
|
|
@@ -59,7 +59,7 @@ async function handleRun(...args: Parameters<HandleRunFn>): Promise<Awaited<Retu
|
|
|
59
59
|
|
|
60
60
|
import { FileCheckpointStore } from "../runtime/checkpoint.ts";
|
|
61
61
|
import { waitForRun } from "../runtime/run-tracker.ts";
|
|
62
|
-
import { normalizeSkillOverride } from "../runtime/skill-instructions.ts";
|
|
62
|
+
import { getSkillCacheStats, normalizeSkillOverride } from "../runtime/skill-instructions.ts";
|
|
63
63
|
import { computeRunCacheKey, getCachedRun, getCacheStats } from "../state/run-cache.ts";
|
|
64
64
|
import { listRunGraphs, loadRunGraph } from "../state/run-graph.ts";
|
|
65
65
|
import { searchAgents, searchTeams } from "../utils/bm25-search.ts";
|
|
@@ -223,10 +223,10 @@ function artifactKey(artifact: ArtifactDescriptor): string {
|
|
|
223
223
|
return `${artifact.kind}:${artifact.path}`;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
function recoverCheckpointedTasks(
|
|
226
|
+
async function recoverCheckpointedTasks(
|
|
227
227
|
manifest: TeamRunManifest,
|
|
228
228
|
tasks: TeamTaskState[],
|
|
229
|
-
): { manifest: TeamRunManifest; tasks: TeamTaskState[]; recovered: string[] } {
|
|
229
|
+
): Promise<{ manifest: TeamRunManifest; tasks: TeamTaskState[]; recovered: string[] }> {
|
|
230
230
|
const recovered: string[] = [];
|
|
231
231
|
let nextManifest = manifest;
|
|
232
232
|
const nextTasks = tasks.map((task) => {
|
|
@@ -302,7 +302,7 @@ function recoverCheckpointedTasks(
|
|
|
302
302
|
artifacts: [...artifacts.values()],
|
|
303
303
|
updatedAt: new Date().toISOString(),
|
|
304
304
|
};
|
|
305
|
-
|
|
305
|
+
await saveRunManifestAsync(nextManifest);
|
|
306
306
|
saveRunTasks(nextManifest, nextTasks);
|
|
307
307
|
}
|
|
308
308
|
return { manifest: nextManifest, tasks: nextTasks, recovered };
|
|
@@ -346,7 +346,7 @@ export async function handleResume(params: TeamToolParamsValue, ctx: TeamContext
|
|
|
346
346
|
const lockedManifest = fresh?.manifest ?? loaded.manifest;
|
|
347
347
|
const lockedTasks = fresh?.tasks ?? loaded.tasks;
|
|
348
348
|
const loadedConfig = loadConfig(ctx.cwd);
|
|
349
|
-
const recovered = recoverCheckpointedTasks(lockedManifest, lockedTasks);
|
|
349
|
+
const recovered = await recoverCheckpointedTasks(lockedManifest, lockedTasks);
|
|
350
350
|
const resumeManifest = recovered.manifest;
|
|
351
351
|
const executedConfig = {
|
|
352
352
|
...effectiveRunConfig(loadedConfig.config, params.config),
|
|
@@ -372,7 +372,7 @@ export async function handleResume(params: TeamToolParamsValue, ctx: TeamContext
|
|
|
372
372
|
runtimeResolution,
|
|
373
373
|
updatedAt: new Date().toISOString(),
|
|
374
374
|
};
|
|
375
|
-
|
|
375
|
+
await saveRunManifestAsync(runtimeManifest);
|
|
376
376
|
appendEvent(runtimeManifest.eventsPath, {
|
|
377
377
|
type: "runtime.resolved",
|
|
378
378
|
runId: runtimeManifest.runId,
|
|
@@ -974,7 +974,12 @@ export async function handleTeamTool(params: TeamToolParamsValue, ctx: TeamConte
|
|
|
974
974
|
});
|
|
975
975
|
}
|
|
976
976
|
const stats = getCacheStats(ctx.cwd);
|
|
977
|
-
|
|
977
|
+
const skillStats = getSkillCacheStats();
|
|
978
|
+
return result(
|
|
979
|
+
`Run cache: ${stats.entries} entries, ${stats.sizeBytes} bytes\n` +
|
|
980
|
+
`Skill cache: ${skillStats.hits} hits, ${skillStats.misses} misses (${(skillStats.hitRate * 100).toFixed(1)}% hit rate), ${skillStats.currentSize}/${skillStats.maxEntries} entries, ${skillStats.evictions} evictions`,
|
|
981
|
+
{ action: "cache", status: "ok" },
|
|
982
|
+
);
|
|
978
983
|
}
|
|
979
984
|
case "checkpoint": {
|
|
980
985
|
if (!params.runId || !params.taskId) {
|
|
@@ -38,12 +38,34 @@ export function validateEndpoint(endpoint: string): void {
|
|
|
38
38
|
// Reject IPv6 loopback and private
|
|
39
39
|
if (hostname.startsWith("[")) {
|
|
40
40
|
const bare = hostname.replace(/^\[|\]$/g, "");
|
|
41
|
-
|
|
41
|
+
const lower = bare.toLowerCase();
|
|
42
|
+
if (lower === "::1") {
|
|
42
43
|
throw new Error(`OTLP endpoint must not target loopback address: ${endpoint}`);
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
+
// Unique Local Addresses (fc00::/7) — fd and fc prefixes
|
|
46
|
+
if (lower.startsWith("fd") || lower.startsWith("fc")) {
|
|
45
47
|
throw new Error(`OTLP endpoint must not target private IPv6 address: ${endpoint}`);
|
|
46
48
|
}
|
|
49
|
+
// Link-Local (fe80::/10) — fe8x, fe9x, feax, febx prefixes
|
|
50
|
+
if (lower.startsWith("fe8") || lower.startsWith("fe9") || lower.startsWith("fea") || lower.startsWith("feb")) {
|
|
51
|
+
throw new Error(`OTLP endpoint must not target IPv6 link-local address: ${endpoint}`);
|
|
52
|
+
}
|
|
53
|
+
// Site-Local (fec0::/10) — deprecated but still routable on misconfigured networks
|
|
54
|
+
if (lower.startsWith("fec") || lower.startsWith("fed") || lower.startsWith("fee") || lower.startsWith("fef")) {
|
|
55
|
+
throw new Error(`OTLP endpoint must not target IPv6 site-local address: ${endpoint}`);
|
|
56
|
+
}
|
|
57
|
+
// Multicast (ff00::/8) — prefix ff
|
|
58
|
+
if (lower.startsWith("ff")) {
|
|
59
|
+
throw new Error(`OTLP endpoint must not target IPv6 multicast address: ${endpoint}`);
|
|
60
|
+
}
|
|
61
|
+
// Unspecified address ::
|
|
62
|
+
if (lower === "::") {
|
|
63
|
+
throw new Error(`OTLP endpoint must not target IPv6 unspecified address: ${endpoint}`);
|
|
64
|
+
}
|
|
65
|
+
// IPv4-mapped IPv6 (::ffff:x.x.x.x)
|
|
66
|
+
if (lower.startsWith("::ffff:")) {
|
|
67
|
+
throw new Error(`OTLP endpoint must not target IPv4-mapped IPv6 address: ${endpoint}`);
|
|
68
|
+
}
|
|
47
69
|
}
|
|
48
70
|
|
|
49
71
|
// Reject IPv4 private/reserved ranges
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import * as fs from "node:fs";
|
|
18
18
|
import { writeArtifact } from "../state/artifact-store.ts";
|
|
19
19
|
import { appendEvent } from "../state/event-log.ts";
|
|
20
|
-
import {
|
|
20
|
+
import { saveRunManifestAsync } from "../state/state-store.ts";
|
|
21
21
|
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
22
22
|
import type { TeamConfig } from "../teams/team-config.ts";
|
|
23
23
|
import type { WorkflowConfig, WorkflowStep } from "../workflows/workflow-config.ts";
|
|
@@ -369,7 +369,7 @@ export interface InjectAdaptivePlanResult {
|
|
|
369
369
|
missingPlan: boolean;
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
-
export function injectAdaptivePlanIfReady(input: InjectAdaptivePlanInput): InjectAdaptivePlanResult {
|
|
372
|
+
export async function injectAdaptivePlanIfReady(input: InjectAdaptivePlanInput): Promise<InjectAdaptivePlanResult> {
|
|
373
373
|
if (input.workflow.name !== "implementation")
|
|
374
374
|
return {
|
|
375
375
|
tasks: input.tasks,
|
|
@@ -442,7 +442,7 @@ export function injectAdaptivePlanIfReady(input: InjectAdaptivePlanInput): Injec
|
|
|
442
442
|
producer: assessTask.id,
|
|
443
443
|
content: `${JSON.stringify({ reason: repair.reason, phases: repair.plan.phases.map((phase) => ({ name: phase.name, count: phase.tasks.length, roles: phase.tasks.map((task) => task.role) })) }, null, 2)}\n`,
|
|
444
444
|
});
|
|
445
|
-
|
|
445
|
+
await saveRunManifestAsync({
|
|
446
446
|
...input.manifest,
|
|
447
447
|
updatedAt: new Date().toISOString(),
|
|
448
448
|
artifacts: [...input.manifest.artifacts, repairArtifact],
|
|
@@ -4,7 +4,7 @@ import { allAgents, discoverAgents } from "../agents/discover-agents.ts";
|
|
|
4
4
|
import { loadConfig } from "../config/config.ts";
|
|
5
5
|
import { appendEvent } from "../state/event-log.ts";
|
|
6
6
|
import { withRunLockSync } from "../state/locks.ts";
|
|
7
|
-
import { createRunPaths, loadRunManifestById,
|
|
7
|
+
import { createRunPaths, loadRunManifestById, saveRunManifestAsync, updateRunStatus } from "../state/state-store.ts";
|
|
8
8
|
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
9
9
|
import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
|
|
10
10
|
import { projectCrewRoot } from "../utils/paths.ts";
|
|
@@ -621,7 +621,7 @@ async function main(): Promise<void> {
|
|
|
621
621
|
status: runStatus,
|
|
622
622
|
updatedAt: new Date().toISOString(),
|
|
623
623
|
};
|
|
624
|
-
|
|
624
|
+
await saveRunManifestAsync(finalGoalManifest);
|
|
625
625
|
earlyResult = {
|
|
626
626
|
manifest: finalGoalManifest,
|
|
627
627
|
tasks: goalResult.tasks,
|
|
@@ -642,7 +642,7 @@ async function main(): Promise<void> {
|
|
|
642
642
|
signal: abortController.signal,
|
|
643
643
|
tokenBudget: wf.maxTokenBudget,
|
|
644
644
|
});
|
|
645
|
-
|
|
645
|
+
await saveRunManifestAsync(dwfResult.manifest);
|
|
646
646
|
earlyResult = dwfResult;
|
|
647
647
|
}
|
|
648
648
|
console.log(`[background-runner] ${manifest.runKind} returned, status=${earlyResult.manifest.status}`);
|
|
@@ -687,7 +687,7 @@ async function main(): Promise<void> {
|
|
|
687
687
|
runConfig,
|
|
688
688
|
updatedAt: new Date().toISOString(),
|
|
689
689
|
};
|
|
690
|
-
|
|
690
|
+
await saveRunManifestAsync(manifest);
|
|
691
691
|
appendEvent(manifest.eventsPath, {
|
|
692
692
|
type: "runtime.resolved",
|
|
693
693
|
runId: manifest.runId,
|