@remixhq/mcp 0.1.11 → 0.1.13
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/cli.js +246 -53
- package/dist/cli.js.map +1 -1
- package/dist/index.js +236 -49
- package/dist/index.js.map +1 -1
- package/dist/server.js +236 -49
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -74,6 +74,7 @@ var ERROR_CODES = {
|
|
|
74
74
|
DIRTY_WORKTREE: "DIRTY_WORKTREE",
|
|
75
75
|
DETACHED_HEAD: "DETACHED_HEAD",
|
|
76
76
|
PREFERRED_BRANCH_MISMATCH: "PREFERRED_BRANCH_MISMATCH",
|
|
77
|
+
FAMILY_SELECTION_AMBIGUOUS: "FAMILY_SELECTION_AMBIGUOUS",
|
|
77
78
|
MISSING_HEAD: "MISSING_HEAD",
|
|
78
79
|
REPO_LOCK_HELD: "REPO_LOCK_HELD",
|
|
79
80
|
REPO_LOCK_TIMEOUT: "REPO_LOCK_TIMEOUT",
|
|
@@ -210,6 +211,14 @@ function normalizeByMessage(err) {
|
|
|
210
211
|
category: "local_state"
|
|
211
212
|
});
|
|
212
213
|
}
|
|
214
|
+
if (message.includes("Multiple canonical Remix families") || message.includes("family selection is ambiguous")) {
|
|
215
|
+
return makeNormalized({
|
|
216
|
+
code: ERROR_CODES.FAMILY_SELECTION_AMBIGUOUS,
|
|
217
|
+
message,
|
|
218
|
+
hint,
|
|
219
|
+
category: "local_state"
|
|
220
|
+
});
|
|
221
|
+
}
|
|
213
222
|
if (message.includes("Failed to resolve local HEAD")) {
|
|
214
223
|
return makeNormalized({
|
|
215
224
|
code: ERROR_CODES.MISSING_HEAD,
|
|
@@ -343,14 +352,6 @@ function assertConfirm(confirm, operation) {
|
|
|
343
352
|
if (confirm) return;
|
|
344
353
|
throw createPolicyError(`${operation} requires explicit confirmation.`, "Pass confirm=true to run this tool.");
|
|
345
354
|
}
|
|
346
|
-
function assertDiffWithinLimit(policy, diff) {
|
|
347
|
-
const sizeBytes = Buffer.byteLength(diff, "utf8");
|
|
348
|
-
if (sizeBytes <= policy.maxDiffBytes) return;
|
|
349
|
-
throw createPolicyError(
|
|
350
|
-
"Diff exceeds the configured maximum size for Remix MCP.",
|
|
351
|
-
`Configured limit=${policy.maxDiffBytes} bytes actual=${sizeBytes} bytes.`
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
355
|
|
|
355
356
|
// src/bootstrap/context.ts
|
|
356
357
|
function createServerContext(params) {
|
|
@@ -498,8 +499,6 @@ var finalizeTurnInputSchema = {
|
|
|
498
499
|
...commonRequestFieldsSchema,
|
|
499
500
|
prompt: z2.string().trim().min(1),
|
|
500
501
|
assistantResponse: z2.string().trim().min(1),
|
|
501
|
-
diffSource: z2.enum(["worktree", "external"]).optional(),
|
|
502
|
-
externalDiff: z2.string().optional(),
|
|
503
502
|
sync: z2.boolean().optional(),
|
|
504
503
|
allowBranchMismatch: z2.boolean().optional(),
|
|
505
504
|
idempotencyKey: z2.string().trim().min(1).optional()
|
|
@@ -507,11 +506,17 @@ var finalizeTurnInputSchema = {
|
|
|
507
506
|
var previewInputSchema = {
|
|
508
507
|
...commonRequestFieldsSchema
|
|
509
508
|
};
|
|
509
|
+
var drainFinalizeQueueInputSchema = {
|
|
510
|
+
...commonRequestFieldsSchema
|
|
511
|
+
};
|
|
510
512
|
var applyInputSchema = {
|
|
511
513
|
...commonRequestFieldsSchema,
|
|
512
514
|
confirm: z2.boolean(),
|
|
513
515
|
allowBranchMismatch: z2.boolean().optional()
|
|
514
516
|
};
|
|
517
|
+
var reAnchorInputSchema = {
|
|
518
|
+
...applyInputSchema
|
|
519
|
+
};
|
|
515
520
|
var requestMergeInputSchema = {
|
|
516
521
|
...commonRequestFieldsSchema
|
|
517
522
|
};
|
|
@@ -621,7 +626,10 @@ var initDataSchema = z2.object({
|
|
|
621
626
|
dashboardUrl: z2.string().url(),
|
|
622
627
|
upstreamAppId: z2.string(),
|
|
623
628
|
bindingPath: z2.string(),
|
|
624
|
-
repoRoot: z2.string()
|
|
629
|
+
repoRoot: z2.string(),
|
|
630
|
+
bindingMode: z2.enum(["legacy", "lane", "explicit_root"]).optional(),
|
|
631
|
+
createdCanonicalFamily: z2.boolean().optional(),
|
|
632
|
+
baselineStatus: z2.enum(["seeded", "existing", "requires_re_anchor", "requires_sync"]).optional()
|
|
625
633
|
});
|
|
626
634
|
var listDataSchema = z2.object({
|
|
627
635
|
apps: genericArraySchema,
|
|
@@ -644,19 +652,26 @@ var checkoutDataSchema = z2.object({
|
|
|
644
652
|
repoRoot: z2.string()
|
|
645
653
|
});
|
|
646
654
|
var addDataSchema = z2.object({
|
|
647
|
-
changeStep: genericRecordSchema
|
|
648
|
-
autoSync: genericRecordSchema
|
|
655
|
+
changeStep: genericRecordSchema
|
|
649
656
|
});
|
|
650
657
|
var recordTurnDataSchema = genericRecordSchema;
|
|
651
658
|
var finalizeTurnDataSchema = z2.object({
|
|
652
659
|
mode: z2.enum(["changed_turn", "no_diff_turn"]),
|
|
653
660
|
idempotencyKey: z2.string().min(1),
|
|
661
|
+
queued: z2.boolean(),
|
|
662
|
+
jobId: z2.string().nullable(),
|
|
663
|
+
repoState: z2.string().nullable(),
|
|
654
664
|
changeStep: genericRecordSchema.nullable(),
|
|
655
665
|
collabTurn: genericRecordSchema.nullable(),
|
|
656
666
|
autoSync: genericRecordSchema.nullable(),
|
|
657
667
|
warnings: z2.array(z2.string())
|
|
658
668
|
});
|
|
669
|
+
var drainFinalizeQueueDataSchema = z2.object({
|
|
670
|
+
processed: z2.number().int().nonnegative(),
|
|
671
|
+
results: z2.array(genericRecordSchema)
|
|
672
|
+
});
|
|
659
673
|
var syncDataSchema = genericRecordSchema;
|
|
674
|
+
var reAnchorDataSchema = genericRecordSchema;
|
|
660
675
|
var requestMergeDataSchema = genericRecordSchema;
|
|
661
676
|
var mergeRequestQueueDataSchema = z2.object({
|
|
662
677
|
queue: mergeRequestQueueSchema,
|
|
@@ -731,7 +746,9 @@ var checkoutSuccessSchema = makeSuccessSchema(checkoutDataSchema);
|
|
|
731
746
|
var addSuccessSchema = makeSuccessSchema(addDataSchema);
|
|
732
747
|
var recordTurnSuccessSchema = makeSuccessSchema(recordTurnDataSchema);
|
|
733
748
|
var finalizeTurnSuccessSchema = makeSuccessSchema(finalizeTurnDataSchema);
|
|
749
|
+
var drainFinalizeQueueSuccessSchema = makeSuccessSchema(drainFinalizeQueueDataSchema);
|
|
734
750
|
var syncSuccessSchema = makeSuccessSchema(syncDataSchema);
|
|
751
|
+
var reAnchorSuccessSchema = makeSuccessSchema(reAnchorDataSchema);
|
|
735
752
|
var requestMergeSuccessSchema = makeSuccessSchema(requestMergeDataSchema);
|
|
736
753
|
var mergeRequestQueueSuccessSchema = makeSuccessSchema(mergeRequestQueueDataSchema);
|
|
737
754
|
var viewMergeRequestSuccessSchema = makeSuccessSchema(viewMergeRequestDataSchema);
|
|
@@ -749,17 +766,18 @@ var accessDebugSuccessSchema = makeSuccessSchema(accessDebugDataSchema);
|
|
|
749
766
|
var updateMemberRoleSuccessSchema = makeSuccessSchema(updateMemberRoleDataSchema);
|
|
750
767
|
|
|
751
768
|
// src/domain/coreAdapter.ts
|
|
769
|
+
import { spawn } from "child_process";
|
|
752
770
|
import {
|
|
753
771
|
collabList as coreCollabList,
|
|
754
772
|
collabListMembers as coreCollabListMembers,
|
|
755
773
|
collabUpdateMemberRole as coreCollabUpdateMemberRole,
|
|
756
|
-
|
|
774
|
+
drainPendingFinalizeQueue as coreDrainPendingFinalizeQueue,
|
|
757
775
|
collabFinalizeTurn as coreCollabFinalizeTurn,
|
|
758
|
-
collabRecordTurn as coreCollabRecordTurn,
|
|
759
776
|
collabApprove as coreCollabApprove,
|
|
760
777
|
collabCheckout as coreCollabCheckout,
|
|
761
778
|
collabListMergeRequests as coreCollabListMergeRequests,
|
|
762
779
|
collabInit as coreCollabInit,
|
|
780
|
+
collabReAnchor as coreCollabReAnchor,
|
|
763
781
|
collabInvite as coreCollabInvite,
|
|
764
782
|
collabReconcile as coreCollabReconcile,
|
|
765
783
|
collabReject as coreCollabReject,
|
|
@@ -770,10 +788,13 @@ import {
|
|
|
770
788
|
collabSyncUpstream as coreCollabSyncUpstream,
|
|
771
789
|
collabView as coreCollabView
|
|
772
790
|
} from "@remixhq/core/collab";
|
|
773
|
-
import { findGitRoot
|
|
791
|
+
import { findGitRoot } from "@remixhq/core/repo";
|
|
774
792
|
function getRiskLevel(status) {
|
|
775
793
|
if (status.recommendedAction === "reconcile") return "high";
|
|
776
|
-
if (status.recommendedAction === "
|
|
794
|
+
if (status.recommendedAction === "choose_family" || status.recommendedAction === "await_finalize") return "medium";
|
|
795
|
+
if (status.recommendedAction === "pull" || status.recommendedAction === "re_anchor" || status.remote.incomingOpenMergeRequestCount) {
|
|
796
|
+
return "medium";
|
|
797
|
+
}
|
|
777
798
|
if (status.repo.branchMismatch || !status.repo.isGitRepo || !status.binding.isBound || !status.repo.worktree.isClean) return "medium";
|
|
778
799
|
return "low";
|
|
779
800
|
}
|
|
@@ -786,12 +807,22 @@ function getRecommendedNextActions(status) {
|
|
|
786
807
|
switch (status.recommendedAction) {
|
|
787
808
|
case "init":
|
|
788
809
|
return ["Run remix_collab_init to bind the repository to Remix before using any Remix collaboration mutation flow."];
|
|
789
|
-
case "
|
|
790
|
-
return ["Run remix_collab_sync_preview, then remix_collab_sync_apply if the preview is acceptable.
|
|
810
|
+
case "pull":
|
|
811
|
+
return ["Run remix_collab_sync_preview, then remix_collab_sync_apply if the preview is acceptable. This pulls the server delta into the local working tree without rewriting local git history."];
|
|
812
|
+
case "re_anchor":
|
|
813
|
+
return ["Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply to re-anchor the lane after manual Git/GitHub history movement."];
|
|
791
814
|
case "reconcile":
|
|
792
|
-
return ["Run remix_collab_reconcile_preview before attempting remix_collab_reconcile_apply. Reconcile
|
|
815
|
+
return ["Run remix_collab_reconcile_preview before attempting remix_collab_reconcile_apply. Reconcile now routes through the explicit Remix recovery/re-anchor flow for diverged state."];
|
|
816
|
+
case "await_finalize":
|
|
817
|
+
return [
|
|
818
|
+
"Run remix_collab_drain_finalize_queue before merge-related or recovery flows. finalize_turn is queued only until the local finalize queue is drained."
|
|
819
|
+
];
|
|
793
820
|
case "review_queue":
|
|
794
821
|
return ["Run remix_collab_review_queue to inspect reviewable merge requests instead of using local git merge flows."];
|
|
822
|
+
case "choose_family":
|
|
823
|
+
return [
|
|
824
|
+
"This checkout is ambiguous across multiple canonical Remix families. Continue from a checkout already bound to the intended family, or run remix_collab_init with forceNew=true to create and bind a new canonical family."
|
|
825
|
+
];
|
|
795
826
|
default:
|
|
796
827
|
return [];
|
|
797
828
|
}
|
|
@@ -819,6 +850,17 @@ function truncateText(value, maxChars) {
|
|
|
819
850
|
originalChars: value.length
|
|
820
851
|
};
|
|
821
852
|
}
|
|
853
|
+
function spawnFinalizeQueueDrainer() {
|
|
854
|
+
const entrypoint = process.argv[1];
|
|
855
|
+
if (!entrypoint) return false;
|
|
856
|
+
const child = spawn(process.execPath, [...process.execArgv, entrypoint, "--drain-finalize-queue"], {
|
|
857
|
+
detached: true,
|
|
858
|
+
stdio: "ignore",
|
|
859
|
+
env: process.env
|
|
860
|
+
});
|
|
861
|
+
child.unref();
|
|
862
|
+
return true;
|
|
863
|
+
}
|
|
822
864
|
async function getStatus(params) {
|
|
823
865
|
const api = params.includeRemote ? await createCollabApiClient() : null;
|
|
824
866
|
const status = await coreCollabStatus({
|
|
@@ -849,7 +891,11 @@ async function initCollab(params) {
|
|
|
849
891
|
return {
|
|
850
892
|
data: result,
|
|
851
893
|
warnings: collectResultWarnings(result),
|
|
852
|
-
recommendedNextActions:
|
|
894
|
+
recommendedNextActions: result.baselineStatus === "requires_re_anchor" ? [
|
|
895
|
+
"Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply to anchor this checkout before recording turns."
|
|
896
|
+
] : result.baselineStatus === "requires_sync" ? [
|
|
897
|
+
"Run remix_collab_sync_preview, then remix_collab_sync_apply to pull the server delta and create the first local baseline for this checkout."
|
|
898
|
+
] : ["Run remix_collab_status to inspect sync, reconcile, and merge-request readiness before mutating bound-repo state."],
|
|
853
899
|
logContext: {
|
|
854
900
|
repoRoot: result.repoRoot,
|
|
855
901
|
appId: result.appId
|
|
@@ -919,23 +965,44 @@ async function finalizeCollabTurn(params) {
|
|
|
919
965
|
cwd: params.cwd,
|
|
920
966
|
prompt: params.prompt,
|
|
921
967
|
assistantResponse: params.assistantResponse,
|
|
922
|
-
diff: params.externalDiff ?? null,
|
|
923
|
-
diffSource: params.diffSource,
|
|
924
968
|
sync: params.sync,
|
|
925
969
|
allowBranchMismatch: params.allowBranchMismatch ?? false,
|
|
926
970
|
idempotencyKey: params.idempotencyKey ?? null,
|
|
927
971
|
actor: params.agent
|
|
928
972
|
});
|
|
973
|
+
if (result.queued) {
|
|
974
|
+
if (!spawnFinalizeQueueDrainer()) {
|
|
975
|
+
await coreDrainPendingFinalizeQueue({ api });
|
|
976
|
+
}
|
|
977
|
+
}
|
|
929
978
|
return {
|
|
930
979
|
data: result,
|
|
931
980
|
warnings: result.warnings,
|
|
932
|
-
recommendedNextActions: [],
|
|
981
|
+
recommendedNextActions: result.queued ? ["Run remix_collab_drain_finalize_queue before merge-related flows if you need this queued turn recorded immediately."] : [],
|
|
933
982
|
logContext: {
|
|
934
983
|
repoRoot,
|
|
935
984
|
appId: result.changeStep?.appId ?? result.collabTurn?.appId ?? null
|
|
936
985
|
}
|
|
937
986
|
};
|
|
938
987
|
}
|
|
988
|
+
async function drainFinalizeQueue(params) {
|
|
989
|
+
const api = await createCollabApiClient();
|
|
990
|
+
const repoRoot = await findGitRoot(params.cwd);
|
|
991
|
+
const results = await coreDrainPendingFinalizeQueue({ api });
|
|
992
|
+
const warnings = results.flatMap((result) => collectResultWarnings(result));
|
|
993
|
+
return {
|
|
994
|
+
data: {
|
|
995
|
+
processed: results.length,
|
|
996
|
+
results
|
|
997
|
+
},
|
|
998
|
+
warnings,
|
|
999
|
+
recommendedNextActions: [],
|
|
1000
|
+
logContext: {
|
|
1001
|
+
repoRoot,
|
|
1002
|
+
appId: null
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
939
1006
|
async function syncCollab(params) {
|
|
940
1007
|
const api = await createCollabApiClient();
|
|
941
1008
|
const result = await coreCollabSync({
|
|
@@ -947,12 +1014,33 @@ async function syncCollab(params) {
|
|
|
947
1014
|
return {
|
|
948
1015
|
data: result,
|
|
949
1016
|
warnings: collectResultWarnings(result),
|
|
950
|
-
recommendedNextActions: params.dryRun ? ["Run remix_collab_sync_apply with confirm=true to apply this
|
|
1017
|
+
recommendedNextActions: params.dryRun ? result.status === "delta_ready" ? ["Run remix_collab_sync_apply with confirm=true to apply this server delta into the local working tree."] : result.status === "base_unknown" ? [
|
|
1018
|
+
"Direct pull is unavailable because Remix cannot diff from the last acknowledged server head.",
|
|
1019
|
+
"Run remix_collab_reconcile_preview next to inspect recovery options before applying any recovery flow."
|
|
1020
|
+
] : [] : [],
|
|
951
1021
|
logContext: {
|
|
952
1022
|
repoRoot: result.repoRoot
|
|
953
1023
|
}
|
|
954
1024
|
};
|
|
955
1025
|
}
|
|
1026
|
+
async function reAnchor(params) {
|
|
1027
|
+
const api = await createCollabApiClient();
|
|
1028
|
+
const result = await coreCollabReAnchor({
|
|
1029
|
+
api,
|
|
1030
|
+
cwd: params.cwd,
|
|
1031
|
+
dryRun: params.dryRun,
|
|
1032
|
+
allowBranchMismatch: params.allowBranchMismatch ?? false
|
|
1033
|
+
});
|
|
1034
|
+
return {
|
|
1035
|
+
data: result,
|
|
1036
|
+
warnings: collectWarnings(result.warnings),
|
|
1037
|
+
recommendedNextActions: params.dryRun ? ["Run remix_collab_re_anchor_apply with confirm=true to re-anchor this lane after manual Git/GitHub history movement."] : [],
|
|
1038
|
+
logContext: {
|
|
1039
|
+
repoRoot: result.repoRoot,
|
|
1040
|
+
appId: result.currentAppId
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
956
1044
|
async function requestMerge(params) {
|
|
957
1045
|
const api = await createCollabApiClient();
|
|
958
1046
|
const result = await coreCollabRequestMerge({
|
|
@@ -1126,8 +1214,8 @@ async function reconcile(params) {
|
|
|
1126
1214
|
return {
|
|
1127
1215
|
data: result,
|
|
1128
1216
|
warnings: collectWarnings(result.warnings),
|
|
1129
|
-
recommendedNextActions: params.dryRun ? ["Run remix_collab_reconcile_apply with confirm=true only if the preview is acceptable.
|
|
1130
|
-
risks: params.dryRun ? ["Reconcile
|
|
1217
|
+
recommendedNextActions: params.dryRun ? ["Run remix_collab_reconcile_apply with confirm=true only if the preview is acceptable. This is the explicit Remix recovery flow for diverged state."] : [],
|
|
1218
|
+
risks: params.dryRun ? ["Reconcile may upload local history to re-anchor the server lane before future recording continues."] : [],
|
|
1131
1219
|
logContext: {
|
|
1132
1220
|
repoRoot: result.repoRoot ?? null
|
|
1133
1221
|
}
|
|
@@ -1703,9 +1791,6 @@ function buildSuccessEnvelope(tool, requestId, result) {
|
|
|
1703
1791
|
};
|
|
1704
1792
|
}
|
|
1705
1793
|
function deriveErrorRisks(tool, normalized) {
|
|
1706
|
-
if (isFinalizeTurnLocalSyncFailure(tool, normalized)) {
|
|
1707
|
-
return ["The change step succeeded remotely, but the local repository may need manual recovery or a follow-up sync."];
|
|
1708
|
-
}
|
|
1709
1794
|
if (normalized.code === "DESTRUCTIVE_OPERATION_BLOCKED") {
|
|
1710
1795
|
return ["A policy guard blocked a potentially destructive or state-mutating operation."];
|
|
1711
1796
|
}
|
|
@@ -1717,16 +1802,9 @@ function deriveErrorRisks(tool, normalized) {
|
|
|
1717
1802
|
}
|
|
1718
1803
|
return [];
|
|
1719
1804
|
}
|
|
1720
|
-
function isFinalizeTurnLocalSyncFailure(tool, normalized) {
|
|
1721
|
-
return tool === "remix_collab_finalize_turn" && normalized.message === "Change step succeeded remotely, but automatic local sync failed.";
|
|
1722
|
-
}
|
|
1723
1805
|
function buildErrorEnvelope(tool, requestId, error) {
|
|
1724
1806
|
const normalized = normalizeToolError(error);
|
|
1725
|
-
const recommendedNextActions =
|
|
1726
|
-
"Run `remix_collab_status` to confirm the bound repo state before attempting recovery.",
|
|
1727
|
-
"Run `remix_collab_sync_preview` next, then `remix_collab_sync_apply` with `confirm=true` if the preview looks correct.",
|
|
1728
|
-
"Inspect `error.hint` for any preserved diff backup path before retrying local recovery, and do not rerun `remix_collab_finalize_turn` immediately."
|
|
1729
|
-
] : normalized.code === "AUTH_REQUIRED" ? ["Set COMERGE_ACCESS_TOKEN, then retry the tool call."] : normalized.code === "REPO_LOCK_TIMEOUT" ? ["Wait for the active Remix mutation to finish, then retry the tool call."] : normalized.code === "REPO_STATE_CHANGED_DURING_OPERATION" ? ["Review local repository changes, then rerun the tool once the worktree is stable."] : normalized.code === "PREFERRED_BRANCH_MISMATCH" ? ["Switch to the repository's preferred Remix branch, or rerun with allowBranchMismatch=true if intentional."] : [];
|
|
1807
|
+
const recommendedNextActions = normalized.code === "AUTH_REQUIRED" ? ["Set COMERGE_ACCESS_TOKEN, then retry the tool call."] : normalized.code === "REPO_LOCK_TIMEOUT" ? ["Wait for the active Remix mutation to finish, then retry the tool call."] : normalized.code === "REPO_STATE_CHANGED_DURING_OPERATION" ? ["Review local repository changes, then rerun the tool once the worktree is stable."] : normalized.code === "PREFERRED_BRANCH_MISMATCH" ? ["Switch to the repository's preferred Remix branch, or rerun with allowBranchMismatch=true if intentional."] : [];
|
|
1730
1808
|
return {
|
|
1731
1809
|
schemaVersion: SCHEMA_VERSION,
|
|
1732
1810
|
ok: false,
|
|
@@ -1872,7 +1950,7 @@ function registerCollabTools(server, context) {
|
|
|
1872
1950
|
});
|
|
1873
1951
|
registerTool(server, context, {
|
|
1874
1952
|
name: "remix_collab_finalize_turn",
|
|
1875
|
-
description: "Primary turn recorder for the current bound repository. Call this exactly once before the final response; it
|
|
1953
|
+
description: "Primary turn recorder for the current bound repository. Call this exactly once before the final response; it captures the current boundary locally and queues remote processing. Queued only: no remote change step exists yet until the finalize queue is drained.",
|
|
1876
1954
|
access: "local_write",
|
|
1877
1955
|
inputSchema: finalizeTurnInputSchema,
|
|
1878
1956
|
outputSchema: finalizeTurnSuccessSchema,
|
|
@@ -1880,15 +1958,10 @@ function registerCollabTools(server, context) {
|
|
|
1880
1958
|
run: async (args) => {
|
|
1881
1959
|
const input = z3.object(finalizeTurnInputSchema).parse(args);
|
|
1882
1960
|
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
1883
|
-
if ((input.diffSource ?? "worktree") === "external" || typeof input.externalDiff === "string") {
|
|
1884
|
-
assertDiffWithinLimit(context.policy, input.externalDiff ?? "");
|
|
1885
|
-
}
|
|
1886
1961
|
return finalizeCollabTurn({
|
|
1887
1962
|
cwd,
|
|
1888
1963
|
prompt: input.prompt,
|
|
1889
1964
|
assistantResponse: input.assistantResponse,
|
|
1890
|
-
diffSource: input.diffSource,
|
|
1891
|
-
externalDiff: input.externalDiff,
|
|
1892
1965
|
sync: input.sync,
|
|
1893
1966
|
allowBranchMismatch: input.allowBranchMismatch ?? false,
|
|
1894
1967
|
idempotencyKey: input.idempotencyKey,
|
|
@@ -1896,9 +1969,22 @@ function registerCollabTools(server, context) {
|
|
|
1896
1969
|
});
|
|
1897
1970
|
}
|
|
1898
1971
|
});
|
|
1972
|
+
registerTool(server, context, {
|
|
1973
|
+
name: "remix_collab_drain_finalize_queue",
|
|
1974
|
+
description: "Drain the local finalize queue and record queued finalize_turn jobs immediately. Use this before request-merge, reconcile, or other flows that require the remote change step to exist already.",
|
|
1975
|
+
access: "local_write",
|
|
1976
|
+
inputSchema: drainFinalizeQueueInputSchema,
|
|
1977
|
+
outputSchema: drainFinalizeQueueSuccessSchema,
|
|
1978
|
+
annotations: getAnnotations("local_write", { idempotent: true }),
|
|
1979
|
+
run: async (args) => {
|
|
1980
|
+
const input = z3.object(drainFinalizeQueueInputSchema).parse(args);
|
|
1981
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
1982
|
+
return drainFinalizeQueue({ cwd });
|
|
1983
|
+
}
|
|
1984
|
+
});
|
|
1899
1985
|
registerTool(server, context, {
|
|
1900
1986
|
name: "remix_collab_sync_preview",
|
|
1901
|
-
description: "Preview whether the current bound repository can
|
|
1987
|
+
description: "Preview whether the current bound repository can pull the server delta into the working tree. Use this instead of raw git pull or rebase for bound-repo alignment.",
|
|
1902
1988
|
access: "read",
|
|
1903
1989
|
inputSchema: previewInputSchema,
|
|
1904
1990
|
outputSchema: syncSuccessSchema,
|
|
@@ -1910,7 +1996,7 @@ function registerCollabTools(server, context) {
|
|
|
1910
1996
|
});
|
|
1911
1997
|
registerTool(server, context, {
|
|
1912
1998
|
name: "remix_collab_sync_apply",
|
|
1913
|
-
description: "
|
|
1999
|
+
description: "Pull the server delta into the local working tree without moving local git history. This is the Remix-native replacement for raw git pull or rebase in a bound repo.",
|
|
1914
2000
|
access: "local_write",
|
|
1915
2001
|
inputSchema: applyInputSchema,
|
|
1916
2002
|
outputSchema: syncSuccessSchema,
|
|
@@ -1921,6 +2007,31 @@ function registerCollabTools(server, context) {
|
|
|
1921
2007
|
return syncCollab({ cwd, dryRun: false, allowBranchMismatch: input.allowBranchMismatch ?? false });
|
|
1922
2008
|
}
|
|
1923
2009
|
});
|
|
2010
|
+
registerTool(server, context, {
|
|
2011
|
+
name: "remix_collab_re_anchor_preview",
|
|
2012
|
+
description: "Preview whether the current checkout needs an explicit re-anchor to adopt or recover external Git/GitHub history.",
|
|
2013
|
+
access: "read",
|
|
2014
|
+
inputSchema: previewInputSchema,
|
|
2015
|
+
outputSchema: reAnchorSuccessSchema,
|
|
2016
|
+
run: async (args) => {
|
|
2017
|
+
const input = z3.object(previewInputSchema).parse(args);
|
|
2018
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2019
|
+
return reAnchor({ cwd, dryRun: true });
|
|
2020
|
+
}
|
|
2021
|
+
});
|
|
2022
|
+
registerTool(server, context, {
|
|
2023
|
+
name: "remix_collab_re_anchor_apply",
|
|
2024
|
+
description: "Explicitly re-anchor the current lane to adopted or recovered external Git/GitHub history, without rewriting the local checkout afterward.",
|
|
2025
|
+
access: "local_write",
|
|
2026
|
+
inputSchema: reAnchorInputSchema,
|
|
2027
|
+
outputSchema: reAnchorSuccessSchema,
|
|
2028
|
+
run: async (args) => {
|
|
2029
|
+
const input = z3.object(reAnchorInputSchema).parse(args);
|
|
2030
|
+
assertConfirm(input.confirm, "remix_collab_re_anchor_apply");
|
|
2031
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2032
|
+
return reAnchor({ cwd, dryRun: false, allowBranchMismatch: input.allowBranchMismatch ?? false });
|
|
2033
|
+
}
|
|
2034
|
+
});
|
|
1924
2035
|
registerTool(server, context, {
|
|
1925
2036
|
name: "remix_collab_request_merge",
|
|
1926
2037
|
description: "Open a prompt-backed Remix merge request from the current bound repository to its upstream app instead of merging locally with raw git.",
|
|
@@ -2061,7 +2172,7 @@ function registerCollabTools(server, context) {
|
|
|
2061
2172
|
});
|
|
2062
2173
|
registerTool(server, context, {
|
|
2063
2174
|
name: "remix_collab_reconcile_preview",
|
|
2064
|
-
description: "Preview
|
|
2175
|
+
description: "Preview the explicit Remix recovery flow when local and server state diverged beyond a simple pull.",
|
|
2065
2176
|
access: "read",
|
|
2066
2177
|
inputSchema: previewInputSchema,
|
|
2067
2178
|
outputSchema: reconcileSuccessSchema,
|
|
@@ -2073,7 +2184,7 @@ function registerCollabTools(server, context) {
|
|
|
2073
2184
|
});
|
|
2074
2185
|
registerTool(server, context, {
|
|
2075
2186
|
name: "remix_collab_reconcile_apply",
|
|
2076
|
-
description: "
|
|
2187
|
+
description: "Run the explicit Remix recovery flow for diverged local/server state.",
|
|
2077
2188
|
access: "local_write",
|
|
2078
2189
|
inputSchema: applyInputSchema,
|
|
2079
2190
|
outputSchema: reconcileSuccessSchema,
|
|
@@ -3198,6 +3309,19 @@ import { z as z9 } from "zod";
|
|
|
3198
3309
|
// src/contracts/ops.ts
|
|
3199
3310
|
import { z as z8 } from "zod";
|
|
3200
3311
|
var genericRecordSchema4 = z8.record(z8.string(), z8.unknown());
|
|
3312
|
+
var appJobKindSchema = z8.enum([
|
|
3313
|
+
"fork",
|
|
3314
|
+
"edit",
|
|
3315
|
+
"bundle",
|
|
3316
|
+
"import_github",
|
|
3317
|
+
"import_upload",
|
|
3318
|
+
"merge",
|
|
3319
|
+
"revert",
|
|
3320
|
+
"change_step",
|
|
3321
|
+
"reconcile",
|
|
3322
|
+
"change_step_replay"
|
|
3323
|
+
]);
|
|
3324
|
+
var appJobStatusSchema = z8.enum(["pending", "enqueued", "processing", "succeeded", "failed", "cancelled"]);
|
|
3201
3325
|
var appScopedInputSchema = {
|
|
3202
3326
|
...commonRequestFieldsSchema,
|
|
3203
3327
|
appId: z8.string().trim().min(1).optional()
|
|
@@ -3207,6 +3331,13 @@ var editQueueInputSchema = {
|
|
|
3207
3331
|
limit: z8.number().int().positive().max(100).optional(),
|
|
3208
3332
|
offset: z8.number().int().nonnegative().optional()
|
|
3209
3333
|
};
|
|
3334
|
+
var appJobQueueInputSchema = {
|
|
3335
|
+
...appScopedInputSchema,
|
|
3336
|
+
limit: z8.number().int().positive().max(100).optional(),
|
|
3337
|
+
offset: z8.number().int().nonnegative().optional(),
|
|
3338
|
+
kind: z8.array(appJobKindSchema).min(1).optional(),
|
|
3339
|
+
status: z8.array(appJobStatusSchema).min(1).optional()
|
|
3340
|
+
};
|
|
3210
3341
|
var bundleInputSchema = {
|
|
3211
3342
|
...appScopedInputSchema,
|
|
3212
3343
|
bundleId: z8.string().trim().min(1)
|
|
@@ -3239,6 +3370,7 @@ var agentRunEventsInputSchema = {
|
|
|
3239
3370
|
};
|
|
3240
3371
|
var appOverviewSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3241
3372
|
var editQueueSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3373
|
+
var appJobQueueSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3242
3374
|
var bundleSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3243
3375
|
var timelineSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3244
3376
|
var agentRunsSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
@@ -3272,7 +3404,7 @@ async function getAppOverview(params) {
|
|
|
3272
3404
|
data,
|
|
3273
3405
|
warnings: [],
|
|
3274
3406
|
recommendedNextActions: [
|
|
3275
|
-
"Use `remix_ops_list_timeline`, `remix_ops_list_agent_runs`, `remix_ops_get_edit_queue`, or `remix_ops_get_sandbox_status` for the next operational drill-down on this app."
|
|
3407
|
+
"Use `remix_ops_list_timeline`, `remix_ops_list_agent_runs`, `remix_ops_get_edit_queue`, `remix_ops_list_app_job_queue`, or `remix_ops_get_sandbox_status` for the next operational drill-down on this app."
|
|
3276
3408
|
],
|
|
3277
3409
|
logContext: target
|
|
3278
3410
|
};
|
|
@@ -3295,6 +3427,42 @@ async function getEditQueue(params) {
|
|
|
3295
3427
|
logContext: target
|
|
3296
3428
|
};
|
|
3297
3429
|
}
|
|
3430
|
+
async function listAppJobQueue(params) {
|
|
3431
|
+
const api = await createApiClient();
|
|
3432
|
+
const target = await resolveAppTarget(api, params);
|
|
3433
|
+
const data = unwrapResponseObject(
|
|
3434
|
+
await api.listAppJobQueue(target.appId, {
|
|
3435
|
+
limit: params.limit,
|
|
3436
|
+
offset: params.offset,
|
|
3437
|
+
kind: params.kind,
|
|
3438
|
+
status: params.status
|
|
3439
|
+
}),
|
|
3440
|
+
"app job queue"
|
|
3441
|
+
);
|
|
3442
|
+
const pageInfo = typeof data.pageInfo === "object" && data.pageInfo ? data.pageInfo : null;
|
|
3443
|
+
const items = Array.isArray(data.items) ? data.items : [];
|
|
3444
|
+
const activeBlockingKinds = /* @__PURE__ */ new Set(["merge", "change_step", "change_step_replay", "reconcile", "revert"]);
|
|
3445
|
+
const hasBlockingJobs = items.some((item) => {
|
|
3446
|
+
if (!item || typeof item !== "object") return false;
|
|
3447
|
+
const record = item;
|
|
3448
|
+
return typeof record.kind === "string" && activeBlockingKinds.has(record.kind) && (typeof record.status !== "string" || ["pending", "enqueued", "processing"].includes(record.status));
|
|
3449
|
+
});
|
|
3450
|
+
const recommendedNextActions = [];
|
|
3451
|
+
if (hasBlockingJobs) {
|
|
3452
|
+
recommendedNextActions.push(
|
|
3453
|
+
"Inspect the returned active workflow jobs before concluding that merge, replay, reconcile, or revert work is stuck. This surface reflects backend app-scoped queue state, not local finalize state."
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
3456
|
+
if (pageInfo?.hasMore === true && typeof pageInfo.limit === "number" && typeof pageInfo.offset === "number") {
|
|
3457
|
+
recommendedNextActions.push(`Pass offset=${pageInfo.offset + pageInfo.limit} to load the next app job queue page.`);
|
|
3458
|
+
}
|
|
3459
|
+
return {
|
|
3460
|
+
data,
|
|
3461
|
+
warnings: [],
|
|
3462
|
+
recommendedNextActions,
|
|
3463
|
+
logContext: target
|
|
3464
|
+
};
|
|
3465
|
+
}
|
|
3298
3466
|
async function getBundle(params) {
|
|
3299
3467
|
const api = await createApiClient();
|
|
3300
3468
|
const target = await resolveAppTarget(api, params);
|
|
@@ -3521,6 +3689,25 @@ function registerOpsTools(server, context) {
|
|
|
3521
3689
|
});
|
|
3522
3690
|
}
|
|
3523
3691
|
});
|
|
3692
|
+
registerTool4(server, context, {
|
|
3693
|
+
name: "remix_ops_list_app_job_queue",
|
|
3694
|
+
description: "Inspect bounded app-scoped workflow queue jobs for one app, including active backend work such as merge, replay, reconcile, revert, bundle, or import processing.",
|
|
3695
|
+
access: "read",
|
|
3696
|
+
inputSchema: appJobQueueInputSchema,
|
|
3697
|
+
outputSchema: appJobQueueSuccessSchema,
|
|
3698
|
+
run: async (args) => {
|
|
3699
|
+
const input = z9.object(appJobQueueInputSchema).parse(args);
|
|
3700
|
+
const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
|
|
3701
|
+
return listAppJobQueue({
|
|
3702
|
+
appId: input.appId,
|
|
3703
|
+
cwd,
|
|
3704
|
+
limit: input.limit,
|
|
3705
|
+
offset: input.offset,
|
|
3706
|
+
kind: input.kind,
|
|
3707
|
+
status: input.status
|
|
3708
|
+
});
|
|
3709
|
+
}
|
|
3710
|
+
});
|
|
3524
3711
|
registerTool4(server, context, {
|
|
3525
3712
|
name: "remix_ops_get_bundle",
|
|
3526
3713
|
description: "Inspect one app bundle by id without downloading the artifact payload.",
|