@fitlab-ai/agent-infra 0.9.3 → 0.9.4
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/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +134 -123
- package/dist/lib/agent-clients/adapter.js +92 -2
- package/dist/lib/{sandbox/credentials.js → agent-clients/adapters/claude-code-credentials.js} +4 -14
- package/dist/lib/agent-clients/adapters/claude-code-refresh.js +85 -0
- package/dist/lib/agent-clients/adapters/claude-code-sandbox.js +247 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +17 -3
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +108 -0
- package/dist/lib/agent-clients/adapters/opencode.js +15 -3
- package/dist/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
- package/dist/lib/agent-clients/registry.js +9 -1
- package/dist/lib/internal/sandbox-control.js +24 -0
- package/dist/lib/internal/task-activity.js +45 -13
- package/dist/lib/internal/task-ledger.js +12 -3
- package/dist/lib/internal/task-lifecycle.js +19 -1
- package/dist/lib/internal/task-orchestration.js +122 -18
- package/dist/lib/platform/github-client.js +19 -2
- package/dist/lib/platform/pr-checks.js +1 -1
- package/dist/lib/platform/pull-requests.js +47 -1
- package/dist/lib/platform/releases.js +13 -5
- package/dist/lib/process-data/index.js +239 -0
- package/dist/lib/process-data/normalize.js +198 -0
- package/dist/lib/process-data/privacy.js +33 -0
- package/dist/lib/process-data/reconcile.js +95 -0
- package/dist/lib/process-data/repair.js +58 -0
- package/dist/lib/process-data/sources.js +345 -0
- package/dist/lib/process-data/store.js +282 -0
- package/dist/lib/process-data/types.js +2 -0
- package/dist/lib/run/index.js +4 -3
- package/dist/lib/run/tui.js +7 -8
- package/dist/lib/sandbox/agent-client-reconciler.js +26 -2
- package/dist/lib/sandbox/capture.js +7 -2
- package/dist/lib/sandbox/commands/create.js +109 -228
- package/dist/lib/sandbox/commands/enter.js +15 -47
- package/dist/lib/sandbox/commands/list-running.js +16 -6
- package/dist/lib/sandbox/commands/ls.js +10 -6
- package/dist/lib/sandbox/commands/rebuild.js +4 -3
- package/dist/lib/sandbox/commands/refresh.js +4 -84
- package/dist/lib/sandbox/commands/rm.js +24 -3
- package/dist/lib/sandbox/commands/show.js +12 -3
- package/dist/lib/sandbox/commands/start.js +7 -4
- package/dist/lib/sandbox/config.js +2 -0
- package/dist/lib/sandbox/constants.js +6 -0
- package/dist/lib/sandbox/control/client.js +44 -0
- package/dist/lib/sandbox/control/protocol.js +35 -0
- package/dist/lib/sandbox/control/server.js +109 -0
- package/dist/lib/sandbox/dockerfile.js +36 -13
- package/dist/lib/sandbox/mounts.js +17 -4
- package/dist/lib/sandbox/recovery.js +128 -5
- package/dist/lib/sandbox/redaction.js +12 -0
- package/dist/lib/sandbox/runtimes/base.dockerfile +24 -74
- package/dist/lib/sandbox/workspace-identity.js +95 -0
- package/dist/lib/sandbox/workspace-view.js +103 -0
- package/dist/lib/task/activity-intent.js +257 -51
- package/dist/lib/task/commit-finalization.js +267 -0
- package/dist/lib/task/commit-intent.js +165 -0
- package/dist/lib/task/decision-intents.js +31 -17
- package/dist/lib/task/delegation-receipts.js +2 -2
- package/dist/lib/task/implementation-inputs.js +54 -7
- package/dist/lib/task/ledger-intents.js +35 -1
- package/dist/lib/task/orchestration.js +363 -28
- package/dist/lib/task/resolve-ref.js +41 -1
- package/lib/agent-clients/adapter.ts +126 -2
- package/lib/{sandbox/credentials.ts → agent-clients/adapters/claude-code-credentials.ts} +3 -18
- package/lib/agent-clients/adapters/claude-code-refresh.ts +128 -0
- package/lib/agent-clients/adapters/claude-code-sandbox.ts +295 -0
- package/lib/agent-clients/adapters/claude-code.ts +21 -3
- package/lib/agent-clients/adapters/opencode-sandbox.ts +136 -0
- package/lib/agent-clients/adapters/opencode.ts +18 -3
- package/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
- package/lib/agent-clients/registry.ts +9 -1
- package/lib/internal/sandbox-control.ts +24 -0
- package/lib/internal/task-activity.ts +50 -14
- package/lib/internal/task-ledger.ts +12 -3
- package/lib/internal/task-lifecycle.ts +22 -1
- package/lib/internal/task-orchestration.ts +81 -11
- package/lib/platform/github-client.ts +20 -2
- package/lib/platform/pr-checks.ts +1 -1
- package/lib/platform/pull-requests.ts +49 -1
- package/lib/platform/releases.ts +16 -4
- package/lib/process-data/index.ts +224 -0
- package/lib/process-data/normalize.ts +202 -0
- package/lib/process-data/privacy.ts +41 -0
- package/lib/process-data/reconcile.ts +110 -0
- package/lib/process-data/repair.ts +64 -0
- package/lib/process-data/sources.ts +354 -0
- package/lib/process-data/store.ts +315 -0
- package/lib/process-data/types.ts +135 -0
- package/lib/run/index.ts +5 -3
- package/lib/run/tui.ts +11 -8
- package/lib/sandbox/agent-client-reconciler.ts +44 -2
- package/lib/sandbox/capture.ts +9 -2
- package/lib/sandbox/commands/create.ts +126 -260
- package/lib/sandbox/commands/enter.ts +13 -59
- package/lib/sandbox/commands/list-running.ts +23 -6
- package/lib/sandbox/commands/ls.ts +22 -6
- package/lib/sandbox/commands/rebuild.ts +4 -3
- package/lib/sandbox/commands/refresh.ts +4 -126
- package/lib/sandbox/commands/rm.ts +24 -3
- package/lib/sandbox/commands/show.ts +29 -3
- package/lib/sandbox/commands/start.ts +6 -4
- package/lib/sandbox/config.ts +4 -0
- package/lib/sandbox/constants.ts +8 -0
- package/lib/sandbox/control/client.ts +55 -0
- package/lib/sandbox/control/protocol.ts +75 -0
- package/lib/sandbox/control/server.ts +117 -0
- package/lib/sandbox/dockerfile.ts +66 -13
- package/lib/sandbox/mounts.ts +24 -4
- package/lib/sandbox/recovery.ts +163 -6
- package/lib/sandbox/redaction.ts +14 -0
- package/lib/sandbox/runtimes/base.dockerfile +24 -74
- package/lib/sandbox/tool-types.ts +16 -0
- package/lib/sandbox/workspace-identity.ts +123 -0
- package/lib/sandbox/workspace-view.ts +143 -0
- package/lib/task/activity-intent.ts +350 -61
- package/lib/task/commit-finalization.ts +356 -0
- package/lib/task/commit-intent.ts +230 -0
- package/lib/task/decision-intents.ts +32 -12
- package/lib/task/delegation-receipts.ts +2 -2
- package/lib/task/implementation-inputs.ts +62 -8
- package/lib/task/ledger-intents.ts +43 -3
- package/lib/task/orchestration.ts +501 -33
- package/lib/task/resolve-ref.ts +46 -1
- package/package.json +1 -1
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/human-decision-context.en.md +11 -2
- package/templates/.agents/rules/human-decision-context.zh-CN.md +11 -2
- package/templates/.agents/rules/pr-checks-commands.en.md +1 -1
- package/templates/.agents/rules/pr-checks-commands.github.en.md +1 -1
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +2 -2
- package/templates/.agents/rules/pr-checks-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/task-management.en.md +2 -0
- package/templates/.agents/rules/task-management.zh-CN.md +2 -0
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/SKILL.en.md +21 -11
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +27 -11
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +49 -0
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +55 -0
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +4 -4
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +4 -4
- package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +4 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/create-task/reference/context-capture.en.md +34 -0
- package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +34 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +1 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +1 -1
- package/templates/.agents/skills/review-pr/SKILL.en.md +36 -8
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +36 -8
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +1 -1
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +67 -50
- package/templates/.agents/skills/watch-pr/SKILL.en.md +4 -4
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
- package/templates/.agents/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.claude/rules/tool-preferences.en.md +20 -0
- package/templates/.claude/rules/tool-preferences.zh-CN.md +20 -0
- package/templates/.opencode/commands/watch-pr.en.md +1 -1
- package/templates/.opencode/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.gemini/commands/_project_/review-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-pr.zh-CN.toml +0 -10
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
4
5
|
import { parseTypedTaskFrontmatter } from './frontmatter.js';
|
|
5
6
|
import { parseReviewSummary } from './review-artifacts.js';
|
|
6
7
|
import { inspectArtifactDirectory } from './artifact-lifecycle.js';
|
|
@@ -13,6 +14,9 @@ import { getAgentClientCapability, getAgentClientDelegationEvidence, getAgentCli
|
|
|
13
14
|
import { isAgentClientId } from '../agent-clients/types.js';
|
|
14
15
|
import { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.js';
|
|
15
16
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
17
|
+
import { CommitIntentError, commitIntentPath, createCommitIntent, digest, readCommitIntent, removeCommitIntent, removeCommitIntentByDigest, serialize, updateCommitIntent } from './commit-intent.js';
|
|
18
|
+
import { inspectCommitFinalization, planCommitTaskFinalization } from './commit-finalization.js';
|
|
19
|
+
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
16
20
|
function supportsLifecycleDelegation(client) {
|
|
17
21
|
return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
|
|
18
22
|
&& getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
|
|
@@ -51,12 +55,70 @@ function atomicWrite(file, value) {
|
|
|
51
55
|
function saveRun(taskDir, run) {
|
|
52
56
|
atomicWrite(orchestrationPath(taskDir), run);
|
|
53
57
|
}
|
|
54
|
-
function withUpdatedRun(run, updates) {
|
|
55
|
-
return Object.freeze({ ...run, ...updates, updatedAt: new Date().toISOString() });
|
|
58
|
+
function withUpdatedRun(run, updates, now) {
|
|
59
|
+
return Object.freeze({ ...run, ...updates, updatedAt: (now ?? (() => new Date().toISOString()))() });
|
|
56
60
|
}
|
|
57
61
|
function failed(code, message, taskId = null, details = {}) {
|
|
58
62
|
return { status: 'failed', changed: false, taskId, run: null, next: null, error: { code, message, ...details } };
|
|
59
63
|
}
|
|
64
|
+
function commitIntentFailed(code, message, taskId = null) {
|
|
65
|
+
return { status: 'failed', changed: false, taskId, intent: null, error: { code, message } };
|
|
66
|
+
}
|
|
67
|
+
function repositoryHead(repoRoot) {
|
|
68
|
+
return execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
69
|
+
cwd: repoRoot,
|
|
70
|
+
encoding: 'utf8',
|
|
71
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
72
|
+
}).trim();
|
|
73
|
+
}
|
|
74
|
+
function isAncestor(repoRoot, ancestor, descendant) {
|
|
75
|
+
return spawnSync('git', ['merge-base', '--is-ancestor', ancestor, descendant], {
|
|
76
|
+
cwd: repoRoot,
|
|
77
|
+
stdio: 'ignore'
|
|
78
|
+
}).status === 0;
|
|
79
|
+
}
|
|
80
|
+
function commitIntentView(intent, currentHead) {
|
|
81
|
+
return {
|
|
82
|
+
mode: intent.mode,
|
|
83
|
+
phase: intent.phase,
|
|
84
|
+
baselineHead: intent.baselineHead,
|
|
85
|
+
currentHead,
|
|
86
|
+
committedHead: intent.committedHead,
|
|
87
|
+
pushEvidence: intent.pushEvidence,
|
|
88
|
+
runId: intent.orchestration?.runId ?? null,
|
|
89
|
+
receiptId: intent.orchestration?.receiptId ?? null
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function commitFinalizationView(inspection) {
|
|
93
|
+
return {
|
|
94
|
+
disposition: inspection.disposition,
|
|
95
|
+
code: inspection.code,
|
|
96
|
+
message: inspection.message,
|
|
97
|
+
currentHead: inspection.currentHead,
|
|
98
|
+
committedHead: inspection.committedHead,
|
|
99
|
+
needsAnchor: inspection.needsAnchor,
|
|
100
|
+
needsLog: inspection.needsLog
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function validCommitRun(run, taskId) {
|
|
104
|
+
return run !== null
|
|
105
|
+
&& [1, 2].includes(run.schemaVersion)
|
|
106
|
+
&& run.taskId === taskId
|
|
107
|
+
&& ['running', 'paused', 'completed'].includes(run.status)
|
|
108
|
+
&& Array.isArray(run.receipts)
|
|
109
|
+
&& Object.prototype.hasOwnProperty.call(run, 'pendingDelegation')
|
|
110
|
+
&& typeof run.commitAuthorization === 'object'
|
|
111
|
+
&& run.commitAuthorization !== null
|
|
112
|
+
&& Object.prototype.hasOwnProperty.call(run.commitAuthorization, 'issuedAt')
|
|
113
|
+
&& Object.prototype.hasOwnProperty.call(run.commitAuthorization, 'consumedAt');
|
|
114
|
+
}
|
|
115
|
+
function mapCommitIntentError(error, taskId) {
|
|
116
|
+
if (error instanceof CommitIntentError)
|
|
117
|
+
return commitIntentFailed(error.code, error.message, taskId);
|
|
118
|
+
if (error instanceof TaskExecutionLockError)
|
|
119
|
+
return commitIntentFailed(error.code, error.message, taskId);
|
|
120
|
+
return commitIntentFailed('ORCHESTRATION_COMMIT_INTENT_INVALID', error instanceof Error ? error.message : String(error), taskId);
|
|
121
|
+
}
|
|
60
122
|
function validModel(value) {
|
|
61
123
|
return typeof value === 'string' && value.length > 0 && value.trim() === value;
|
|
62
124
|
}
|
|
@@ -476,6 +538,289 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
476
538
|
}
|
|
477
539
|
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
478
540
|
}
|
|
541
|
+
function beginCommitIntent(taskRef, input, options = {}) {
|
|
542
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
543
|
+
if (!resolved.ok)
|
|
544
|
+
return commitIntentFailed(resolved.code, resolved.message, resolved.taskId);
|
|
545
|
+
try {
|
|
546
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'commit-intent.begin', () => {
|
|
547
|
+
const currentHead = repositoryHead(resolved.repoRoot);
|
|
548
|
+
if (currentHead !== input.baselineHead) {
|
|
549
|
+
return commitIntentFailed('ORCHESTRATION_COMMIT_BASELINE_MISMATCH', 'baseline HEAD does not match the repository HEAD', resolved.taskId);
|
|
550
|
+
}
|
|
551
|
+
if (fs.existsSync(commitIntentPath(resolved.taskDir))) {
|
|
552
|
+
try {
|
|
553
|
+
readCommitIntent(resolved.taskDir, resolved.taskId);
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
return mapCommitIntentError(error, resolved.taskId);
|
|
557
|
+
}
|
|
558
|
+
return commitIntentFailed('ORCHESTRATION_COMMIT_INTENT_BUSY', 'an active commit intent already exists', resolved.taskId);
|
|
559
|
+
}
|
|
560
|
+
const runFile = orchestrationPath(resolved.taskDir);
|
|
561
|
+
let run = null;
|
|
562
|
+
let sourceRunBytes = null;
|
|
563
|
+
if (fs.existsSync(runFile)) {
|
|
564
|
+
sourceRunBytes = fs.readFileSync(runFile, 'utf8');
|
|
565
|
+
try {
|
|
566
|
+
run = readRun(resolved.taskDir);
|
|
567
|
+
}
|
|
568
|
+
catch (error) {
|
|
569
|
+
return commitIntentFailed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
570
|
+
}
|
|
571
|
+
if (!validCommitRun(run, resolved.taskId)) {
|
|
572
|
+
return commitIntentFailed('ORCHESTRATION_STATE_INVALID', 'orchestration state has an invalid schema', resolved.taskId);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
576
|
+
const failAndPause = (code, message) => {
|
|
577
|
+
if (run?.status === 'running') {
|
|
578
|
+
saveRun(resolved.taskDir, withUpdatedRun(run, {
|
|
579
|
+
status: 'paused', pause: { code, message, recoverable: true }
|
|
580
|
+
}, () => now));
|
|
581
|
+
}
|
|
582
|
+
return commitIntentFailed(code, message, resolved.taskId);
|
|
583
|
+
};
|
|
584
|
+
let orchestration = null;
|
|
585
|
+
if (!input.orchestrated) {
|
|
586
|
+
if (run?.pendingDelegation) {
|
|
587
|
+
return commitIntentFailed('ORCHESTRATION_STANDALONE_BUSY', 'standalone commit is blocked by a pending orchestration delegation', resolved.taskId);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
if (!run || run.status !== 'running') {
|
|
592
|
+
return commitIntentFailed('ORCHESTRATION_RUN_NOT_RUNNING', 'orchestrated commit requires a running orchestration', resolved.taskId);
|
|
593
|
+
}
|
|
594
|
+
const receipt = run.pendingDelegation;
|
|
595
|
+
if (!receipt
|
|
596
|
+
|| receipt.status !== 'activated'
|
|
597
|
+
|| receipt.stage !== 'commit'
|
|
598
|
+
|| receipt.round !== 1
|
|
599
|
+
|| receipt.artifact !== 'commit'
|
|
600
|
+
|| receipt.role !== 'executor') {
|
|
601
|
+
return failAndPause('ORCHESTRATION_PROVENANCE_MISMATCH', 'orchestrated commit requires one matching activated commit delegation');
|
|
602
|
+
}
|
|
603
|
+
if (!run.commitAuthorization.issuedAt || run.commitAuthorization.consumedAt) {
|
|
604
|
+
return failAndPause('ORCHESTRATION_COMMIT_AUTHORIZATION_INVALID', 'orchestrated commit requires an unconsumed one-use authorization');
|
|
605
|
+
}
|
|
606
|
+
const planned = planOrchestrationStageCompletion(taskRef, {
|
|
607
|
+
stage: 'commit', round: 1, artifact: 'commit', role: 'executor', agent: input.agent
|
|
608
|
+
}, { ...options, now: () => now });
|
|
609
|
+
if (planned.result.status === 'failed' || !planned.plan?.updatedRun.pendingDelegation) {
|
|
610
|
+
return failAndPause(planned.result.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH', planned.result.error?.message ?? 'commit delegation could not be completed');
|
|
611
|
+
}
|
|
612
|
+
const plannedRunBytes = serialize(planned.plan.updatedRun);
|
|
613
|
+
orchestration = {
|
|
614
|
+
runId: run.runId,
|
|
615
|
+
receiptId: receipt.id,
|
|
616
|
+
authorizationIssuedAt: run.commitAuthorization.issuedAt,
|
|
617
|
+
sourceRunDigest: digest(sourceRunBytes),
|
|
618
|
+
plannedRunDigest: digest(plannedRunBytes),
|
|
619
|
+
completionUpdatedAt: planned.plan.updatedRun.updatedAt,
|
|
620
|
+
plannedReceipt: planned.plan.updatedRun.pendingDelegation
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
const created = createCommitIntent(resolved.taskDir, {
|
|
624
|
+
taskId: resolved.taskId,
|
|
625
|
+
mode: input.orchestrated ? 'orchestrated' : 'standalone',
|
|
626
|
+
phase: 'prepared',
|
|
627
|
+
baselineHead: input.baselineHead,
|
|
628
|
+
committedHead: null,
|
|
629
|
+
pushEvidence: null,
|
|
630
|
+
orchestration,
|
|
631
|
+
createdAt: now,
|
|
632
|
+
updatedAt: now
|
|
633
|
+
}, { token: options.token });
|
|
634
|
+
return {
|
|
635
|
+
status: 'ready', changed: true, taskId: resolved.taskId,
|
|
636
|
+
intent: commitIntentView(created.intent, currentHead), token: created.token, error: null
|
|
637
|
+
};
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
catch (error) {
|
|
641
|
+
return mapCommitIntentError(error, resolved.taskId);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
function checkpointCommitIntent(taskRef, input, options = {}) {
|
|
645
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
646
|
+
if (!resolved.ok)
|
|
647
|
+
return commitIntentFailed(resolved.code, resolved.message, resolved.taskId);
|
|
648
|
+
try {
|
|
649
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'commit-intent.checkpoint', () => {
|
|
650
|
+
const intent = readCommitIntent(resolved.taskDir, resolved.taskId, input.token);
|
|
651
|
+
const currentHead = repositoryHead(resolved.repoRoot);
|
|
652
|
+
if (currentHead !== input.head) {
|
|
653
|
+
return commitIntentFailed('ORCHESTRATION_COMMIT_RECOVERY_REQUIRED', 'checkpoint HEAD does not match the repository HEAD', resolved.taskId);
|
|
654
|
+
}
|
|
655
|
+
if (input.kind === 'committed' && !isAncestor(resolved.repoRoot, intent.baselineHead, input.head)) {
|
|
656
|
+
return commitIntentFailed('ORCHESTRATION_COMMIT_RECOVERY_REQUIRED', 'committed HEAD does not descend from the baseline', resolved.taskId);
|
|
657
|
+
}
|
|
658
|
+
if (input.kind === 'pushed') {
|
|
659
|
+
const expectedHead = intent.committedHead ?? intent.baselineHead;
|
|
660
|
+
if (input.head !== expectedHead || !input.remote || !input.ref) {
|
|
661
|
+
return commitIntentFailed('ORCHESTRATION_COMMIT_INTENT_STATE_INVALID', 'pushed checkpoint requires matching remote, ref and HEAD', resolved.taskId);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
665
|
+
const updated = updateCommitIntent(resolved.taskDir, resolved.taskId, input.token, input.kind === 'committed'
|
|
666
|
+
? { phase: 'committed', committedHead: input.head, updatedAt: now }
|
|
667
|
+
: {
|
|
668
|
+
phase: 'pushed', committedHead: intent.committedHead ?? intent.baselineHead, updatedAt: now,
|
|
669
|
+
pushEvidence: { remote: input.remote, ref: input.ref, head: input.head }
|
|
670
|
+
});
|
|
671
|
+
return {
|
|
672
|
+
status: 'ready', changed: true, taskId: resolved.taskId,
|
|
673
|
+
intent: commitIntentView(updated, currentHead), error: null
|
|
674
|
+
};
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
catch (error) {
|
|
678
|
+
return mapCommitIntentError(error, resolved.taskId);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
function completeCommitOrchestration(resolved, intent, agent) {
|
|
682
|
+
if (intent.orchestration === null)
|
|
683
|
+
return null;
|
|
684
|
+
if (intent.orchestration.plannedReceipt.agent !== agent) {
|
|
685
|
+
return { code: 'ORCHESTRATION_PROVENANCE_MISMATCH', message: 'completion agent does not match the planned receipt' };
|
|
686
|
+
}
|
|
687
|
+
const runFile = orchestrationPath(resolved.taskDir);
|
|
688
|
+
if (!fs.existsSync(runFile)) {
|
|
689
|
+
return { code: 'ORCHESTRATION_RUN_MISSING', message: 'orchestration run disappeared' };
|
|
690
|
+
}
|
|
691
|
+
const currentBytes = fs.readFileSync(runFile, 'utf8');
|
|
692
|
+
const currentDigest = digest(currentBytes);
|
|
693
|
+
if (currentDigest === intent.orchestration.sourceRunDigest) {
|
|
694
|
+
const run = readRun(resolved.taskDir);
|
|
695
|
+
if (!run)
|
|
696
|
+
return { code: 'ORCHESTRATION_RUN_MISSING', message: 'orchestration run disappeared' };
|
|
697
|
+
const plannedRun = Object.freeze({
|
|
698
|
+
...run,
|
|
699
|
+
pendingDelegation: intent.orchestration.plannedReceipt,
|
|
700
|
+
updatedAt: intent.orchestration.completionUpdatedAt
|
|
701
|
+
});
|
|
702
|
+
if (digest(serialize(plannedRun)) !== intent.orchestration.plannedRunDigest) {
|
|
703
|
+
return { code: 'ORCHESTRATION_COMMIT_RECOVERY_REQUIRED', message: 'planned orchestration bytes no longer match the intent' };
|
|
704
|
+
}
|
|
705
|
+
atomicWrite(runFile, plannedRun);
|
|
706
|
+
if (digest(fs.readFileSync(runFile)) !== intent.orchestration.plannedRunDigest) {
|
|
707
|
+
return { code: 'ORCHESTRATION_COMMIT_COMPLETE_PARTIAL', message: 'orchestration completion could not be verified' };
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
else if (currentDigest !== intent.orchestration.plannedRunDigest) {
|
|
711
|
+
return { code: 'ORCHESTRATION_COMMIT_RECOVERY_REQUIRED', message: 'orchestration state changed after commit begin' };
|
|
712
|
+
}
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
function finalizeCommitIntentUnlocked(resolved, inspection, agent, remove) {
|
|
716
|
+
if (inspection.disposition !== 'recoverable' || !inspection.intent) {
|
|
717
|
+
return commitIntentFailed(inspection.code ?? 'COMMIT_FINALIZATION_PENDING', inspection.message, resolved.taskId);
|
|
718
|
+
}
|
|
719
|
+
if (inspection.intent.orchestration !== null
|
|
720
|
+
&& inspection.intent.orchestration.plannedReceipt.agent !== agent) {
|
|
721
|
+
return commitIntentFailed('ORCHESTRATION_PROVENANCE_MISMATCH', 'completion agent does not match the planned receipt', resolved.taskId);
|
|
722
|
+
}
|
|
723
|
+
const metadata = captureTaskWriteMetadata();
|
|
724
|
+
const taskPlan = planCommitTaskFinalization(resolved.taskDir, inspection, agent, metadata.timestamp);
|
|
725
|
+
const written = writeTask({
|
|
726
|
+
taskRef: resolved.taskId,
|
|
727
|
+
expectedState: 'active',
|
|
728
|
+
mutations: taskPlan.mutations
|
|
729
|
+
}, {
|
|
730
|
+
repoRoot: resolved.repoRoot,
|
|
731
|
+
metadataProvider: () => metadata
|
|
732
|
+
});
|
|
733
|
+
if (written.status === 'failed') {
|
|
734
|
+
return commitIntentFailed(written.error.code, written.error.message, resolved.taskId);
|
|
735
|
+
}
|
|
736
|
+
const orchestrationError = completeCommitOrchestration(resolved, inspection.intent, agent);
|
|
737
|
+
if (orchestrationError) {
|
|
738
|
+
return commitIntentFailed(orchestrationError.code, orchestrationError.message, resolved.taskId);
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
remove();
|
|
742
|
+
}
|
|
743
|
+
catch (error) {
|
|
744
|
+
return commitIntentFailed('ORCHESTRATION_COMMIT_COMPLETE_PARTIAL', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
745
|
+
}
|
|
746
|
+
return { status: 'ready', changed: true, taskId: resolved.taskId, intent: null, error: null };
|
|
747
|
+
}
|
|
748
|
+
function completeCommitIntent(taskRef, input, options = {}) {
|
|
749
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
750
|
+
if (!resolved.ok)
|
|
751
|
+
return commitIntentFailed(resolved.code, resolved.message, resolved.taskId);
|
|
752
|
+
try {
|
|
753
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'commit-intent.complete', () => {
|
|
754
|
+
readCommitIntent(resolved.taskDir, resolved.taskId, input.token);
|
|
755
|
+
const inspection = inspectCommitFinalization(resolved.taskDir, resolved.repoRoot, resolved.taskId);
|
|
756
|
+
return finalizeCommitIntentUnlocked(resolved, inspection, input.agent, () => {
|
|
757
|
+
removeCommitIntent(resolved.taskDir, resolved.taskId, input.token);
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
catch (error) {
|
|
762
|
+
return mapCommitIntentError(error, resolved.taskId);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function recoverCommitIntent(taskRef, input, options = {}) {
|
|
766
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
767
|
+
if (!resolved.ok)
|
|
768
|
+
return commitIntentFailed(resolved.code, resolved.message, resolved.taskId);
|
|
769
|
+
try {
|
|
770
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'commit-intent.recover', () => {
|
|
771
|
+
const inspection = inspectCommitFinalization(resolved.taskDir, resolved.repoRoot, resolved.taskId);
|
|
772
|
+
if (inspection.disposition === 'prepared' && inspection.intentDigest) {
|
|
773
|
+
removeCommitIntentByDigest(resolved.taskDir, resolved.taskId, inspection.intentDigest);
|
|
774
|
+
return { status: 'ready', changed: true, taskId: resolved.taskId, intent: null, error: null };
|
|
775
|
+
}
|
|
776
|
+
if (!inspection.intentDigest) {
|
|
777
|
+
return commitIntentFailed(inspection.code ?? 'COMMIT_FINALIZATION_EVIDENCE_MISSING', inspection.message, resolved.taskId);
|
|
778
|
+
}
|
|
779
|
+
return finalizeCommitIntentUnlocked(resolved, inspection, input.agent, () => {
|
|
780
|
+
removeCommitIntentByDigest(resolved.taskDir, resolved.taskId, inspection.intentDigest);
|
|
781
|
+
});
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
catch (error) {
|
|
785
|
+
return mapCommitIntentError(error, resolved.taskId);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
function abortCommitIntent(taskRef, input, options = {}) {
|
|
789
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
790
|
+
if (!resolved.ok)
|
|
791
|
+
return commitIntentFailed(resolved.code, resolved.message, resolved.taskId);
|
|
792
|
+
try {
|
|
793
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'commit-intent.abort', () => {
|
|
794
|
+
const intent = readCommitIntent(resolved.taskDir, resolved.taskId, input.token);
|
|
795
|
+
const currentHead = repositoryHead(resolved.repoRoot);
|
|
796
|
+
if (intent.phase !== 'prepared'
|
|
797
|
+
|| intent.pushEvidence !== null
|
|
798
|
+
|| input.expectedHead !== intent.baselineHead
|
|
799
|
+
|| currentHead !== intent.baselineHead) {
|
|
800
|
+
return commitIntentFailed('ORCHESTRATION_COMMIT_RECOVERY_REQUIRED', 'commit intent has side effects or repository drift', resolved.taskId);
|
|
801
|
+
}
|
|
802
|
+
removeCommitIntent(resolved.taskDir, resolved.taskId, input.token);
|
|
803
|
+
return { status: 'ready', changed: true, taskId: resolved.taskId, intent: null, error: null };
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
catch (error) {
|
|
807
|
+
return mapCommitIntentError(error, resolved.taskId);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function statusCommitIntent(taskRef, options = {}) {
|
|
811
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
812
|
+
if (!resolved.ok)
|
|
813
|
+
return commitIntentFailed(resolved.code, resolved.message, resolved.taskId);
|
|
814
|
+
const inspection = inspectCommitFinalization(resolved.taskDir, resolved.repoRoot, resolved.taskId);
|
|
815
|
+
return {
|
|
816
|
+
status: 'ready',
|
|
817
|
+
changed: false,
|
|
818
|
+
taskId: resolved.taskId,
|
|
819
|
+
intent: inspection.intent ? commitIntentView(inspection.intent, inspection.currentHead) : null,
|
|
820
|
+
finalization: commitFinalizationView(inspection),
|
|
821
|
+
error: null
|
|
822
|
+
};
|
|
823
|
+
}
|
|
479
824
|
function statusOrchestration(taskRef, options = {}) {
|
|
480
825
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
481
826
|
if (!resolved.ok)
|
|
@@ -489,6 +834,15 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
489
834
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
490
835
|
if (!resolved.ok)
|
|
491
836
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
837
|
+
if (fs.existsSync(commitIntentPath(resolved.taskDir))) {
|
|
838
|
+
try {
|
|
839
|
+
readCommitIntent(resolved.taskDir, resolved.taskId);
|
|
840
|
+
}
|
|
841
|
+
catch (error) {
|
|
842
|
+
return failed('ORCHESTRATION_COMMIT_INTENT_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
843
|
+
}
|
|
844
|
+
return failed('ORCHESTRATION_COMMIT_INTENT_BUSY', 'an active commit intent blocks delegation preparation', resolved.taskId);
|
|
845
|
+
}
|
|
492
846
|
if (!(options.supportsLifecycleDelegation ?? supportsLifecycleDelegation)(input.client)) {
|
|
493
847
|
return failed('ORCHESTRATION_CLIENT_UNSUPPORTED', `client '${input.client}' does not support lifecycle orchestration`, resolved.taskId);
|
|
494
848
|
}
|
|
@@ -499,8 +853,7 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
499
853
|
return failed('ORCHESTRATION_MODEL_EVIDENCE_MISSING', 'running orchestration has no persisted model policy', resolved.taskId);
|
|
500
854
|
if (run.pendingDelegation)
|
|
501
855
|
return failed('ORCHESTRATION_DELEGATION_BUSY', 'the run already has a pending delegation', resolved.taskId);
|
|
502
|
-
const repositoryPending =
|
|
503
|
-
.flatMap((client) => matchingDelegations(client, () => true, options));
|
|
856
|
+
const repositoryPending = matchingDelegations(() => true, options);
|
|
504
857
|
if (repositoryPending.length > 0) {
|
|
505
858
|
return failed('ORCHESTRATION_DELEGATION_BUSY', 'the repository already has a pending lifecycle delegation', resolved.taskId);
|
|
506
859
|
}
|
|
@@ -558,7 +911,7 @@ function prepareOrchestrationDelegation(taskRef, input, options = {}) {
|
|
|
558
911
|
if (!resolved.ok)
|
|
559
912
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
560
913
|
try {
|
|
561
|
-
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.prepare', () => prepareOrchestrationDelegationUnlocked(taskRef, input, options));
|
|
914
|
+
return withTaskExecutionLock(resolved.repoRoot, '__repository__', 'task-orchestration.prepare.repository', () => withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.prepare.task', () => prepareOrchestrationDelegationUnlocked(taskRef, input, options)));
|
|
562
915
|
}
|
|
563
916
|
catch (error) {
|
|
564
917
|
if (error instanceof TaskExecutionLockError)
|
|
@@ -566,7 +919,7 @@ function prepareOrchestrationDelegation(taskRef, input, options = {}) {
|
|
|
566
919
|
throw error;
|
|
567
920
|
}
|
|
568
921
|
}
|
|
569
|
-
function matchingDelegations(
|
|
922
|
+
function matchingDelegations(predicate, options) {
|
|
570
923
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
571
924
|
const activeRoot = path.join(repoRoot, '.agents', 'workspace', 'active');
|
|
572
925
|
if (!fs.existsSync(activeRoot))
|
|
@@ -578,13 +931,13 @@ function matchingDelegations(client, predicate, options) {
|
|
|
578
931
|
if (!run?.pendingDelegation)
|
|
579
932
|
return [];
|
|
580
933
|
const receipt = run.pendingDelegation;
|
|
581
|
-
return
|
|
934
|
+
return predicate(receipt)
|
|
582
935
|
? [{ taskId: entry.name, run }]
|
|
583
936
|
: [];
|
|
584
937
|
});
|
|
585
938
|
}
|
|
586
939
|
function uniqueMatchingDelegation(client, predicate, options) {
|
|
587
|
-
const matches = matchingDelegations(client
|
|
940
|
+
const matches = matchingDelegations((receipt) => receipt.client === client && predicate(receipt), options);
|
|
588
941
|
if (matches.length === 0)
|
|
589
942
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no matching lifecycle delegation exists');
|
|
590
943
|
if (matches.length > 1)
|
|
@@ -700,7 +1053,7 @@ function planOrchestrationStageCompletion(taskRef, event, options = {}) {
|
|
|
700
1053
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
701
1054
|
if (!resolved.ok)
|
|
702
1055
|
return { result: failed(resolved.code, resolved.message, resolved.taskId), plan: null };
|
|
703
|
-
const updatedRun = withUpdatedRun(inspected.run, { pendingDelegation: completed.receipt });
|
|
1056
|
+
const updatedRun = withUpdatedRun(inspected.run, { pendingDelegation: completed.receipt }, options.now);
|
|
704
1057
|
return {
|
|
705
1058
|
result: { status: 'running', changed: true, taskId: resolved.taskId, run: updatedRun, next: null, error: null },
|
|
706
1059
|
plan: {
|
|
@@ -713,24 +1066,6 @@ function planOrchestrationStageCompletion(taskRef, event, options = {}) {
|
|
|
713
1066
|
function commitOrchestrationStageCompletion(plan) {
|
|
714
1067
|
saveRun(plan.taskDir, plan.updatedRun);
|
|
715
1068
|
}
|
|
716
|
-
function completeCommitOrchestrationStage(taskRef, agent, options = {}) {
|
|
717
|
-
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
718
|
-
if (!resolved.ok)
|
|
719
|
-
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
720
|
-
const run = readRun(resolved.taskDir);
|
|
721
|
-
if (!run)
|
|
722
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
|
|
723
|
-
const receipt = run.pendingDelegation;
|
|
724
|
-
if (!receipt || receipt.stage !== 'commit') {
|
|
725
|
-
return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending commit delegation', resolved.taskId);
|
|
726
|
-
}
|
|
727
|
-
return completeOrchestrationStage(taskRef, {
|
|
728
|
-
stage: receipt.stage,
|
|
729
|
-
round: receipt.round,
|
|
730
|
-
artifact: receipt.artifact,
|
|
731
|
-
agent
|
|
732
|
-
}, options);
|
|
733
|
-
}
|
|
734
1069
|
function sealOrchestrationDelegation(taskRef, event, options = {}) {
|
|
735
1070
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
736
1071
|
if (!resolved.ok)
|
|
@@ -780,5 +1115,5 @@ function pauseOrchestration(taskRef, code, message, recoverable, options = {}) {
|
|
|
780
1115
|
saveRun(resolved.taskDir, updated);
|
|
781
1116
|
return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
782
1117
|
}
|
|
783
|
-
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, beginOrResumeOrchestration, commitOrchestrationStageCompletion,
|
|
1118
|
+
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, abortCommitIntent, beginCommitIntent, beginOrResumeOrchestration, checkpointCommitIntent, commitOrchestrationStageCompletion, completeCommitIntent, completeOrchestrationStage, inspectOrchestrationStage, orchestrationPath, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, recoverCommitIntent, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusCommitIntent, statusOrchestration };
|
|
784
1119
|
//# sourceMappingURL=orchestration.js.map
|
|
@@ -91,6 +91,46 @@ function enumerateTaskDirs(repoRoot) {
|
|
|
91
91
|
}
|
|
92
92
|
return out;
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Enumerate every task directory, including the cold YYYY/MM/DD archive.
|
|
96
|
+
* Invalid directory names, symlinks and entries without task.md are ignored;
|
|
97
|
+
* duplicate task ids fail closed because their identity would be ambiguous.
|
|
98
|
+
*/
|
|
99
|
+
function enumerateAllTaskDirs(repoRoot) {
|
|
100
|
+
const out = [];
|
|
101
|
+
const seen = new Set();
|
|
102
|
+
const add = (taskId, taskDir, state) => {
|
|
103
|
+
if (!TASK_ID_RE.test(taskId))
|
|
104
|
+
return;
|
|
105
|
+
const stat = fs.lstatSync(taskDir);
|
|
106
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || !fs.existsSync(path.join(taskDir, 'task.md')))
|
|
107
|
+
return;
|
|
108
|
+
if (seen.has(taskId))
|
|
109
|
+
throw new Error(`duplicate task identity: ${taskId}`);
|
|
110
|
+
seen.add(taskId);
|
|
111
|
+
out.push({ taskId, taskDir, state });
|
|
112
|
+
};
|
|
113
|
+
for (const state of FLAT_WORKSPACE_DIRS) {
|
|
114
|
+
const base = path.join(repoRoot, '.agents', 'workspace', state);
|
|
115
|
+
if (!fs.existsSync(base))
|
|
116
|
+
continue;
|
|
117
|
+
for (const taskId of fs.readdirSync(base).sort())
|
|
118
|
+
add(taskId, path.join(base, taskId), state);
|
|
119
|
+
}
|
|
120
|
+
const archive = path.join(repoRoot, '.agents', 'workspace', 'archive');
|
|
121
|
+
for (const year of listSortedNumeric(archive, 4).reverse()) {
|
|
122
|
+
const yearDir = path.join(archive, year);
|
|
123
|
+
for (const month of listSortedNumeric(yearDir, 2).reverse()) {
|
|
124
|
+
const monthDir = path.join(yearDir, month);
|
|
125
|
+
for (const day of listSortedNumeric(monthDir, 2).reverse()) {
|
|
126
|
+
const dayDir = path.join(monthDir, day);
|
|
127
|
+
for (const taskId of fs.readdirSync(dayDir).sort())
|
|
128
|
+
add(taskId, path.join(dayDir, taskId), 'archive');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
94
134
|
function locateHotTaskDirs(repoRoot, taskId) {
|
|
95
135
|
return FLAT_WORKSPACE_DIRS.flatMap((state) => {
|
|
96
136
|
const taskDir = path.join(repoRoot, '.agents', 'workspace', state, taskId);
|
|
@@ -266,5 +306,5 @@ function resolveTaskContext(taskRef, options = {}) {
|
|
|
266
306
|
const match = matches[0];
|
|
267
307
|
return { ok: true, repoRoot, ...match, state: 'active' };
|
|
268
308
|
}
|
|
269
|
-
export { resolveTaskRef, resolveTaskContext, detectRepoRoot, enumerateTaskDirs, locateHotTaskDirs, TASK_ID_RE };
|
|
309
|
+
export { resolveTaskRef, resolveTaskContext, detectRepoRoot, enumerateTaskDirs, enumerateAllTaskDirs, locateHotTaskDirs, TASK_ID_RE };
|
|
270
310
|
//# sourceMappingURL=resolve-ref.js.map
|