@fitlab-ai/agent-infra 0.11.9 → 0.11.10
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/bin/internal-cli.js +0 -6
- package/dist/lib/git/workflow.js +1 -1
- package/dist/lib/internal/cli-route-inventory.js +0 -3
- package/dist/lib/internal/sandbox-control.js +30 -5
- package/dist/lib/internal/task-finalization.js +14 -23
- package/dist/lib/internal/task-operation-registry.js +2 -3
- package/dist/lib/internal/task-platform-recovery.js +9 -4
- package/dist/lib/platform/completion-backfill.js +2 -1
- package/dist/lib/platform/context.js +4 -2
- package/dist/lib/platform/github-client.js +20 -17
- package/dist/lib/platform/github-provider.js +42 -22
- package/dist/lib/platform/github-release-notes.js +4 -2
- package/dist/lib/platform/in-label-sync.js +2 -1
- package/dist/lib/platform/issue-comments.js +13 -9
- package/dist/lib/platform/operation-coordinator.js +1 -1
- package/dist/lib/platform/pr-review.js +2 -1
- package/dist/lib/platform/provider-bridge.js +4 -5
- package/dist/lib/platform/provider-contract.d.ts +3 -6
- package/dist/lib/platform/provider-contract.js +3 -15
- package/dist/lib/platform/provider-validation.js +9 -4
- package/dist/lib/platform/pull-requests.js +4 -2
- package/dist/lib/sandbox/control/client.js +54 -6
- package/dist/lib/sandbox/control/executor.js +8 -3
- package/dist/lib/sandbox/removal.js +0 -1
- package/dist/lib/task/artifact-command.js +11 -1
- package/dist/lib/task/artifact-lifecycle.js +119 -62
- package/dist/lib/task/artifact-selection.js +1 -1
- package/dist/lib/task/capabilities.js +55 -21
- package/dist/lib/task/commands/status.js +0 -27
- package/dist/lib/task/commit-identity.js +22 -7
- package/dist/lib/task/commit-intent.js +67 -26
- package/dist/lib/task/commit-operation.js +19 -8
- package/dist/lib/task/completion-facts.js +32 -10
- package/dist/lib/task/control-authority.js +2 -4
- package/dist/lib/task/current-contract.js +1 -5
- package/dist/lib/task/events.js +8 -274
- package/dist/lib/task/execution-inputs.js +14 -0
- package/dist/lib/task/finalization-envelope.js +4 -0
- package/dist/lib/task/finalization.js +10 -36
- package/dist/lib/task/frontmatter.js +1 -1
- package/dist/lib/task/ledger-intents.js +4 -7
- package/dist/lib/task/orchestration.js +11 -10
- package/dist/lib/task/platform-operation-journal.js +17 -10
- package/dist/lib/task/platform-operation-recovery.js +95 -28
- package/dist/lib/task/review-fingerprint.js +4 -26
- package/dist/lib/task/verification-engine.js +95 -6
- package/dist/lib/task/write.js +0 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/git/workflow.ts +1 -1
- package/lib/internal/cli-route-inventory.ts +0 -2
- package/lib/internal/sandbox-control.ts +39 -6
- package/lib/internal/task-finalization.ts +16 -24
- package/lib/internal/task-operation-registry.ts +2 -3
- package/lib/internal/task-platform-recovery.ts +9 -4
- package/lib/platform/completion-backfill.ts +2 -1
- package/lib/platform/context.ts +8 -4
- package/lib/platform/github-client.ts +24 -20
- package/lib/platform/github-provider.ts +47 -26
- package/lib/platform/github-release-notes.ts +5 -3
- package/lib/platform/in-label-sync.ts +2 -1
- package/lib/platform/issue-comments.ts +14 -9
- package/lib/platform/operation-coordinator.ts +1 -1
- package/lib/platform/pr-review.ts +2 -1
- package/lib/platform/provider-bridge.ts +10 -9
- package/lib/platform/provider-contract.ts +6 -21
- package/lib/platform/provider-loader.ts +7 -6
- package/lib/platform/provider-validation.ts +30 -8
- package/lib/platform/pull-requests.ts +4 -2
- package/lib/platform/repository-metadata.ts +3 -2
- package/lib/platform/security-alerts.ts +1 -1
- package/lib/platform/types.ts +2 -0
- package/lib/sandbox/control/client.ts +57 -6
- package/lib/sandbox/control/executor.ts +8 -3
- package/lib/sandbox/removal.ts +2 -10
- package/lib/task/artifact-command.ts +10 -1
- package/lib/task/artifact-lifecycle.ts +100 -52
- package/lib/task/artifact-selection.ts +1 -1
- package/lib/task/capabilities.ts +47 -24
- package/lib/task/commands/status.ts +0 -31
- package/lib/task/commit-identity.ts +24 -7
- package/lib/task/commit-intent.ts +58 -24
- package/lib/task/commit-operation.ts +17 -8
- package/lib/task/completion-facts.ts +31 -9
- package/lib/task/control-authority.ts +1 -4
- package/lib/task/current-contract.ts +1 -6
- package/lib/task/events.ts +8 -247
- package/lib/task/execution-inputs.ts +25 -0
- package/lib/task/finalization-envelope.ts +21 -0
- package/lib/task/finalization.ts +10 -35
- package/lib/task/frontmatter.ts +1 -1
- package/lib/task/ledger-intents.ts +4 -7
- package/lib/task/orchestration.ts +11 -17
- package/lib/task/platform-operation-journal.ts +18 -11
- package/lib/task/platform-operation-recovery.ts +90 -31
- package/lib/task/review-fingerprint.ts +4 -26
- package/lib/task/verification-engine.ts +103 -9
- package/lib/task/write.ts +0 -19
- package/package.json +1 -1
- package/templates/.agents/rules/commit-and-pr.en.md +5 -5
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +5 -5
- package/templates/.agents/rules/decision-qualification.en.md +1 -1
- package/templates/.agents/rules/decision-qualification.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +25 -9
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +26 -9
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +5 -16
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +5 -16
- package/templates/.agents/skills/code-task/reference/report-template.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +15 -12
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +11 -10
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -1
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +0 -12
- package/templates/.github/workflows/pr-label.yml +1 -2
- package/dist/lib/internal/task-invalidation.js +0 -63
- package/dist/lib/task/intermediate-cleanup.js +0 -327
- package/dist/lib/task/invalidation-command.js +0 -88
- package/dist/lib/task/invalidation.js +0 -242
- package/lib/internal/task-invalidation.ts +0 -41
- package/lib/task/intermediate-cleanup.ts +0 -393
- package/lib/task/invalidation-command.ts +0 -88
- package/lib/task/invalidation.ts +0 -313
|
@@ -7,6 +7,7 @@ import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
|
7
7
|
import { coordinatePlatformWrite } from './operation-coordinator.ts';
|
|
8
8
|
import { resolvePlatformProviderContext } from './context.ts';
|
|
9
9
|
import type { PlatformClient } from './context.ts';
|
|
10
|
+
import { providerErrorRetryable } from './provider-validation.ts';
|
|
10
11
|
import { platformResult } from './types.ts';
|
|
11
12
|
import type { PlatformOperation, PlatformResult } from './types.ts';
|
|
12
13
|
import {
|
|
@@ -34,6 +35,10 @@ import {
|
|
|
34
35
|
} from './comment-safety.ts';
|
|
35
36
|
import type { FenceRange } from './comment-safety.ts';
|
|
36
37
|
|
|
38
|
+
function isRetryable(error: { code: string; retryable?: boolean }): boolean {
|
|
39
|
+
return error.retryable ?? providerErrorRetryable(error.code);
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
type RemoteComment = {
|
|
38
43
|
id: number | string;
|
|
39
44
|
body: string;
|
|
@@ -578,7 +583,7 @@ async function syncPlatformCommentImpl(taskRef: string, options: SyncOptions): P
|
|
|
578
583
|
const issue = resourceIdentityNumber(issueIdentityFromTask);
|
|
579
584
|
const listed = await listedComments(loaded.value.provider, loaded.value, issueIdentityFromTask);
|
|
580
585
|
if (!listed.ok) {
|
|
581
|
-
return platformResult(listed.error
|
|
586
|
+
return platformResult(isRetryable(listed.error) ? 'blocked' : 'failed', {
|
|
582
587
|
...contextFields(context), resource: { kind: 'issue', number: issue }, error: listed.error
|
|
583
588
|
});
|
|
584
589
|
}
|
|
@@ -648,10 +653,10 @@ async function syncPlatformCommentImpl(taskRef: string, options: SyncOptions): P
|
|
|
648
653
|
})
|
|
649
654
|
: unsupportedProviderOperation(loaded.value.provider, 'comments.write');
|
|
650
655
|
if (!written.ok) {
|
|
651
|
-
if (!current && written.error
|
|
656
|
+
if (!current && isRetryable(written.error)) {
|
|
652
657
|
// A provider owns reconciliation because only it knows how to address the resource.
|
|
653
658
|
}
|
|
654
|
-
return platformResult(written.error
|
|
659
|
+
return platformResult(isRetryable(written.error) ? 'blocked' : 'failed', {
|
|
655
660
|
...contextFields(context),
|
|
656
661
|
resource: { kind: 'issue', number: issue },
|
|
657
662
|
operations,
|
|
@@ -674,7 +679,7 @@ async function syncPlatformCommentImpl(taskRef: string, options: SyncOptions): P
|
|
|
674
679
|
})
|
|
675
680
|
: unsupportedProviderOperation(loaded.value.provider, 'comments.delete');
|
|
676
681
|
if (!deleted.ok) {
|
|
677
|
-
return platformResult(deleted.error
|
|
682
|
+
return platformResult(isRetryable(deleted.error) ? 'blocked' : 'failed', {
|
|
678
683
|
...contextFields(context),
|
|
679
684
|
resource: { kind: 'issue', number: issue },
|
|
680
685
|
operations,
|
|
@@ -686,7 +691,7 @@ async function syncPlatformCommentImpl(taskRef: string, options: SyncOptions): P
|
|
|
686
691
|
if (options.kind === 'summary') {
|
|
687
692
|
const refreshed = await listedComments(loaded.value.provider, loaded.value, issueIdentityFromTask);
|
|
688
693
|
if (!refreshed.ok) {
|
|
689
|
-
return platformResult(refreshed.error
|
|
694
|
+
return platformResult(isRetryable(refreshed.error) ? 'blocked' : 'failed', {
|
|
690
695
|
...contextFields(context), resource: { kind: 'issue', number: issue }, operations, error: refreshed.error
|
|
691
696
|
});
|
|
692
697
|
}
|
|
@@ -714,7 +719,7 @@ async function syncPlatformCommentImpl(taskRef: string, options: SyncOptions): P
|
|
|
714
719
|
})
|
|
715
720
|
: unsupportedProviderOperation(loaded.value.provider, 'comments.delete');
|
|
716
721
|
if (!deleted.ok) {
|
|
717
|
-
return platformResult(deleted.error
|
|
722
|
+
return platformResult(isRetryable(deleted.error) ? 'blocked' : 'failed', {
|
|
718
723
|
...contextFields(context), resource: { kind: 'issue', number: issue }, operations, error: deleted.error
|
|
719
724
|
});
|
|
720
725
|
}
|
|
@@ -725,7 +730,7 @@ async function syncPlatformCommentImpl(taskRef: string, options: SyncOptions): P
|
|
|
725
730
|
})
|
|
726
731
|
: unsupportedProviderOperation(loaded.value.provider, 'comments.write');
|
|
727
732
|
if (!written.ok) {
|
|
728
|
-
return platformResult(written.error
|
|
733
|
+
return platformResult(isRetryable(written.error) ? 'blocked' : 'failed', {
|
|
729
734
|
...contextFields(context), resource: { kind: 'issue', number: issue }, operations, error: written.error
|
|
730
735
|
});
|
|
731
736
|
}
|
|
@@ -736,7 +741,7 @@ async function syncPlatformCommentImpl(taskRef: string, options: SyncOptions): P
|
|
|
736
741
|
const error = reconciled.ok
|
|
737
742
|
? { code: 'SUMMARY_POSITION_UNVERIFIED', message: 'Summary comment is not provably last among task-managed comments', retryable: true }
|
|
738
743
|
: reconciled.error;
|
|
739
|
-
return platformResult(error
|
|
744
|
+
return platformResult(isRetryable(error) ? 'blocked' : 'failed', {
|
|
740
745
|
...contextFields(context), resource: { kind: 'issue', number: issue }, operations, error
|
|
741
746
|
});
|
|
742
747
|
}
|
|
@@ -837,7 +842,7 @@ async function checkPlatformCommentOwner(taskRef: string, options: { cwd?: strin
|
|
|
837
842
|
? { ok: true as const, value: response.value.map((comment) => ({ id: comment.id, body: comment.body, user: comment.author?.name ? { login: comment.author.name } : undefined })) }
|
|
838
843
|
: response)
|
|
839
844
|
: unsupportedProviderOperation(loaded.value.provider, 'comments.list');
|
|
840
|
-
if (!listed.ok) return platformResult(listed.error
|
|
845
|
+
if (!listed.ok) return platformResult(isRetryable(listed.error) ? 'blocked' : 'failed', { ...contextFields(context), error: listed.error });
|
|
841
846
|
const matches = findMarkerComments(listed.value, MARKERS.task(resolved.taskId));
|
|
842
847
|
if (matches.length > 1) return platformResult('failed', { ...contextFields(context), error: { code: 'COMMENT_MARKER_CONFLICT', message: 'Multiple task comments use the registered marker', retryable: false } });
|
|
843
848
|
const owner = matches[0]?.user?.login;
|
|
@@ -48,7 +48,7 @@ async function coordinatePlatformWrite<T extends OperationResult>(input: Coordin
|
|
|
48
48
|
excludeId: operationId
|
|
49
49
|
});
|
|
50
50
|
if (drained.status !== 'applied' && drained.status !== 'no-op') {
|
|
51
|
-
return input.block(drained.error
|
|
51
|
+
return input.block(drained.error ? { ...drained.error, retryable: true } : {
|
|
52
52
|
code: 'PLATFORM_OPERATION_QUEUE_BLOCKED',
|
|
53
53
|
message: 'A previous platform operation is still unresolved; the current write remains queued',
|
|
54
54
|
retryable: true
|
|
@@ -5,6 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { coordinatePlatformWrite } from './operation-coordinator.ts';
|
|
6
6
|
import { resolvePlatformProviderContext } from './context.ts';
|
|
7
7
|
import type { PlatformClient } from './context.ts';
|
|
8
|
+
import { providerErrorRetryable } from './provider-validation.ts';
|
|
8
9
|
import { platformResult } from './types.ts';
|
|
9
10
|
import type { PlatformOperation, PlatformResult } from './types.ts';
|
|
10
11
|
import {
|
|
@@ -283,7 +284,7 @@ export async function publishPrReview(options: {
|
|
|
283
284
|
})
|
|
284
285
|
: unsupportedProviderOperation(loaded.value.provider, 'reviews.publish');
|
|
285
286
|
if (!published.ok) {
|
|
286
|
-
if (published.error.retryable) {
|
|
287
|
+
if (published.error.retryable || providerErrorRetryable(published.error.code)) {
|
|
287
288
|
const reconciled = await listPrReviews(options.prNumber, { cwd: options.cwd, client: options.client });
|
|
288
289
|
const found = reconciled.reviews.find((review) => firstLine(review.body) === marker);
|
|
289
290
|
if (found) return platformResult('applied', {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
-
|
|
4
|
+
ProviderError,
|
|
5
5
|
ProviderOperationContext,
|
|
6
6
|
PlatformProvider,
|
|
7
7
|
ProviderResult,
|
|
@@ -15,6 +15,9 @@ import {
|
|
|
15
15
|
} from './resource-identity.ts';
|
|
16
16
|
import type { LoadedContext } from './context.ts';
|
|
17
17
|
import type { PlatformResult } from './types.ts';
|
|
18
|
+
import type { PlatformError } from './types.ts';
|
|
19
|
+
import { providerErrorRetryable } from './provider-validation.ts';
|
|
20
|
+
import type { NormalizedProviderResult } from './provider-validation.ts';
|
|
18
21
|
|
|
19
22
|
function providerOperationContext(
|
|
20
23
|
loaded: LoadedContext,
|
|
@@ -29,30 +32,28 @@ function providerOperationContext(
|
|
|
29
32
|
};
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
function providerError(error: PlatformError, fallbackCode: string): PlatformError {
|
|
35
|
+
function providerError(error: ProviderError | PlatformError, fallbackCode: string): PlatformError {
|
|
33
36
|
return {
|
|
34
37
|
code: error.code || fallbackCode,
|
|
35
38
|
message: error.message || 'Platform provider operation failed',
|
|
36
|
-
retryable: error.retryable
|
|
39
|
+
retryable: 'retryable' in error ? error.retryable : providerErrorRetryable(error.code)
|
|
37
40
|
};
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
function providerStatus(error: PlatformError): PlatformResult['status'] {
|
|
41
|
-
return error.retryable ? 'blocked' : 'failed';
|
|
43
|
+
function providerStatus(error: ProviderError | PlatformError): PlatformResult['status'] {
|
|
44
|
+
return ('retryable' in error ? error.retryable : providerErrorRetryable(error.code)) ? 'blocked' : 'failed';
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
function unsupportedProviderOperation(
|
|
45
48
|
provider: PlatformProvider,
|
|
46
49
|
operation: string
|
|
47
|
-
):
|
|
50
|
+
): NormalizedProviderResult<never> {
|
|
48
51
|
return {
|
|
49
52
|
ok: false,
|
|
50
53
|
error: {
|
|
51
54
|
code: 'PLATFORM_CAPABILITY_UNSUPPORTED',
|
|
52
55
|
message: `Platform '${provider.type}' does not provide ${operation}`,
|
|
53
|
-
retryable: false
|
|
54
|
-
providerType: provider.type,
|
|
55
|
-
phase: 'operation'
|
|
56
|
+
retryable: false
|
|
56
57
|
}
|
|
57
58
|
};
|
|
58
59
|
}
|
|
@@ -12,17 +12,14 @@ type JsonValue =
|
|
|
12
12
|
| JsonValue[]
|
|
13
13
|
| { [key: string]: JsonValue };
|
|
14
14
|
|
|
15
|
-
type
|
|
15
|
+
type ProviderError = {
|
|
16
16
|
code: string;
|
|
17
17
|
message: string;
|
|
18
|
-
retryable: boolean;
|
|
19
|
-
providerType?: string;
|
|
20
|
-
phase?: string;
|
|
21
18
|
};
|
|
22
19
|
|
|
23
20
|
type ProviderResult<T> =
|
|
24
21
|
| { ok: true; value: T }
|
|
25
|
-
| { ok: false; error:
|
|
22
|
+
| { ok: false; error: ProviderError; value?: T };
|
|
26
23
|
|
|
27
24
|
type ProviderOperationContext = {
|
|
28
25
|
repositoryRoot: string;
|
|
@@ -534,10 +531,7 @@ function validatePlatformProvider(
|
|
|
534
531
|
ok: false,
|
|
535
532
|
error: {
|
|
536
533
|
code: 'PLATFORM_PROVIDER_CONTRACT_INVALID',
|
|
537
|
-
message: 'Provider must expose matching type, contractVersion, and context.resolve'
|
|
538
|
-
retryable: false,
|
|
539
|
-
providerType,
|
|
540
|
-
phase: 'provider-validation'
|
|
534
|
+
message: 'Provider must expose matching type, contractVersion, and context.resolve'
|
|
541
535
|
}
|
|
542
536
|
};
|
|
543
537
|
}
|
|
@@ -550,10 +544,7 @@ function validatePlatformProvider(
|
|
|
550
544
|
ok: false,
|
|
551
545
|
error: {
|
|
552
546
|
code: 'PLATFORM_PROVIDER_CONTRACT_INVALID',
|
|
553
|
-
message: 'Provider identity declaration is invalid'
|
|
554
|
-
retryable: false,
|
|
555
|
-
providerType,
|
|
556
|
-
phase: 'provider-validation'
|
|
547
|
+
message: 'Provider identity declaration is invalid'
|
|
557
548
|
}
|
|
558
549
|
};
|
|
559
550
|
}
|
|
@@ -567,10 +558,7 @@ function validatePlatformProvider(
|
|
|
567
558
|
ok: false,
|
|
568
559
|
error: {
|
|
569
560
|
code: 'PLATFORM_PROVIDER_CONTRACT_INVALID',
|
|
570
|
-
message: `Provider operation group ${groupName} is incomplete
|
|
571
|
-
retryable: false,
|
|
572
|
-
providerType,
|
|
573
|
-
phase: 'provider-validation'
|
|
561
|
+
message: `Provider operation group ${groupName} is incomplete`
|
|
574
562
|
}
|
|
575
563
|
};
|
|
576
564
|
}
|
|
@@ -583,9 +571,6 @@ function validatePlatformProvider(
|
|
|
583
571
|
error: {
|
|
584
572
|
code: 'PLATFORM_PROVIDER_CONTRACT_INVALID',
|
|
585
573
|
message: `Provider operation group ${groupName} requires an identity declaration for ${resourceKind}`,
|
|
586
|
-
retryable: false,
|
|
587
|
-
providerType,
|
|
588
|
-
phase: 'provider-validation'
|
|
589
574
|
}
|
|
590
575
|
};
|
|
591
576
|
}
|
|
@@ -617,7 +602,7 @@ export type {
|
|
|
617
602
|
MutationReceipt,
|
|
618
603
|
PlatformCapabilities,
|
|
619
604
|
PlatformContextSnapshot,
|
|
620
|
-
|
|
605
|
+
ProviderError,
|
|
621
606
|
PlatformProvider,
|
|
622
607
|
PlatformProviderFactory,
|
|
623
608
|
PlatformProviderFactoryInput,
|
|
@@ -12,13 +12,14 @@ import {
|
|
|
12
12
|
} from './provider-contract.ts';
|
|
13
13
|
import type {
|
|
14
14
|
JsonValue,
|
|
15
|
-
PlatformError,
|
|
16
15
|
PlatformProvider,
|
|
17
16
|
PlatformProviderFactoryInput
|
|
18
17
|
} from './provider-contract.ts';
|
|
19
18
|
import { createGitHubProvider } from './github-provider.ts';
|
|
20
19
|
import { createNoneProvider } from './none-provider.ts';
|
|
21
20
|
import { wrapProviderOperations } from './provider-validation.ts';
|
|
21
|
+
import type { LoadedRuntimeProvider, RuntimePlatformProvider } from './provider-validation.ts';
|
|
22
|
+
import type { PlatformError } from './types.ts';
|
|
22
23
|
import type { PlatformClient } from './context.ts';
|
|
23
24
|
|
|
24
25
|
type ProviderLoaderOptions = {
|
|
@@ -28,7 +29,7 @@ type ProviderLoaderOptions = {
|
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
type LoadedPlatformProvider = {
|
|
31
|
-
provider:
|
|
32
|
+
provider: LoadedRuntimeProvider;
|
|
32
33
|
providerType: string;
|
|
33
34
|
repositoryRoot: string;
|
|
34
35
|
workingDirectory: string;
|
|
@@ -44,7 +45,7 @@ type ResolvedExternalSource = {
|
|
|
44
45
|
importUrl: string;
|
|
45
46
|
};
|
|
46
47
|
|
|
47
|
-
const sessions = new Map<string, Promise<
|
|
48
|
+
const sessions = new Map<string, Promise<LoadedRuntimeProvider>>();
|
|
48
49
|
|
|
49
50
|
function findRepositoryRoot(cwd: string): string {
|
|
50
51
|
try {
|
|
@@ -151,15 +152,15 @@ async function instantiateProvider(
|
|
|
151
152
|
importUrl: string | null,
|
|
152
153
|
config: Readonly<Record<string, JsonValue>>,
|
|
153
154
|
client?: PlatformClient
|
|
154
|
-
): Promise<
|
|
155
|
+
): Promise<LoadedRuntimeProvider> {
|
|
155
156
|
const input: PlatformProviderFactoryInput = {
|
|
156
157
|
providerType,
|
|
157
158
|
contractVersion: PLATFORM_PROVIDER_CONTRACT_VERSION,
|
|
158
159
|
repositoryRoot,
|
|
159
160
|
config
|
|
160
161
|
};
|
|
161
|
-
if (providerType === 'github') return createGitHubProvider(input, client as never);
|
|
162
|
-
if (providerType === 'none') return createNoneProvider(input);
|
|
162
|
+
if (providerType === 'github') return createGitHubProvider(input, client as never) as unknown as RuntimePlatformProvider;
|
|
163
|
+
if (providerType === 'none') return createNoneProvider(input) as unknown as RuntimePlatformProvider;
|
|
163
164
|
|
|
164
165
|
let moduleValue: Record<string, unknown>;
|
|
165
166
|
try {
|
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
MilestoneReconciliation,
|
|
14
14
|
MutationReceipt,
|
|
15
15
|
PlatformContextSnapshot,
|
|
16
|
-
|
|
16
|
+
ProviderError,
|
|
17
17
|
PlatformProvider,
|
|
18
18
|
ProviderResult,
|
|
19
19
|
ReleaseNoteAuthor,
|
|
@@ -29,8 +29,23 @@ import type {
|
|
|
29
29
|
VerificationRemoteFacts
|
|
30
30
|
} from './provider-contract.ts';
|
|
31
31
|
import type { JsonValue } from './provider-contract.ts';
|
|
32
|
+
import type { PlatformError } from './types.ts';
|
|
32
33
|
|
|
33
34
|
type ResultValidator<T> = (value: unknown) => T;
|
|
35
|
+
type NormalizedProviderResult<T> =
|
|
36
|
+
| { ok: true; value: T }
|
|
37
|
+
| { ok: false; error: PlatformError; value?: T };
|
|
38
|
+
type RuntimeResult<T> = T extends { ok: true; value: infer V }
|
|
39
|
+
? { ok: true; value: V }
|
|
40
|
+
: T extends { ok: false; error: ProviderError; value?: infer V }
|
|
41
|
+
? { ok: false; error: PlatformError; value?: V }
|
|
42
|
+
: T;
|
|
43
|
+
type RuntimeMethod<T> = T extends (...args: infer A) => Promise<infer R>
|
|
44
|
+
? (...args: A) => Promise<RuntimeResult<R>>
|
|
45
|
+
: T;
|
|
46
|
+
type RuntimeProviderValue<T> = T extends object ? { [K in keyof T]: RuntimeMethod<T[K]> } : T;
|
|
47
|
+
type RuntimePlatformProvider = { [K in keyof PlatformProvider]: RuntimeProviderValue<PlatformProvider[K]> };
|
|
48
|
+
type LoadedRuntimeProvider = RuntimePlatformProvider;
|
|
34
49
|
|
|
35
50
|
function validationError(providerType: string, phase: string, message: string, code = 'PLATFORM_PROVIDER_RESULT_INVALID'): PlatformError {
|
|
36
51
|
return {
|
|
@@ -494,9 +509,11 @@ const PROVIDER_ERROR_CATALOG: Readonly<Record<string, { message: string; retryab
|
|
|
494
509
|
REMOTE_INVALID: { message: 'The platform remote is invalid', retryable: false },
|
|
495
510
|
UPSTREAM_UNRESOLVED: { message: 'The platform upstream repository could not be resolved', retryable: false },
|
|
496
511
|
RESOURCE_NOT_FOUND: { message: 'The platform resource was not found', retryable: false },
|
|
512
|
+
COMMENT_LIST_FAILED: { message: 'Comments could not be listed', retryable: true },
|
|
497
513
|
PR_NOT_FOUND: { message: 'The pull request was not found', retryable: false },
|
|
498
514
|
ISSUE_NOT_FOUND: { message: 'The issue was not found', retryable: false },
|
|
499
515
|
REMOTE_BUSY: { message: 'Platform provider is temporarily busy', retryable: true },
|
|
516
|
+
IN_LABEL_SYNC_PARTIAL: { message: 'In-label synchronization is partial or unknown', retryable: true },
|
|
500
517
|
NETWORK_TRANSIENT: { message: 'The platform request failed temporarily', retryable: true },
|
|
501
518
|
NETWORK_RETRY: { message: 'The platform request failed temporarily', retryable: true },
|
|
502
519
|
NETWORK_RETRY_EXHAUSTED: { message: 'The platform request retries were exhausted', retryable: true },
|
|
@@ -538,12 +555,16 @@ function safeProviderError(providerType: string, operation: string, value: unkno
|
|
|
538
555
|
return {
|
|
539
556
|
code: definition ? item.code as string : 'PLATFORM_PROVIDER_OPERATION_FAILED',
|
|
540
557
|
message: definition?.message || PROVIDER_ERROR_CATALOG.PLATFORM_PROVIDER_OPERATION_FAILED!.message,
|
|
541
|
-
retryable: definition?.retryable
|
|
558
|
+
retryable: definition?.retryable ?? false,
|
|
542
559
|
providerType,
|
|
543
560
|
phase: operation
|
|
544
561
|
};
|
|
545
562
|
}
|
|
546
563
|
|
|
564
|
+
function providerErrorRetryable(code: string): boolean {
|
|
565
|
+
return PROVIDER_ERROR_CATALOG[code]?.retryable ?? false;
|
|
566
|
+
}
|
|
567
|
+
|
|
547
568
|
function validators(providerType: string, declaration?: ProviderIdentityDeclaration): Record<string, ResultValidator<unknown>> {
|
|
548
569
|
return {
|
|
549
570
|
'context.resolve': (value) => validateContext(value, providerType),
|
|
@@ -629,7 +650,7 @@ async function invokeProviderOperation<T>(
|
|
|
629
650
|
operation: string,
|
|
630
651
|
call: () => Promise<unknown>,
|
|
631
652
|
validate: ResultValidator<T>
|
|
632
|
-
): Promise<
|
|
653
|
+
): Promise<NormalizedProviderResult<T>> {
|
|
633
654
|
try {
|
|
634
655
|
const raw = await call();
|
|
635
656
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return { ok: false, error: validationError(providerType, operation, 'Provider operation returned an invalid result envelope') };
|
|
@@ -646,8 +667,8 @@ async function invokeProviderOperation<T>(
|
|
|
646
667
|
try {
|
|
647
668
|
exactKeys(envelope, Object.hasOwn(envelope, 'value') ? ['ok', 'error', 'value'] : ['ok', 'error'], `${operation} result`);
|
|
648
669
|
const error = envelope.error as Record<string, unknown>;
|
|
649
|
-
exactKeys(error, ['code', 'message'
|
|
650
|
-
if (typeof error.code !== 'string' || !error.code || typeof error.message !== 'string' || !error.message
|
|
670
|
+
exactKeys(error, ['code', 'message'], `${operation} error`);
|
|
671
|
+
if (typeof error.code !== 'string' || !error.code || typeof error.message !== 'string' || !error.message) throw new Error('invalid provider error');
|
|
651
672
|
return {
|
|
652
673
|
ok: false,
|
|
653
674
|
error: safeProviderError(providerType, operation, error),
|
|
@@ -663,7 +684,7 @@ async function invokeProviderOperation<T>(
|
|
|
663
684
|
}
|
|
664
685
|
}
|
|
665
686
|
|
|
666
|
-
function wrapProviderOperations(provider: PlatformProvider):
|
|
687
|
+
function wrapProviderOperations(provider: PlatformProvider): RuntimePlatformProvider {
|
|
667
688
|
const map = validators(provider.type, provider.identity);
|
|
668
689
|
const wrapped: PlatformProvider = { ...provider, context: { ...provider.context } };
|
|
669
690
|
wrapped.context.resolve = (input) => invokeProviderOperation(provider.type, 'context.resolve', () => provider.context.resolve(input), map['context.resolve']! as ResultValidator<PlatformContextSnapshot>);
|
|
@@ -680,7 +701,8 @@ function wrapProviderOperations(provider: PlatformProvider): PlatformProvider {
|
|
|
680
701
|
}
|
|
681
702
|
(wrapped as unknown as Record<string, unknown>)[groupName] = target;
|
|
682
703
|
}
|
|
683
|
-
return wrapped;
|
|
704
|
+
return wrapped as RuntimePlatformProvider;
|
|
684
705
|
}
|
|
685
706
|
|
|
686
|
-
export { invokeProviderOperation, validationError, wrapProviderOperations };
|
|
707
|
+
export { invokeProviderOperation, providerErrorRetryable, validationError, wrapProviderOperations };
|
|
708
|
+
export type { LoadedRuntimeProvider, NormalizedProviderResult, RuntimePlatformProvider };
|
|
@@ -12,6 +12,7 @@ import type { PlatformChangeRequestSnapshot } from './snapshots.ts';
|
|
|
12
12
|
import { resolvePlatformProviderContext } from './context.ts';
|
|
13
13
|
import type { PlatformClient } from './context.ts';
|
|
14
14
|
import { inspectPlatformIssue } from './issues.ts';
|
|
15
|
+
import { providerErrorRetryable } from './provider-validation.ts';
|
|
15
16
|
import { planPullRequestMetadata } from './pull-request-metadata.ts';
|
|
16
17
|
import {
|
|
17
18
|
planInLabelUpdate,
|
|
@@ -741,9 +742,10 @@ async function syncPlatformPullRequestInLabels(prNumber: number, options: Shared
|
|
|
741
742
|
: unsupportedProviderOperation(provider, 'changeRequests.update');
|
|
742
743
|
if (!synced.ok) {
|
|
743
744
|
const state = resourcesState.find((resource) => resource.kind === item.kind && resourceIdentityEquals(resource.identity, item.identity))!;
|
|
744
|
-
|
|
745
|
+
const retryable = synced.error.retryable || providerErrorRetryable(synced.error.code);
|
|
746
|
+
state.effect = retryable ? 'unknown' : 'no-op';
|
|
745
747
|
state.after = state.effect === 'unknown' ? null : item.snapshot.labels;
|
|
746
|
-
const partial = successfulWrites > 0 ||
|
|
748
|
+
const partial = successfulWrites > 0 || retryable;
|
|
747
749
|
const error = partial
|
|
748
750
|
? { ...synced.error, code: 'IN_LABEL_SYNC_PARTIAL', message: `In-label synchronization is partial or unknown: ${synced.error.message}` }
|
|
749
751
|
: synced.error;
|
|
@@ -14,9 +14,10 @@ import type {
|
|
|
14
14
|
LabelReconciliation,
|
|
15
15
|
MilestoneDefinition,
|
|
16
16
|
MilestoneInitialization,
|
|
17
|
-
|
|
17
|
+
ProviderError,
|
|
18
18
|
ProviderResult
|
|
19
19
|
} from './provider-contract.ts';
|
|
20
|
+
import type { PlatformError } from './types.ts';
|
|
20
21
|
import { platformResult } from './types.ts';
|
|
21
22
|
import type { PlatformResult } from './types.ts';
|
|
22
23
|
|
|
@@ -288,7 +289,7 @@ function operationStatus(changed: boolean): 'applied' | 'no-op' {
|
|
|
288
289
|
return changed ? 'applied' : 'no-op';
|
|
289
290
|
}
|
|
290
291
|
|
|
291
|
-
function errorStatus(error: PlatformError): PlatformResult['status'] {
|
|
292
|
+
function errorStatus(error: PlatformError | ProviderError): PlatformResult['status'] {
|
|
292
293
|
return error.code === 'PLATFORM_CAPABILITY_UNSUPPORTED' ? 'degraded' : providerStatus(error);
|
|
293
294
|
}
|
|
294
295
|
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
providerStatus
|
|
10
10
|
} from './provider-bridge.ts';
|
|
11
11
|
import type {
|
|
12
|
-
PlatformError,
|
|
13
12
|
SecurityAlertKind,
|
|
14
13
|
SecurityAlertSnapshot
|
|
15
14
|
} from './provider-contract.ts';
|
|
15
|
+
import type { PlatformError } from './types.ts';
|
|
16
16
|
import { platformResult } from './types.ts';
|
|
17
17
|
import type { PlatformResult } from './types.ts';
|
|
18
18
|
|
package/lib/platform/types.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { readSandboxControlIdentitySentinel } from './identity-sentinel.ts';
|
|
|
27
27
|
import { configuredShortIdLength, resolveShortIdReadOnly } from '../../task/short-id.ts';
|
|
28
28
|
|
|
29
29
|
const SANDBOX_CONTROL_RESPONSE_SETTLE_MS = 250;
|
|
30
|
+
const SANDBOX_TASK_FINALIZATION_RECOVERY_BUDGET_MS = 5 * 60_000;
|
|
30
31
|
|
|
31
32
|
export class SandboxControlClientError extends Error {
|
|
32
33
|
readonly detail: SandboxControlError;
|
|
@@ -114,7 +115,8 @@ function preflight(
|
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
function taskViewEffectForRequest(request: SandboxControlRequest): TaskViewAccessEffect | null {
|
|
117
|
-
if (request.family === 'task-lifecycle'
|
|
118
|
+
if (request.family === 'task-lifecycle') return 'progress';
|
|
119
|
+
if (request.family === 'task-finalization') return 'recovery';
|
|
118
120
|
return null;
|
|
119
121
|
}
|
|
120
122
|
|
|
@@ -347,13 +349,20 @@ export function recoverSandboxControl(requestId: string, params: Readonly<{
|
|
|
347
349
|
} else if (now - malformedResponseObservedAt >= SANDBOX_CONTROL_RESPONSE_SETTLE_MS) {
|
|
348
350
|
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response remained malformed', false, accepted, requestId);
|
|
349
351
|
}
|
|
350
|
-
|
|
352
|
+
const remainingMs = deadline - Date.now();
|
|
353
|
+
if (remainingMs > 0) sleep(Math.min(25, Math.ceil(remainingMs)));
|
|
351
354
|
continue;
|
|
352
355
|
}
|
|
353
|
-
if (response.phase === 'accepted')
|
|
354
|
-
|
|
356
|
+
if (response.phase === 'accepted') {
|
|
357
|
+
accepted = true;
|
|
358
|
+
} else if (response.phase === 'rejected' && response.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN') {
|
|
359
|
+
accepted = true;
|
|
360
|
+
} else {
|
|
361
|
+
return response;
|
|
362
|
+
}
|
|
355
363
|
}
|
|
356
|
-
|
|
364
|
+
const remainingMs = deadline - Date.now();
|
|
365
|
+
if (remainingMs > 0) sleep(Math.min(25, Math.ceil(remainingMs)));
|
|
357
366
|
}
|
|
358
367
|
clientError(
|
|
359
368
|
'SANDBOX_CONTROL_RESULT_UNKNOWN',
|
|
@@ -399,6 +408,7 @@ export function requestSandboxTaskFinalization(params: Readonly<{
|
|
|
399
408
|
token?: string;
|
|
400
409
|
generation?: string;
|
|
401
410
|
timeoutMs?: number;
|
|
411
|
+
recoveryBudgetMs?: number;
|
|
402
412
|
}>): SandboxControlResponse {
|
|
403
413
|
const agent = normalizeAgentToken(params.agent);
|
|
404
414
|
if (!agent) clientError('SANDBOX_CONTROL_REQUEST_INVALID', 'task-finalization agent is invalid', false);
|
|
@@ -420,7 +430,48 @@ export function requestSandboxTaskFinalization(params: Readonly<{
|
|
|
420
430
|
controllerProcess: null,
|
|
421
431
|
controllerProof: null
|
|
422
432
|
};
|
|
423
|
-
|
|
433
|
+
let response: SandboxControlResponse;
|
|
434
|
+
try {
|
|
435
|
+
response = exchangeSandboxControl(request, params);
|
|
436
|
+
} catch (error) {
|
|
437
|
+
if (!(error instanceof SandboxControlClientError)
|
|
438
|
+
|| error.detail.code !== 'SANDBOX_CONTROL_RESULT_UNKNOWN'
|
|
439
|
+
|| !error.accepted || !error.requestId) throw error;
|
|
440
|
+
return recoverAcceptedTaskFinalization(error.requestId, params);
|
|
441
|
+
}
|
|
442
|
+
if (response.phase === 'rejected' && response.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN') {
|
|
443
|
+
return recoverAcceptedTaskFinalization(response.id, params);
|
|
444
|
+
}
|
|
445
|
+
return response;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export function recoverAcceptedTaskFinalization(
|
|
449
|
+
requestId: string,
|
|
450
|
+
params: Readonly<{ channelDir?: string; recoveryBudgetMs?: number }>,
|
|
451
|
+
recover: typeof recoverSandboxControl = recoverSandboxControl
|
|
452
|
+
): SandboxControlResponse {
|
|
453
|
+
const deadline = Date.now() + (params.recoveryBudgetMs ?? SANDBOX_TASK_FINALIZATION_RECOVERY_BUDGET_MS);
|
|
454
|
+
while (Date.now() < deadline) {
|
|
455
|
+
const timeoutMs = Math.min(30_000, deadline - Date.now());
|
|
456
|
+
if (timeoutMs <= 0) break;
|
|
457
|
+
try {
|
|
458
|
+
const response = recover(requestId, { channelDir: params.channelDir, timeoutMs });
|
|
459
|
+
if (response.phase === 'rejected' && response.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN') continue;
|
|
460
|
+
return response;
|
|
461
|
+
} catch (error) {
|
|
462
|
+
if (error instanceof SandboxControlClientError
|
|
463
|
+
&& error.detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN'
|
|
464
|
+
&& error.requestId === requestId) continue;
|
|
465
|
+
throw error;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
clientError(
|
|
469
|
+
'SANDBOX_CONTROL_RESULT_UNKNOWN',
|
|
470
|
+
'accepted finalization request remained unknown after the automatic recovery budget; inspect the receipt and resume by request id',
|
|
471
|
+
false,
|
|
472
|
+
true,
|
|
473
|
+
requestId
|
|
474
|
+
);
|
|
424
475
|
}
|
|
425
476
|
|
|
426
477
|
type CodexControllerClosed = Readonly<{
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type TaskCreateResult
|
|
13
13
|
} from '../../task/create-service.ts';
|
|
14
14
|
import { applyTaskFinalization } from '../../task/finalization.ts';
|
|
15
|
+
import { serializeTaskFinalizationEnvelope } from '../../task/finalization-envelope.ts';
|
|
15
16
|
import {
|
|
16
17
|
bindSandboxControlTask,
|
|
17
18
|
validateSandboxControlRequest,
|
|
@@ -535,10 +536,14 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
|
|
|
535
536
|
result = request.family === 'task-finalization'
|
|
536
537
|
? {
|
|
537
538
|
exitCode: 1,
|
|
538
|
-
stdout:
|
|
539
|
-
|
|
539
|
+
stdout: serializeTaskFinalizationEnvelope({
|
|
540
|
+
status: code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 'unknown' : 'failed',
|
|
541
|
+
changed: code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? null : false,
|
|
542
|
+
accepted: true,
|
|
543
|
+
requestId: request.id,
|
|
544
|
+
result: null,
|
|
540
545
|
error: { code, message: detail, retryable: false }
|
|
541
|
-
})
|
|
546
|
+
}),
|
|
542
547
|
stderr: ''
|
|
543
548
|
}
|
|
544
549
|
: request.family === 'task-create'
|
package/lib/sandbox/removal.ts
CHANGED
|
@@ -50,14 +50,6 @@ import { getProcessStartTime } from '../server/process-state.ts';
|
|
|
50
50
|
import { releaseStaleShortIdRegistry } from '../task/short-id.ts';
|
|
51
51
|
import { withRepositoryMutationLock } from '../task/task-execution-lock.ts';
|
|
52
52
|
import { fetchSandboxRows, type SandboxRow } from './commands/list-running.ts';
|
|
53
|
-
import {
|
|
54
|
-
formatIntermediateCleanupReport,
|
|
55
|
-
cleanupIntermediateUnderRemovalCoordinator,
|
|
56
|
-
protectIntermediateCleanupReport,
|
|
57
|
-
mergeIntermediateCleanupReports,
|
|
58
|
-
type IntermediateCleanupReport,
|
|
59
|
-
scanIntermediateCleanup
|
|
60
|
-
} from '../task/intermediate-cleanup.ts';
|
|
61
53
|
import { createSandboxControlBindingEvidence } from './task-cleanup.ts';
|
|
62
54
|
import {
|
|
63
55
|
createCleanPermit,
|
|
@@ -946,7 +938,7 @@ async function runRmOneUnderRepositoryLock(
|
|
|
946
938
|
tools: SandboxTool[],
|
|
947
939
|
branch: string,
|
|
948
940
|
options: RmOneOptions = {}
|
|
949
|
-
): Promise<
|
|
941
|
+
): Promise<null> {
|
|
950
942
|
const target = options.target ?? resolveRmTarget(
|
|
951
943
|
config,
|
|
952
944
|
tools,
|
|
@@ -1048,7 +1040,7 @@ async function rmOneCore(
|
|
|
1048
1040
|
tools: SandboxTool[],
|
|
1049
1041
|
branch: string,
|
|
1050
1042
|
options: RmOneOptions = {}
|
|
1051
|
-
): Promise<
|
|
1043
|
+
): Promise<null> {
|
|
1052
1044
|
const target = options.target ?? resolveRmTarget(
|
|
1053
1045
|
config,
|
|
1054
1046
|
tools,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
|
|
3
4
|
import { resolveArtifactContext, hasOpenArtifactRound } from './artifact-lifecycle.ts';
|
|
4
5
|
import { parseArtifactName } from './artifact-name.ts';
|
|
@@ -83,7 +84,15 @@ export function executeArtifactCommand(
|
|
|
83
84
|
const input = { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskDir, family: schema.family, artifact };
|
|
84
85
|
if (operation === 'init') {
|
|
85
86
|
const context = resolveArtifactContext(taskRef, family, { repoRoot: resolved.repoRoot });
|
|
86
|
-
|
|
87
|
+
const existingCandidate = path.join(taskDir, artifact);
|
|
88
|
+
let matchingInitializedSkeleton = false;
|
|
89
|
+
try {
|
|
90
|
+
const existing = fs.readFileSync(existingCandidate, 'utf8');
|
|
91
|
+
matchingInitializedSkeleton = existing.includes(`<!-- artifact-context:${resolved.taskId}:${family}:${parsed.round} -->`);
|
|
92
|
+
} catch { /* an absent candidate is created only for the selected identity */ }
|
|
93
|
+
if ((context.status !== 'ready' && !matchingInitializedSkeleton) || (context.next?.name !== artifact
|
|
94
|
+
&& !hasOpenArtifactRound(fs.readFileSync(resolved.taskMdPath, 'utf8'), family, parsed.round)
|
|
95
|
+
&& !matchingInitializedSkeleton)) {
|
|
87
96
|
return fail('ARTIFACT_INIT_CONTEXT_INVALID', context.error?.message ?? `artifact '${artifact}' is not the next ${family} artifact`);
|
|
88
97
|
}
|
|
89
98
|
return { ...initializeArtifactSkeleton({ ...input, ...(locale ? { locale } : {}) }), ...identity };
|