@sideboard-ai/core 0.1.43 → 0.1.45
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/agents/cursor-runner.js +1 -1
- package/dist/{agents-T3EA7GZV.js → agents-JSHCAZUZ.js} +15 -5
- package/dist/{app-settings-ZKVZHJPQ.js → app-settings-LYGVDGZY.js} +5 -1
- package/dist/{chunk-Z2BQMXVM.js → chunk-6NAPN2N5.js} +6 -2
- package/dist/{chunk-VC7NORFX.js → chunk-ANZ566Z5.js} +184 -3
- package/dist/{chunk-7PCTK4WO.js → chunk-FV6FN6V5.js} +1 -1
- package/dist/{chunk-AGU52GTV.js → chunk-I6QGZOOS.js} +488 -32
- package/dist/{chunk-PU27NUO4.js → chunk-J5JTEJ5O.js} +7 -2
- package/dist/{chunk-ENSD62HW.js → chunk-O6W3P7V3.js} +3 -1
- package/dist/{chunk-FUCEOJFO.js → chunk-U3EQKJHA.js} +2 -2
- package/dist/{chunk-FSIK442J.js → chunk-WYY3J7GR.js} +21 -0
- package/dist/{chunk-X6P2QVRJ.js → chunk-ZNSM2DDD.js} +3 -3
- package/dist/{coordinator-prompt-BHXBQRPM.js → coordinator-prompt-2XFYG3C5.js} +3 -3
- package/dist/{global-workspace-SNN45OCN.js → global-workspace-YFOQUGWD.js} +4 -4
- package/dist/index.cjs +735 -25
- package/dist/index.d.cts +107 -2
- package/dist/index.d.ts +107 -2
- package/dist/index.js +35 -9
- package/dist/mcp/run-stdio.cjs +805 -123
- package/dist/mcp/run-stdio.js +9 -9
- package/dist/{thread-store-EHROA3VZ.js → thread-store-OV2X6PYO.js} +1 -1
- package/dist/{workspaces-AUYIJ64Z.js → workspaces-TIKLNDW3.js} +5 -5
- package/dist/{worktree-N4PRV4V3.js → worktree-TEDAJ57S.js} +2 -2
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
ensureWorkspace,
|
|
4
4
|
removeWorkspace,
|
|
5
5
|
syncWorkspacesFromThreads
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-U3EQKJHA.js";
|
|
7
7
|
import {
|
|
8
8
|
GLOBAL_WORKSPACE_ID,
|
|
9
9
|
createGlobalChat,
|
|
@@ -13,34 +13,41 @@ import {
|
|
|
13
13
|
isGlobalThread,
|
|
14
14
|
isOrchestratorThread,
|
|
15
15
|
orchestratorSessionPoisonedByBuiltins
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-ZNSM2DDD.js";
|
|
17
17
|
import {
|
|
18
18
|
coordinatorSystemPrompt,
|
|
19
19
|
coordinatorTurnReminder,
|
|
20
20
|
enrichWorkspacesWithGithub,
|
|
21
21
|
ensureGlobalCoordinatorCwd
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-6NAPN2N5.js";
|
|
23
23
|
import {
|
|
24
24
|
PLAN_MODE_INSTRUCTION,
|
|
25
25
|
allAdapters,
|
|
26
26
|
getAdapter,
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
isSessionQuotaLimit,
|
|
28
|
+
listModelsForAgent,
|
|
29
|
+
parseBrightsyCliLine,
|
|
30
|
+
parseSessionQuotaResetAt,
|
|
31
|
+
resolveQuotaFallbackAgent
|
|
32
|
+
} from "./chunk-ANZ566Z5.js";
|
|
29
33
|
import {
|
|
30
34
|
fallbackTurnFailDetail,
|
|
31
35
|
formatTurnExitError,
|
|
36
|
+
humanizeAgentFailDetail,
|
|
32
37
|
looksLikeAgentFailureMessage,
|
|
33
38
|
pushTurnStderr,
|
|
34
39
|
summarizeTurnStderr
|
|
35
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-J5JTEJ5O.js";
|
|
36
41
|
import {
|
|
37
42
|
childEnvWithAppSettings,
|
|
38
43
|
getLinearApiKey,
|
|
39
44
|
isLinearConnected,
|
|
40
45
|
loadAppSettings,
|
|
46
|
+
orchestrationQuotaFallbackAgent,
|
|
47
|
+
orchestrationQuotaOnLimit,
|
|
41
48
|
resolveEffectiveIssueSource,
|
|
42
49
|
updateAdvancedSettings
|
|
43
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-WYY3J7GR.js";
|
|
44
51
|
import {
|
|
45
52
|
allocateTeamName,
|
|
46
53
|
allocateTeamSlug,
|
|
@@ -71,7 +78,7 @@ import {
|
|
|
71
78
|
takenSlugsFromThread,
|
|
72
79
|
threadDisplayLabel,
|
|
73
80
|
worktreeNameFromPath
|
|
74
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-FV6FN6V5.js";
|
|
75
82
|
import {
|
|
76
83
|
appendMessage,
|
|
77
84
|
createEmptyThread,
|
|
@@ -83,7 +90,7 @@ import {
|
|
|
83
90
|
updateThread,
|
|
84
91
|
withThreadLock,
|
|
85
92
|
writeThread
|
|
86
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-O6W3P7V3.js";
|
|
87
94
|
import {
|
|
88
95
|
loadRepoSettings,
|
|
89
96
|
loadWorkspaceSettings,
|
|
@@ -459,9 +466,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
459
466
|
`Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
|
|
460
467
|
);
|
|
461
468
|
}
|
|
462
|
-
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-
|
|
469
|
+
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-YFOQUGWD.js");
|
|
463
470
|
if (isGlobalThread2(thread)) {
|
|
464
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-
|
|
471
|
+
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-2XFYG3C5.js");
|
|
465
472
|
ensureGlobalCoordinatorCwd2();
|
|
466
473
|
}
|
|
467
474
|
const adapter = getAdapter(thread.agent);
|
|
@@ -2640,7 +2647,7 @@ async function createThread(input, onSetupLine) {
|
|
|
2640
2647
|
return readThread(thread.id) ?? thread;
|
|
2641
2648
|
}
|
|
2642
2649
|
async function listLinearIssues(agent, repoPath) {
|
|
2643
|
-
const { getAdapter: getAdapter2 } = await import("./agents-
|
|
2650
|
+
const { getAdapter: getAdapter2 } = await import("./agents-JSHCAZUZ.js");
|
|
2644
2651
|
await requireAgent(agent, { requireLinear: true });
|
|
2645
2652
|
const adapter = getAdapter2(agent);
|
|
2646
2653
|
if (!adapter.listLinearIssues) {
|
|
@@ -2735,12 +2742,19 @@ function forkChatTab(input) {
|
|
|
2735
2742
|
const from = requireThread(input.threadId);
|
|
2736
2743
|
const slice = forkMessageSlice(from, input.throughIndex);
|
|
2737
2744
|
const attachment = buildForkTranscriptAttachment(from.title || "Chat", slice);
|
|
2738
|
-
|
|
2745
|
+
const tab = createChatTab({
|
|
2739
2746
|
fromThreadId: input.threadId,
|
|
2740
2747
|
agent: input.agent ?? from.agent,
|
|
2748
|
+
model: input.model,
|
|
2741
2749
|
title: input.title?.trim() || void 0,
|
|
2742
2750
|
attachments: [attachment]
|
|
2743
2751
|
});
|
|
2752
|
+
if (isOrchestratorThread(from) && tab.parentThreadId !== from.id) {
|
|
2753
|
+
const next = { ...tab, parentThreadId: from.id };
|
|
2754
|
+
writeThread(next);
|
|
2755
|
+
return next;
|
|
2756
|
+
}
|
|
2757
|
+
return tab;
|
|
2744
2758
|
}
|
|
2745
2759
|
|
|
2746
2760
|
// src/threads/fork-worktree.ts
|
|
@@ -2751,16 +2765,28 @@ function requireThread2(idOrRef) {
|
|
|
2751
2765
|
}
|
|
2752
2766
|
async function forkThreadWorktree(input, onSetupLine) {
|
|
2753
2767
|
const from = requireThread2(input.threadId);
|
|
2768
|
+
if (isOrchestratorThread(from)) {
|
|
2769
|
+
throw new Error(
|
|
2770
|
+
"fork_worktree targets a worktree agent thread (not the orchestrator). Pass a child/worktree thread ref."
|
|
2771
|
+
);
|
|
2772
|
+
}
|
|
2773
|
+
if (!from.branchName?.trim() || !from.repoPath?.trim()) {
|
|
2774
|
+
throw new Error(
|
|
2775
|
+
`Cannot fork worktree: thread ${from.id} has no branch/repo (need a real worktree chat).`
|
|
2776
|
+
);
|
|
2777
|
+
}
|
|
2754
2778
|
const slice = forkMessageSlice(from, input.throughIndex);
|
|
2755
2779
|
const attachment = buildForkTranscriptAttachment(from.title || "Chat", slice);
|
|
2780
|
+
const nextAgent = input.agent ?? from.agent;
|
|
2781
|
+
const nextModel = input.model !== void 0 ? input.model : input.agent && input.agent !== from.agent ? null : from.model;
|
|
2756
2782
|
const thread = await createThread(
|
|
2757
2783
|
{
|
|
2758
2784
|
sourceType: "branch",
|
|
2759
2785
|
sourceRef: from.branchName,
|
|
2760
2786
|
repoPath: from.repoPath,
|
|
2761
|
-
agent:
|
|
2787
|
+
agent: nextAgent,
|
|
2762
2788
|
autonomy: from.autonomy,
|
|
2763
|
-
model:
|
|
2789
|
+
model: nextModel,
|
|
2764
2790
|
effort: from.effort,
|
|
2765
2791
|
fast: from.fast,
|
|
2766
2792
|
planMode: from.planMode,
|
|
@@ -2862,7 +2888,7 @@ async function adoptThread(input) {
|
|
|
2862
2888
|
messages: input.messages ?? []
|
|
2863
2889
|
});
|
|
2864
2890
|
writeThread(thread);
|
|
2865
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
2891
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-TIKLNDW3.js");
|
|
2866
2892
|
await ensureWorkspace2(repoPath);
|
|
2867
2893
|
return thread;
|
|
2868
2894
|
}
|
|
@@ -3289,9 +3315,164 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
3289
3315
|
return { repoPath, workspace };
|
|
3290
3316
|
}
|
|
3291
3317
|
|
|
3318
|
+
// src/review/request-review.ts
|
|
3319
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
3320
|
+
import { readFileSync as readFileSync8, existsSync as existsSync11 } from "fs";
|
|
3321
|
+
import { join as join10 } from "path";
|
|
3322
|
+
var REVIEW_REQUEST_PATH = ".sideboard/attachments/Review request.md";
|
|
3323
|
+
var REVIEW_REQUEST_NAME = "Review request.md";
|
|
3324
|
+
var REVIEW_REQUEST_PREFILL = `Please review the changes in this workspace and recommend whether they are ready to merge.
|
|
3325
|
+
|
|
3326
|
+
Start with a **Recommendation**: Approve, Approve with nits, Request changes, or Needs more information \u2014 and say why in 1\u20133 sentences. Then list blocking findings vs nits (findings may be empty).`;
|
|
3327
|
+
function buildReviewRequestAttachment(content) {
|
|
3328
|
+
return {
|
|
3329
|
+
id: randomUUID3(),
|
|
3330
|
+
name: REVIEW_REQUEST_NAME,
|
|
3331
|
+
kind: "file",
|
|
3332
|
+
path: REVIEW_REQUEST_PATH,
|
|
3333
|
+
content
|
|
3334
|
+
};
|
|
3335
|
+
}
|
|
3336
|
+
function readExistingReviewRequestFile(worktreePath) {
|
|
3337
|
+
const abs = join10(worktreePath, REVIEW_REQUEST_PATH);
|
|
3338
|
+
if (!existsSync11(abs)) return null;
|
|
3339
|
+
try {
|
|
3340
|
+
const content = readFileSync8(abs, "utf8");
|
|
3341
|
+
return content.trim() ? content : null;
|
|
3342
|
+
} catch {
|
|
3343
|
+
return null;
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
async function requestReview(threadRef, send) {
|
|
3347
|
+
const from = findThreadByRef(threadRef);
|
|
3348
|
+
if (!from) throw new Error(`Thread not found: ${threadRef}`);
|
|
3349
|
+
if (isOrchestratorThread(from)) {
|
|
3350
|
+
throw new Error(
|
|
3351
|
+
"request_review targets a worktree agent thread (not the orchestrator). Pass a child/worktree thread ref."
|
|
3352
|
+
);
|
|
3353
|
+
}
|
|
3354
|
+
if (from.status === "archived") {
|
|
3355
|
+
throw new Error(`Thread is archived: ${from.id}`);
|
|
3356
|
+
}
|
|
3357
|
+
const existing = readExistingReviewRequestFile(from.worktreePath);
|
|
3358
|
+
const attachments = existing ? [buildReviewRequestAttachment(existing)] : [];
|
|
3359
|
+
const tab = createChatTab({
|
|
3360
|
+
fromThreadId: from.id,
|
|
3361
|
+
title: "Review",
|
|
3362
|
+
attachments
|
|
3363
|
+
});
|
|
3364
|
+
const started = await send(tab.id, REVIEW_REQUEST_PREFILL);
|
|
3365
|
+
return { tab: started, from };
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3292
3368
|
// src/orchestrator/orchestrator.ts
|
|
3293
3369
|
import { EventEmitter } from "events";
|
|
3294
|
-
import { existsSync as
|
|
3370
|
+
import { existsSync as existsSync12 } from "fs";
|
|
3371
|
+
|
|
3372
|
+
// src/orchestrator/quota-failover.ts
|
|
3373
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
3374
|
+
function planOrchestrationQuotaFailover(thread, limitText, opts) {
|
|
3375
|
+
if (!isOrchestratorThread(thread)) return null;
|
|
3376
|
+
if (!isSessionQuotaLimit(limitText)) return null;
|
|
3377
|
+
const onLimit = opts?.onLimit ?? orchestrationQuotaOnLimit();
|
|
3378
|
+
const resumeAt = parseSessionQuotaResetAt(limitText, opts?.now);
|
|
3379
|
+
if (thread.quotaContinuedFromId) {
|
|
3380
|
+
if (resumeAt) {
|
|
3381
|
+
return {
|
|
3382
|
+
action: "wait_reset",
|
|
3383
|
+
reason: "Already continued once; waiting for quota reset instead.",
|
|
3384
|
+
limitText,
|
|
3385
|
+
resumeAt
|
|
3386
|
+
};
|
|
3387
|
+
}
|
|
3388
|
+
return {
|
|
3389
|
+
action: "none",
|
|
3390
|
+
reason: "Already continued once; no parseable reset time.",
|
|
3391
|
+
limitText
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
if (onLimit === "wait_reset") {
|
|
3395
|
+
if (!resumeAt) {
|
|
3396
|
+
return {
|
|
3397
|
+
action: "none",
|
|
3398
|
+
reason: "wait_reset configured but reset time could not be parsed.",
|
|
3399
|
+
limitText
|
|
3400
|
+
};
|
|
3401
|
+
}
|
|
3402
|
+
return {
|
|
3403
|
+
action: "wait_reset",
|
|
3404
|
+
reason: "Settings: wait for quota reset.",
|
|
3405
|
+
limitText,
|
|
3406
|
+
resumeAt
|
|
3407
|
+
};
|
|
3408
|
+
}
|
|
3409
|
+
const preferred = opts?.fallbackAgent ?? orchestrationQuotaFallbackAgent();
|
|
3410
|
+
const fallbackAgent = resolveQuotaFallbackAgent(thread.agent, preferred);
|
|
3411
|
+
return {
|
|
3412
|
+
action: "switch_agent",
|
|
3413
|
+
reason: `Continue on ${fallbackAgent} (Auto) after ${thread.agent} session limit.`,
|
|
3414
|
+
limitText,
|
|
3415
|
+
fallbackAgent
|
|
3416
|
+
};
|
|
3417
|
+
}
|
|
3418
|
+
function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
3419
|
+
const children = listThreads({ includeArchived: false }).filter((t) => t.parentThreadId === from.id && t.status !== "archived").slice(0, 40).map(
|
|
3420
|
+
(t) => `- ${t.title} \xB7 ${t.status} \xB7 ${t.agent} \xB7 sideboard://thread/${t.id}`
|
|
3421
|
+
);
|
|
3422
|
+
const recent = from.messages.slice(-8).map((m) => {
|
|
3423
|
+
const role = m.role === "user" ? "User" : m.role === "agent" ? "Agent" : "Summary";
|
|
3424
|
+
const text = m.text.trim().replace(/\s+/g, " ").slice(0, 280);
|
|
3425
|
+
return text ? `- ${role}: ${text}` : null;
|
|
3426
|
+
}).filter(Boolean);
|
|
3427
|
+
const body = [
|
|
3428
|
+
`# Orchestration handoff`,
|
|
3429
|
+
"",
|
|
3430
|
+
`Previous chat: ${from.title} (\`${from.id}\`) on **${from.agent}** hit a session/usage limit.`,
|
|
3431
|
+
`Limit: ${limitText.trim()}`,
|
|
3432
|
+
`Continuing on **${fallbackAgent}** with Auto model.`,
|
|
3433
|
+
"",
|
|
3434
|
+
`## Goal`,
|
|
3435
|
+
from.sourceRef?.trim() || "(none)",
|
|
3436
|
+
"",
|
|
3437
|
+
`## Child threads`,
|
|
3438
|
+
children.length ? children.join("\n") : "(none listed \u2014 call list_threads)",
|
|
3439
|
+
"",
|
|
3440
|
+
`## Recent turns (truncated)`,
|
|
3441
|
+
recent.length ? recent.join("\n") : "(none)",
|
|
3442
|
+
"",
|
|
3443
|
+
`## Instructions`,
|
|
3444
|
+
`- Continue fleet orchestration from this handoff.`,
|
|
3445
|
+
`- Prefer Sideboard MCP (list_threads, get_thread, send_to_thread, \u2026) for live status.`,
|
|
3446
|
+
`- Leave model Auto unless there is a specific reason to pin one.`,
|
|
3447
|
+
`- Do not wait on the limited ${from.agent} account; keep going on ${fallbackAgent}.`
|
|
3448
|
+
].join("\n");
|
|
3449
|
+
return {
|
|
3450
|
+
id: randomUUID4(),
|
|
3451
|
+
name: "Orchestration quota handoff.md",
|
|
3452
|
+
kind: "transcript",
|
|
3453
|
+
content: body
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
var QUOTA_CONTINUE_PROMPT = (fromAgent, fallback) => [
|
|
3457
|
+
`${fromAgent} hit a session/usage limit. Continue this orchestration on ${fallback} using the attached handoff.`,
|
|
3458
|
+
"Call list_threads for live fleet status, then proceed with the goal. Leave model Auto unless needed."
|
|
3459
|
+
].join(" ");
|
|
3460
|
+
var QUOTA_RESUME_PROMPT = "Session/usage limit window should have reset. Continue the orchestration from where you left off. Use list_threads for fleet status.";
|
|
3461
|
+
function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
3462
|
+
const handoff = buildQuotaHandoffAttachment(from, limitText, fallbackAgent);
|
|
3463
|
+
const tab = createChatTab({
|
|
3464
|
+
fromThreadId: from.id,
|
|
3465
|
+
agent: fallbackAgent,
|
|
3466
|
+
model: null,
|
|
3467
|
+
attachments: [handoff]
|
|
3468
|
+
});
|
|
3469
|
+
return updateThread(tab.id, {
|
|
3470
|
+
parentThreadId: from.id,
|
|
3471
|
+
quotaContinuedFromId: from.id,
|
|
3472
|
+
sourceRef: from.sourceRef,
|
|
3473
|
+
sourceType: "orchestration"
|
|
3474
|
+
});
|
|
3475
|
+
}
|
|
3295
3476
|
|
|
3296
3477
|
// src/threads/sync-branch.ts
|
|
3297
3478
|
async function syncThreadBranchFromGit(threadId) {
|
|
@@ -3353,6 +3534,8 @@ var Orchestrator = class {
|
|
|
3353
3534
|
haltDrain = /* @__PURE__ */ new Set();
|
|
3354
3535
|
/** WIP snapshot SHA at the start of the latest agent turn (per thread). */
|
|
3355
3536
|
turnBaselines = /* @__PURE__ */ new Map();
|
|
3537
|
+
/** Timers for orchestration session-quota auto-resume. */
|
|
3538
|
+
quotaResumeTimers = /* @__PURE__ */ new Map();
|
|
3356
3539
|
maxConcurrent;
|
|
3357
3540
|
runningCount = 0;
|
|
3358
3541
|
constructor(opts) {
|
|
@@ -3402,7 +3585,7 @@ var Orchestrator = class {
|
|
|
3402
3585
|
}
|
|
3403
3586
|
continue;
|
|
3404
3587
|
}
|
|
3405
|
-
if (!
|
|
3588
|
+
if (!existsSync12(thread.worktreePath)) {
|
|
3406
3589
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
3407
3590
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
3408
3591
|
continue;
|
|
@@ -3421,7 +3604,7 @@ var Orchestrator = class {
|
|
|
3421
3604
|
orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
|
|
3422
3605
|
});
|
|
3423
3606
|
}
|
|
3424
|
-
const { autoCleanupOrphansEnabled } = await import("./app-settings-
|
|
3607
|
+
const { autoCleanupOrphansEnabled } = await import("./app-settings-LYGVDGZY.js");
|
|
3425
3608
|
if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
|
|
3426
3609
|
await cleanupOrphanWorktrees({ repoPaths });
|
|
3427
3610
|
}
|
|
@@ -3432,6 +3615,113 @@ var Orchestrator = class {
|
|
|
3432
3615
|
void this.drainQueue(thread.id);
|
|
3433
3616
|
}
|
|
3434
3617
|
}
|
|
3618
|
+
this.schedulePendingQuotaResumes();
|
|
3619
|
+
}
|
|
3620
|
+
clearQuotaResumeTimer(threadId) {
|
|
3621
|
+
const timer = this.quotaResumeTimers.get(threadId);
|
|
3622
|
+
if (timer) clearTimeout(timer);
|
|
3623
|
+
this.quotaResumeTimers.delete(threadId);
|
|
3624
|
+
}
|
|
3625
|
+
/** Schedule (or fire) auto-retry after a provider session/usage limit reset. */
|
|
3626
|
+
scheduleQuotaResume(threadId, resumeAt) {
|
|
3627
|
+
this.clearQuotaResumeTimer(threadId);
|
|
3628
|
+
updateThread(threadId, { quotaResumeAt: resumeAt.toISOString() });
|
|
3629
|
+
const delay = Math.max(5e3, resumeAt.getTime() - Date.now());
|
|
3630
|
+
const capped = Math.min(delay, 2147483647);
|
|
3631
|
+
const timer = setTimeout(() => {
|
|
3632
|
+
this.quotaResumeTimers.delete(threadId);
|
|
3633
|
+
void this.resumeAfterQuotaWait(threadId);
|
|
3634
|
+
}, capped);
|
|
3635
|
+
this.quotaResumeTimers.set(threadId, timer);
|
|
3636
|
+
}
|
|
3637
|
+
schedulePendingQuotaResumes() {
|
|
3638
|
+
for (const thread of listThreads({ includeArchived: false })) {
|
|
3639
|
+
if (!thread.quotaResumeAt) continue;
|
|
3640
|
+
const at = new Date(thread.quotaResumeAt);
|
|
3641
|
+
if (Number.isNaN(at.getTime())) continue;
|
|
3642
|
+
if (at.getTime() <= Date.now()) {
|
|
3643
|
+
void this.resumeAfterQuotaWait(thread.id);
|
|
3644
|
+
} else if (!this.quotaResumeTimers.has(thread.id)) {
|
|
3645
|
+
this.scheduleQuotaResume(thread.id, at);
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
async resumeAfterQuotaWait(threadId) {
|
|
3650
|
+
const thread = findThreadByRef(threadId);
|
|
3651
|
+
if (!thread || thread.status === "archived") return;
|
|
3652
|
+
this.clearQuotaResumeTimer(threadId);
|
|
3653
|
+
try {
|
|
3654
|
+
updateThread(threadId, { quotaResumeAt: null });
|
|
3655
|
+
} catch {
|
|
3656
|
+
return;
|
|
3657
|
+
}
|
|
3658
|
+
if (thread.status === "running" || this.activeTurns.has(threadId) || this.startingTurns.has(threadId)) {
|
|
3659
|
+
return;
|
|
3660
|
+
}
|
|
3661
|
+
await this.send(threadId, QUOTA_RESUME_PROMPT);
|
|
3662
|
+
}
|
|
3663
|
+
/**
|
|
3664
|
+
* Host-side continue when an orchestration chat hits a provider session/usage
|
|
3665
|
+
* limit (not context size): switch agent (Auto) or wait until reset.
|
|
3666
|
+
*/
|
|
3667
|
+
async maybeHandleOrchestrationQuotaFailover(threadId, limitText) {
|
|
3668
|
+
const thread = findThreadByRef(threadId);
|
|
3669
|
+
if (!thread) return;
|
|
3670
|
+
const plan = planOrchestrationQuotaFailover(thread, limitText);
|
|
3671
|
+
if (!plan || plan.action === "none") return;
|
|
3672
|
+
if (plan.action === "wait_reset" && plan.resumeAt) {
|
|
3673
|
+
this.haltDrain.add(threadId);
|
|
3674
|
+
this.scheduleQuotaResume(threadId, plan.resumeAt);
|
|
3675
|
+
setStatus(threadId, "idle", null);
|
|
3676
|
+
appendMessage(threadId, {
|
|
3677
|
+
role: "agent",
|
|
3678
|
+
text: `Sideboard will auto-retry this orchestration around ${plan.resumeAt.toLocaleString()} when the session limit resets.`,
|
|
3679
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
3680
|
+
});
|
|
3681
|
+
this.emit({
|
|
3682
|
+
type: "quota_failover",
|
|
3683
|
+
threadId,
|
|
3684
|
+
action: "wait_reset",
|
|
3685
|
+
message: plan.reason,
|
|
3686
|
+
resumeAt: plan.resumeAt.toISOString()
|
|
3687
|
+
});
|
|
3688
|
+
this.emit({ type: "status_changed", threadId, status: "idle" });
|
|
3689
|
+
return;
|
|
3690
|
+
}
|
|
3691
|
+
if (plan.action === "switch_agent" && plan.fallbackAgent) {
|
|
3692
|
+
this.haltDrain.add(threadId);
|
|
3693
|
+
const next = createQuotaFailoverChat(
|
|
3694
|
+
thread,
|
|
3695
|
+
plan.fallbackAgent,
|
|
3696
|
+
plan.limitText
|
|
3697
|
+
);
|
|
3698
|
+
this.clearQuotaResumeTimer(threadId);
|
|
3699
|
+
try {
|
|
3700
|
+
updateThread(threadId, { quotaResumeAt: null });
|
|
3701
|
+
} catch {
|
|
3702
|
+
}
|
|
3703
|
+
appendMessage(threadId, {
|
|
3704
|
+
role: "agent",
|
|
3705
|
+
text: `Session limit on ${thread.agent}. Sideboard continued on ${plan.fallbackAgent} (Auto) in [${next.title}](sideboard://thread/${next.id}).`,
|
|
3706
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
3707
|
+
});
|
|
3708
|
+
this.emit({
|
|
3709
|
+
type: "quota_failover",
|
|
3710
|
+
threadId,
|
|
3711
|
+
action: "switch_agent",
|
|
3712
|
+
toThreadId: next.id,
|
|
3713
|
+
message: plan.reason
|
|
3714
|
+
});
|
|
3715
|
+
this.emit({
|
|
3716
|
+
type: "status_changed",
|
|
3717
|
+
threadId: next.id,
|
|
3718
|
+
status: next.status
|
|
3719
|
+
});
|
|
3720
|
+
await this.send(
|
|
3721
|
+
next.id,
|
|
3722
|
+
QUOTA_CONTINUE_PROMPT(thread.agent, plan.fallbackAgent)
|
|
3723
|
+
);
|
|
3724
|
+
}
|
|
3435
3725
|
}
|
|
3436
3726
|
getThreads(includeArchived = false) {
|
|
3437
3727
|
return listThreads({ includeArchived });
|
|
@@ -3448,7 +3738,7 @@ var Orchestrator = class {
|
|
|
3448
3738
|
});
|
|
3449
3739
|
});
|
|
3450
3740
|
this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
|
|
3451
|
-
const { autoRunAfterSetupEnabled } = await import("./app-settings-
|
|
3741
|
+
const { autoRunAfterSetupEnabled } = await import("./app-settings-LYGVDGZY.js");
|
|
3452
3742
|
if (autoRunAfterSetupEnabled()) {
|
|
3453
3743
|
try {
|
|
3454
3744
|
await this.startDev(thread.id);
|
|
@@ -3694,7 +3984,7 @@ var Orchestrator = class {
|
|
|
3694
3984
|
});
|
|
3695
3985
|
const artifactDirective = isBrightsy ? null : formatArtifactDirective();
|
|
3696
3986
|
const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
|
|
3697
|
-
const { autoRenameBranchEnabled } = await import("./app-settings-
|
|
3987
|
+
const { autoRenameBranchEnabled } = await import("./app-settings-LYGVDGZY.js");
|
|
3698
3988
|
const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled() ? formatRenameBranchDirective(fresh, {
|
|
3699
3989
|
customPrompt: settings?.prompts?.renameBranch
|
|
3700
3990
|
}) : null;
|
|
@@ -3790,11 +4080,16 @@ var Orchestrator = class {
|
|
|
3790
4080
|
}
|
|
3791
4081
|
}
|
|
3792
4082
|
}
|
|
3793
|
-
const
|
|
3794
|
-
|
|
4083
|
+
const lastStderr = summarizeTurnStderr(stderrTail);
|
|
4084
|
+
const detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
4085
|
+
let chatText = assistantText;
|
|
4086
|
+
if (exitCode !== 0 && !chatText && looksLikeAgentFailureMessage(detail)) {
|
|
4087
|
+
chatText = humanizeAgentFailDetail(detail);
|
|
4088
|
+
}
|
|
4089
|
+
if (chatText || parts.length > 0) {
|
|
3795
4090
|
appendMessage(threadId, {
|
|
3796
4091
|
role: "agent",
|
|
3797
|
-
text:
|
|
4092
|
+
text: chatText,
|
|
3798
4093
|
parts: parts.length > 0 ? parts : void 0,
|
|
3799
4094
|
durationMs: Math.max(0, Date.now() - turnStartedAt),
|
|
3800
4095
|
usage,
|
|
@@ -3813,13 +4108,12 @@ var Orchestrator = class {
|
|
|
3813
4108
|
this.emit({ type: "status_changed", threadId, status: "stopped" });
|
|
3814
4109
|
this.emit({ type: "turn_finished", threadId, exitCode });
|
|
3815
4110
|
} else {
|
|
3816
|
-
const lastStderr = summarizeTurnStderr(stderrTail);
|
|
3817
|
-
const detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
3818
4111
|
const failDetail = formatTurnExitError(exitCode, detail);
|
|
4112
|
+
const explainedInChat = exitCode !== 0 && Boolean(chatText) && (looksLikeAgentFailureMessage(chatText) || failDetail && chatText.includes(failDetail.replace(/^exit\s*\d+:\s*/i, "").trim()));
|
|
3819
4113
|
setStatus(
|
|
3820
4114
|
threadId,
|
|
3821
4115
|
exitCode === 0 ? "idle" : "error",
|
|
3822
|
-
exitCode === 0 ? null : failDetail
|
|
4116
|
+
exitCode === 0 || explainedInChat ? null : failDetail
|
|
3823
4117
|
);
|
|
3824
4118
|
this.emit({
|
|
3825
4119
|
type: "status_changed",
|
|
@@ -3827,6 +4121,10 @@ var Orchestrator = class {
|
|
|
3827
4121
|
status: exitCode === 0 ? "idle" : "error"
|
|
3828
4122
|
});
|
|
3829
4123
|
this.emit({ type: "turn_finished", threadId, exitCode });
|
|
4124
|
+
if (exitCode !== 0) {
|
|
4125
|
+
const blob = [chatText, detail].filter(Boolean).join("\n");
|
|
4126
|
+
void this.maybeHandleOrchestrationQuotaFailover(threadId, blob);
|
|
4127
|
+
}
|
|
3830
4128
|
}
|
|
3831
4129
|
} catch (err) {
|
|
3832
4130
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -3840,6 +4138,7 @@ var Orchestrator = class {
|
|
|
3840
4138
|
this.emit({ type: "error", threadId, message });
|
|
3841
4139
|
this.emit({ type: "status_changed", threadId, status: "error" });
|
|
3842
4140
|
this.emit({ type: "turn_finished", threadId, exitCode: 1 });
|
|
4141
|
+
void this.maybeHandleOrchestrationQuotaFailover(threadId, message);
|
|
3843
4142
|
}
|
|
3844
4143
|
} finally {
|
|
3845
4144
|
this.startingTurns.delete(threadId);
|
|
@@ -4300,6 +4599,15 @@ var Orchestrator = class {
|
|
|
4300
4599
|
setAutonomy(threadRef, autonomy) {
|
|
4301
4600
|
return this.setThreadOptions(threadRef, { autonomy });
|
|
4302
4601
|
}
|
|
4602
|
+
/**
|
|
4603
|
+
* Open a Review chat tab on a worktree thread (same as the desktop Review button)
|
|
4604
|
+
* and send the merge-readiness prefill.
|
|
4605
|
+
*/
|
|
4606
|
+
async requestReview(threadRef) {
|
|
4607
|
+
const { tab } = await requestReview(threadRef, (ref, prompt) => this.send(ref, prompt));
|
|
4608
|
+
this.emit({ type: "status_changed", threadId: tab.id, status: tab.status });
|
|
4609
|
+
return tab;
|
|
4610
|
+
}
|
|
4303
4611
|
setThreadOptions(threadRef, patch) {
|
|
4304
4612
|
const thread = this.requireThread(threadRef);
|
|
4305
4613
|
const next = {};
|
|
@@ -4406,7 +4714,7 @@ var Orchestrator = class {
|
|
|
4406
4714
|
await runArchiveScript(thread.repoPath, thread.worktreePath);
|
|
4407
4715
|
} catch {
|
|
4408
4716
|
}
|
|
4409
|
-
const { deleteBranchOnPurgeEnabled } = await import("./app-settings-
|
|
4717
|
+
const { deleteBranchOnPurgeEnabled } = await import("./app-settings-LYGVDGZY.js");
|
|
4410
4718
|
const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
|
|
4411
4719
|
await removeWorktree(thread.repoPath, thread.worktreePath, {
|
|
4412
4720
|
deleteBranch: deleteBranch ? thread.branchName : void 0
|
|
@@ -4424,8 +4732,8 @@ var Orchestrator = class {
|
|
|
4424
4732
|
updateThread(thread.id, { worktreePath: globalAgentCwd() });
|
|
4425
4733
|
return setStatus(thread.id, "idle");
|
|
4426
4734
|
}
|
|
4427
|
-
if (!
|
|
4428
|
-
const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-
|
|
4735
|
+
if (!existsSync12(thread.worktreePath)) {
|
|
4736
|
+
const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-TEDAJ57S.js");
|
|
4429
4737
|
const { execa: execa6 } = await import("execa");
|
|
4430
4738
|
const slug = thread.worktreePath.split("/").pop();
|
|
4431
4739
|
const dest = thread.worktreePath;
|
|
@@ -4510,7 +4818,7 @@ async function startOrchestration(opts) {
|
|
|
4510
4818
|
sourceRef: "default",
|
|
4511
4819
|
...createOpts
|
|
4512
4820
|
}).catch(async () => {
|
|
4513
|
-
const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-
|
|
4821
|
+
const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-TEDAJ57S.js");
|
|
4514
4822
|
const repo = await resolveRepoRoot2(repoPath);
|
|
4515
4823
|
const def = await resolveDefaultBranch2(repo);
|
|
4516
4824
|
return createThread({
|
|
@@ -4520,7 +4828,7 @@ async function startOrchestration(opts) {
|
|
|
4520
4828
|
repoPath: repo
|
|
4521
4829
|
});
|
|
4522
4830
|
});
|
|
4523
|
-
const { updateThread: upd } = await import("./thread-store-
|
|
4831
|
+
const { updateThread: upd } = await import("./thread-store-OV2X6PYO.js");
|
|
4524
4832
|
const updated = upd(thread.id, {
|
|
4525
4833
|
sourceType: "orchestration",
|
|
4526
4834
|
sourceRef: goal
|
|
@@ -4923,6 +5231,148 @@ async function startMcpServer() {
|
|
|
4923
5231
|
};
|
|
4924
5232
|
}
|
|
4925
5233
|
);
|
|
5234
|
+
server.tool(
|
|
5235
|
+
"request_review",
|
|
5236
|
+
"Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, optionally attaches .sideboard/attachments/Review request.md when present, and asks for Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn / get_turn_result on the returned review tab id.",
|
|
5237
|
+
{ ref: z.string().describe("Worktree thread id/ref to review") },
|
|
5238
|
+
async ({ ref }) => {
|
|
5239
|
+
try {
|
|
5240
|
+
const tab = await orch.requestReview(ref);
|
|
5241
|
+
const from = orch.getThread(ref);
|
|
5242
|
+
return {
|
|
5243
|
+
content: [
|
|
5244
|
+
{
|
|
5245
|
+
type: "text",
|
|
5246
|
+
text: JSON.stringify({
|
|
5247
|
+
id: tab.id,
|
|
5248
|
+
title: tab.title,
|
|
5249
|
+
status: tab.status,
|
|
5250
|
+
fromThreadId: from?.id ?? ref,
|
|
5251
|
+
link: `sideboard://thread/${tab.id}`
|
|
5252
|
+
})
|
|
5253
|
+
}
|
|
5254
|
+
]
|
|
5255
|
+
};
|
|
5256
|
+
} catch (err) {
|
|
5257
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5258
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
5259
|
+
}
|
|
5260
|
+
}
|
|
5261
|
+
);
|
|
5262
|
+
const agentEnum = z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
5263
|
+
server.tool(
|
|
5264
|
+
"list_models",
|
|
5265
|
+
"List models for an agent. Prefer Auto: do not call this unless you have a reason to pin a specific model (user request, cost/latency, capability). Omit agent to list all.",
|
|
5266
|
+
{
|
|
5267
|
+
agent: agentEnum.optional().describe("Limit to one agent; omit for all")
|
|
5268
|
+
},
|
|
5269
|
+
async ({ agent }) => {
|
|
5270
|
+
try {
|
|
5271
|
+
const catalogs = await listModelsForAgent(agent);
|
|
5272
|
+
return {
|
|
5273
|
+
content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
|
|
5274
|
+
};
|
|
5275
|
+
} catch (err) {
|
|
5276
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5277
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
);
|
|
5281
|
+
server.tool(
|
|
5282
|
+
"fork_worktree",
|
|
5283
|
+
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_thread / wait_for_turn on the returned id.",
|
|
5284
|
+
{
|
|
5285
|
+
ref: z.string().describe("Worktree thread id/ref to fork"),
|
|
5286
|
+
through_index: z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
5287
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
5288
|
+
model: z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
5289
|
+
title: z.string().optional()
|
|
5290
|
+
},
|
|
5291
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
5292
|
+
try {
|
|
5293
|
+
const source = orch.getThread(ref);
|
|
5294
|
+
if (source) await orch.reconcile(source.repoPath);
|
|
5295
|
+
const thread = await orch.forkThreadWorktree({
|
|
5296
|
+
threadId: ref,
|
|
5297
|
+
throughIndex: through_index,
|
|
5298
|
+
agent,
|
|
5299
|
+
model,
|
|
5300
|
+
title
|
|
5301
|
+
});
|
|
5302
|
+
return {
|
|
5303
|
+
content: [
|
|
5304
|
+
{
|
|
5305
|
+
type: "text",
|
|
5306
|
+
text: JSON.stringify({
|
|
5307
|
+
id: thread.id,
|
|
5308
|
+
title: thread.title,
|
|
5309
|
+
status: thread.status,
|
|
5310
|
+
agent: thread.agent,
|
|
5311
|
+
model: thread.model,
|
|
5312
|
+
branchName: thread.branchName,
|
|
5313
|
+
worktreePath: thread.worktreePath,
|
|
5314
|
+
fromThreadId: source?.id ?? ref,
|
|
5315
|
+
link: `sideboard://thread/${thread.id}`
|
|
5316
|
+
})
|
|
5317
|
+
}
|
|
5318
|
+
]
|
|
5319
|
+
};
|
|
5320
|
+
} catch (err) {
|
|
5321
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5322
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
5323
|
+
}
|
|
5324
|
+
}
|
|
5325
|
+
);
|
|
5326
|
+
server.tool(
|
|
5327
|
+
"fork_chat",
|
|
5328
|
+
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Remote coordinators use this to continue an orchestration chat on another agent after session limits. Then send_to_thread / wait_for_turn on the returned id. Use fork_worktree only for worktree agents that need a new git worktree.",
|
|
5329
|
+
{
|
|
5330
|
+
ref: z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
5331
|
+
through_index: z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
5332
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
5333
|
+
model: z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
5334
|
+
title: z.string().optional()
|
|
5335
|
+
},
|
|
5336
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
5337
|
+
try {
|
|
5338
|
+
const source = orch.getThread(ref);
|
|
5339
|
+
if (!source) {
|
|
5340
|
+
return {
|
|
5341
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
5342
|
+
isError: true
|
|
5343
|
+
};
|
|
5344
|
+
}
|
|
5345
|
+
const tab = orch.forkChatTab({
|
|
5346
|
+
threadId: source.id,
|
|
5347
|
+
throughIndex: through_index,
|
|
5348
|
+
agent,
|
|
5349
|
+
model,
|
|
5350
|
+
title
|
|
5351
|
+
});
|
|
5352
|
+
return {
|
|
5353
|
+
content: [
|
|
5354
|
+
{
|
|
5355
|
+
type: "text",
|
|
5356
|
+
text: JSON.stringify({
|
|
5357
|
+
id: tab.id,
|
|
5358
|
+
title: tab.title,
|
|
5359
|
+
status: tab.status,
|
|
5360
|
+
agent: tab.agent,
|
|
5361
|
+
model: tab.model,
|
|
5362
|
+
sourceType: tab.sourceType,
|
|
5363
|
+
worktreePath: tab.worktreePath,
|
|
5364
|
+
fromThreadId: source.id,
|
|
5365
|
+
link: `sideboard://thread/${tab.id}`
|
|
5366
|
+
})
|
|
5367
|
+
}
|
|
5368
|
+
]
|
|
5369
|
+
};
|
|
5370
|
+
} catch (err) {
|
|
5371
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5372
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
5373
|
+
}
|
|
5374
|
+
}
|
|
5375
|
+
);
|
|
4926
5376
|
server.tool(
|
|
4927
5377
|
"run_dev_script",
|
|
4928
5378
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
@@ -5203,6 +5653,12 @@ export {
|
|
|
5203
5653
|
worktreeCleanupSettings,
|
|
5204
5654
|
applyThreadIntoMain,
|
|
5205
5655
|
cloneRepoIntoSideboard,
|
|
5656
|
+
REVIEW_REQUEST_PATH,
|
|
5657
|
+
REVIEW_REQUEST_NAME,
|
|
5658
|
+
REVIEW_REQUEST_PREFILL,
|
|
5659
|
+
buildReviewRequestAttachment,
|
|
5660
|
+
readExistingReviewRequestFile,
|
|
5661
|
+
requestReview,
|
|
5206
5662
|
isPidAlive,
|
|
5207
5663
|
Orchestrator,
|
|
5208
5664
|
getOrchestrator,
|