@quantiya/codevibe-claude-plugin 2.0.42 → 2.0.43
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/.claude-plugin/plugin.json +1 -1
- package/node_modules/@quantiya/codevibe-core/dist/index.js +446 -437
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/class-b-consumer.d.ts +5 -5
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/hook-bridge.d.ts +1 -1
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/local-executor-impl.d.ts +52 -20
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/types.d.ts +7 -2
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/workspace-shadow.d.ts +16 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.js +1190 -359
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/components/InputBar.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/gate-decision-submit.d.ts +26 -5
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/index.d.ts +17 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/non-tty-fallback.d.ts +1 -1
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/quorum-loop.d.ts +223 -51
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/reducer.d.ts +7 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/slash-routes/continuation.d.ts +5 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/task-label.d.ts +25 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/types.d.ts +83 -0
- package/node_modules/@quantiya/codevibe-core/package.json +1 -1
- package/package.json +2 -2
|
@@ -7955,6 +7955,39 @@ function createExplicitTtyExitCoordinator(deps) {
|
|
|
7955
7955
|
// src/orchestration-shell/reducer.ts
|
|
7956
7956
|
var import_ulid = require("ulid");
|
|
7957
7957
|
|
|
7958
|
+
// src/orchestration-shell/task-label.ts
|
|
7959
|
+
var GROUP_GATE_PREFIX = "group:";
|
|
7960
|
+
function shortTaskId(taskId) {
|
|
7961
|
+
return taskId.length > 8 ? taskId.slice(0, 8) : taskId;
|
|
7962
|
+
}
|
|
7963
|
+
function taskLabel(state, taskId) {
|
|
7964
|
+
let direct = state.taskOrdinals.get(taskId);
|
|
7965
|
+
if (direct)
|
|
7966
|
+
return direct.retryOf ? `#${direct.ordinal} retry` : `#${direct.ordinal}`;
|
|
7967
|
+
if (taskId.startsWith(GROUP_GATE_PREFIX)) {
|
|
7968
|
+
let group = state.taskOrdinals.get(taskId.slice(GROUP_GATE_PREFIX.length));
|
|
7969
|
+
if (group) return `#${group.ordinal}`;
|
|
7970
|
+
}
|
|
7971
|
+
let team = state.team;
|
|
7972
|
+
if (team && team.taskGroupId) {
|
|
7973
|
+
let group = state.taskOrdinals.get(team.taskGroupId);
|
|
7974
|
+
if (group) {
|
|
7975
|
+
for (let [trackIndex, track] of team.tracks)
|
|
7976
|
+
if (track.taskId === taskId) return `#${group.ordinal}.${trackIndex + 1}`;
|
|
7977
|
+
}
|
|
7978
|
+
}
|
|
7979
|
+
return shortTaskId(taskId);
|
|
7980
|
+
}
|
|
7981
|
+
function taskIdForOrdinal(state, ordinal) {
|
|
7982
|
+
for (let [id, record] of state.taskOrdinals)
|
|
7983
|
+
if (record.ordinal === ordinal) return id;
|
|
7984
|
+
return null;
|
|
7985
|
+
}
|
|
7986
|
+
function endedTaskAdvisory(described, taskId) {
|
|
7987
|
+
let ended = described.find((t) => t.taskId === taskId && t.outcome === "discarded");
|
|
7988
|
+
return ended ? `Task ${ended.label} ended on this side \u2014 its workspace copy was discarded; accepting this review applies nothing. Choose Abort (or Cancel) and re-run the request.` : null;
|
|
7989
|
+
}
|
|
7990
|
+
|
|
7958
7991
|
// src/orchestration-shell/web/sanitize.ts
|
|
7959
7992
|
function sanitizeForTerminal(input) {
|
|
7960
7993
|
if (!input) return "";
|
|
@@ -8270,6 +8303,23 @@ function liveLabel(event) {
|
|
|
8270
8303
|
// src/orchestration-shell/reducer.ts
|
|
8271
8304
|
var CONVERSATION_BUFFER_MAX = 500;
|
|
8272
8305
|
function reducer(state, action) {
|
|
8306
|
+
return normalizeActiveGatePrompt(reduceAction(state, action));
|
|
8307
|
+
}
|
|
8308
|
+
function isAnswerableGatePrompt(entry) {
|
|
8309
|
+
if (entry.kind !== "gate-prompt" || entry.final !== !1) return !1;
|
|
8310
|
+
let phase = entry.uiState.phase;
|
|
8311
|
+
return phase === "awaiting-number" || phase === "awaiting-notes";
|
|
8312
|
+
}
|
|
8313
|
+
function normalizeActiveGatePrompt(state) {
|
|
8314
|
+
let current = state.activeGatePromptId;
|
|
8315
|
+
if (current !== null) {
|
|
8316
|
+
let entry = state.conversation.find((e) => e.id === current);
|
|
8317
|
+
if (entry && isAnswerableGatePrompt(entry)) return state;
|
|
8318
|
+
}
|
|
8319
|
+
let next = state.conversation.find(isAnswerableGatePrompt)?.id ?? null;
|
|
8320
|
+
return next === current ? state : { ...state, activeGatePromptId: next };
|
|
8321
|
+
}
|
|
8322
|
+
function reduceAction(state, action) {
|
|
8273
8323
|
switch (action.type) {
|
|
8274
8324
|
case "USER_INPUT":
|
|
8275
8325
|
return reduceUserInput(state, action.text, action.attachments, action.imagePaths);
|
|
@@ -8302,6 +8352,16 @@ function reducer(state, action) {
|
|
|
8302
8352
|
return reducePlannerOfferPresented(state, action.offer);
|
|
8303
8353
|
case "CLEAR_PENDING_PLANNER_OFFER":
|
|
8304
8354
|
return { ...state, pendingPlannerOffer: null };
|
|
8355
|
+
case "TASK_ORDINAL_ASSIGNED":
|
|
8356
|
+
return reduceTaskOrdinalAssigned(state, action.taskId, action.kind, action.retryOf);
|
|
8357
|
+
case "APPLY_CONFLICT_PRESENTED":
|
|
8358
|
+
return reduceApplyConflictPresented(state, action.menu);
|
|
8359
|
+
case "CLEAR_PENDING_APPLY_CONFLICT": {
|
|
8360
|
+
if (action.taskId === void 0) return { ...state, pendingApplyConflicts: /* @__PURE__ */ new Map() };
|
|
8361
|
+
if (!state.pendingApplyConflicts.has(action.taskId)) return state;
|
|
8362
|
+
let rest = new Map(state.pendingApplyConflicts);
|
|
8363
|
+
return rest.delete(action.taskId), { ...state, pendingApplyConflicts: rest };
|
|
8364
|
+
}
|
|
8305
8365
|
case "STRUCTURAL_SUMMARY_GENERATED":
|
|
8306
8366
|
return {
|
|
8307
8367
|
...state,
|
|
@@ -8545,13 +8605,13 @@ function reduceReviewerStatusLine(state, event) {
|
|
|
8545
8605
|
function reduceTaskProgress(state, event) {
|
|
8546
8606
|
let s = state;
|
|
8547
8607
|
if (MILESTONE_SCROLLBACK_PHASES.has(event.phase)) {
|
|
8548
|
-
let milestone = {
|
|
8608
|
+
let milestoneTaskId = "taskId" in event && typeof event.taskId == "string" ? event.taskId : null, milestoneLabel = milestoneTaskId ? taskLabel(state, milestoneTaskId) : "", milestonePrefix = milestoneLabel.startsWith("#") ? `[Task ${milestoneLabel}] ` : "", milestone = {
|
|
8549
8609
|
kind: "advisory",
|
|
8550
8610
|
id: (0, import_ulid.ulid)(),
|
|
8551
8611
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8552
8612
|
final: !0,
|
|
8553
8613
|
source: "shell",
|
|
8554
|
-
text: renderProgressLine(event)
|
|
8614
|
+
text: `${milestonePrefix}${renderProgressLine(event)}`,
|
|
8555
8615
|
// R7 A4 (Stage-2 r4 Codex MED) — the halt_notice echo is DESKTOP-ONLY by
|
|
8556
8616
|
// contract: `localOnly` keeps it out of the turn mirror + handoff briefs
|
|
8557
8617
|
// (index.ts outbound-context filters), so operational halt text can never
|
|
@@ -8732,6 +8792,43 @@ function reducePlannerOfferPresented(state, offer) {
|
|
|
8732
8792
|
pendingClarification: null
|
|
8733
8793
|
};
|
|
8734
8794
|
}
|
|
8795
|
+
function reduceTaskOrdinalAssigned(state, taskId, kind, retryOf) {
|
|
8796
|
+
if (state.taskOrdinals.has(taskId)) return state;
|
|
8797
|
+
let taskOrdinals = new Map(state.taskOrdinals);
|
|
8798
|
+
return taskOrdinals.set(taskId, {
|
|
8799
|
+
ordinal: state.nextTaskOrdinal,
|
|
8800
|
+
kind,
|
|
8801
|
+
...retryOf ? { retryOf } : {}
|
|
8802
|
+
}), { ...state, taskOrdinals, nextTaskOrdinal: state.nextTaskOrdinal + 1 };
|
|
8803
|
+
}
|
|
8804
|
+
var APPLY_CONFLICT_MENU_HINT = "Reply with a number 1-2 (or /task retry <n> / /task discard <n> while another review is open); other text closes this menu.";
|
|
8805
|
+
function reduceApplyConflictPresented(state, menu) {
|
|
8806
|
+
let text2 = [
|
|
8807
|
+
...menu.conflicts.map(
|
|
8808
|
+
(c) => ` ${c.path} \u2014 ${c.cause === "session-task" ? `changed by task ${c.byTaskLabel ?? "?"}` : c.cause === "write-error" ? "could not be written" : "changed outside CodeVibe"}`
|
|
8809
|
+
),
|
|
8810
|
+
"",
|
|
8811
|
+
" 1. Retry as a new task \u2014 re-run the request on a fresh snapshot (reviewed and approved again)",
|
|
8812
|
+
" 2. Discard \u2014 keep your files as they are",
|
|
8813
|
+
"",
|
|
8814
|
+
APPLY_CONFLICT_MENU_HINT
|
|
8815
|
+
].join(`
|
|
8816
|
+
`), advisoryEntry = {
|
|
8817
|
+
kind: "advisory",
|
|
8818
|
+
id: (0, import_ulid.ulid)(),
|
|
8819
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8820
|
+
final: !0,
|
|
8821
|
+
source: "shell",
|
|
8822
|
+
text: text2,
|
|
8823
|
+
// Desktop-only by contract (design §3 C3 / §9): never mirrored, never folded
|
|
8824
|
+
// into a workflow-handoff brief.
|
|
8825
|
+
localOnly: !0
|
|
8826
|
+
}, withEntry = appendConversation(state, advisoryEntry), menus = new Map(state.pendingApplyConflicts);
|
|
8827
|
+
return menus.set(menu.taskId, { ...menu, conversationEntryId: advisoryEntry.id }), {
|
|
8828
|
+
...withEntry,
|
|
8829
|
+
pendingApplyConflicts: menus
|
|
8830
|
+
};
|
|
8831
|
+
}
|
|
8735
8832
|
function reduceEventReceived(state, event, role) {
|
|
8736
8833
|
if (state.conversation.some(
|
|
8737
8834
|
(entry2) => entry2.kind === "subagent-event" && entry2.event.eventId === event.eventId
|
|
@@ -8841,13 +8938,18 @@ function findUnfinalizedGatePromptIndex(state, taskId) {
|
|
|
8841
8938
|
}
|
|
8842
8939
|
return -1;
|
|
8843
8940
|
}
|
|
8844
|
-
function makeGatePromptPanel(envelope) {
|
|
8941
|
+
function makeGatePromptPanel(envelope, state) {
|
|
8942
|
+
let label = state ? taskLabel(state, envelope.taskId) : "";
|
|
8845
8943
|
return {
|
|
8846
8944
|
kind: "gate-panel",
|
|
8847
8945
|
id: (0, import_ulid.ulid)(),
|
|
8848
8946
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8849
8947
|
final: !0,
|
|
8850
|
-
panel: {
|
|
8948
|
+
panel: {
|
|
8949
|
+
variant: "prompt",
|
|
8950
|
+
envelope,
|
|
8951
|
+
...label.startsWith("#") ? { taskLabel: `Task ${label}` } : {}
|
|
8952
|
+
}
|
|
8851
8953
|
};
|
|
8852
8954
|
}
|
|
8853
8955
|
function makeGateSummaryPanel(reviewSummary) {
|
|
@@ -8876,7 +8978,7 @@ function promoteQueuedGatePrompt(state, idx, target) {
|
|
|
8876
8978
|
...state.conversation.slice(idx + 1)
|
|
8877
8979
|
], promoted = appendConversation(
|
|
8878
8980
|
{ ...state, conversation: nextConversation },
|
|
8879
|
-
makeGatePromptPanel(head)
|
|
8981
|
+
makeGatePromptPanel(head, state)
|
|
8880
8982
|
);
|
|
8881
8983
|
return promotedReviewSummary !== void 0 && promotedReviewSummary.rounds.length > 0 && (promoted = appendConversation(promoted, makeGateSummaryPanel(promotedReviewSummary))), promoted;
|
|
8882
8984
|
}
|
|
@@ -8909,7 +9011,7 @@ function reduceGatePromptReceived(state, envelope) {
|
|
|
8909
9011
|
queue: [],
|
|
8910
9012
|
uiState: durableResolution ? { phase: "resolved", resolvedBy: "durable", resolution: durableResolution } : isClaimed ? { phase: "claimed", decisionClaimId: state.gateActionClaims.get(envelope.gateId) ?? null } : { phase: "awaiting-number" }
|
|
8911
9013
|
}, withControl = appendConversation(state, entry);
|
|
8912
|
-
return appendConversation(withControl, makeGatePromptPanel(envelope));
|
|
9014
|
+
return appendConversation(withControl, makeGatePromptPanel(envelope, state));
|
|
8913
9015
|
}
|
|
8914
9016
|
function reduceGatePromptDecisionFenced(state, promptEntryId, gateId, decisionAttemptId, canonicalRequestHash, decisionClaimId) {
|
|
8915
9017
|
let idx = state.conversation.findIndex(
|
|
@@ -9326,6 +9428,12 @@ function buildInitialState(args) {
|
|
|
9326
9428
|
// A1a #3/#4: the lightweight local interactive offer. null until the shell
|
|
9327
9429
|
// presents a team-decomposition / build-transition offer.
|
|
9328
9430
|
pendingPlannerOffer: null,
|
|
9431
|
+
// P46a D1/C3: in-session task numbers (one counter, starts at 1) + the
|
|
9432
|
+
// desktop-local apply-conflict menu (null until a promote collides).
|
|
9433
|
+
taskOrdinals: /* @__PURE__ */ new Map(),
|
|
9434
|
+
nextTaskOrdinal: 1,
|
|
9435
|
+
pendingApplyConflicts: /* @__PURE__ */ new Map(),
|
|
9436
|
+
activeGatePromptId: null,
|
|
9329
9437
|
inputHistory: [],
|
|
9330
9438
|
// CP-1.d §4.8 LOCK: structuralSummary stays null until session-start
|
|
9331
9439
|
// generation completes; structuralSummaryError populated on failure.
|
|
@@ -10687,8 +10795,8 @@ function formatHaltReason3(reason) {
|
|
|
10687
10795
|
function formatPromptKind2(promptKind) {
|
|
10688
10796
|
return promptKind === ORCHESTRATION_ESCALATED_GATE_PROMPT_KIND ? "Escalated review" : promptKind === ORCHESTRATION_FINAL_APPROVAL_PROMPT_KIND ? "Final approval" : promptKind === CONTINUATION_OFFER_HANDOFF_PROMPT_KIND ? "Continue with which agent?" : promptKind;
|
|
10689
10797
|
}
|
|
10690
|
-
function renderPromptVariant(envelope) {
|
|
10691
|
-
let { ink } = getInkRuntime(), { Box, Text } = ink, haltReasonText = envelope.promptKind === ORCHESTRATION_ESCALATED_GATE_PROMPT_KIND ? formatHaltReason3(envelope.reason) : null, kindLabel = formatPromptKind2(envelope.promptKind), children = [], context = GateContextPanel({ envelope });
|
|
10798
|
+
function renderPromptVariant(envelope, taskLabel2) {
|
|
10799
|
+
let { ink } = getInkRuntime(), { Box, Text } = ink, haltReasonText = envelope.promptKind === ORCHESTRATION_ESCALATED_GATE_PROMPT_KIND ? formatHaltReason3(envelope.reason) : null, kindLabel = taskLabel2 ? `${formatPromptKind2(envelope.promptKind)} \xB7 ${taskLabel2}` : formatPromptKind2(envelope.promptKind), children = [], context = GateContextPanel({ envelope });
|
|
10692
10800
|
context && children.push(
|
|
10693
10801
|
React11.createElement(
|
|
10694
10802
|
Box,
|
|
@@ -10758,7 +10866,7 @@ function renderPromptVariant(envelope) {
|
|
|
10758
10866
|
}
|
|
10759
10867
|
function GatePanelEntry(props) {
|
|
10760
10868
|
let { panel } = props.entry;
|
|
10761
|
-
return panel.variant === "prompt" ? renderPromptVariant(panel.envelope) : renderReviewSummarySection(panel.reviewSummary);
|
|
10869
|
+
return panel.variant === "prompt" ? renderPromptVariant(panel.envelope, panel.taskLabel) : renderReviewSummarySection(panel.reviewSummary);
|
|
10762
10870
|
}
|
|
10763
10871
|
|
|
10764
10872
|
// src/orchestration-shell/components/ConversationPane.tsx
|
|
@@ -17006,7 +17114,7 @@ function InputBar(props) {
|
|
|
17006
17114
|
)
|
|
17007
17115
|
);
|
|
17008
17116
|
let placeholder = props.placeholder ?? "";
|
|
17009
|
-
gatePromptMode?.kind === "awaiting-number" ? placeholder = `Type a number 1..${gatePromptMode.maxOption}` : gatePromptMode?.kind === "awaiting-notes" && (placeholder = "Type your notes + Enter");
|
|
17117
|
+
gatePromptMode?.kind === "awaiting-number" ? placeholder = gatePromptMode.taskLabel ? `Type a number 1..${gatePromptMode.maxOption} \xB7 ${gatePromptMode.taskLabel}` : `Type a number 1..${gatePromptMode.maxOption}` : gatePromptMode?.kind === "awaiting-notes" && (placeholder = "Type your notes + Enter");
|
|
17010
17118
|
let shown = displayValue(value), cur = clampCursor2(cursor, shown.length), valueWidth = Math.max(1, columns - 6), win = computeInputWindow(shown, cur, valueWidth, scrollRef.current);
|
|
17011
17119
|
scrollRef.current = win.scroll;
|
|
17012
17120
|
let origin = rowOrigin ?? { left: 0, top: 0 };
|
|
@@ -17383,13 +17491,20 @@ function decisionToGroupOutcome(decision) {
|
|
|
17383
17491
|
// src/orchestration-shell/gate-decision-submit.ts
|
|
17384
17492
|
var import_uuid3 = require("uuid");
|
|
17385
17493
|
var defaultDecisionAttemptReceiptStore = new DecisionAttemptReceiptStore();
|
|
17386
|
-
function findActiveGatePromptEntry(conversation) {
|
|
17387
|
-
|
|
17388
|
-
let entry
|
|
17389
|
-
|
|
17390
|
-
|
|
17494
|
+
function findActiveGatePromptEntry(conversation, activeGatePromptId = null) {
|
|
17495
|
+
if (activeGatePromptId !== null) {
|
|
17496
|
+
for (let entry of conversation)
|
|
17497
|
+
if (entry.id === activeGatePromptId && entry.kind === "gate-prompt" && entry.final === !1)
|
|
17498
|
+
return entry;
|
|
17391
17499
|
}
|
|
17392
|
-
|
|
17500
|
+
let oldestOpen = null;
|
|
17501
|
+
for (let entry of conversation) {
|
|
17502
|
+
if (entry.kind !== "gate-prompt" || entry.final !== !1) continue;
|
|
17503
|
+
let phase = entry.uiState.phase;
|
|
17504
|
+
if (phase === "awaiting-number" || phase === "awaiting-notes") return entry;
|
|
17505
|
+
oldestOpen ??= entry;
|
|
17506
|
+
}
|
|
17507
|
+
return oldestOpen;
|
|
17393
17508
|
}
|
|
17394
17509
|
function getStoreSessionId(store) {
|
|
17395
17510
|
return store.getState().session.sessionId;
|
|
@@ -18019,7 +18134,7 @@ var readline = __toESM(require("readline"));
|
|
|
18019
18134
|
function plannerDecisionRenderEnabled(env = process.env) {
|
|
18020
18135
|
return env.CODEVIBE_PLANNER_DEBUG === "1";
|
|
18021
18136
|
}
|
|
18022
|
-
function renderEntryAsLine(entry) {
|
|
18137
|
+
function renderEntryAsLine(entry, labelFor) {
|
|
18023
18138
|
switch (entry.kind) {
|
|
18024
18139
|
case "user-message":
|
|
18025
18140
|
return `> ${entry.text}`;
|
|
@@ -18040,9 +18155,9 @@ function renderEntryAsLine(entry) {
|
|
|
18040
18155
|
case "advisory":
|
|
18041
18156
|
return `[${entry.source}] ${entry.text}`;
|
|
18042
18157
|
case "gate-prompt": {
|
|
18043
|
-
let lines = [], halt = entry.envelope.reason ? ` (${entry.envelope.reason})` : "";
|
|
18158
|
+
let lines = [], halt = entry.envelope.reason ? ` (${entry.envelope.reason})` : "", label = labelFor?.(entry.envelope.taskId);
|
|
18044
18159
|
lines.push(
|
|
18045
|
-
`[gate-prompt ${entry.envelope.promptKind}${halt}] ${entry.envelope.summary ?? ""}`
|
|
18160
|
+
`[gate-prompt ${entry.envelope.promptKind}${halt}]${label ? ` [${label}]` : ""} ${entry.envelope.summary ?? ""}`
|
|
18046
18161
|
);
|
|
18047
18162
|
for (let i = 0; i < entry.envelope.options.length; i++) {
|
|
18048
18163
|
let opt = entry.envelope.options[i];
|
|
@@ -18086,8 +18201,13 @@ async function runLineLogFallback(args) {
|
|
|
18086
18201
|
let newEntries = state.conversation.slice(printedUpTo);
|
|
18087
18202
|
printedUpTo = state.conversation.length;
|
|
18088
18203
|
for (let entry of newEntries)
|
|
18089
|
-
entry.kind !== "gate-panel" && (entry.kind === "planner-decision" && !plannerDecisionRenderEnabled() || out.write(
|
|
18090
|
-
|
|
18204
|
+
entry.kind !== "gate-panel" && (entry.kind === "planner-decision" && !plannerDecisionRenderEnabled() || out.write(
|
|
18205
|
+
renderEntryAsLine(entry, (taskId) => {
|
|
18206
|
+
let label = taskLabel(state, taskId);
|
|
18207
|
+
return label.startsWith("#") ? `Task ${label}` : void 0;
|
|
18208
|
+
}) + `
|
|
18209
|
+
`
|
|
18210
|
+
));
|
|
18091
18211
|
}), rl = readline.createInterface({ input: inp, output: out, terminal: !1 }), onAbort = null;
|
|
18092
18212
|
args.signal && (args.signal.aborted ? rl.close() : (onAbort = () => {
|
|
18093
18213
|
try {
|
|
@@ -18146,13 +18266,32 @@ function buildTrackLabelByTaskId(team) {
|
|
|
18146
18266
|
entry.taskId && map.set(entry.taskId, `Track ${displayTrackNumber(idx)}`);
|
|
18147
18267
|
return map.size > 0 ? map : null;
|
|
18148
18268
|
}
|
|
18149
|
-
function
|
|
18269
|
+
function buildGateLabelByTaskId(state) {
|
|
18270
|
+
let trackLabels = buildTrackLabelByTaskId(state.team), map = new Map(trackLabels ?? []);
|
|
18271
|
+
for (let entry of state.conversation) {
|
|
18272
|
+
if (entry.kind !== "gate-prompt") continue;
|
|
18273
|
+
let id = entry.envelope.taskId;
|
|
18274
|
+
if (map.has(id) && map.get(id).startsWith("Task ")) continue;
|
|
18275
|
+
let label = taskLabel(state, id);
|
|
18276
|
+
if (!label.startsWith("#")) continue;
|
|
18277
|
+
let track = trackLabels?.get(id);
|
|
18278
|
+
map.set(id, track ? `Task ${label} \xB7 ${track}` : `Task ${label}`);
|
|
18279
|
+
}
|
|
18280
|
+
return map.size > 0 ? map : null;
|
|
18281
|
+
}
|
|
18282
|
+
function deriveGatePromptMode(active, gateActionRecoveryBlocked = !1, labelByTaskId = null) {
|
|
18150
18283
|
if (!active) return null;
|
|
18151
18284
|
if (gateActionRecoveryBlocked && active.uiState.phase !== "resolved")
|
|
18152
18285
|
return { kind: "submitting" };
|
|
18153
18286
|
switch (active.uiState.phase) {
|
|
18154
|
-
case "awaiting-number":
|
|
18155
|
-
|
|
18287
|
+
case "awaiting-number": {
|
|
18288
|
+
let taskLabel2 = labelByTaskId?.get(active.envelope.taskId);
|
|
18289
|
+
return {
|
|
18290
|
+
kind: "awaiting-number",
|
|
18291
|
+
maxOption: active.envelope.options.length,
|
|
18292
|
+
...taskLabel2 ? { taskLabel: taskLabel2 } : {}
|
|
18293
|
+
};
|
|
18294
|
+
}
|
|
18156
18295
|
case "awaiting-notes":
|
|
18157
18296
|
return { kind: "awaiting-notes", decisionDraft: active.uiState.decisionDraft };
|
|
18158
18297
|
case "submitting":
|
|
@@ -18184,7 +18323,7 @@ function OrchestrationApp(props) {
|
|
|
18184
18323
|
}, [wizardOpen, isRawModeSupported]);
|
|
18185
18324
|
let staticOrderRef = React18.useRef(null);
|
|
18186
18325
|
staticOrderRef.current || (staticOrderRef.current = makeStaticOrderAccum());
|
|
18187
|
-
let trackLabelByTaskId =
|
|
18326
|
+
let trackLabelByTaskId = buildGateLabelByTaskId(state), staticConversation = plannerDecisionRenderEnabled() ? state.conversation : state.conversation.filter((e) => e.kind !== "planner-decision");
|
|
18188
18327
|
appendNewlyFinal(staticOrderRef.current, staticConversation, (entry) => ({
|
|
18189
18328
|
key: entry.id,
|
|
18190
18329
|
element: renderConversationEntry(entry, trackLabelByTaskId),
|
|
@@ -18195,9 +18334,10 @@ function OrchestrationApp(props) {
|
|
|
18195
18334
|
...staticOrderRef.current.items
|
|
18196
18335
|
], nonFinal = state.conversation.filter(
|
|
18197
18336
|
(e) => !e.final
|
|
18198
|
-
), activeGateEntry = findActiveGatePromptEntry(state.conversation), gatePromptMode = deriveGatePromptMode(
|
|
18337
|
+
), activeGateEntry = findActiveGatePromptEntry(state.conversation, state.activeGatePromptId), gatePromptMode = deriveGatePromptMode(
|
|
18199
18338
|
activeGateEntry,
|
|
18200
|
-
state.gateActionRecoveryBlocked
|
|
18339
|
+
state.gateActionRecoveryBlocked,
|
|
18340
|
+
trackLabelByTaskId
|
|
18201
18341
|
), terminalRows = process.stdout.rows ?? 24, FIXED_CHROME_ROWS = 8, teamReserveRows = state.team ? 5 + state.team.tracks.size : 0, dropdownFits = terminalRows - FIXED_CHROME_ROWS - teamReserveRows >= DROPDOWN_MAX_ROWS + MIN_LIVE_ROWS, dropdownActive = state.progress === null && gatePromptMode === null && state.reviewerWizard === null && dropdownFits, dropdownReserveRows = dropdownActive ? DROPDOWN_MAX_ROWS : 0, wizardReserveRows = state.reviewerWizard ? WIZARD_MAX_ROWS : 0, { rendered: cappedNonFinal, hiddenCount } = capLiveEntries(
|
|
18202
18342
|
nonFinal,
|
|
18203
18343
|
terminalRows,
|
|
@@ -26444,7 +26584,7 @@ async function requestSubcommand(deps, rawTarget) {
|
|
|
26444
26584
|
return { exitCode: 1, stdout: "Continuation request is unavailable in this session." };
|
|
26445
26585
|
let ctx = deps.getActiveRequestContext();
|
|
26446
26586
|
if (!ctx)
|
|
26447
|
-
return { exitCode: 1, stdout: "No active task to hand off." };
|
|
26587
|
+
return { exitCode: 1, stdout: deps.explainNoActiveRequest?.() ?? "No active task to hand off." };
|
|
26448
26588
|
try {
|
|
26449
26589
|
return await deps.request(ctx, targetAgent) ? {
|
|
26450
26590
|
exitCode: 0,
|
|
@@ -31786,8 +31926,8 @@ var HookBridge = class {
|
|
|
31786
31926
|
// EXECUTOR_REFUSAL (Class A audit) and THEN LOCAL_AUTHORITY_REFUSAL (shell-
|
|
31787
31927
|
// visible). Reverse order would race the user-visible message ahead of the
|
|
31788
31928
|
// audit trail. Tests assert the ordering invariant.
|
|
31789
|
-
async bridgeAuthorityRefusal(err, args) {
|
|
31790
|
-
let ctx = this.contextOrNull();
|
|
31929
|
+
async bridgeAuthorityRefusal(err, args, taskCtx) {
|
|
31930
|
+
let ctx = taskCtx ?? this.contextOrNull();
|
|
31791
31931
|
ctx !== null && await this.deps.emitter.emitExecutorRefusal(ctx, {
|
|
31792
31932
|
refusal: err.refusal,
|
|
31793
31933
|
refusedMessageId: args.refusedMessageId
|
|
@@ -32369,7 +32509,7 @@ var ClassBConsumer = class {
|
|
|
32369
32509
|
}
|
|
32370
32510
|
}
|
|
32371
32511
|
async handlePolicyRejection(packet, envelopeTaskId) {
|
|
32372
|
-
let payload = packet.payload, ctx = this.deps.getContext(), hostedRecovery = payload?.recommendedRecovery, recommendedRecovery = hostedRecovery === "reauthorize_locally" || hostedRecovery === "abort_task" || hostedRecovery === "retry_after_resync" ? hostedRecovery : "ask_user";
|
|
32512
|
+
let payload = packet.payload, ctx = envelopeTaskId ? { ...this.deps.getContext(), taskId: envelopeTaskId } : this.deps.getContext(), hostedRecovery = payload?.recommendedRecovery, recommendedRecovery = hostedRecovery === "reauthorize_locally" || hostedRecovery === "abort_task" || hostedRecovery === "retry_after_resync" ? hostedRecovery : "ask_user";
|
|
32373
32513
|
await this.deps.emitter.emitExecutorRefusal(ctx, {
|
|
32374
32514
|
refusal: {
|
|
32375
32515
|
category: "policy_rejection",
|
|
@@ -38624,6 +38764,23 @@ var WorkspaceShadow = class _WorkspaceShadow {
|
|
|
38624
38764
|
)
|
|
38625
38765
|
);
|
|
38626
38766
|
}
|
|
38767
|
+
/**
|
|
38768
|
+
* P46a C1 — the real tree's current content-hash + mode for a workspace-
|
|
38769
|
+
* relative path, read exactly as the promote compare-and-set reads it
|
|
38770
|
+
* (`readRealFileMetadata`; `{ hash: null, mode: null }` when absent). Used to
|
|
38771
|
+
* record a promoted path's post-image and to attribute a later collision.
|
|
38772
|
+
*/
|
|
38773
|
+
async readRealMetadata(rel) {
|
|
38774
|
+
return assertSafeRelativePath(rel), assertNotInternalShadowPath(rel), readRealFileMetadata(this.workingDir, rel, this.workspaceRootAuthority);
|
|
38775
|
+
}
|
|
38776
|
+
/**
|
|
38777
|
+
* P46a C2 — the real tree's current text content for a workspace-relative
|
|
38778
|
+
* path (`null` when absent), for the retry brief's "files changed since your
|
|
38779
|
+
* first attempt" diff. Bounded by `MAX_REVIEW_FILE_BYTES`; never logged.
|
|
38780
|
+
*/
|
|
38781
|
+
async readRealFileText(rel) {
|
|
38782
|
+
return assertSafeRelativePath(rel), assertNotInternalShadowPath(rel), (await readRealFileForManifest(this.workingDir, rel, this.workspaceRootAuthority)).content;
|
|
38783
|
+
}
|
|
38627
38784
|
/** Read the crash-durable reviewed artifact, or null for an unreviewed snapshot. */
|
|
38628
38785
|
async readReviewedDiff() {
|
|
38629
38786
|
return (await this.readStateMarker())?.reviewedDiff?.map((file) => ({ ...file })) ?? null;
|
|
@@ -47534,7 +47691,15 @@ function makeNoopTrackHandle(agentKind) {
|
|
|
47534
47691
|
}
|
|
47535
47692
|
var LocalExecutorImpl = class {
|
|
47536
47693
|
constructor(deps) {
|
|
47537
|
-
|
|
47694
|
+
/**
|
|
47695
|
+
* P46a B2 — PER-TASK authority scopes: each task's TaskAuthorized path scope
|
|
47696
|
+
* merged over the seeded scope (`mergeAuthorityScope`), keyed by task id. A
|
|
47697
|
+
* spawn reads its OWN task's scope, never a union; authority never
|
|
47698
|
+
* accumulates across tasks. Replaces the singleton `this.scope`/`this.taskId`
|
|
47699
|
+
* pair, which made the SECOND concurrent task's spawn run under the FIRST
|
|
47700
|
+
* task's authorized paths (and mis-attributed its audit context).
|
|
47701
|
+
*/
|
|
47702
|
+
this.scopeByTask = /* @__PURE__ */ new Map();
|
|
47538
47703
|
// CP-12 W2.b — per-track registry (idempotent N-track spawn) + a snapshot of
|
|
47539
47704
|
// the most-recent TrackAssigned per track (for state-dir + scope on
|
|
47540
47705
|
// MergeGate). EMPTY for single-track sessions (never touched).
|
|
@@ -47578,7 +47743,7 @@ var LocalExecutorImpl = class {
|
|
|
47578
47743
|
// re-drain from a duplicate without relying on wall-clock resolution.
|
|
47579
47744
|
this._lastMergeGateEmitMs = 0;
|
|
47580
47745
|
this.memberReviewEvidenceByTask = /* @__PURE__ */ new Map();
|
|
47581
|
-
this.sessionId = deps.sessionId, this.substrateEngager = deps.substrateEngager, this.strictBadgeSink = deps.strictBadgeSink, this.
|
|
47746
|
+
this.sessionId = deps.sessionId, this.substrateEngager = deps.substrateEngager, this.strictBadgeSink = deps.strictBadgeSink, this.baseScope = deps.initialScope ?? makeEmptyScope(), this.baseCtx = deps.baseCtx, this.adapter = deps.adapter, this.emitShellEvent = deps.emitShellEvent, this.teamDeps = deps, this.onTeamMergeAuditSummary = deps.onTeamMergeAuditSummary, this.logger = deps.logger ?? { warn: (m, x) => console.warn(m, x) }, this.teamModeEnabled = !!(deps.spawnTrackImplementor && deps.appsyncClient && deps.repoRoot && deps.withSnapshotMergeGateFence), this.teamModeEnabled && typeof this.emitShellEvent == "function" && this.sessionId && this.replayPendingTeamDeliveries().catch((err) => {
|
|
47582
47747
|
this.logger.warn("[LocalExecutorImpl] startup background replay of pending team deliveries failed (non-fatal)", {
|
|
47583
47748
|
err: err?.message
|
|
47584
47749
|
});
|
|
@@ -47606,8 +47771,8 @@ var LocalExecutorImpl = class {
|
|
|
47606
47771
|
)), this.bridge = new HookBridge({
|
|
47607
47772
|
emitter: this.emitter,
|
|
47608
47773
|
emitShellEvent: this.emitShellEvent,
|
|
47609
|
-
getAuthorityScope: () => this.
|
|
47610
|
-
getCurrentTaskId: () =>
|
|
47774
|
+
getAuthorityScope: () => this.baseScope,
|
|
47775
|
+
getCurrentTaskId: () => null,
|
|
47611
47776
|
getContextWithoutTask: () => this.baseCtx,
|
|
47612
47777
|
adapter: this.adapter
|
|
47613
47778
|
}), this.classBConsumer = new ClassBConsumer({
|
|
@@ -47615,7 +47780,7 @@ var LocalExecutorImpl = class {
|
|
|
47615
47780
|
emitShellEvent: this.emitShellEvent,
|
|
47616
47781
|
getContext: () => this.contextOrFallback(),
|
|
47617
47782
|
advanceAuthorityScope: (newScope, taskId) => {
|
|
47618
|
-
this.
|
|
47783
|
+
this.scopeByTask.set(taskId, mergeAuthorityScope(this.baseScope, newScope));
|
|
47619
47784
|
},
|
|
47620
47785
|
notifyPolicyRejection: (detail, category, recommendedRecovery, rejectedTaskId) => {
|
|
47621
47786
|
this.emitShellEvent({
|
|
@@ -47707,19 +47872,20 @@ var LocalExecutorImpl = class {
|
|
|
47707
47872
|
* is responsible for routing to `bridgeAuthorityErrorToShellRefusal` /
|
|
47708
47873
|
* `HookBridge.bridgeAuthorityRefusal` per LOCK #C4-3.
|
|
47709
47874
|
*/
|
|
47710
|
-
async enforceAuthority(action) {
|
|
47875
|
+
async enforceAuthority(action, taskId) {
|
|
47876
|
+
let scope = this.scopeFor(taskId);
|
|
47711
47877
|
switch (action.kind) {
|
|
47712
47878
|
case "Write":
|
|
47713
|
-
await enforceWrite(
|
|
47879
|
+
await enforceWrite(scope, action.path);
|
|
47714
47880
|
return;
|
|
47715
47881
|
case "Read":
|
|
47716
|
-
await enforceRead(
|
|
47882
|
+
await enforceRead(scope, action.path);
|
|
47717
47883
|
return;
|
|
47718
47884
|
case "Network":
|
|
47719
|
-
enforceNetwork(
|
|
47885
|
+
enforceNetwork(scope);
|
|
47720
47886
|
return;
|
|
47721
47887
|
case "Command":
|
|
47722
|
-
enforceCommand(
|
|
47888
|
+
enforceCommand(scope, action.argv);
|
|
47723
47889
|
return;
|
|
47724
47890
|
default: {
|
|
47725
47891
|
let _exhaustive = action;
|
|
@@ -47728,36 +47894,64 @@ var LocalExecutorImpl = class {
|
|
|
47728
47894
|
}
|
|
47729
47895
|
}
|
|
47730
47896
|
/**
|
|
47731
|
-
*
|
|
47897
|
+
* P46a B2 — resolve the authority scope a caller runs under: the task's OWN
|
|
47898
|
+
* merged scope once ITS TaskAuthorized arrived, else the seeded scope (before
|
|
47899
|
+
* authorization, for a task this executor never saw authorized, and for a
|
|
47900
|
+
* task-less caller). Never a union across tasks.
|
|
47901
|
+
*/
|
|
47902
|
+
scopeFor(taskId) {
|
|
47903
|
+
return (taskId ? this.scopeByTask.get(taskId) : void 0) ?? this.baseScope;
|
|
47904
|
+
}
|
|
47905
|
+
/**
|
|
47906
|
+
* Read-only view of the SEEDED authority scope per master §9:1286 (P46a B2:
|
|
47907
|
+
* `authorityScope()` keeps today's value at its only production caller — the
|
|
47908
|
+
* continuation writer's clamp, frozen at wiring — which IS the seeded scope).
|
|
47732
47909
|
* Returns a deep-frozen copy so callers cannot mutate the LE's state.
|
|
47733
47910
|
*/
|
|
47734
47911
|
authorityScope() {
|
|
47735
47912
|
return Object.freeze({
|
|
47736
|
-
writeScopes: [...this.
|
|
47737
|
-
readScopes: [...this.
|
|
47738
|
-
networkAllowed: this.
|
|
47739
|
-
commandAllowlist: [...this.
|
|
47740
|
-
expiresAt: this.
|
|
47913
|
+
writeScopes: [...this.baseScope.writeScopes],
|
|
47914
|
+
readScopes: [...this.baseScope.readScopes],
|
|
47915
|
+
networkAllowed: this.baseScope.networkAllowed,
|
|
47916
|
+
commandAllowlist: [...this.baseScope.commandAllowlist],
|
|
47917
|
+
expiresAt: this.baseScope.expiresAt
|
|
47918
|
+
});
|
|
47919
|
+
}
|
|
47920
|
+
/** P46a B2 (Stage 1 r1 F7) — drop a task's merged scope when the task ends. */
|
|
47921
|
+
forgetTaskScope(taskId) {
|
|
47922
|
+
this.scopeByTask.delete(taskId);
|
|
47923
|
+
}
|
|
47924
|
+
/** P46a B2 — read-only view of ONE task's merged scope (the seeded scope when unauthorized). */
|
|
47925
|
+
authorityScopeForTask(taskId) {
|
|
47926
|
+
let scope = this.scopeFor(taskId);
|
|
47927
|
+
return Object.freeze({
|
|
47928
|
+
writeScopes: [...scope.writeScopes],
|
|
47929
|
+
readScopes: [...scope.readScopes],
|
|
47930
|
+
networkAllowed: scope.networkAllowed,
|
|
47931
|
+
commandAllowlist: [...scope.commandAllowlist],
|
|
47932
|
+
expiresAt: scope.expiresAt
|
|
47741
47933
|
});
|
|
47742
47934
|
}
|
|
47743
47935
|
// --- Authority surface (re-exports through this class for the bound scope) ---
|
|
47744
|
-
|
|
47745
|
-
|
|
47936
|
+
// P46a B2 — each takes an optional task id and resolves the seeded scope
|
|
47937
|
+
// without one (none has a production caller today).
|
|
47938
|
+
enforceCommand(argv, taskId) {
|
|
47939
|
+
enforceCommand(this.scopeFor(taskId), argv);
|
|
47746
47940
|
}
|
|
47747
|
-
enforceNetwork() {
|
|
47748
|
-
enforceNetwork(this.
|
|
47941
|
+
enforceNetwork(taskId) {
|
|
47942
|
+
enforceNetwork(this.scopeFor(taskId));
|
|
47749
47943
|
}
|
|
47750
|
-
async enforceWrite(p) {
|
|
47751
|
-
return enforceWrite(this.
|
|
47944
|
+
async enforceWrite(p, taskId) {
|
|
47945
|
+
return enforceWrite(this.scopeFor(taskId), p);
|
|
47752
47946
|
}
|
|
47753
|
-
async enforceRead(p) {
|
|
47754
|
-
return enforceRead(this.
|
|
47947
|
+
async enforceRead(p, taskId) {
|
|
47948
|
+
return enforceRead(this.scopeFor(taskId), p);
|
|
47755
47949
|
}
|
|
47756
|
-
async safeWriteFile(p, data) {
|
|
47757
|
-
return safeWriteFile(this.
|
|
47950
|
+
async safeWriteFile(p, data, taskId) {
|
|
47951
|
+
return safeWriteFile(this.scopeFor(taskId), p, data);
|
|
47758
47952
|
}
|
|
47759
|
-
async safeReadFile(p, opts) {
|
|
47760
|
-
return safeReadFile(this.
|
|
47953
|
+
async safeReadFile(p, opts, taskId) {
|
|
47954
|
+
return safeReadFile(this.scopeFor(taskId), p, opts);
|
|
47761
47955
|
}
|
|
47762
47956
|
// --- Spawn surface (wires lifecycle hooks to the emitter) ---
|
|
47763
47957
|
// Per master CP-1 §9:1281-1283 — all three spawn methods take full
|
|
@@ -47788,11 +47982,11 @@ var LocalExecutorImpl = class {
|
|
|
47788
47982
|
signal: advisoryAdmission.controller.signal
|
|
47789
47983
|
});
|
|
47790
47984
|
try {
|
|
47791
|
-
let {
|
|
47985
|
+
let spawnTaskScope = this.scopeFor(promptBoundArgs.taskId ?? null), {
|
|
47792
47986
|
args: launchArgs,
|
|
47793
47987
|
onExit,
|
|
47794
47988
|
substrateEngaged
|
|
47795
|
-
} = await this.engageSubstrateForSpawn(promptBoundArgs,
|
|
47989
|
+
} = await this.engageSubstrateForSpawn(promptBoundArgs, spawnTaskScope), outerBoundaryConfines = substrateEngaged || isTrustedContainerBoundary(), finalArgs = {
|
|
47796
47990
|
...launchArgs,
|
|
47797
47991
|
argv: applyOuterBoundarySandbox(
|
|
47798
47992
|
relocateCodexLastMessageForSubstrate(
|
|
@@ -47803,7 +47997,7 @@ var LocalExecutorImpl = class {
|
|
|
47803
47997
|
launchArgs.agentKind ?? this.adapter,
|
|
47804
47998
|
outerBoundaryConfines
|
|
47805
47999
|
)
|
|
47806
|
-
}, spawnScope = outerBoundaryConfines ? withBoundaryConfinedCommandAuthority(
|
|
48000
|
+
}, spawnScope = outerBoundaryConfines ? withBoundaryConfinedCommandAuthority(spawnTaskScope) : spawnTaskScope, handle = await this.spawnWithLifecycle(
|
|
47807
48001
|
finalArgs,
|
|
47808
48002
|
(a) => spawnImplementor(spawnScope, a),
|
|
47809
48003
|
onExit
|
|
@@ -47821,7 +48015,7 @@ var LocalExecutorImpl = class {
|
|
|
47821
48015
|
async spawnHealthProbe(args) {
|
|
47822
48016
|
if (this.workspaceShutdown)
|
|
47823
48017
|
throw new Error("LocalExecutor session shutdown has fenced new health probes");
|
|
47824
|
-
return this.spawnWithLifecycle(args, (a) => spawnHealthProbe(this.
|
|
48018
|
+
return this.spawnWithLifecycle(args, (a) => spawnHealthProbe(this.baseScope, a));
|
|
47825
48019
|
}
|
|
47826
48020
|
/**
|
|
47827
48021
|
* Publish advisory ownership synchronously before the first spawn await.
|
|
@@ -47924,7 +48118,7 @@ var LocalExecutorImpl = class {
|
|
|
47924
48118
|
);
|
|
47925
48119
|
return { args, substrateEngaged: !1 };
|
|
47926
48120
|
}
|
|
47927
|
-
let effectiveTaskId = args.taskId ??
|
|
48121
|
+
let effectiveTaskId = args.taskId ?? null;
|
|
47928
48122
|
if (effectiveTaskId === null) {
|
|
47929
48123
|
if (args.requireConfined)
|
|
47930
48124
|
throw new SpawnConfinementUnavailable(
|
|
@@ -48075,7 +48269,7 @@ var LocalExecutorImpl = class {
|
|
|
48075
48269
|
}
|
|
48076
48270
|
}
|
|
48077
48271
|
async spawnWithLifecycle(args, spawnFn, onSubstrateExit) {
|
|
48078
|
-
let role = args.role, lifecycleAudit = args.lifecycleAudit ?? "active_task", lifecycleTaskId = args.taskId ??
|
|
48272
|
+
let role = args.role, lifecycleAudit = args.lifecycleAudit ?? "active_task", lifecycleTaskId = args.taskId ?? null, lifecycleCtx = lifecycleTaskId ? { taskId: lifecycleTaskId, ...this.baseCtx } : null, substrateCleanupError, substrateCleanupPromise, cleanupSubstrate = async () => {
|
|
48079
48273
|
onSubstrateExit && (substrateCleanupPromise || (substrateCleanupPromise = onSubstrateExit().catch((error) => {
|
|
48080
48274
|
throw substrateCleanupError = error, error;
|
|
48081
48275
|
})), await substrateCleanupPromise);
|
|
@@ -48128,11 +48322,15 @@ var LocalExecutorImpl = class {
|
|
|
48128
48322
|
...err.refusal,
|
|
48129
48323
|
detail: safeDetail
|
|
48130
48324
|
});
|
|
48131
|
-
await this.bridge.bridgeAuthorityRefusal(
|
|
48132
|
-
|
|
48133
|
-
|
|
48134
|
-
|
|
48135
|
-
|
|
48325
|
+
await this.bridge.bridgeAuthorityRefusal(
|
|
48326
|
+
safeError,
|
|
48327
|
+
{
|
|
48328
|
+
refusedMessageId: "spawn:" + role + ":" + Date.now(),
|
|
48329
|
+
shellContent: `Refused spawn (${role}): ${safeDetail}`,
|
|
48330
|
+
shellMetadata: { role, argv: safeArgv }
|
|
48331
|
+
},
|
|
48332
|
+
lifecycleCtx
|
|
48333
|
+
), primary = safeError;
|
|
48136
48334
|
}
|
|
48137
48335
|
throw substrateCleanupError !== void 0 ? Object.assign(
|
|
48138
48336
|
new Error("implementor spawn failed and substrate cleanup also failed"),
|
|
@@ -49531,11 +49729,12 @@ var LocalExecutorImpl = class {
|
|
|
49531
49729
|
];
|
|
49532
49730
|
return {
|
|
49533
49731
|
writeScopes,
|
|
49534
|
-
// Reads cover the shared workspace (
|
|
49535
|
-
|
|
49536
|
-
|
|
49537
|
-
|
|
49538
|
-
|
|
49732
|
+
// Reads cover the shared workspace (the seeded read scopes) + own writes.
|
|
49733
|
+
// P46a B2 — derived from the SEEDED scope, never another task's grants.
|
|
49734
|
+
readScopes: [...this.baseScope.readScopes, ...writeScopes],
|
|
49735
|
+
networkAllowed: this.baseScope.networkAllowed,
|
|
49736
|
+
commandAllowlist: [...this.baseScope.commandAllowlist],
|
|
49737
|
+
expiresAt: this.baseScope.expiresAt
|
|
49539
49738
|
};
|
|
49540
49739
|
}
|
|
49541
49740
|
/**
|
|
@@ -49661,13 +49860,18 @@ var LocalExecutorImpl = class {
|
|
|
49661
49860
|
}
|
|
49662
49861
|
}
|
|
49663
49862
|
// --- Test seams ------------------------------------------------------------
|
|
49664
|
-
/**
|
|
49665
|
-
|
|
49666
|
-
|
|
49667
|
-
|
|
49668
|
-
|
|
49863
|
+
/**
|
|
49864
|
+
* Replace the SEEDED authority scope (test seam; production seeds it at
|
|
49865
|
+
* construction via `initialScope`). P46a B2: per-task scopes are set only by
|
|
49866
|
+
* a TaskAuthorized (`advanceAuthorityScope`) — use `setAuthorityScopeForTask`
|
|
49867
|
+
* to simulate one in tests.
|
|
49868
|
+
*/
|
|
49669
49869
|
setAuthorityScope(scope) {
|
|
49670
|
-
this.
|
|
49870
|
+
this.baseScope = scope;
|
|
49871
|
+
}
|
|
49872
|
+
/** P46a B2 test seam — install ONE task's merged scope as a TaskAuthorized would. */
|
|
49873
|
+
setAuthorityScopeForTask(taskId, scope) {
|
|
49874
|
+
this.scopeByTask.set(taskId, mergeAuthorityScope(this.baseScope, scope));
|
|
49671
49875
|
}
|
|
49672
49876
|
/** Expose emitter for §8.5 integration test introspection. */
|
|
49673
49877
|
get emitterForTests() {
|
|
@@ -49685,21 +49889,18 @@ var LocalExecutorImpl = class {
|
|
|
49685
49889
|
get trackHeartbeatCountForTests() {
|
|
49686
49890
|
return this.trackHeartbeats.size;
|
|
49687
49891
|
}
|
|
49688
|
-
contextOrNull() {
|
|
49689
|
-
return this.taskId === null ? null : { taskId: this.taskId, ...this.baseCtx };
|
|
49690
|
-
}
|
|
49691
49892
|
/**
|
|
49692
49893
|
* Context resolver for ClassBConsumer emit paths (refusals + PolicyRejection).
|
|
49693
|
-
* Class B inbound packets
|
|
49694
|
-
*
|
|
49695
|
-
*
|
|
49696
|
-
* refusal-audit emit
|
|
49697
|
-
*
|
|
49698
|
-
*
|
|
49894
|
+
* Class B inbound packets carry no task the executor could attribute a
|
|
49895
|
+
* refusal to before they are verified (signature_invalid / ulid_replay /
|
|
49896
|
+
* policy_rejection / malformed), and P46a B2 removed the singleton task id,
|
|
49897
|
+
* so the refusal-audit emit uses the deterministic `pending` placeholder —
|
|
49898
|
+
* never another task's id. Spawn-attributed emits resolve their task from
|
|
49899
|
+
* the per-spawn `args.taskId` (`lifecycleCtx`).
|
|
49699
49900
|
*/
|
|
49700
49901
|
contextOrFallback() {
|
|
49701
49902
|
return {
|
|
49702
|
-
taskId:
|
|
49903
|
+
taskId: "pending",
|
|
49703
49904
|
...this.baseCtx
|
|
49704
49905
|
};
|
|
49705
49906
|
}
|
|
@@ -52101,6 +52302,97 @@ function resolveTeamExecution(_override, _env = process.env) {
|
|
|
52101
52302
|
// src/orchestration-shell/quorum-loop.ts
|
|
52102
52303
|
init_env_scrub();
|
|
52103
52304
|
|
|
52305
|
+
// src/credential-broker/scrubber.ts
|
|
52306
|
+
var REDACTION = "[REDACTED-CP7]", KEY_REDACTION = "[REDACTED-CP7-KEY]", SECRET_PATTERNS = [
|
|
52307
|
+
{
|
|
52308
|
+
// PEM private-key block (RSA / EC / OPENSSH / generic PRIVATE KEY).
|
|
52309
|
+
patternClass: "private_key_pem",
|
|
52310
|
+
regex: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |ENCRYPTED )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |DSA |ENCRYPTED )?PRIVATE KEY-----/g
|
|
52311
|
+
},
|
|
52312
|
+
{
|
|
52313
|
+
// Anthropic API key: sk-ant-... .
|
|
52314
|
+
patternClass: "anthropic_api_key",
|
|
52315
|
+
regex: /sk-ant-[A-Za-z0-9_-]{20,}/g
|
|
52316
|
+
},
|
|
52317
|
+
{
|
|
52318
|
+
// OpenAI API key: sk-... or sk-proj-... (>= 20 trailing chars).
|
|
52319
|
+
patternClass: "openai_api_key",
|
|
52320
|
+
regex: /sk-(?:proj-)?[A-Za-z0-9_-]{20,}/g
|
|
52321
|
+
},
|
|
52322
|
+
{
|
|
52323
|
+
// AWS access key id (AKIA / ASIA + 16 uppercase alphanumerics).
|
|
52324
|
+
patternClass: "aws_access_key_id",
|
|
52325
|
+
regex: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g
|
|
52326
|
+
},
|
|
52327
|
+
{
|
|
52328
|
+
// AWS secret access key VALUE assignment — a 40-char base64-ish secret
|
|
52329
|
+
// bound to an `aws_secret_access_key`/`AWS_SECRET_ACCESS_KEY` key. Only
|
|
52330
|
+
// the secret value is redacted, not the assignment label.
|
|
52331
|
+
patternClass: "aws_secret_access_key",
|
|
52332
|
+
regex: /(aws_secret_access_key|AWS_SECRET_ACCESS_KEY)(\s*[=:]\s*["']?)([A-Za-z0-9/+]{40})(["']?)/g
|
|
52333
|
+
},
|
|
52334
|
+
{
|
|
52335
|
+
// GitHub tokens (ghp_/gho_/ghu_/ghs_/ghr_ + 36 chars).
|
|
52336
|
+
patternClass: "github_token",
|
|
52337
|
+
regex: /\bgh[pousr]_[A-Za-z0-9]{36}\b/g
|
|
52338
|
+
},
|
|
52339
|
+
{
|
|
52340
|
+
// Authorization: Bearer <token> embedded in content (a bearer token
|
|
52341
|
+
// riding in a model-bound field). Redacts the token, keeps the scheme.
|
|
52342
|
+
patternClass: "bearer_token",
|
|
52343
|
+
regex: /(Bearer\s+)([A-Za-z0-9._~+/=-]{16,})/g
|
|
52344
|
+
}
|
|
52345
|
+
];
|
|
52346
|
+
function scrubString(value) {
|
|
52347
|
+
return redactSecretShapes(value, REDACTION);
|
|
52348
|
+
}
|
|
52349
|
+
function redactSecretShapes(value, placeholder) {
|
|
52350
|
+
let redacted = value, classes = [];
|
|
52351
|
+
for (let pattern of SECRET_PATTERNS)
|
|
52352
|
+
pattern.regex.lastIndex = 0, pattern.regex.test(redacted) && (pattern.regex.lastIndex = 0, pattern.patternClass === "aws_secret_access_key" ? redacted = redacted.replace(pattern.regex, `$1$2${placeholder}$4`) : pattern.patternClass === "bearer_token" ? redacted = redacted.replace(pattern.regex, `$1${placeholder}`) : redacted = redacted.replace(pattern.regex, placeholder), classes.push(pattern.patternClass));
|
|
52353
|
+
return { redacted, classes };
|
|
52354
|
+
}
|
|
52355
|
+
function redactSecretShapesInText(value, placeholder = "[redacted secret]") {
|
|
52356
|
+
return redactSecretShapes(value, placeholder).redacted;
|
|
52357
|
+
}
|
|
52358
|
+
function keyIsSecret(key) {
|
|
52359
|
+
for (let pattern of SECRET_PATTERNS) {
|
|
52360
|
+
pattern.regex.lastIndex = 0;
|
|
52361
|
+
let hit = pattern.regex.test(key);
|
|
52362
|
+
if (pattern.regex.lastIndex = 0, hit) return !0;
|
|
52363
|
+
}
|
|
52364
|
+
return !1;
|
|
52365
|
+
}
|
|
52366
|
+
function joinPath(base, key) {
|
|
52367
|
+
return typeof key == "number" ? `${base}[${key}]` : /^[A-Za-z_][A-Za-z0-9_]*$/.test(key) ? `${base}.${key}` : `${base}['${key.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}']`;
|
|
52368
|
+
}
|
|
52369
|
+
function scrubValue(value, path75, findings) {
|
|
52370
|
+
if (typeof value == "string") {
|
|
52371
|
+
let { redacted, classes } = scrubString(value);
|
|
52372
|
+
for (let patternClass of classes)
|
|
52373
|
+
findings.push({ field: path75, patternClass });
|
|
52374
|
+
return redacted;
|
|
52375
|
+
}
|
|
52376
|
+
if (Array.isArray(value))
|
|
52377
|
+
return value.map((item, i) => scrubValue(item, joinPath(path75, i), findings));
|
|
52378
|
+
if (value !== null && typeof value == "object") {
|
|
52379
|
+
let out = /* @__PURE__ */ Object.create(null), redactedKeyCount = 0;
|
|
52380
|
+
for (let [k, v] of Object.entries(value))
|
|
52381
|
+
if (keyIsSecret(k)) {
|
|
52382
|
+
redactedKeyCount += 1;
|
|
52383
|
+
let placeholder = `${KEY_REDACTION}-${redactedKeyCount}`, placeholderPath = joinPath(path75, placeholder);
|
|
52384
|
+
findings.push({ field: placeholderPath, patternClass: "secret_object_key" }), out[placeholder] = scrubValue(v, placeholderPath, findings);
|
|
52385
|
+
} else
|
|
52386
|
+
out[k] = scrubValue(v, joinPath(path75, k), findings);
|
|
52387
|
+
return out;
|
|
52388
|
+
}
|
|
52389
|
+
return value;
|
|
52390
|
+
}
|
|
52391
|
+
function scrubRequestBody(body) {
|
|
52392
|
+
let findings = [];
|
|
52393
|
+
return { scrubbed: scrubValue(body, "$", findings), findings };
|
|
52394
|
+
}
|
|
52395
|
+
|
|
52104
52396
|
// src/orchestration-shell/context-items.ts
|
|
52105
52397
|
var fs39 = __toESM(require("fs/promises")), import_fs2 = require("fs"), path57 = __toESM(require("path")), os32 = __toESM(require("os")), crypto27 = __toESM(require("crypto")), import_ulid3 = require("ulid"), import_uuid7 = require("uuid");
|
|
52106
52398
|
init_logger2();
|
|
@@ -53976,97 +54268,6 @@ function renderRepoSliceCompact(repos, maxChars) {
|
|
|
53976
54268
|
var fs40 = __toESM(require("fs/promises")), path58 = __toESM(require("path"));
|
|
53977
54269
|
init_logger2();
|
|
53978
54270
|
|
|
53979
|
-
// src/credential-broker/scrubber.ts
|
|
53980
|
-
var REDACTION = "[REDACTED-CP7]", KEY_REDACTION = "[REDACTED-CP7-KEY]", SECRET_PATTERNS = [
|
|
53981
|
-
{
|
|
53982
|
-
// PEM private-key block (RSA / EC / OPENSSH / generic PRIVATE KEY).
|
|
53983
|
-
patternClass: "private_key_pem",
|
|
53984
|
-
regex: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |ENCRYPTED )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |DSA |ENCRYPTED )?PRIVATE KEY-----/g
|
|
53985
|
-
},
|
|
53986
|
-
{
|
|
53987
|
-
// Anthropic API key: sk-ant-... .
|
|
53988
|
-
patternClass: "anthropic_api_key",
|
|
53989
|
-
regex: /sk-ant-[A-Za-z0-9_-]{20,}/g
|
|
53990
|
-
},
|
|
53991
|
-
{
|
|
53992
|
-
// OpenAI API key: sk-... or sk-proj-... (>= 20 trailing chars).
|
|
53993
|
-
patternClass: "openai_api_key",
|
|
53994
|
-
regex: /sk-(?:proj-)?[A-Za-z0-9_-]{20,}/g
|
|
53995
|
-
},
|
|
53996
|
-
{
|
|
53997
|
-
// AWS access key id (AKIA / ASIA + 16 uppercase alphanumerics).
|
|
53998
|
-
patternClass: "aws_access_key_id",
|
|
53999
|
-
regex: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g
|
|
54000
|
-
},
|
|
54001
|
-
{
|
|
54002
|
-
// AWS secret access key VALUE assignment — a 40-char base64-ish secret
|
|
54003
|
-
// bound to an `aws_secret_access_key`/`AWS_SECRET_ACCESS_KEY` key. Only
|
|
54004
|
-
// the secret value is redacted, not the assignment label.
|
|
54005
|
-
patternClass: "aws_secret_access_key",
|
|
54006
|
-
regex: /(aws_secret_access_key|AWS_SECRET_ACCESS_KEY)(\s*[=:]\s*["']?)([A-Za-z0-9/+]{40})(["']?)/g
|
|
54007
|
-
},
|
|
54008
|
-
{
|
|
54009
|
-
// GitHub tokens (ghp_/gho_/ghu_/ghs_/ghr_ + 36 chars).
|
|
54010
|
-
patternClass: "github_token",
|
|
54011
|
-
regex: /\bgh[pousr]_[A-Za-z0-9]{36}\b/g
|
|
54012
|
-
},
|
|
54013
|
-
{
|
|
54014
|
-
// Authorization: Bearer <token> embedded in content (a bearer token
|
|
54015
|
-
// riding in a model-bound field). Redacts the token, keeps the scheme.
|
|
54016
|
-
patternClass: "bearer_token",
|
|
54017
|
-
regex: /(Bearer\s+)([A-Za-z0-9._~+/=-]{16,})/g
|
|
54018
|
-
}
|
|
54019
|
-
];
|
|
54020
|
-
function scrubString(value) {
|
|
54021
|
-
return redactSecretShapes(value, REDACTION);
|
|
54022
|
-
}
|
|
54023
|
-
function redactSecretShapes(value, placeholder) {
|
|
54024
|
-
let redacted = value, classes = [];
|
|
54025
|
-
for (let pattern of SECRET_PATTERNS)
|
|
54026
|
-
pattern.regex.lastIndex = 0, pattern.regex.test(redacted) && (pattern.regex.lastIndex = 0, pattern.patternClass === "aws_secret_access_key" ? redacted = redacted.replace(pattern.regex, `$1$2${placeholder}$4`) : pattern.patternClass === "bearer_token" ? redacted = redacted.replace(pattern.regex, `$1${placeholder}`) : redacted = redacted.replace(pattern.regex, placeholder), classes.push(pattern.patternClass));
|
|
54027
|
-
return { redacted, classes };
|
|
54028
|
-
}
|
|
54029
|
-
function redactSecretShapesInText(value, placeholder = "[redacted secret]") {
|
|
54030
|
-
return redactSecretShapes(value, placeholder).redacted;
|
|
54031
|
-
}
|
|
54032
|
-
function keyIsSecret(key) {
|
|
54033
|
-
for (let pattern of SECRET_PATTERNS) {
|
|
54034
|
-
pattern.regex.lastIndex = 0;
|
|
54035
|
-
let hit = pattern.regex.test(key);
|
|
54036
|
-
if (pattern.regex.lastIndex = 0, hit) return !0;
|
|
54037
|
-
}
|
|
54038
|
-
return !1;
|
|
54039
|
-
}
|
|
54040
|
-
function joinPath(base, key) {
|
|
54041
|
-
return typeof key == "number" ? `${base}[${key}]` : /^[A-Za-z_][A-Za-z0-9_]*$/.test(key) ? `${base}.${key}` : `${base}['${key.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}']`;
|
|
54042
|
-
}
|
|
54043
|
-
function scrubValue(value, path75, findings) {
|
|
54044
|
-
if (typeof value == "string") {
|
|
54045
|
-
let { redacted, classes } = scrubString(value);
|
|
54046
|
-
for (let patternClass of classes)
|
|
54047
|
-
findings.push({ field: path75, patternClass });
|
|
54048
|
-
return redacted;
|
|
54049
|
-
}
|
|
54050
|
-
if (Array.isArray(value))
|
|
54051
|
-
return value.map((item, i) => scrubValue(item, joinPath(path75, i), findings));
|
|
54052
|
-
if (value !== null && typeof value == "object") {
|
|
54053
|
-
let out = /* @__PURE__ */ Object.create(null), redactedKeyCount = 0;
|
|
54054
|
-
for (let [k, v] of Object.entries(value))
|
|
54055
|
-
if (keyIsSecret(k)) {
|
|
54056
|
-
redactedKeyCount += 1;
|
|
54057
|
-
let placeholder = `${KEY_REDACTION}-${redactedKeyCount}`, placeholderPath = joinPath(path75, placeholder);
|
|
54058
|
-
findings.push({ field: placeholderPath, patternClass: "secret_object_key" }), out[placeholder] = scrubValue(v, placeholderPath, findings);
|
|
54059
|
-
} else
|
|
54060
|
-
out[k] = scrubValue(v, joinPath(path75, k), findings);
|
|
54061
|
-
return out;
|
|
54062
|
-
}
|
|
54063
|
-
return value;
|
|
54064
|
-
}
|
|
54065
|
-
function scrubRequestBody(body) {
|
|
54066
|
-
let findings = [];
|
|
54067
|
-
return { scrubbed: scrubValue(body, "$", findings), findings };
|
|
54068
|
-
}
|
|
54069
|
-
|
|
54070
54271
|
// src/orchestration-shell/user-rules.ts
|
|
54071
54272
|
var USER_RULE_ACTION = "user_rule", USER_RULE_RETIRED_ACTION = "user_rule_retired", USER_WORDS_VISIBLE_TO = ["planner", "brainstorm", "implementor"], USER_RULE_MAX_CHARS = 600, WORKFLOW_HANDOFF_SECTION_LABEL = "Workflow handoff context:", HANDOFF_SECTION_START = new RegExp(
|
|
54072
54273
|
`(?:^|\\n)${WORKFLOW_HANDOFF_SECTION_LABEL.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\nSource: `
|
|
@@ -57014,10 +57215,74 @@ function teamRound0GateId(taskId, taskGroupId, trackIndex, dispatchGeneration) {
|
|
|
57014
57215
|
TEAM_ROUND0_GATE_NAMESPACE
|
|
57015
57216
|
);
|
|
57016
57217
|
}
|
|
57218
|
+
var RETRY_BRIEF_SECTION_CAP = 8 * 1024, RETRY_BRIEF_TOTAL_CAP = 64 * 1024, UNIFIED_DIFF_MAX_CELLS = 4e6;
|
|
57219
|
+
function capText2(text2, cap) {
|
|
57220
|
+
if (Buffer.byteLength(text2, "utf8") <= cap) return text2;
|
|
57221
|
+
let marker = `
|
|
57222
|
+
\u2026 [truncated]`, cut = text2;
|
|
57223
|
+
for (; Buffer.byteLength(cut, "utf8") + Buffer.byteLength(marker, "utf8") > cap && cut.length > 0; )
|
|
57224
|
+
cut = cut.slice(0, Math.max(0, Math.floor(cut.length * 0.9) - 1));
|
|
57225
|
+
return cut + marker;
|
|
57226
|
+
}
|
|
57227
|
+
function unifiedDiff(fromLabel, toLabel, fromText, toText) {
|
|
57228
|
+
let a = fromText.length === 0 ? [] : fromText.split(`
|
|
57229
|
+
`), b = toText.length === 0 ? [] : toText.split(`
|
|
57230
|
+
`), ops = [];
|
|
57231
|
+
if (a.length * b.length > UNIFIED_DIFF_MAX_CELLS) {
|
|
57232
|
+
for (let i = 0; i < a.length; i++) ops.push({ kind: "-", line: a[i], ai: i, bi: 0 });
|
|
57233
|
+
for (let j = 0; j < b.length; j++) ops.push({ kind: "+", line: b[j], ai: a.length, bi: j });
|
|
57234
|
+
} else {
|
|
57235
|
+
let rows = a.length + 1, cols = b.length + 1, table = new Uint32Array(rows * cols);
|
|
57236
|
+
for (let i2 = a.length - 1; i2 >= 0; i2--)
|
|
57237
|
+
for (let j2 = b.length - 1; j2 >= 0; j2--)
|
|
57238
|
+
table[i2 * cols + j2] = a[i2] === b[j2] ? table[(i2 + 1) * cols + j2 + 1] + 1 : Math.max(table[(i2 + 1) * cols + j2], table[i2 * cols + j2 + 1]);
|
|
57239
|
+
let i = 0, j = 0;
|
|
57240
|
+
for (; i < a.length && j < b.length; )
|
|
57241
|
+
a[i] === b[j] ? (ops.push({ kind: " ", line: a[i], ai: i, bi: j }), i++, j++) : table[(i + 1) * cols + j] >= table[i * cols + j + 1] ? (ops.push({ kind: "-", line: a[i], ai: i, bi: j }), i++) : (ops.push({ kind: "+", line: b[j], ai: i, bi: j }), j++);
|
|
57242
|
+
for (; i < a.length; ) ops.push({ kind: "-", line: a[i], ai: i, bi: j }), i++;
|
|
57243
|
+
for (; j < b.length; ) ops.push({ kind: "+", line: b[j], ai: i, bi: j }), j++;
|
|
57244
|
+
}
|
|
57245
|
+
let out = [`--- ${fromLabel}`, `+++ ${toLabel}`], CONTEXT = 3, k = 0;
|
|
57246
|
+
for (; k < ops.length; ) {
|
|
57247
|
+
if (ops[k].kind === " ") {
|
|
57248
|
+
k++;
|
|
57249
|
+
continue;
|
|
57250
|
+
}
|
|
57251
|
+
let start = Math.max(0, k - CONTEXT), end = k, lastChange = k;
|
|
57252
|
+
for (; end < ops.length && (ops[end].kind !== " " && (lastChange = end), !(end - lastChange > 2 * CONTEXT)); )
|
|
57253
|
+
end++;
|
|
57254
|
+
end = Math.min(ops.length, lastChange + CONTEXT + 1);
|
|
57255
|
+
let slice = ops.slice(start, end), fromStart = (slice.find((o) => o.kind !== "+")?.ai ?? ops[start].ai) + 1, toStart = (slice.find((o) => o.kind !== "-")?.bi ?? ops[start].bi) + 1, fromCount = slice.filter((o) => o.kind !== "+").length, toCount = slice.filter((o) => o.kind !== "-").length;
|
|
57256
|
+
out.push(`@@ -${fromStart},${fromCount} +${toStart},${toCount} @@`);
|
|
57257
|
+
for (let o of slice) out.push(`${o.kind}${o.line}`);
|
|
57258
|
+
k = end, start = end;
|
|
57259
|
+
}
|
|
57260
|
+
return out.length === 2 && out.push("(no textual difference)"), out.join(`
|
|
57261
|
+
`);
|
|
57262
|
+
}
|
|
57017
57263
|
var QuorumLoop = class _QuorumLoop {
|
|
57018
57264
|
constructor(deps) {
|
|
57019
|
-
/**
|
|
57020
|
-
|
|
57265
|
+
/**
|
|
57266
|
+
* P46a B1 — the FOCUSED single task: the task the UI means when a command
|
|
57267
|
+
* names none (`/continue`, `/review-reset`, the status "starting" row).
|
|
57268
|
+
* Default = the most recently CONFIRMED START; `/task focus <n>` sets it
|
|
57269
|
+
* explicitly; it advances to the newest remaining single task when the
|
|
57270
|
+
* focused task ends; a rejected START never moves it. It replaces the three
|
|
57271
|
+
* P45 singletons (`activeTaskId` / `activeBrief` / `activeImplementorAgent`):
|
|
57272
|
+
* every reader now resolves through the task's OWN record
|
|
57273
|
+
* (`singleTaskContextByTask`), never through "the latest task".
|
|
57274
|
+
*/
|
|
57275
|
+
this.focusedTaskId = null;
|
|
57276
|
+
/** P46a B1 — confirmed single tasks in START-confirmation order (focus fallback). */
|
|
57277
|
+
this.confirmedSingleOrder = [];
|
|
57278
|
+
/**
|
|
57279
|
+
* P46a B1 — the most recent START still in flight (or null). A legacy
|
|
57280
|
+
* GATE_DISPATCH with an EMPTY envelope task id can only belong to it (the P45
|
|
57281
|
+
* rule); with no START in flight such a packet is a no-op left to recovery.
|
|
57282
|
+
*/
|
|
57283
|
+
this.latestInFlightStart = null;
|
|
57284
|
+
/** Stage 1 r1 F13 — every START still in flight, in issue order; `latestInFlightStart` is its last entry. */
|
|
57285
|
+
this.startOrderInFlight = [];
|
|
57021
57286
|
/** Tasks whose next automatic revise round must start a fresh review baseline. */
|
|
57022
57287
|
this.reviewScopeResetTasks = /* @__PURE__ */ new Set();
|
|
57023
57288
|
/** Non-terminal tasks for which `/review-reset` may arm the next revise round. */
|
|
@@ -57041,16 +57306,9 @@ var QuorumLoop = class _QuorumLoop {
|
|
|
57041
57306
|
// only per QuorumLoop life; a `0`/absent entry omits the ` · ↓ <tokens>` segment.
|
|
57042
57307
|
this.tokensByTaskId = /* @__PURE__ */ new Map();
|
|
57043
57308
|
this.tokensCountedGates = /* @__PURE__ */ new Set();
|
|
57044
|
-
/**
|
|
57045
|
-
* The implementor brief for the active task (set at start_task). The
|
|
57046
|
-
* GATE_DISPATCH consumer reads this to drive the round-0 implementor spawn —
|
|
57047
|
-
* the brief is no longer threaded inline through the spawn call (the spawn
|
|
57048
|
-
* moved out of `startTask` into the GATE_DISPATCH consumer, §3.C.20 FIX).
|
|
57049
|
-
*/
|
|
57050
|
-
this.activeBrief = null;
|
|
57051
57309
|
/**
|
|
57052
57310
|
* IMAGE-ATTACHMENT-DESIGN.md §5 — TASK-SCOPED image attachments (parallel to
|
|
57053
|
-
* `
|
|
57311
|
+
* `singleTaskContextByTask`/`activeBriefByTask`). The single-impl path uses `activeAttachments`;
|
|
57054
57312
|
* the team path keys by the child `taskId` in `activeAttachmentsByTask` (armed at
|
|
57055
57313
|
* `registerTeamTaskBrief`, before its buffered GATE_DISPATCH drains). Re-copied
|
|
57056
57314
|
* into EACH round's shadow at `runImplementorRound` (round-0, revise, continuation,
|
|
@@ -57105,20 +57363,44 @@ var QuorumLoop = class _QuorumLoop {
|
|
|
57105
57363
|
this.recoveredBriefsByAuthority = /* @__PURE__ */ new Map();
|
|
57106
57364
|
this.activeBriefByTask = /* @__PURE__ */ new Map();
|
|
57107
57365
|
/**
|
|
57108
|
-
* P45 D1 (M7 E2E step 8, 2026-09-14) — the brief + agent of EVERY
|
|
57109
|
-
* implementor task this loop started, keyed by task id.
|
|
57110
|
-
*
|
|
57111
|
-
*
|
|
57112
|
-
*
|
|
57113
|
-
*
|
|
57114
|
-
*
|
|
57115
|
-
*
|
|
57116
|
-
* continuation context resolve through this map first; the singletons stay
|
|
57117
|
-
* as the legacy fallback (an empty envelope task id, the test seam). Entries
|
|
57118
|
-
* are removed with the task's other per-task state at its terminal cleanup.
|
|
57366
|
+
* P45 D1 (M7 E2E step 8, 2026-09-14) / P46a B1 — the brief + agent of EVERY
|
|
57367
|
+
* LIVE single-implementor task this loop started, keyed by task id. P45 added
|
|
57368
|
+
* it beside the three "latest task" singletons (a second single task's
|
|
57369
|
+
* request had replaced the first task's in its revise round); P46a deletes
|
|
57370
|
+
* the singletons — round-0 spawns, revise rounds, the continuation context and
|
|
57371
|
+
* every other reader resolve ONLY through this map. An entry exists from the
|
|
57372
|
+
* START call (in flight) until the task's terminal cleanup; `retryOf` links a
|
|
57373
|
+
* `#n retry` task to its `apply_failed` original (P46a C2/C3).
|
|
57119
57374
|
*/
|
|
57120
57375
|
this.singleTaskContextByTask = /* @__PURE__ */ new Map();
|
|
57376
|
+
/** Agent web browsing (upstream 2.0.37) — whether a task's ORIGINAL request needs web access; ends with the task (`forgetSingleTask`, team cleanup). */
|
|
57121
57377
|
this.webAccessByTask = /* @__PURE__ */ new Map();
|
|
57378
|
+
/**
|
|
57379
|
+
* P46a D2 — append-only per-loop-life record of every single task this loop
|
|
57380
|
+
* started (confirmed or in flight) with its terminal outcome once known, for
|
|
57381
|
+
* `/status` (`describeTasks`). Never deleted (bounded by the session's tasks).
|
|
57382
|
+
*/
|
|
57383
|
+
this.singleTaskHistory = /* @__PURE__ */ new Map();
|
|
57384
|
+
/**
|
|
57385
|
+
* P46a C1 — every single-task promote THIS session landed, by workspace-
|
|
57386
|
+
* relative path: the promoting task + the post-image hash/mode the promote
|
|
57387
|
+
* compare-and-set reads (`readRealMetadata`; `null` hash = a deletion). Used
|
|
57388
|
+
* to attribute a later collision to a `session-task` (the path's current
|
|
57389
|
+
* metadata equals a recorded post-image) or `external` (anything else —
|
|
57390
|
+
* including a session-promoted file the user edited afterwards, and every
|
|
57391
|
+
* promote by another session). In-memory, fail-safe: empty after a desktop
|
|
57392
|
+
* restart, so every collision then classifies as `external`.
|
|
57393
|
+
*/
|
|
57394
|
+
this.promotedByPath = /* @__PURE__ */ new Map();
|
|
57395
|
+
/** P46a C2 — originals whose ONE automatic retry has been issued (never a second). */
|
|
57396
|
+
this.autoRetryIssuedFor = /* @__PURE__ */ new Set();
|
|
57397
|
+
/**
|
|
57398
|
+
* P46a C2 — retry STARTs planned inside the promote critical section and
|
|
57399
|
+
* issued only after it (and the durable `apply_failed` record) completed:
|
|
57400
|
+
* `startTask` runs the round-0 spawn before returning, so it must never run
|
|
57401
|
+
* inside `promoteShadowLocked`.
|
|
57402
|
+
*/
|
|
57403
|
+
this.pendingPromoteRetries = [];
|
|
57122
57404
|
/**
|
|
57123
57405
|
* CP-1.f revise-context fix (dogfood task 2ac68708, 2026-06-11) — the BINDING
|
|
57124
57406
|
* user-requested changes for a task, accumulated across ALL revise rounds.
|
|
@@ -57581,8 +57863,6 @@ var QuorumLoop = class _QuorumLoop {
|
|
|
57581
57863
|
this.lastWorkspaceOutcome = null;
|
|
57582
57864
|
// ─── §3.C.18 — real Ed25519 signature verify (cached key) ───────────────
|
|
57583
57865
|
this.cachedSigningKey = null;
|
|
57584
|
-
/** The implementor agent the loop spawns (set at start_task). */
|
|
57585
|
-
this.activeImplementorAgent = "CLAUDE";
|
|
57586
57866
|
this.deps = deps, this.sleep = deps.sleep ?? realSleep2, this.loadCompletedSummariesFromDisk();
|
|
57587
57867
|
}
|
|
57588
57868
|
getCompletedSummariesPath() {
|
|
@@ -58171,13 +58451,95 @@ var QuorumLoop = class _QuorumLoop {
|
|
|
58171
58451
|
isTaskTerminallyRetired(taskId) {
|
|
58172
58452
|
return this.isWorkspaceTaskTerminallyRetired(taskId);
|
|
58173
58453
|
}
|
|
58174
|
-
/**
|
|
58454
|
+
/**
|
|
58455
|
+
* P46a B1 — the FOCUSED single task (null when no single task is live). The
|
|
58456
|
+
* P45 `activeTask` name is kept for the shell's readers; it now means the
|
|
58457
|
+
* focused task, never "the latest task".
|
|
58458
|
+
*/
|
|
58175
58459
|
get activeTask() {
|
|
58176
|
-
return this.
|
|
58460
|
+
return this.focusedTaskId;
|
|
58461
|
+
}
|
|
58462
|
+
/** P46a B1 — every LIVE single task this loop started (in flight or confirmed). */
|
|
58463
|
+
knownSingleTasks() {
|
|
58464
|
+
return [...this.singleTaskContextByTask.keys()];
|
|
58465
|
+
}
|
|
58466
|
+
/**
|
|
58467
|
+
* P46a B1 — `/task focus <n>`: make `taskId` the focused task. Refused (false)
|
|
58468
|
+
* for a task this loop does not know as a live single task.
|
|
58469
|
+
*/
|
|
58470
|
+
setFocusedTask(taskId) {
|
|
58471
|
+
return this.singleTaskContextByTask.has(taskId) ? (this.focusedTaskId = taskId, !0) : !1;
|
|
58472
|
+
}
|
|
58473
|
+
/** P46a D1 — install the store-owned ordinal sink (the shell, after construction). */
|
|
58474
|
+
setTaskOrdinalSink(sink) {
|
|
58475
|
+
this.taskOrdinalSink = sink;
|
|
58476
|
+
}
|
|
58477
|
+
/** P46a C3 — install the desktop-local apply-conflict menu sink. */
|
|
58478
|
+
setApplyConflictSink(sink) {
|
|
58479
|
+
this.applyConflictSink = sink;
|
|
58480
|
+
}
|
|
58481
|
+
/** P46a C2 — install the retry-started notifier (the shell records the lifecycle row). */
|
|
58482
|
+
setRetryStartedSink(sink) {
|
|
58483
|
+
this.retryStartedSink = sink;
|
|
58484
|
+
}
|
|
58485
|
+
/** P46a D1 — `#n` / `#n retry` / short id for any task id (never throws). */
|
|
58486
|
+
labelFor(taskId) {
|
|
58487
|
+
try {
|
|
58488
|
+
let label = this.taskOrdinalSink?.label(taskId);
|
|
58489
|
+
if (label) return label;
|
|
58490
|
+
} catch {
|
|
58491
|
+
}
|
|
58492
|
+
return taskId.length > 8 ? taskId.slice(0, 8) : taskId;
|
|
58493
|
+
}
|
|
58494
|
+
/**
|
|
58495
|
+
* P46a D2 — every single task this loop started, for `/status`: its label,
|
|
58496
|
+
* agent, request (desktop-local plaintext — the user's own words), link to an
|
|
58497
|
+
* `apply_failed` original, loop state and whether it is the focused task.
|
|
58498
|
+
*/
|
|
58499
|
+
describeTasks() {
|
|
58500
|
+
let rows = [];
|
|
58501
|
+
for (let [taskId, rec] of this.singleTaskHistory)
|
|
58502
|
+
rows.push({
|
|
58503
|
+
taskId,
|
|
58504
|
+
label: this.labelFor(taskId),
|
|
58505
|
+
agent: rec.agent,
|
|
58506
|
+
// Stage 1 r3 F1 — a retry's excerpt is the user's own request, not its composed brief.
|
|
58507
|
+
brief: rec.request ?? rec.brief,
|
|
58508
|
+
...rec.retryOf ? { retryOf: rec.retryOf } : {},
|
|
58509
|
+
outcome: rec.outcome,
|
|
58510
|
+
implementorActive: this.activeImplementorByTask.has(taskId),
|
|
58511
|
+
focused: taskId === this.focusedTaskId
|
|
58512
|
+
});
|
|
58513
|
+
return rows;
|
|
58177
58514
|
}
|
|
58178
|
-
/**
|
|
58515
|
+
/** Stage 1 r1 F13 — a START confirmed or rejected: the newest START still in flight (if any) becomes the empty-envelope target. */
|
|
58516
|
+
dropInFlightStart(taskId) {
|
|
58517
|
+
let at = this.startOrderInFlight.indexOf(taskId);
|
|
58518
|
+
at >= 0 && this.startOrderInFlight.splice(at, 1), this.latestInFlightStart = this.startOrderInFlight.length > 0 ? this.startOrderInFlight[this.startOrderInFlight.length - 1] : null;
|
|
58519
|
+
}
|
|
58520
|
+
/**
|
|
58521
|
+
* P46a B1 — a single task ended (promote, terminal discard, rejected START):
|
|
58522
|
+
* drop its live record and, when it was the focused task, advance the focus
|
|
58523
|
+
* to the newest remaining confirmed single task (or none).
|
|
58524
|
+
*/
|
|
58525
|
+
forgetSingleTask(taskId, outcome) {
|
|
58526
|
+
this.singleTaskContextByTask.delete(taskId), this.webAccessByTask.delete(taskId), this.deps.localExecutor.forgetTaskScope?.(taskId);
|
|
58527
|
+
let history = this.singleTaskHistory.get(taskId);
|
|
58528
|
+
history && history.outcome === "running" && (history.outcome = outcome);
|
|
58529
|
+
let at = this.confirmedSingleOrder.indexOf(taskId);
|
|
58530
|
+
at >= 0 && this.confirmedSingleOrder.splice(at, 1), this.focusedTaskId === taskId && (this.focusedTaskId = this.confirmedSingleOrder.length > 0 ? this.confirmedSingleOrder[this.confirmedSingleOrder.length - 1] : null);
|
|
58531
|
+
}
|
|
58532
|
+
/**
|
|
58533
|
+
* Arm an explicit, one-shot comprehensive baseline for the FOCUSED task
|
|
58534
|
+
* (P46a B1). Refused with the candidate list when the focused task has no
|
|
58535
|
+
* eligible gate (or no single task is focused) — the shell renders the
|
|
58536
|
+
* `#n` candidates so the user can `/task focus <n>` first.
|
|
58537
|
+
*/
|
|
58179
58538
|
requestReviewScopeReset() {
|
|
58180
|
-
|
|
58539
|
+
let focused = this.focusedTaskId;
|
|
58540
|
+
return focused && this.reviewScopeResetEligibleTasks.has(focused) ? (this.reviewScopeResetTasks.add(focused), { armed: !0, taskId: focused }) : { armed: !1, candidates: [...this.singleTaskContextByTask.keys()].filter(
|
|
58541
|
+
(id) => id !== focused && this.reviewScopeResetEligibleTasks.has(id)
|
|
58542
|
+
) };
|
|
58181
58543
|
}
|
|
58182
58544
|
/**
|
|
58183
58545
|
* Audited-path fix (Fix 5) — the host's detected implementor agents
|
|
@@ -58359,8 +58721,7 @@ var QuorumLoop = class _QuorumLoop {
|
|
|
58359
58721
|
return logger.warn("[QuorumLoop] startTask aborted \u2014 no session key", {
|
|
58360
58722
|
sessionId: this.deps.session.sessionId
|
|
58361
58723
|
}), null;
|
|
58362
|
-
|
|
58363
|
-
this.activeTaskId = taskId, this.activeBrief = args.brief, appendContextItem(this.deps.session.sessionId, {
|
|
58724
|
+
appendContextItem(this.deps.session.sessionId, {
|
|
58364
58725
|
kind: "task_spec",
|
|
58365
58726
|
author: { role: "engine" },
|
|
58366
58727
|
sensitivity: "user",
|
|
@@ -58368,7 +58729,14 @@ var QuorumLoop = class _QuorumLoop {
|
|
|
58368
58729
|
task_id: taskId,
|
|
58369
58730
|
body: { text: args.brief }
|
|
58370
58731
|
}).catch(() => {
|
|
58371
|
-
}), this.activeAttachments = args.attachments ?? [], this.activeAttachmentsByTask.set(taskId, this.activeAttachments)
|
|
58732
|
+
}), this.activeAttachments = args.attachments ?? [], this.activeAttachmentsByTask.set(taskId, this.activeAttachments);
|
|
58733
|
+
let implementorAgent = this.resolveForcedImplementorAgent() ?? args.agent, record = {
|
|
58734
|
+
brief: args.brief,
|
|
58735
|
+
agent: implementorAgent,
|
|
58736
|
+
...args.retryOf ? { retryOf: args.retryOf } : {},
|
|
58737
|
+
...args.request !== void 0 ? { request: args.request } : {}
|
|
58738
|
+
};
|
|
58739
|
+
this.singleTaskContextByTask.set(taskId, record), this.singleTaskHistory.set(taskId, { ...record, outcome: "running" }), this.startTasksInFlight.add(taskId), this.startOrderInFlight.push(taskId), this.latestInFlightStart = taskId;
|
|
58372
58740
|
let workflowState;
|
|
58373
58741
|
try {
|
|
58374
58742
|
workflowState = (await this.deps.appsyncClient.startTask({
|
|
@@ -58381,31 +58749,40 @@ var QuorumLoop = class _QuorumLoop {
|
|
|
58381
58749
|
// an empty `[]` (host detected nothing) is sent verbatim →
|
|
58382
58750
|
// Claude-only fail-safe.
|
|
58383
58751
|
availableAgents: this.deps.detectedAgents,
|
|
58384
|
-
implementorAgent:
|
|
58752
|
+
implementorAgent: implementorAgent.toLowerCase()
|
|
58385
58753
|
})).workflowState;
|
|
58386
58754
|
} catch (err) {
|
|
58387
|
-
let
|
|
58388
|
-
|
|
58755
|
+
let wasLatestStart2 = this.latestInFlightStart === taskId;
|
|
58756
|
+
this.forgetSingleTask(taskId, "rejected"), this.startTasksInFlight.delete(taskId), this.dropInFlightStart(taskId);
|
|
58389
58757
|
for (let i = this.pendingGateDispatches.length - 1; i >= 0; i--) {
|
|
58390
58758
|
let pending = this.pendingGateDispatches[i];
|
|
58391
|
-
(pending.envelopeTaskId === taskId || !pending.envelopeTaskId &&
|
|
58759
|
+
(pending.envelopeTaskId === taskId || !pending.envelopeTaskId && wasLatestStart2) && this.pendingGateDispatches.splice(i, 1);
|
|
58392
58760
|
}
|
|
58393
|
-
return
|
|
58761
|
+
return logger.warn("[QuorumLoop] startTask failed \u2014 dropped the task record + discarded its buffered GATE_DISPATCH", {
|
|
58394
58762
|
err: err.message
|
|
58395
58763
|
}), null;
|
|
58396
58764
|
}
|
|
58397
|
-
this.startTasksInFlight.delete(taskId)
|
|
58765
|
+
this.startTasksInFlight.delete(taskId);
|
|
58766
|
+
let wasLatestStart = this.latestInFlightStart === taskId;
|
|
58767
|
+
this.dropInFlightStart(taskId), this.reviewScopeResetEligibleTasks.add(taskId);
|
|
58768
|
+
let ordinal = this.taskOrdinalSink?.assign(taskId, {
|
|
58769
|
+
kind: "single",
|
|
58770
|
+
...args.retryOf ? { retryOf: args.retryOf } : {}
|
|
58771
|
+
});
|
|
58772
|
+
this.confirmedSingleOrder.push(taskId), this.focusedTaskId = taskId, logger.info("[QuorumLoop] startTask succeeded \u2014 draining buffered GATE_DISPATCH (if any) then awaiting more", {
|
|
58398
58773
|
taskId,
|
|
58774
|
+
...ordinal !== void 0 ? { ordinal } : {},
|
|
58775
|
+
...args.retryOf ? { retryOf: args.retryOf } : {},
|
|
58399
58776
|
workflowState,
|
|
58400
58777
|
buffered: this.pendingGateDispatches.length
|
|
58401
58778
|
});
|
|
58402
58779
|
let drained = [];
|
|
58403
58780
|
for (let i = this.pendingGateDispatches.length - 1; i >= 0; i--) {
|
|
58404
58781
|
let entry = this.pendingGateDispatches[i];
|
|
58405
|
-
(!entry.envelopeTaskId || entry.envelopeTaskId === taskId) && drained.unshift(this.pendingGateDispatches.splice(i, 1)[0]);
|
|
58782
|
+
(!entry.envelopeTaskId && wasLatestStart || entry.envelopeTaskId === taskId) && drained.unshift(this.pendingGateDispatches.splice(i, 1)[0]);
|
|
58406
58783
|
}
|
|
58407
58784
|
for (let entry of drained)
|
|
58408
|
-
await this.handleGateDispatch(entry.payload, entry.envelopeTaskId);
|
|
58785
|
+
await this.handleGateDispatch(entry.payload, entry.envelopeTaskId ?? taskId);
|
|
58409
58786
|
return { taskId };
|
|
58410
58787
|
}
|
|
58411
58788
|
// ─── §3.C.20 — GATE_DISPATCH → round-0 implementor spawn ────────────────
|
|
@@ -58428,7 +58805,7 @@ var QuorumLoop = class _QuorumLoop {
|
|
|
58428
58805
|
* never thrown upward.
|
|
58429
58806
|
*/
|
|
58430
58807
|
async handleGateDispatch(payload, envelopeTaskId) {
|
|
58431
|
-
let dispatchTaskId = envelopeTaskId || this.
|
|
58808
|
+
let dispatchTaskId = envelopeTaskId || this.latestInFlightStart;
|
|
58432
58809
|
if (dispatchTaskId && Array.isArray(payload.reviewerSeats) && payload.reviewerSeats.length > 0) {
|
|
58433
58810
|
let roster = this.expectedRosterByTask.get(dispatchTaskId);
|
|
58434
58811
|
roster || (roster = /* @__PURE__ */ new Map(), this.expectedRosterByTask.set(dispatchTaskId, roster));
|
|
@@ -58446,11 +58823,12 @@ var QuorumLoop = class _QuorumLoop {
|
|
|
58446
58823
|
logger.info("[QuorumLoop] GateDispatch dedup no-op", { gateRunId: payload.gateRunId });
|
|
58447
58824
|
return;
|
|
58448
58825
|
}
|
|
58449
|
-
if (typeof envelopeTaskId == "string" && envelopeTaskId.length > 0 &&
|
|
58450
|
-
//
|
|
58826
|
+
if (typeof envelopeTaskId == "string" && envelopeTaskId.length > 0 && // P45 D1 / P46a B1 — a dispatch for ANY single task this loop started (in
|
|
58827
|
+
// flight or confirmed, earlier or later) is a single-implementor dispatch;
|
|
58828
|
+
// only an unknown task id routes to the team handler.
|
|
58451
58829
|
!this.singleTaskContextByTask.has(envelopeTaskId))
|
|
58452
58830
|
return this.bufferTeamPacketDuringSeeding("gateDispatch", payload, envelopeTaskId) ? void 0 : this.handleTeamGateDispatch(payload, envelopeTaskId);
|
|
58453
|
-
let inFlightFor = typeof envelopeTaskId == "string" && envelopeTaskId.length > 0 ? envelopeTaskId : this.
|
|
58831
|
+
let inFlightFor = typeof envelopeTaskId == "string" && envelopeTaskId.length > 0 ? envelopeTaskId : this.latestInFlightStart;
|
|
58454
58832
|
if (inFlightFor !== null && this.startTasksInFlight.has(inFlightFor)) {
|
|
58455
58833
|
logger.info("[QuorumLoop] GateDispatch buffered \u2014 startTask in flight (early-packet race)", {
|
|
58456
58834
|
gateRunId: payload.gateRunId,
|
|
@@ -58458,15 +58836,15 @@ var QuorumLoop = class _QuorumLoop {
|
|
|
58458
58836
|
}), this.pendingGateDispatches.push({ payload, envelopeTaskId });
|
|
58459
58837
|
return;
|
|
58460
58838
|
}
|
|
58461
|
-
let single = typeof envelopeTaskId == "string" && envelopeTaskId.length > 0 ? this.singleTaskContextByTask.get(envelopeTaskId) : void 0
|
|
58462
|
-
if (!
|
|
58463
|
-
logger.warn("[QuorumLoop] GateDispatch
|
|
58839
|
+
let single = typeof envelopeTaskId == "string" && envelopeTaskId.length > 0 ? this.singleTaskContextByTask.get(envelopeTaskId) : void 0;
|
|
58840
|
+
if (!single || !envelopeTaskId) {
|
|
58841
|
+
logger.warn("[QuorumLoop] GateDispatch names no task this loop started \u2014 no-op (recovery will handle)", {
|
|
58464
58842
|
gateRunId: payload.gateRunId,
|
|
58465
|
-
|
|
58466
|
-
hasBrief: !!brief
|
|
58843
|
+
hasEnvelopeTaskId: typeof envelopeTaskId == "string" && envelopeTaskId.length > 0
|
|
58467
58844
|
});
|
|
58468
58845
|
return;
|
|
58469
58846
|
}
|
|
58847
|
+
let taskId = envelopeTaskId, brief = single.brief, agent = single.agent;
|
|
58470
58848
|
this.seenGateDispatchRunIds.add(payload.gateRunId);
|
|
58471
58849
|
let sessionKey = await this.deps.getSessionKey(this.deps.session.sessionId);
|
|
58472
58850
|
if (!sessionKey) {
|
|
@@ -59023,7 +59401,8 @@ ${section}`);
|
|
|
59023
59401
|
...args.teamAuthority ? { teamAuthority: args.teamAuthority } : {}
|
|
59024
59402
|
}, needsWeb = this.webAccessByTask.get(args.taskId);
|
|
59025
59403
|
if (needsWeb === void 0) {
|
|
59026
|
-
let
|
|
59404
|
+
let singleRecord = this.singleTaskContextByTask.get(args.taskId), originalPrompt = (singleRecord ? singleRecord.request ?? singleRecord.brief : void 0) ?? this.activeBriefByTask.get(args.taskId)?.brief ?? // P46a B1 — no singleton fallback: a task without a record reads as no request.
|
|
59405
|
+
(args.roundNumber === 0 ? args.brief : "");
|
|
59027
59406
|
needsWeb = isWebAccessNeeded(originalPrompt), this.webAccessByTask.set(args.taskId, needsWeb);
|
|
59028
59407
|
}
|
|
59029
59408
|
if (needsWeb && (args.agent === "CLAUDE" || args.agent === "ANTIGRAVITY"))
|
|
@@ -59057,12 +59436,11 @@ ${section}`);
|
|
|
59057
59436
|
workingDirAuthority: snapshotAuthority,
|
|
59058
59437
|
role: "implementor",
|
|
59059
59438
|
agentKind: args.agent,
|
|
59060
|
-
// CP-12 W2.b — thread THIS round's task id as per-spawn
|
|
59061
|
-
// authority. On the TEAM path `args.taskId` is
|
|
59062
|
-
// `task_id
|
|
59063
|
-
//
|
|
59064
|
-
// (
|
|
59065
|
-
// yields byte-identical single-impl substrate engagement.
|
|
59439
|
+
// CP-12 W2.b / P46a B2 — thread THIS round's task id as the per-spawn
|
|
59440
|
+
// substrate/audit/authority identity. On the TEAM path `args.taskId` is
|
|
59441
|
+
// the per-track child `task_id`; on the single-impl path it is the task's
|
|
59442
|
+
// own id — the executor keeps no singleton any more and resolves the
|
|
59443
|
+
// per-task authority scope (seeded until ITS TaskAuthorized) from it.
|
|
59066
59444
|
taskId: args.taskId,
|
|
59067
59445
|
webTurnActive: !!agentWebTurn?.mcpConfigPath,
|
|
59068
59446
|
timeoutMs: null,
|
|
@@ -60365,7 +60743,7 @@ ${section}`);
|
|
|
60365
60743
|
});
|
|
60366
60744
|
return;
|
|
60367
60745
|
}
|
|
60368
|
-
if (opts?.preserveReviseContext || (this.userNotesByTask.delete(taskId), this.reviewScopeResetTasks.delete(taskId), this.reviewScopeResetEligibleTasks.delete(taskId), this.roundHistoryByTask.delete(taskId), opts?.taskContinues !== !0 &&
|
|
60746
|
+
if (opts?.preserveReviseContext || (this.userNotesByTask.delete(taskId), this.reviewScopeResetTasks.delete(taskId), this.reviewScopeResetEligibleTasks.delete(taskId), this.roundHistoryByTask.delete(taskId), opts?.taskContinues !== !0 && this.forgetSingleTask(taskId, "discarded"), this.rationaleByTask.delete(taskId), this.taskDegradedMetadataByTask.delete(taskId), this.expectedRosterByTask.delete(taskId), this.activeGateByTaskId.delete(taskId), this.conflictedSeatsByTask.delete(taskId), this.nonApprovedSeatsByTask.delete(taskId), this.restartFenceByTaskId.delete(taskId), appendContextItem(this.deps.session.sessionId, {
|
|
60369
60747
|
kind: "verdict",
|
|
60370
60748
|
author: { role: "engine" },
|
|
60371
60749
|
sensitivity: "user",
|
|
@@ -60411,7 +60789,123 @@ ${section}`);
|
|
|
60411
60789
|
*/
|
|
60412
60790
|
async promoteShadow(taskId) {
|
|
60413
60791
|
if (!(this.shuttingDown && (this.terminalClassBAdmissionsByTask.get(taskId)?.size ?? 0) === 0))
|
|
60414
|
-
|
|
60792
|
+
try {
|
|
60793
|
+
await this.runExclusiveShadowOp(taskId, () => this.promoteShadowLocked(taskId));
|
|
60794
|
+
} finally {
|
|
60795
|
+
this.pendingPromoteRetries.length > 0 && setImmediate(() => {
|
|
60796
|
+
this.drainPromoteRetries().catch((err) => {
|
|
60797
|
+
logger.warn("[QuorumLoop] automatic retry drain failed", { err: err.message });
|
|
60798
|
+
});
|
|
60799
|
+
});
|
|
60800
|
+
}
|
|
60801
|
+
}
|
|
60802
|
+
/** P46a C2 — start every planned automatic retry as a new linked task. */
|
|
60803
|
+
async drainPromoteRetries() {
|
|
60804
|
+
for (; this.pendingPromoteRetries.length > 0; ) {
|
|
60805
|
+
let plan = this.pendingPromoteRetries.shift();
|
|
60806
|
+
if (this.shuttingDown) {
|
|
60807
|
+
logger.info("[QuorumLoop] automatic retry skipped \u2014 shutting down", { retryOf: plan.retryOf });
|
|
60808
|
+
continue;
|
|
60809
|
+
}
|
|
60810
|
+
try {
|
|
60811
|
+
let started = await this.startTask({
|
|
60812
|
+
decision: { action: "start_task", rationale: "P46a automatic retry after a session-task collision" },
|
|
60813
|
+
brief: plan.brief,
|
|
60814
|
+
agent: plan.agent,
|
|
60815
|
+
retryOf: plan.retryOf,
|
|
60816
|
+
request: plan.request
|
|
60817
|
+
});
|
|
60818
|
+
started ? (this.retryStartedSink?.({ taskId: started.taskId, retryOf: plan.retryOf, agent: plan.agent }), this.surfaceHalt(
|
|
60819
|
+
`Task ${this.labelFor(started.taskId)} started \u2014 the retry of task ${this.labelFor(plan.retryOf)}.`
|
|
60820
|
+
)) : this.surfaceHalt(
|
|
60821
|
+
`Could not start the automatic retry of task ${this.labelFor(plan.retryOf)} (the START was rejected). Re-issue the request to try again.`
|
|
60822
|
+
);
|
|
60823
|
+
} catch (err) {
|
|
60824
|
+
logger.warn("[QuorumLoop] automatic retry START threw (non-fatal)", {
|
|
60825
|
+
retryOf: plan.retryOf,
|
|
60826
|
+
err: err.message
|
|
60827
|
+
}), this.surfaceHalt(
|
|
60828
|
+
`Could not start the automatic retry of task ${this.labelFor(plan.retryOf)}. Re-issue the request to try again.`
|
|
60829
|
+
);
|
|
60830
|
+
}
|
|
60831
|
+
}
|
|
60832
|
+
}
|
|
60833
|
+
/** P46a D2 — correct a task's recorded outcome after its terminal cleanup ran. */
|
|
60834
|
+
markTaskOutcome(taskId, outcome) {
|
|
60835
|
+
let history = this.singleTaskHistory.get(taskId);
|
|
60836
|
+
history && (history.outcome = outcome);
|
|
60837
|
+
}
|
|
60838
|
+
/**
|
|
60839
|
+
* P46a C1 — classify each conflicting path: `session-task` when its current
|
|
60840
|
+
* real-tree metadata equals a post-image a single-task promote of THIS
|
|
60841
|
+
* session recorded (`promotedByPath`), else `external`.
|
|
60842
|
+
*/
|
|
60843
|
+
async attributeConflicts(shadow, paths) {
|
|
60844
|
+
let out = [];
|
|
60845
|
+
for (let rel of paths) {
|
|
60846
|
+
let recorded = this.promotedByPath.get(rel), cause = "external";
|
|
60847
|
+
if (recorded)
|
|
60848
|
+
try {
|
|
60849
|
+
let current = await shadow.readRealMetadata(rel);
|
|
60850
|
+
current.hash === recorded.postHash && current.mode === recorded.postMode && (cause = "session-task");
|
|
60851
|
+
} catch {
|
|
60852
|
+
cause = "external";
|
|
60853
|
+
}
|
|
60854
|
+
out.push({ path: rel, cause, ...cause === "session-task" && recorded ? { byTaskId: recorded.taskId } : {} });
|
|
60855
|
+
}
|
|
60856
|
+
return out;
|
|
60857
|
+
}
|
|
60858
|
+
/**
|
|
60859
|
+
* P46a C2 — the retry brief: the original request, a "Files changed since
|
|
60860
|
+
* your first attempt" section (a unified diff from the failed task's REVIEWED
|
|
60861
|
+
* contents to the CURRENT real contents, per conflicting path, naming the
|
|
60862
|
+
* session task that changed it), and the reviewed contents themselves. Every
|
|
60863
|
+
* section is capped (`RETRY_BRIEF_SECTION_CAP` per path,
|
|
60864
|
+
* `RETRY_BRIEF_TOTAL_CAP` overall — the brief becomes the retry task's
|
|
60865
|
+
* durable `task_spec`, whose write is fire-and-forget) and secret-scrubbed
|
|
60866
|
+
* (`redactSecretShapesInText`) before insertion.
|
|
60867
|
+
*/
|
|
60868
|
+
async buildRetryBrief(shadow, failedTaskId, originalBrief, attributed, reviewed) {
|
|
60869
|
+
let failedLabel = this.labelFor(failedTaskId), reviewedByPath = new Map(reviewed.map((file) => [file.path, file])), changed = [], contents = [];
|
|
60870
|
+
for (let conflict of attributed) {
|
|
60871
|
+
let who = conflict.cause === "session-task" && conflict.byTaskId ? `changed by task ${this.labelFor(conflict.byTaskId)} in this session` : "changed outside CodeVibe", reviewedFile = reviewedByPath.get(conflict.path), reviewedText = reviewedFile && reviewedFile.change_kind !== "deleted" ? reviewedFile.content : "", currentText = null, currentState = "";
|
|
60872
|
+
try {
|
|
60873
|
+
currentText = await shadow.readRealFileText(conflict.path);
|
|
60874
|
+
} catch (err) {
|
|
60875
|
+
currentText = null, currentState = err.code === "ENOENT" ? "; now absent" : "; unreadable now";
|
|
60876
|
+
}
|
|
60877
|
+
let diffText = unifiedDiff(
|
|
60878
|
+
`reviewed (task ${failedLabel})`,
|
|
60879
|
+
"current",
|
|
60880
|
+
reviewedText,
|
|
60881
|
+
currentText ?? ""
|
|
60882
|
+
);
|
|
60883
|
+
changed.push(
|
|
60884
|
+
`### ${conflict.path} (${who}${currentState})`,
|
|
60885
|
+
"```diff",
|
|
60886
|
+
capText2(diffText, RETRY_BRIEF_SECTION_CAP),
|
|
60887
|
+
"```"
|
|
60888
|
+
), reviewedFile && contents.push(
|
|
60889
|
+
`### ${conflict.path}${reviewedFile.change_kind === "deleted" ? " (you deleted this file)" : ""}`,
|
|
60890
|
+
"```",
|
|
60891
|
+
capText2(reviewedText, RETRY_BRIEF_SECTION_CAP),
|
|
60892
|
+
"```"
|
|
60893
|
+
);
|
|
60894
|
+
}
|
|
60895
|
+
let changedSection = redactSecretShapesInText(changed.join(`
|
|
60896
|
+
`)), contentsSection = redactSecretShapesInText(contents.join(`
|
|
60897
|
+
`)), assembled = [
|
|
60898
|
+
originalBrief.trim(),
|
|
60899
|
+
"",
|
|
60900
|
+
"## Files changed since your first attempt",
|
|
60901
|
+
`Your first attempt (task ${failedLabel}) was reviewed and approved, but could not be applied: ${attributed.length} file(s) changed in the workspace after your attempt. Re-do the request against the CURRENT files. Each diff below goes from the contents you produced (your reviewed version) to the contents now in the workspace.`,
|
|
60902
|
+
changedSection,
|
|
60903
|
+
"",
|
|
60904
|
+
"## Your reviewed contents from the first attempt",
|
|
60905
|
+
contentsSection
|
|
60906
|
+
].join(`
|
|
60907
|
+
`);
|
|
60908
|
+
return capText2(assembled, RETRY_BRIEF_TOTAL_CAP);
|
|
60415
60909
|
}
|
|
60416
60910
|
/**
|
|
60417
60911
|
* CP-12 W2.b (§3.C.2b (d) / H3-4) — promote-quiescence BARRIER for `taskId`.
|
|
@@ -60434,6 +60928,12 @@ ${section}`);
|
|
|
60434
60928
|
return this.promoteShadowLockedInner(taskId, originEpoch);
|
|
60435
60929
|
}
|
|
60436
60930
|
async promoteShadowLockedInner(taskId, originEpoch) {
|
|
60931
|
+
let singleContext = this.singleTaskContextByTask.get(taskId), retryContext = singleContext ? {
|
|
60932
|
+
brief: singleContext.brief,
|
|
60933
|
+
agent: singleContext.agent,
|
|
60934
|
+
...singleContext.retryOf ? { retryOf: singleContext.retryOf } : {},
|
|
60935
|
+
...singleContext.request !== void 0 ? { request: singleContext.request } : {}
|
|
60936
|
+
} : void 0;
|
|
60437
60937
|
try {
|
|
60438
60938
|
let teamAuthority = this.teamAuthorityForTask(taskId);
|
|
60439
60939
|
if (this.teamRunIsHalted(teamAuthority)) return;
|
|
@@ -60446,9 +60946,15 @@ ${section}`);
|
|
|
60446
60946
|
error: err?.message
|
|
60447
60947
|
}), historySnapshot = null;
|
|
60448
60948
|
}
|
|
60449
|
-
this.userNotesByTask.delete(taskId), this.reviewScopeResetTasks.delete(taskId), this.reviewScopeResetEligibleTasks.delete(taskId), this.roundHistoryByTask.delete(taskId), this.
|
|
60949
|
+
this.userNotesByTask.delete(taskId), this.reviewScopeResetTasks.delete(taskId), this.reviewScopeResetEligibleTasks.delete(taskId), this.roundHistoryByTask.delete(taskId), this.forgetSingleTask(taskId, "apply_failed"), this.rationaleByTask.delete(taskId), this.taskDegradedMetadataByTask.delete(taskId), this.expectedRosterByTask.delete(taskId), this.activeGateByTaskId.delete(taskId), this.conflictedSeatsByTask.delete(taskId), this.nonApprovedSeatsByTask.delete(taskId), this.restartFenceByTaskId.delete(taskId);
|
|
60450
60950
|
let shadow = this.shadowsByTask.get(taskId);
|
|
60451
60951
|
if (!shadow) {
|
|
60952
|
+
if (this.singleTaskHistory.get(taskId)?.outcome === "discarded") {
|
|
60953
|
+
this.surfaceHalt(
|
|
60954
|
+
`Could not apply task ${this.labelFor(taskId)} \u2014 its workspace copy was discarded after its implementor round failed on this side; nothing was applied. Re-run the request.`
|
|
60955
|
+
), logger.warn("[QuorumLoop] promote on a discarded task \u2014 nothing applied", { taskId });
|
|
60956
|
+
return;
|
|
60957
|
+
}
|
|
60452
60958
|
logger.info("[QuorumLoop] promote no-op \u2014 no in-memory shadow for task", { taskId });
|
|
60453
60959
|
return;
|
|
60454
60960
|
}
|
|
@@ -60698,7 +61204,8 @@ ${section}`);
|
|
|
60698
61204
|
shadow,
|
|
60699
61205
|
diff,
|
|
60700
61206
|
terminalOutcomeReserved,
|
|
60701
|
-
historySnapshot
|
|
61207
|
+
historySnapshot,
|
|
61208
|
+
retryContext
|
|
60702
61209
|
);
|
|
60703
61210
|
} finally {
|
|
60704
61211
|
this.emitProgress({ phase: "progress_cleared" }, originEpoch);
|
|
@@ -60716,7 +61223,7 @@ ${section}`);
|
|
|
60716
61223
|
* no-op/re-delivery/discard returns (which must stay silent). All emits carry the
|
|
60717
61224
|
* caller-resolved `originEpoch`.
|
|
60718
61225
|
*/
|
|
60719
|
-
async promoteApplyLocked(taskId, originEpoch, shadow, diff, terminalOutcomeReserved, historySnapshot) {
|
|
61226
|
+
async promoteApplyLocked(taskId, originEpoch, shadow, diff, terminalOutcomeReserved, historySnapshot, retryContext) {
|
|
60720
61227
|
this.emitProgress({ phase: "promoting", files: diff.length }, originEpoch);
|
|
60721
61228
|
let teamPromoteEntry = this.activeBriefByTask.get(taskId), captureManifest = this.deps.durableStore && teamPromoteEntry && teamPromoteEntry.taskGroupId.length > 0 ? {
|
|
60722
61229
|
trackIndex: teamPromoteEntry.trackIndex,
|
|
@@ -60736,21 +61243,85 @@ ${section}`);
|
|
|
60736
61243
|
}
|
|
60737
61244
|
} : void 0, res = await shadow.promote(diff, captureManifest ? { captureManifest } : void 0);
|
|
60738
61245
|
if (res.conflicts.length > 0 || res.errors.length > 0) {
|
|
60739
|
-
let paths = [...res.conflicts, ...res.errors.map((e) => e.path)];
|
|
60740
|
-
if (
|
|
60741
|
-
|
|
60742
|
-
|
|
60743
|
-
logger.warn("[QuorumLoop] unresolved promotion retained for reconciliation", {
|
|
61246
|
+
let paths = [...res.conflicts, ...res.errors.map((e) => e.path)], label = this.labelFor(taskId);
|
|
61247
|
+
if (res.mutationState === "unresolved") {
|
|
61248
|
+
this.surfaceHalt(
|
|
61249
|
+
`Could not apply task ${label} \u2014 ${paths.length} file(s) at ${paths.join(", ")} changed under the task or failed to write; the snapshot is retained until it is reconciled.`
|
|
61250
|
+
), logger.warn("[QuorumLoop] unresolved promotion retained for reconciliation", {
|
|
60744
61251
|
taskId,
|
|
60745
61252
|
paths
|
|
61253
|
+
}), this.markTaskOutcome(taskId, "unresolved");
|
|
61254
|
+
return;
|
|
61255
|
+
}
|
|
61256
|
+
let isSingleTask = retryContext !== void 0 && !this.activeBriefByTask.has(taskId), attributed = isSingleTask ? await this.attributeConflicts(shadow, res.conflicts) : [], writeErrors = res.errors.length > 0, retryBrief = null, rootRequest = retryContext ? retryContext.request ?? retryContext.brief : "";
|
|
61257
|
+
if (isSingleTask)
|
|
61258
|
+
if (attributed.length > 0)
|
|
61259
|
+
try {
|
|
61260
|
+
retryBrief = await this.buildRetryBrief(
|
|
61261
|
+
shadow,
|
|
61262
|
+
taskId,
|
|
61263
|
+
rootRequest,
|
|
61264
|
+
attributed,
|
|
61265
|
+
this.reviewedSnapshotByTask.get(taskId) ?? []
|
|
61266
|
+
);
|
|
61267
|
+
} catch (err) {
|
|
61268
|
+
logger.warn("[QuorumLoop] retry brief composition failed (offering the original request only)", {
|
|
61269
|
+
taskId,
|
|
61270
|
+
err: err.message
|
|
61271
|
+
}), retryBrief = rootRequest;
|
|
61272
|
+
}
|
|
61273
|
+
else
|
|
61274
|
+
retryBrief = rootRequest;
|
|
61275
|
+
if (await this.discardShadowLocked(taskId, shadow), this.markTaskOutcome(taskId, "apply_failed"), terminalOutcomeReserved && this.terminallyRetiredWorkspaceTasks.has(taskId) && await this.workspaceOutcomeSink.completeTask(taskId, "apply_failed"), !isSingleTask || retryBrief === null) {
|
|
61276
|
+
this.surfaceHalt(
|
|
61277
|
+
`Could not apply task ${label} \u2014 ${paths.length} file(s) at ${paths.join(", ")} changed under the task or failed to write. The approved change was not applied.`
|
|
61278
|
+
);
|
|
61279
|
+
return;
|
|
61280
|
+
}
|
|
61281
|
+
let sessionChanged = attributed.filter((c) => c.cause === "session-task"), byLabels = [...new Set(sessionChanged.map((c) => this.labelFor(c.byTaskId)))], causes = [];
|
|
61282
|
+
sessionChanged.length > 0 && causes.push(`${sessionChanged.length} changed by task ${byLabels.join(", ")}`), attributed.length - sessionChanged.length > 0 && causes.push(`${attributed.length - sessionChanged.length} changed outside CodeVibe`), writeErrors && causes.push(`${res.errors.length} could not be written`);
|
|
61283
|
+
let total = attributed.length + res.errors.length, summary = `${total} ${total === 1 ? "file" : "files"} (${causes.join("; ")})`;
|
|
61284
|
+
if (!writeErrors && attributed.every((c) => c.cause === "session-task") && retryContext.retryOf === void 0 && !this.autoRetryIssuedFor.has(taskId)) {
|
|
61285
|
+
this.autoRetryIssuedFor.add(taskId), this.pendingPromoteRetries.push({ retryOf: taskId, brief: retryBrief, agent: retryContext.agent, request: rootRequest }), this.surfaceHalt(
|
|
61286
|
+
`Could not apply task ${label}: ${summary} after it was reviewed. Retrying automatically as a new task on a fresh snapshot (it will be reviewed and approved again).`
|
|
61287
|
+
), logger.info("[QuorumLoop] promote collided with a session task \u2014 automatic retry planned", {
|
|
61288
|
+
taskId,
|
|
61289
|
+
conflicts: attributed.length
|
|
60746
61290
|
});
|
|
60747
61291
|
return;
|
|
60748
61292
|
}
|
|
60749
|
-
|
|
61293
|
+
let autoRetried = retryContext.retryOf !== void 0 && this.autoRetryIssuedFor.has(retryContext.retryOf);
|
|
61294
|
+
this.surfaceHalt(
|
|
61295
|
+
`Could not apply task ${label}: ${summary}` + (autoRetried ? " \u2014 the automatic retry collided too." : ".")
|
|
61296
|
+
);
|
|
61297
|
+
let menu = {
|
|
61298
|
+
taskId,
|
|
61299
|
+
taskLabel: label,
|
|
61300
|
+
conflicts: [
|
|
61301
|
+
...attributed.map((c) => ({
|
|
61302
|
+
path: c.path,
|
|
61303
|
+
cause: c.cause,
|
|
61304
|
+
...c.byTaskId ? { byTaskLabel: this.labelFor(c.byTaskId) } : {}
|
|
61305
|
+
})),
|
|
61306
|
+
...res.errors.map((e) => ({ path: e.path, cause: "write-error" }))
|
|
61307
|
+
],
|
|
61308
|
+
autoRetried,
|
|
61309
|
+
retryBrief,
|
|
61310
|
+
request: rootRequest,
|
|
61311
|
+
agent: retryContext.agent
|
|
61312
|
+
};
|
|
61313
|
+
try {
|
|
61314
|
+
this.applyConflictSink?.(menu);
|
|
61315
|
+
} catch (err) {
|
|
61316
|
+
logger.warn("[QuorumLoop] apply-conflict menu sink threw (ignored)", {
|
|
61317
|
+
taskId,
|
|
61318
|
+
err: err.message
|
|
61319
|
+
});
|
|
61320
|
+
}
|
|
60750
61321
|
return;
|
|
60751
61322
|
}
|
|
60752
61323
|
if (!res.materialized)
|
|
60753
|
-
if (res.recovered)
|
|
61324
|
+
if (this.markTaskOutcome(taskId, "applied"), res.recovered)
|
|
60754
61325
|
logger.info("[QuorumLoop] recovered fully-applied snapshot promotion", { taskId });
|
|
60755
61326
|
else {
|
|
60756
61327
|
logger.debug("[QuorumLoop] promote no-op \u2014 marker flipped to promoted mid-call", { taskId }), terminalOutcomeReserved && await this.workspaceOutcomeSink.completeTask(taskId, "applied", {
|
|
@@ -60769,6 +61340,19 @@ ${section}`);
|
|
|
60769
61340
|
return;
|
|
60770
61341
|
}
|
|
60771
61342
|
let appliedPaths = res.recovered ? diff.map((file) => file.path) : res.promoted;
|
|
61343
|
+
if (this.markTaskOutcome(taskId, "applied"), retryContext !== void 0 && !this.activeBriefByTask.has(taskId)) {
|
|
61344
|
+
let at = (/* @__PURE__ */ new Date()).toISOString();
|
|
61345
|
+
for (let rel of appliedPaths)
|
|
61346
|
+
try {
|
|
61347
|
+
let meta = await shadow.readRealMetadata(rel);
|
|
61348
|
+
this.promotedByPath.set(rel, { taskId, postHash: meta.hash, postMode: meta.mode, at });
|
|
61349
|
+
} catch (err) {
|
|
61350
|
+
this.promotedByPath.delete(rel), logger.debug("[QuorumLoop] promoted-path post-image read failed (left unattributed)", {
|
|
61351
|
+
taskId,
|
|
61352
|
+
err: err.message
|
|
61353
|
+
});
|
|
61354
|
+
}
|
|
61355
|
+
}
|
|
60772
61356
|
terminalOutcomeReserved && await this.workspaceOutcomeSink.completeTask(taskId, "applied", {
|
|
60773
61357
|
filesApplied: appliedPaths.length
|
|
60774
61358
|
}), logger.info("[QuorumLoop] shadow promoted to real tree", {
|
|
@@ -61021,7 +61605,11 @@ ${section}`);
|
|
|
61021
61605
|
});
|
|
61022
61606
|
return;
|
|
61023
61607
|
}
|
|
61024
|
-
if (
|
|
61608
|
+
if (logger.info("[QuorumLoop] ReviewerDispatch received", {
|
|
61609
|
+
gateId: payload.gateId,
|
|
61610
|
+
seatId: seat,
|
|
61611
|
+
...dispatchTaskId ? { taskId: dispatchTaskId } : {}
|
|
61612
|
+
}), dispatchTaskId) {
|
|
61025
61613
|
let roster = this.expectedRosterByTask.get(dispatchTaskId);
|
|
61026
61614
|
roster || (roster = /* @__PURE__ */ new Map(), this.expectedRosterByTask.set(dispatchTaskId, roster)), roster.set(String(seat), String(payload.role || "reviewer"));
|
|
61027
61615
|
}
|
|
@@ -61306,7 +61894,7 @@ ${section}`);
|
|
|
61306
61894
|
"[QuorumLoop] reviewer-sandboxing DISABLED by operator opt-out (CODEVIBE_SANDBOX_REVIEWERS=0) \u2014 legacy path",
|
|
61307
61895
|
{ key }
|
|
61308
61896
|
), NO_TEARDOWN;
|
|
61309
|
-
let taskId = this.taskByGateId.get(args.gateId)
|
|
61897
|
+
let taskId = this.taskByGateId.get(args.gateId);
|
|
61310
61898
|
if (!taskId)
|
|
61311
61899
|
return this.surfaceReviewerReducedTrust(
|
|
61312
61900
|
args,
|
|
@@ -61871,32 +62459,51 @@ ${section}`);
|
|
|
61871
62459
|
// ─── §3.C.21b — dropped-packet recovery ─────────────────────────────────
|
|
61872
62460
|
/**
|
|
61873
62461
|
* On WS (re)connect/startup, poll for the seats assigned to this desktop for
|
|
61874
|
-
* any in_review gate of
|
|
61875
|
-
*
|
|
61876
|
-
*
|
|
62462
|
+
* any in_review gate of EVERY in-flight task (P46a B3: the live single tasks,
|
|
62463
|
+
* the tasks with an active implementor, and the registered team tracks — one
|
|
62464
|
+
* query each) and spawn any seat NEITHER already running-in-memory NOR already
|
|
62465
|
+
* verdict-submitted (double-guard). Without this, a REVIEWER_DISPATCH dropped
|
|
62466
|
+
* during a WS gap hangs the gate forever. Residual (unchanged): after a full
|
|
62467
|
+
* desktop restart the in-memory maps are empty.
|
|
61877
62468
|
*/
|
|
61878
62469
|
async recoverInReviewAssignments() {
|
|
61879
|
-
if (this.shuttingDown
|
|
61880
|
-
let
|
|
61881
|
-
|
|
61882
|
-
|
|
61883
|
-
|
|
61884
|
-
|
|
61885
|
-
|
|
61886
|
-
});
|
|
61887
|
-
|
|
61888
|
-
|
|
61889
|
-
|
|
61890
|
-
|
|
61891
|
-
|
|
61892
|
-
|
|
61893
|
-
|
|
61894
|
-
|
|
61895
|
-
|
|
61896
|
-
|
|
61897
|
-
|
|
61898
|
-
}
|
|
62470
|
+
if (this.shuttingDown) return;
|
|
62471
|
+
let taskIds = /* @__PURE__ */ new Set([
|
|
62472
|
+
...this.singleTaskContextByTask.keys(),
|
|
62473
|
+
...this.activeImplementorByTask.keys(),
|
|
62474
|
+
...this.activeBriefByTask.keys()
|
|
62475
|
+
]);
|
|
62476
|
+
if (taskIds.size !== 0) {
|
|
62477
|
+
logger.info("[QuorumLoop] recovering in-review assignments", { taskIds: [...taskIds] });
|
|
62478
|
+
for (let taskId of taskIds) {
|
|
62479
|
+
if (this.shuttingDown) return;
|
|
62480
|
+
let assignments;
|
|
62481
|
+
try {
|
|
62482
|
+
assignments = await this.deps.appsyncClient.getInReviewAssignments(taskId);
|
|
62483
|
+
} catch (err) {
|
|
62484
|
+
logger.warn("[QuorumLoop] getInReviewAssignments failed (non-fatal)", {
|
|
62485
|
+
taskId,
|
|
62486
|
+
err: err.message
|
|
62487
|
+
});
|
|
62488
|
+
continue;
|
|
62489
|
+
}
|
|
62490
|
+
if (this.shuttingDown) return;
|
|
62491
|
+
for (let a of assignments) {
|
|
62492
|
+
if (this.shuttingDown) break;
|
|
62493
|
+
let key = seatKey(a.gateId, a.seatId);
|
|
62494
|
+
this.runningSeats.has(key) || this.submittedSeats.has(key) || (logger.info("[QuorumLoop] recovered in-review seat (its REVIEWER_DISPATCH was not consumed)", {
|
|
62495
|
+
taskId,
|
|
62496
|
+
gateId: a.gateId,
|
|
62497
|
+
seatId: a.seatId
|
|
62498
|
+
}), this.spawnOneSeat({
|
|
62499
|
+
gateId: a.gateId,
|
|
62500
|
+
seatId: a.seatId,
|
|
62501
|
+
role: a.role,
|
|
62502
|
+
agentKind: a.agentKind
|
|
62503
|
+
}));
|
|
62504
|
+
}
|
|
61899
62505
|
}
|
|
62506
|
+
}
|
|
61900
62507
|
}
|
|
61901
62508
|
// ─── §4 / #PR-4 / #GC-1 — shadow recovery + GC on reconnect/startup ──────
|
|
61902
62509
|
/**
|
|
@@ -61936,7 +62543,7 @@ ${section}`);
|
|
|
61936
62543
|
]), signal);
|
|
61937
62544
|
if (!this.admittedSessionWorkMayContinue(admission)) return;
|
|
61938
62545
|
let now = Date.now(), ttlMs = env.ttlMs ?? DEFAULT_SHADOW_TTL_MS, keep = /* @__PURE__ */ new Set();
|
|
61939
|
-
this.
|
|
62546
|
+
for (let taskId of this.singleTaskContextByTask.keys()) keep.add(taskId);
|
|
61940
62547
|
for (let taskId of this.shadowsByTask.keys()) keep.add(taskId);
|
|
61941
62548
|
for (let taskId of this.shadowCreateInFlight.keys()) keep.add(taskId);
|
|
61942
62549
|
let failures = [], sameWorkspace = recoverable.filter(
|
|
@@ -62821,7 +63428,7 @@ ${section}`);
|
|
|
62821
63428
|
});
|
|
62822
63429
|
return;
|
|
62823
63430
|
}
|
|
62824
|
-
let taskId = this.taskByGateId.get(payload.gateId) ??
|
|
63431
|
+
let taskId = this.taskByGateId.get(payload.gateId) ?? "", originEpoch = this.originEpochFor(taskId);
|
|
62825
63432
|
if (taskId === "") {
|
|
62826
63433
|
logger.warn(
|
|
62827
63434
|
"[QuorumLoop] ReviseFeedback could not resolve a task (in-memory state lost after restart?) \u2014 failing loud",
|
|
@@ -62831,7 +63438,17 @@ ${section}`);
|
|
|
62831
63438
|
), this.emitProgress({ phase: "progress_cleared" }, originEpoch);
|
|
62832
63439
|
return;
|
|
62833
63440
|
}
|
|
62834
|
-
let trackEntry = this.activeBriefByTask.get(taskId),
|
|
63441
|
+
let trackEntry = this.activeBriefByTask.get(taskId), singleEntry = this.singleTaskContextByTask.get(taskId);
|
|
63442
|
+
if (!trackEntry && !singleEntry) {
|
|
63443
|
+
logger.warn(
|
|
63444
|
+
"[QuorumLoop] ReviseFeedback names a task with no live record (ended, or lost after restart?) \u2014 failing loud",
|
|
63445
|
+
{ taskId, gateId: payload.gateId, nextGateId: payload.nextGateId }
|
|
63446
|
+
), this.surfaceHalt(
|
|
63447
|
+
`Could not resume this revise for task ${this.labelFor(taskId)} \u2014 its context is gone (re-issue the task to continue).`
|
|
63448
|
+
), this.emitProgress({ phase: "progress_cleared" }, originEpoch);
|
|
63449
|
+
return;
|
|
63450
|
+
}
|
|
63451
|
+
let parentTeamAuthority = this.teamAuthorityByGateId.get(payload.gateId), teamAuthority = parentTeamAuthority ?? (trackEntry ? _QuorumLoop.teamAuthorityForEntry(trackEntry) : void 0);
|
|
62835
63452
|
if (parentTeamAuthority && (!trackEntry || !this.teamEntryMayContinue(taskId, trackEntry, parentTeamAuthority))) {
|
|
62836
63453
|
logger.info("[QuorumLoop] stale team ReviseFeedback refused \u2014 generation retired", {
|
|
62837
63454
|
taskId,
|
|
@@ -62866,7 +63483,9 @@ ${section}`);
|
|
|
62866
63483
|
gateId: payload.nextGateId,
|
|
62867
63484
|
roundNumber: payload.nextRound,
|
|
62868
63485
|
brief,
|
|
62869
|
-
|
|
63486
|
+
// P46a B1 — the TRACK's agent (team) or the single task's OWN agent (its
|
|
63487
|
+
// record is guaranteed above); never a session-wide "active agent".
|
|
63488
|
+
agent: trackEntry?.agent ?? singleEntry.agent,
|
|
62870
63489
|
sessionKey,
|
|
62871
63490
|
...teamAuthority ? { teamAuthority } : {},
|
|
62872
63491
|
...priorRationale ? { priorRationale } : {},
|
|
@@ -62882,32 +63501,53 @@ ${section}`);
|
|
|
62882
63501
|
} : {}
|
|
62883
63502
|
});
|
|
62884
63503
|
}
|
|
62885
|
-
|
|
62886
|
-
|
|
63504
|
+
/**
|
|
63505
|
+
* P46a B1 — set a single task's implementor agent on ITS record (the only
|
|
63506
|
+
* caller is `resumeFromContinuation`, after the user chose a target agent).
|
|
63507
|
+
* Replaces the session-wide `setActiveImplementorAgent` singleton.
|
|
63508
|
+
*/
|
|
63509
|
+
setTaskImplementorAgent(taskId, agent) {
|
|
63510
|
+
let current = this.singleTaskContextByTask.get(taskId);
|
|
63511
|
+
current && this.singleTaskContextByTask.set(taskId, { ...current, agent });
|
|
63512
|
+
let history = this.singleTaskHistory.get(taskId);
|
|
63513
|
+
history && (history.agent = agent);
|
|
62887
63514
|
}
|
|
62888
63515
|
// ─── PHASE-CP-10-MIN (#585) — continuation handoff ────────────────────────
|
|
62889
63516
|
/**
|
|
62890
|
-
* #585 #C10M-9 — resolve
|
|
62891
|
-
* request`
|
|
62892
|
-
*
|
|
62893
|
-
*
|
|
62894
|
-
*
|
|
62895
|
-
*
|
|
63517
|
+
* #585 #C10M-9 / P46a B1 (OQ-1, decided) — resolve WHICH single task a
|
|
63518
|
+
* `/continue request` means: the FOCUSED task if it has an active implementor
|
|
63519
|
+
* round; else the single task that has one; else refuse — `ambiguous` lists
|
|
63520
|
+
* the candidates (the shell renders their `#n` so the user can
|
|
63521
|
+
* `/task focus <n>` first), `none` means no task is running a round. The
|
|
63522
|
+
* `gateId`/`roundNumber` come from the ACTIVE implementor registry (the round
|
|
63523
|
+
* currently running); the brief + source agent from the task's own record.
|
|
62896
63524
|
*/
|
|
62897
|
-
|
|
62898
|
-
let
|
|
62899
|
-
|
|
62900
|
-
|
|
62901
|
-
if (
|
|
62902
|
-
|
|
63525
|
+
resolveContinuationRequest() {
|
|
63526
|
+
let withActiveRound = [...this.singleTaskContextByTask.keys()].filter(
|
|
63527
|
+
(id) => this.activeImplementorByTask.has(id)
|
|
63528
|
+
), taskId = null;
|
|
63529
|
+
if (this.focusedTaskId && this.activeImplementorByTask.has(this.focusedTaskId))
|
|
63530
|
+
taskId = this.focusedTaskId;
|
|
63531
|
+
else if (withActiveRound.length === 1)
|
|
63532
|
+
taskId = withActiveRound[0];
|
|
63533
|
+
else return withActiveRound.length === 0 ? { kind: "none" } : { kind: "ambiguous", candidates: withActiveRound };
|
|
63534
|
+
let active = this.activeImplementorByTask.get(taskId), single = this.singleTaskContextByTask.get(taskId);
|
|
62903
63535
|
return {
|
|
62904
|
-
|
|
62905
|
-
|
|
62906
|
-
|
|
62907
|
-
|
|
62908
|
-
|
|
63536
|
+
kind: "ok",
|
|
63537
|
+
ctx: {
|
|
63538
|
+
taskId,
|
|
63539
|
+
gateId: active.gateId,
|
|
63540
|
+
roundNumber: active.roundNumber,
|
|
63541
|
+
brief: this.activeBriefByTask.get(taskId)?.brief ?? single.brief,
|
|
63542
|
+
sourceAgent: single.agent
|
|
63543
|
+
}
|
|
62909
63544
|
};
|
|
62910
63545
|
}
|
|
63546
|
+
/** The `ok` projection of {@link resolveContinuationRequest} (null otherwise). */
|
|
63547
|
+
getActiveContinuationRequestContext() {
|
|
63548
|
+
let resolved = this.resolveContinuationRequest();
|
|
63549
|
+
return resolved.kind === "ok" ? resolved.ctx : null;
|
|
63550
|
+
}
|
|
62911
63551
|
/**
|
|
62912
63552
|
* #585 L-A — mint a fresh `expiresAt = now + TTL` at millisecond precision
|
|
62913
63553
|
* (RFC-3339). Fresh per request so a CANCEL→re-request produces a distinct
|
|
@@ -63192,9 +63832,7 @@ ${section}`);
|
|
|
63192
63832
|
}
|
|
63193
63833
|
if (trackEntry && teamAuthority && !this.teamEntryMayContinue(input.taskId, trackEntry, teamAuthority) || this.shuttingDown) return !1;
|
|
63194
63834
|
let brief = this.buildResumeBrief(packet);
|
|
63195
|
-
this.
|
|
63196
|
-
let resumedSingle = this.singleTaskContextByTask.get(input.taskId);
|
|
63197
|
-
resumedSingle && this.singleTaskContextByTask.set(input.taskId, { brief: resumedSingle.brief, agent: input.targetAgent }), logger.info("[QuorumLoop] resuming from continuation", {
|
|
63835
|
+
this.setTaskImplementorAgent(input.taskId, input.targetAgent), logger.info("[QuorumLoop] resuming from continuation", {
|
|
63198
63836
|
taskId: input.taskId,
|
|
63199
63837
|
sourceAgent: packet.sourceAgent,
|
|
63200
63838
|
targetAgent: input.targetAgent,
|
|
@@ -63319,7 +63957,7 @@ ${section}`);
|
|
|
63319
63957
|
* user requirement.
|
|
63320
63958
|
*/
|
|
63321
63959
|
buildReviseBrief(taskId, payload) {
|
|
63322
|
-
let originalBrief = this.activeBriefByTask.get(taskId)?.brief ?? this.singleTaskContextByTask.get(taskId)?.brief ??
|
|
63960
|
+
let originalBrief = this.activeBriefByTask.get(taskId)?.brief ?? this.singleTaskContextByTask.get(taskId)?.brief ?? "", userNotes = this.userNotesByTask.get(taskId) ?? [], history = (this.roundHistoryByTask.get(taskId) ?? []).filter(
|
|
63323
63961
|
(r) => r.round < payload.nextRound - 1
|
|
63324
63962
|
);
|
|
63325
63963
|
return composeReviseBrief(originalBrief, userNotes, payload, history);
|
|
@@ -63480,9 +64118,16 @@ ${section}`);
|
|
|
63480
64118
|
get _submittedSeatsForTests() {
|
|
63481
64119
|
return this.submittedSeats;
|
|
63482
64120
|
}
|
|
63483
|
-
/**
|
|
63484
|
-
|
|
63485
|
-
|
|
64121
|
+
/**
|
|
64122
|
+
* @internal — test seam to register a single task (its record + focus)
|
|
64123
|
+
* without start_task. P46a B1: the record is what every reader resolves, so
|
|
64124
|
+
* the seam always creates one (an empty brief when none is given).
|
|
64125
|
+
*/
|
|
64126
|
+
_setActiveTaskForTests(taskId, brief, agent = "CLAUDE", gateIds = []) {
|
|
64127
|
+
this.reviewScopeResetEligibleTasks.add(taskId);
|
|
64128
|
+
let existing = this.singleTaskContextByTask.get(taskId), record = { brief: brief ?? existing?.brief ?? "", agent: existing?.agent ?? agent };
|
|
64129
|
+
this.singleTaskContextByTask.set(taskId, record), this.singleTaskHistory.has(taskId) || this.singleTaskHistory.set(taskId, { ...record, outcome: "running" }), this.confirmedSingleOrder.includes(taskId) || this.confirmedSingleOrder.push(taskId), this.focusedTaskId = taskId;
|
|
64130
|
+
for (let gateId of gateIds) this.taskByGateId.set(gateId, taskId);
|
|
63486
64131
|
}
|
|
63487
64132
|
/** @internal — read a task's accumulated binding user notes. */
|
|
63488
64133
|
_userNotesForTests(taskId) {
|
|
@@ -64331,6 +64976,23 @@ function resolveBrowseUrls(modelUrls, prompt) {
|
|
|
64331
64976
|
let usable = modelUrls.filter(isWellFormedHttpUrl);
|
|
64332
64977
|
return usable.length > 0 ? usable : void 0;
|
|
64333
64978
|
}
|
|
64979
|
+
function numberedLabel(state, taskId) {
|
|
64980
|
+
let label = taskLabel(state, taskId);
|
|
64981
|
+
return label.startsWith("#") ? label : null;
|
|
64982
|
+
}
|
|
64983
|
+
function findActiveContinuationOfferEntry(conversation) {
|
|
64984
|
+
for (let entry of conversation)
|
|
64985
|
+
if (entry.kind === "gate-prompt" && entry.final === !1 && entry.envelope.promptKind === CONTINUATION_OFFER_HANDOFF_PROMPT_KIND)
|
|
64986
|
+
return entry;
|
|
64987
|
+
return null;
|
|
64988
|
+
}
|
|
64989
|
+
function mobileGateReplyAmbiguity(state, text2) {
|
|
64990
|
+
if (!/^\s*\d+\s*$/.test(text2)) return null;
|
|
64991
|
+
let open15 = state.conversation.filter(
|
|
64992
|
+
(e) => e.kind === "gate-prompt" && e.final === !1 && (e.uiState.phase === "awaiting-number" || e.uiState.phase === "awaiting-notes")
|
|
64993
|
+
);
|
|
64994
|
+
return open15.length <= 1 ? null : `Several reviews are open on the desktop (${[...new Set(open15.map((e) => taskLabel(state, e.envelope.taskId)))].map((l) => `task ${l}`).join(", ")}); a number from the phone cannot choose between them yet \u2014 answer on the desktop, or wait until one review is left.`;
|
|
64995
|
+
}
|
|
64334
64996
|
var OrchestrationShellStartupError = class extends Error {
|
|
64335
64997
|
constructor(message, cause) {
|
|
64336
64998
|
super(message), this.name = "OrchestrationShellStartupError", this.cause = cause;
|
|
@@ -64433,11 +65095,21 @@ function wireTeamMergeAuditSummary(store, target) {
|
|
|
64433
65095
|
}
|
|
64434
65096
|
async function runOrchestrationShell(args) {
|
|
64435
65097
|
let store = createOrchestrationStore({ session: args.session }), originalStoreDispatch = store.dispatch.bind(store);
|
|
64436
|
-
store.dispatch = ((action) =>
|
|
64437
|
-
action.envelope.
|
|
64438
|
-
|
|
64439
|
-
|
|
64440
|
-
|
|
65098
|
+
store.dispatch = ((action) => {
|
|
65099
|
+
action && action.type === "GATE_PROMPT_RECEIVED" && action.envelope && args.quorumLoop && typeof args.quorumLoop.registerActiveGate == "function" && args.quorumLoop.registerActiveGate(
|
|
65100
|
+
action.envelope.taskId,
|
|
65101
|
+
action.envelope.gateId,
|
|
65102
|
+
action.envelope.currentRound ?? 0
|
|
65103
|
+
);
|
|
65104
|
+
let gateAlreadyShown = action && action.type === "GATE_PROMPT_RECEIVED" && action.envelope ? store.getState().conversation.some(
|
|
65105
|
+
(e) => e.kind === "gate-prompt" && (e.envelope.gateId === action.envelope.gateId || e.queue.some((queued) => queued.gateId === action.envelope.gateId))
|
|
65106
|
+
) : !0, dispatched = originalStoreDispatch(action);
|
|
65107
|
+
if (action && action.type === "GATE_PROMPT_RECEIVED" && action.envelope && !gateAlreadyShown) {
|
|
65108
|
+
let described = typeof args.quorumLoop?.describeTasks == "function" ? args.quorumLoop.describeTasks() : [], advisory = endedTaskAdvisory(described, action.envelope.taskId);
|
|
65109
|
+
advisory && originalStoreDispatch({ type: "SHELL_ADVISORY", source: "shell", text: advisory, localOnly: !0 });
|
|
65110
|
+
}
|
|
65111
|
+
return dispatched;
|
|
65112
|
+
});
|
|
64441
65113
|
let advisoryAttachmentJournal = advisoryAttachmentJournalFor(args);
|
|
64442
65114
|
try {
|
|
64443
65115
|
await advisoryAttachmentJournal.recoverDeadOwners();
|
|
@@ -64455,7 +65127,23 @@ async function runOrchestrationShell(args) {
|
|
|
64455
65127
|
store.dispatch({ type: "SHELL_ADVISORY", source: "shell", text: message });
|
|
64456
65128
|
}
|
|
64457
65129
|
}) : void 0;
|
|
64458
|
-
args.workspaceOutcomeTap && (args.workspaceOutcomeTap.sink = workspaceTerminalCoordinator), args.quorumLoop?.setWorkspaceOutcomeSink?.(workspaceTerminalCoordinator),
|
|
65130
|
+
args.workspaceOutcomeTap && (args.workspaceOutcomeTap.sink = workspaceTerminalCoordinator), args.quorumLoop?.setWorkspaceOutcomeSink?.(workspaceTerminalCoordinator), args.quorumLoop?.setTaskOrdinalSink?.({
|
|
65131
|
+
assign: (taskId, opts) => (store.dispatch({ type: "TASK_ORDINAL_ASSIGNED", taskId, kind: opts.kind, ...opts.retryOf ? { retryOf: opts.retryOf } : {} }), store.getState().taskOrdinals.get(taskId)?.ordinal),
|
|
65132
|
+
label: (taskId) => taskLabel(store.getState(), taskId)
|
|
65133
|
+
}), args.quorumLoop?.setApplyConflictSink?.((menu) => {
|
|
65134
|
+
store.dispatch({ type: "APPLY_CONFLICT_PRESENTED", menu });
|
|
65135
|
+
}), args.quorumLoop?.setRetryStartedSink?.((info) => {
|
|
65136
|
+
store.dispatch({
|
|
65137
|
+
type: "TASK_LIFECYCLE",
|
|
65138
|
+
task: {
|
|
65139
|
+
taskId: info.taskId,
|
|
65140
|
+
agentKind: info.agent,
|
|
65141
|
+
pid: 0,
|
|
65142
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
65143
|
+
status: "running"
|
|
65144
|
+
}
|
|
65145
|
+
});
|
|
65146
|
+
}), plannerOfferE1Ledger.beginRun(
|
|
64459
65147
|
(promptIds) => args.appsyncClient.refreshOpenPromptTtl(promptIds)
|
|
64460
65148
|
);
|
|
64461
65149
|
let unsubscribeWaitingUser = null;
|
|
@@ -64959,8 +65647,13 @@ async function runOrchestrationShell(args) {
|
|
|
64959
65647
|
if (shellSubmissionsFenced) return;
|
|
64960
65648
|
let fromMobile = options?.fromMobile === !0, convLenBefore = store.getState().conversation.length, userTurnTimestamp = (/* @__PURE__ */ new Date()).toISOString(), turnOwnership = { brainstormPanelOwned: !1 }, ownEntries = [];
|
|
64961
65649
|
await turnAuthoringContext.run({ ownEntries }, async () => {
|
|
64962
|
-
let handledByGate = !1;
|
|
64963
|
-
if (
|
|
65650
|
+
let handledByGate = !1, mobileAmbiguity = fromMobile ? mobileGateReplyAmbiguity(store.getState(), text2) : null;
|
|
65651
|
+
if (mobileAmbiguity ? (store.dispatch({
|
|
65652
|
+
type: "SHELL_ADVISORY",
|
|
65653
|
+
source: "shell",
|
|
65654
|
+
text: mobileAmbiguity,
|
|
65655
|
+
handoffExcluded: !0
|
|
65656
|
+
}), handledByGate = !0) : fromMobile && findActiveGatePromptEntry(store.getState().conversation, store.getState().activeGatePromptId) && (mobileGateDecisionDeps === null ? (store.dispatch({
|
|
64964
65657
|
type: "SHELL_ADVISORY",
|
|
64965
65658
|
source: "shell",
|
|
64966
65659
|
text: "The desktop is still preparing the interactive prompt. Please send your choice again.",
|
|
@@ -65292,9 +65985,13 @@ async function runOrchestrationShell(args) {
|
|
|
65292
65985
|
}
|
|
65293
65986
|
}));
|
|
65294
65987
|
}, onTerminalDecision = args.quorumLoop ? async (resolvedTaskId, postAction, expectedShadow, canonicalDecision, gateResolution) => {
|
|
65295
|
-
|
|
65296
|
-
|
|
65297
|
-
|
|
65988
|
+
if (!resolvedTaskId) {
|
|
65989
|
+
logger.warn("[orchestration-shell] terminal decision without a task id \u2014 no shadow effect applied", {
|
|
65990
|
+
kind: postAction.kind
|
|
65991
|
+
});
|
|
65992
|
+
return;
|
|
65993
|
+
}
|
|
65994
|
+
let taskId = resolvedTaskId, finalApprovalCancelled = postAction.kind === "final_approval_resolved" && postAction.decision.trim().toLowerCase() !== "approve", terminalAbort = postAction.kind === "abort_task";
|
|
65298
65995
|
workspaceTerminalCoordinator?.hasTaskResolution(taskId) && (finalApprovalCancelled || terminalAbort) && await workspaceTerminalCoordinator.reserveTask(
|
|
65299
65996
|
taskId,
|
|
65300
65997
|
finalApprovalCancelled ? "CANCEL" : "ABORT"
|
|
@@ -65560,7 +66257,7 @@ async function runOrchestrationShell(args) {
|
|
|
65560
66257
|
// Codex LOW). Only `handled:false` falls through to the planner.
|
|
65561
66258
|
resolveGateInput: (text2) => routeGatePromptInput(
|
|
65562
66259
|
gateDecisionDeps,
|
|
65563
|
-
findActiveGatePromptEntry(store.getState().conversation),
|
|
66260
|
+
findActiveGatePromptEntry(store.getState().conversation, store.getState().activeGatePromptId),
|
|
65564
66261
|
text2
|
|
65565
66262
|
),
|
|
65566
66263
|
signal: nonTtyAbort.signal
|
|
@@ -66097,11 +66794,11 @@ async function routeMobileUserPrompt(event, sessionKeyResolver, submit, updateEv
|
|
|
66097
66794
|
}
|
|
66098
66795
|
}
|
|
66099
66796
|
async function routeMobileGatePromptInput(deps, text2) {
|
|
66100
|
-
let before = findActiveGatePromptEntry(deps.store.getState().conversation);
|
|
66797
|
+
let before = findActiveGatePromptEntry(deps.store.getState().conversation, deps.store.getState().activeGatePromptId);
|
|
66101
66798
|
if (!before) return !1;
|
|
66102
66799
|
let beforePhase = before.uiState.phase;
|
|
66103
66800
|
if (!(await routeGatePromptInput(deps, before, text2)).handled) return !1;
|
|
66104
|
-
let after = findActiveGatePromptEntry(deps.store.getState().conversation);
|
|
66801
|
+
let after = findActiveGatePromptEntry(deps.store.getState().conversation, deps.store.getState().activeGatePromptId);
|
|
66105
66802
|
return beforePhase === "awaiting-number" && after?.id === before.id && after.uiState.phase === "awaiting-notes" && deps.store.dispatch({
|
|
66106
66803
|
type: "SHELL_ADVISORY",
|
|
66107
66804
|
source: "shell",
|
|
@@ -66280,14 +66977,28 @@ function routeTeamShellEventToStore(store, evt) {
|
|
|
66280
66977
|
}
|
|
66281
66978
|
function parseTaskCommand(text2) {
|
|
66282
66979
|
let rest = text2.trim().slice(5).trim();
|
|
66283
|
-
if (rest.length === 0) return { agent: null, brief: "" };
|
|
66980
|
+
if (rest.length === 0) return { agent: null, brief: "", focus: null, conflictChoice: null };
|
|
66981
|
+
let focusMatch = /^focus\s+#?(\d{1,6})$/i.exec(rest);
|
|
66982
|
+
if (focusMatch)
|
|
66983
|
+
return { agent: null, brief: "", focus: Number.parseInt(focusMatch[1], 10), conflictChoice: null };
|
|
66984
|
+
let conflictMatch = /^(retry|discard)\s+#?(\d{1,6})$/i.exec(rest);
|
|
66985
|
+
if (conflictMatch)
|
|
66986
|
+
return {
|
|
66987
|
+
agent: null,
|
|
66988
|
+
brief: "",
|
|
66989
|
+
focus: null,
|
|
66990
|
+
conflictChoice: {
|
|
66991
|
+
choice: conflictMatch[1].toLowerCase() === "retry" ? 1 : 2,
|
|
66992
|
+
ordinal: Number.parseInt(conflictMatch[2], 10)
|
|
66993
|
+
}
|
|
66994
|
+
};
|
|
66284
66995
|
let tokens = rest.split(/\s+/), agent = null, startIdx = 0;
|
|
66285
66996
|
if (tokens[0] === "--agent") {
|
|
66286
66997
|
let value = (tokens[1] ?? "").toUpperCase();
|
|
66287
66998
|
value === "CLAUDE" || value === "CODEX" || value === "ANTIGRAVITY" ? (agent = value, startIdx = 2) : startIdx = 1;
|
|
66288
66999
|
}
|
|
66289
67000
|
let brief = tokens.slice(startIdx).join(" ").trim();
|
|
66290
|
-
return { agent, brief };
|
|
67001
|
+
return { agent, brief, focus: null, conflictChoice: null };
|
|
66291
67002
|
}
|
|
66292
67003
|
function resolveEnvImplementorAgentOverride() {
|
|
66293
67004
|
let raw = (process.env.CODEVIBE_IMPLEMENTOR_AGENT ?? "").trim().toUpperCase();
|
|
@@ -67084,10 +67795,10 @@ async function launchTeamFromWorkItems(deps) {
|
|
|
67084
67795
|
});
|
|
67085
67796
|
if (result.accepted && result.taskGroupId) {
|
|
67086
67797
|
let gid = result.taskGroupId;
|
|
67087
|
-
store.dispatch({ type: "TEAM_STARTED", taskGroupId: gid }), store.dispatch({
|
|
67798
|
+
store.dispatch({ type: "TEAM_STARTED", taskGroupId: gid }), store.dispatch({ type: "TASK_ORDINAL_ASSIGNED", taskId: gid, kind: "group" }), store.dispatch({
|
|
67088
67799
|
type: "SHELL_ADVISORY",
|
|
67089
67800
|
source: "shell",
|
|
67090
|
-
text: `Agent Teams group
|
|
67801
|
+
text: `Task ${taskLabel(store.getState(), gid)} started \u2014 Agent Teams group (${gid}), ${result.dispatchedTracks ?? workItems.length} tracks dispatched.`
|
|
67091
67802
|
});
|
|
67092
67803
|
try {
|
|
67093
67804
|
createShellEventEmitter(appsyncClient)({
|
|
@@ -67207,10 +67918,10 @@ async function dispatchSynthesizedSingleStartTask(deps) {
|
|
|
67207
67918
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
67208
67919
|
status: "running"
|
|
67209
67920
|
}
|
|
67210
|
-
}),
|
|
67921
|
+
}), store.dispatch({
|
|
67211
67922
|
type: "SHELL_ADVISORY",
|
|
67212
67923
|
source: "shell",
|
|
67213
|
-
text: note
|
|
67924
|
+
text: `Task ${taskLabel(store.getState(), result.taskId)} started with ${selectedAgent}.${note ? ` ${note}` : ""}`
|
|
67214
67925
|
})) : store.dispatch({
|
|
67215
67926
|
type: "SHELL_ADVISORY",
|
|
67216
67927
|
source: "shell",
|
|
@@ -67853,6 +68564,12 @@ function buildContinuationActionDeps(args, store) {
|
|
|
67853
68564
|
let loop = args.quorumLoop;
|
|
67854
68565
|
return loop ? {
|
|
67855
68566
|
getActiveRequestContext: () => loop.getActiveContinuationRequestContext(),
|
|
68567
|
+
// P46a B1 (OQ-1) — when no single task resolves, say why: several tasks are
|
|
68568
|
+
// running a round (list their `#n` so the user can `/task focus <n>`), or none.
|
|
68569
|
+
explainNoActiveRequest: () => {
|
|
68570
|
+
let resolved = loop.resolveContinuationRequest();
|
|
68571
|
+
return resolved.kind === "ambiguous" ? "Several tasks are running \u2014 focus one first with `/task focus <n>`: " + resolved.candidates.map((id) => taskLabel(store.getState(), id)).join(", ") + "." : "No active task to hand off.";
|
|
68572
|
+
},
|
|
67856
68573
|
request: async (ctx, targetAgent) => loop.requestContinuation({
|
|
67857
68574
|
taskId: ctx.taskId,
|
|
67858
68575
|
gateId: ctx.gateId,
|
|
@@ -67863,8 +68580,8 @@ function buildContinuationActionDeps(args, store) {
|
|
|
67863
68580
|
...targetAgent ? { targetAgent } : {}
|
|
67864
68581
|
}),
|
|
67865
68582
|
getActiveOfferContext: () => {
|
|
67866
|
-
let entry =
|
|
67867
|
-
if (!entry
|
|
68583
|
+
let entry = findActiveContinuationOfferEntry(store.getState().conversation);
|
|
68584
|
+
if (!entry)
|
|
67868
68585
|
return null;
|
|
67869
68586
|
let offerId = entry.envelope.offerId;
|
|
67870
68587
|
return typeof offerId != "string" || offerId.length === 0 ? null : {
|
|
@@ -68151,6 +68868,42 @@ function dispatchRefusalAdvisory(store, decision) {
|
|
|
68151
68868
|
function isInteractiveTty() {
|
|
68152
68869
|
return !!process.stdout.isTTY && process.env.CODEVIBE_NO_TUI !== "1";
|
|
68153
68870
|
}
|
|
68871
|
+
async function resolveApplyConflict(deps) {
|
|
68872
|
+
let { store, args, pending, choice, emit } = deps;
|
|
68873
|
+
store.dispatch({ type: "CLEAR_PENDING_APPLY_CONFLICT", taskId: pending.taskId });
|
|
68874
|
+
let echo = `\u2192 ${choice}. ${choice === 1 ? "Retry as a new task" : "Discard"}`;
|
|
68875
|
+
emit === "advisory" && store.dispatch({ type: "SHELL_ADVISORY", source: "shell", text: echo, localOnly: !0 });
|
|
68876
|
+
let result = choice === 1 ? await dispatchApplyConflictRetry({ store, quorumLoop: args.quorumLoop, menu: pending }) : `Discarded task ${pending.taskLabel} \u2014 your files are unchanged.`;
|
|
68877
|
+
return emit === "advisory" && store.dispatch({ type: "SHELL_ADVISORY", source: "shell", text: result }), [result];
|
|
68878
|
+
}
|
|
68879
|
+
async function dispatchApplyConflictRetry(deps) {
|
|
68880
|
+
let { store, quorumLoop, menu } = deps;
|
|
68881
|
+
if (!quorumLoop)
|
|
68882
|
+
return "Task start is unavailable in this session.";
|
|
68883
|
+
try {
|
|
68884
|
+
let result = await quorumLoop.startTask({
|
|
68885
|
+
decision: { action: "start_task", rationale: `retry of task ${menu.taskLabel} after an apply conflict` },
|
|
68886
|
+
brief: menu.retryBrief,
|
|
68887
|
+
agent: menu.agent,
|
|
68888
|
+
retryOf: menu.taskId,
|
|
68889
|
+
request: menu.request
|
|
68890
|
+
});
|
|
68891
|
+
return result ? (store.dispatch({
|
|
68892
|
+
type: "TASK_LIFECYCLE",
|
|
68893
|
+
task: {
|
|
68894
|
+
taskId: result.taskId,
|
|
68895
|
+
agentKind: menu.agent,
|
|
68896
|
+
pid: 0,
|
|
68897
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
68898
|
+
status: "running"
|
|
68899
|
+
}
|
|
68900
|
+
}), `Task ${taskLabel(store.getState(), result.taskId)} started with ${menu.agent} \u2014 the retry of task ${menu.taskLabel}.`) : `Couldn't start the retry of task ${menu.taskLabel} (the task START was rejected). Please re-issue the request.`;
|
|
68901
|
+
} catch (err) {
|
|
68902
|
+
return logger.warn("[orchestration-shell] apply-conflict retry dispatch failed (non-fatal)", {
|
|
68903
|
+
error: err.message
|
|
68904
|
+
}), `Couldn't start the retry of task ${menu.taskLabel}: ${err.message}`;
|
|
68905
|
+
}
|
|
68906
|
+
}
|
|
68154
68907
|
async function resolvePlannerOffer(deps) {
|
|
68155
68908
|
let { store, args, emitShellEventBound, generator, ensureFreshContextStoreFn, offer, choice } = deps;
|
|
68156
68909
|
await plannerOfferE1Ledger.resolveOffer(
|
|
@@ -68304,7 +69057,38 @@ async function handleShellUserInput(deps) {
|
|
|
68304
69057
|
browseSignal = deps.browseSignal
|
|
68305
69058
|
} = deps;
|
|
68306
69059
|
if (text2.trim().length === 0) return;
|
|
68307
|
-
let slashCommandText = text2.trimStart(),
|
|
69060
|
+
let slashCommandText = text2.trimStart(), pendingConflicts = [...store.getState().pendingApplyConflicts.values()];
|
|
69061
|
+
if (pendingConflicts.length > 0 && deps.inputOrigin !== "mobile") {
|
|
69062
|
+
let reply = text2.trim(), openLabels = pendingConflicts.map((m) => `task ${m.taskLabel}`).join(", "), conflictHint = () => {
|
|
69063
|
+
store.dispatch({
|
|
69064
|
+
type: "SHELL_ADVISORY",
|
|
69065
|
+
source: "shell",
|
|
69066
|
+
text: pendingConflicts.length === 1 ? `Reply 1 to retry task ${pendingConflicts[0].taskLabel} as a new task or 2 to discard it (or /task retry <n> / /task discard <n>); other text closes the menu.` : `Several apply-conflict menus are open (${openLabels}) \u2014 answer each with /task retry <n> or /task discard <n>; other text closes them.` + // Stage 1 r3 N2 — a pending suggestion's number is taken by the menus until they close.
|
|
69067
|
+
(store.getState().pendingPlannerOffer ? " The pending suggestion can be answered by number once they are closed." : ""),
|
|
69068
|
+
localOnly: !0
|
|
69069
|
+
});
|
|
69070
|
+
};
|
|
69071
|
+
if (/^[0-9]$/.test(reply)) {
|
|
69072
|
+
let choice = Number.parseInt(reply, 10);
|
|
69073
|
+
if (pendingConflicts.length === 1 && (choice === 1 || choice === 2)) {
|
|
69074
|
+
await resolveApplyConflict({ store, args, pending: pendingConflicts[0], choice, emit: "advisory" });
|
|
69075
|
+
return;
|
|
69076
|
+
}
|
|
69077
|
+
conflictHint();
|
|
69078
|
+
return;
|
|
69079
|
+
}
|
|
69080
|
+
if (/^[0-9]/.test(reply)) {
|
|
69081
|
+
conflictHint();
|
|
69082
|
+
return;
|
|
69083
|
+
}
|
|
69084
|
+
reply.startsWith("/") || (store.dispatch({ type: "CLEAR_PENDING_APPLY_CONFLICT" }), store.dispatch({
|
|
69085
|
+
type: "SHELL_ADVISORY",
|
|
69086
|
+
source: "shell",
|
|
69087
|
+
text: pendingConflicts.length === 1 ? `Closed the apply-conflict menu for task ${pendingConflicts[0].taskLabel} \u2014 no retry was started; re-run the request if you still want that change.` : `Closed the apply-conflict menus for ${openLabels} \u2014 no retry was started; re-run the requests if you still want those changes.`,
|
|
69088
|
+
localOnly: !0
|
|
69089
|
+
}));
|
|
69090
|
+
}
|
|
69091
|
+
let pendingOffer = store.getState().pendingPlannerOffer;
|
|
68308
69092
|
if (pendingOffer) {
|
|
68309
69093
|
let interactionAdvisoryFlags = deps.inputOrigin === "mobile" ? { handoffExcluded: !0 } : { localOnly: !0 }, offerReply = text2.trim(), optionCount = pendingOffer.options.length, offerHint = () => {
|
|
68310
69094
|
store.dispatch({
|
|
@@ -68550,7 +69334,12 @@ async function handleShellUserInput(deps) {
|
|
|
68550
69334
|
kind: "gated",
|
|
68551
69335
|
headline: AUDIT_BROWSER_MAX_HEADLINE,
|
|
68552
69336
|
upgradeHint: AUDIT_BROWSER_UPGRADE_HINT
|
|
68553
|
-
}) : renderSessionTaskList(
|
|
69337
|
+
}) : renderSessionTaskList(
|
|
69338
|
+
store.getState(),
|
|
69339
|
+
// P46a B1 — every live single task (not only "the latest") may
|
|
69340
|
+
// still be inside its `startTask` without a lifecycle row.
|
|
69341
|
+
typeof args.quorumLoop?.knownSingleTasks == "function" ? args.quorumLoop.knownSingleTasks() : args.quorumLoop?.activeTask ? [args.quorumLoop.activeTask] : []
|
|
69342
|
+
);
|
|
68554
69343
|
else
|
|
68555
69344
|
try {
|
|
68556
69345
|
let effectiveSessionId = explicitSessionId || args.session.sessionId, result = runAuditBrowserFn ? await runAuditBrowserFn(taskId, {
|
|
@@ -68639,14 +69428,33 @@ async function handleShellUserInput(deps) {
|
|
|
68639
69428
|
}), output.output = "") : output.output = loaded.message;
|
|
68640
69429
|
}
|
|
68641
69430
|
else if (output.command === "/review-reset" && !output.sideEffect && output.output === "PENDING \u2014 entrypoint arms comprehensive review")
|
|
68642
|
-
|
|
69431
|
+
if (!args.quorumLoop)
|
|
69432
|
+
output.output = "/review-reset requires an orchestration session with a running loop (Pro/Max). It is unavailable in this session.";
|
|
69433
|
+
else {
|
|
69434
|
+
let reset = args.quorumLoop.requestReviewScopeReset();
|
|
69435
|
+
reset.armed ? output.output = `Task ${taskLabel(store.getState(), reset.taskId)}: the next revise round will start a fresh comprehensive review baseline.` : reset.candidates.length > 0 ? output.output = "The focused task has no review round to reset. Tasks that do: " + reset.candidates.map((id) => taskLabel(store.getState(), id)).join(", ") + " \u2014 run `/task focus <n>` first." : output.output = "/review-reset requires an active task. Start a task before requesting a reset.";
|
|
69436
|
+
}
|
|
68643
69437
|
else if (output.command === "/task" && !output.sideEffect && output.output === "PENDING \u2014 entrypoint drives startTask")
|
|
68644
69438
|
if (!args.quorumLoop)
|
|
68645
69439
|
output.output = "/task requires an orchestration session with a running loop (Pro/Max). It is unavailable in this session.";
|
|
68646
69440
|
else {
|
|
68647
69441
|
let parsed = parseTaskCommand(slashCommandText);
|
|
68648
|
-
if (
|
|
68649
|
-
|
|
69442
|
+
if (parsed.conflictChoice !== null) {
|
|
69443
|
+
let { choice, ordinal } = parsed.conflictChoice, open15 = store.getState().pendingApplyConflicts, target = taskIdForOrdinal(store.getState(), ordinal), pending2 = target === null ? void 0 : open15.get(target);
|
|
69444
|
+
if (deps.inputOrigin === "mobile")
|
|
69445
|
+
output.output = "The apply-conflict menu is answered on the desktop (it is not mirrored to the phone yet).";
|
|
69446
|
+
else if (pending2)
|
|
69447
|
+
output.output = (await resolveApplyConflict({ store, args, pending: pending2, choice, emit: "collect" })).join(`
|
|
69448
|
+
`);
|
|
69449
|
+
else {
|
|
69450
|
+
let openLabels = [...open15.values()].map((m) => `task ${m.taskLabel}`).join(", ");
|
|
69451
|
+
output.output = `No apply-conflict menu is open for task #${ordinal}.` + (open15.size > 0 ? ` Open: ${openLabels}.` : "");
|
|
69452
|
+
}
|
|
69453
|
+
} else if (parsed.focus !== null) {
|
|
69454
|
+
let target = taskIdForOrdinal(store.getState(), parsed.focus);
|
|
69455
|
+
target === null ? output.output = `No task #${parsed.focus} in this session. Run /status to list the tasks.` : args.quorumLoop.setFocusedTask(target) ? output.output = `Focused task ${taskLabel(store.getState(), target)}.` : output.output = `Task ${taskLabel(store.getState(), target)} is not a running single task (it has ended, or it is a team group).`;
|
|
69456
|
+
} else if (!parsed.brief)
|
|
69457
|
+
output.output = "Usage: /task [--agent claude|codex|antigravity] <implementation-request> | /task focus <n> | /task retry <n> | /task discard <n>. Pro/Max only; runs without planner classification.";
|
|
68650
69458
|
else {
|
|
68651
69459
|
let detected = typeof args.quorumLoop.getDetectedAgents == "function" ? args.quorumLoop.getDetectedAgents() : [], { agent, note } = selectImplementorAgent(detected, parsed.agent);
|
|
68652
69460
|
try {
|
|
@@ -68666,16 +69474,21 @@ async function handleShellUserInput(deps) {
|
|
|
68666
69474
|
agent,
|
|
68667
69475
|
...turnAttachments.length ? { attachments: turnAttachments } : {}
|
|
68668
69476
|
});
|
|
68669
|
-
result
|
|
68670
|
-
|
|
68671
|
-
|
|
68672
|
-
|
|
68673
|
-
|
|
68674
|
-
|
|
68675
|
-
|
|
68676
|
-
|
|
68677
|
-
|
|
68678
|
-
|
|
69477
|
+
if (result) {
|
|
69478
|
+
store.dispatch({
|
|
69479
|
+
type: "TASK_LIFECYCLE",
|
|
69480
|
+
task: {
|
|
69481
|
+
taskId: result.taskId,
|
|
69482
|
+
agentKind: agent,
|
|
69483
|
+
pid: 0,
|
|
69484
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
69485
|
+
status: "running"
|
|
69486
|
+
}
|
|
69487
|
+
});
|
|
69488
|
+
let startedLabel = taskLabel(store.getState(), result.taskId);
|
|
69489
|
+
output.output = note ? `Started task ${startedLabel} with ${agent}. ${note}` : `Started task ${startedLabel} with ${agent}.`;
|
|
69490
|
+
} else
|
|
69491
|
+
output.output = "Could not start the task (no session key / START rejected). Check that you are signed in and try again.";
|
|
68679
69492
|
} catch (err) {
|
|
68680
69493
|
output.output = `Failed to start task: ${err.message ?? String(err)}`, logger.warn("[orchestration-shell] /task dispatch failed", {
|
|
68681
69494
|
error: err.message
|
|
@@ -69135,10 +69948,10 @@ async function handleShellUserInput(deps) {
|
|
|
69135
69948
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
69136
69949
|
status: "running"
|
|
69137
69950
|
}
|
|
69138
|
-
}),
|
|
69951
|
+
}), store.dispatch({
|
|
69139
69952
|
type: "SHELL_ADVISORY",
|
|
69140
69953
|
source: "shell",
|
|
69141
|
-
text: note
|
|
69954
|
+
text: `Task ${taskLabel(store.getState(), result.taskId)} started with ${selectedAgent}.${note ? ` ${note}` : ""}`
|
|
69142
69955
|
})) : store.dispatch({
|
|
69143
69956
|
type: "SHELL_ADVISORY",
|
|
69144
69957
|
source: "shell",
|
|
@@ -69676,17 +70489,20 @@ async function emitWithProvenNoWriteDowngrade(emitter, args) {
|
|
|
69676
70489
|
let result = await emitter(args);
|
|
69677
70490
|
!result.emitted && args.disableWriterAttestation !== !0 && (result.reason === "writer_ineligible" || result.reason === "definitive_rejection") && await emitter({ ...args, disableWriterAttestation: !0 });
|
|
69678
70491
|
}
|
|
69679
|
-
function renderSessionTaskList(state,
|
|
70492
|
+
function renderSessionTaskList(state, liveTaskIds = []) {
|
|
69680
70493
|
let tasks = [...state.sessionTasks.values()].sort(
|
|
69681
70494
|
(a, b) => b.startedAt.localeCompare(a.startedAt)
|
|
69682
|
-
),
|
|
69683
|
-
if (tasks.length === 0 &&
|
|
70495
|
+
), unlisted = liveTaskIds.filter((id) => !state.sessionTasks.has(id));
|
|
70496
|
+
if (tasks.length === 0 && unlisted.length === 0)
|
|
69684
70497
|
return "No tasks have run in this session yet. Once a task runs, `/audit <task-id>` opens its full review history.";
|
|
69685
70498
|
let lines = ["Tasks this session (newest first):"];
|
|
69686
|
-
|
|
70499
|
+
for (let id of [...unlisted].reverse()) {
|
|
70500
|
+
let n = numberedLabel(state, id);
|
|
70501
|
+
lines.push(` ${id} \u2014 task${n ? ` ${n}` : ""} \xB7 running \xB7 starting`);
|
|
70502
|
+
}
|
|
69687
70503
|
for (let t of tasks)
|
|
69688
70504
|
lines.push(
|
|
69689
|
-
` ${t.taskId} \u2014 ${t.agentKind ?? "team"} \xB7 ${t.status} \xB7 started ${t.startedAt}`
|
|
70505
|
+
` ${t.taskId} \u2014 ${numberedLabel(state, t.taskId) ? `${numberedLabel(state, t.taskId)} \xB7 ` : ""}${t.agentKind ?? "team"} \xB7 ${t.status} \xB7 started ${t.startedAt}`
|
|
69690
70506
|
);
|
|
69691
70507
|
return lines.push("Run `/audit <task-id>` to open a task\u2019s full review history."), lines.join(`
|
|
69692
70508
|
`);
|
|
@@ -69702,7 +70518,20 @@ function parseIsoTimestamp(iso) {
|
|
|
69702
70518
|
return Number.isFinite(t) ? t : 0;
|
|
69703
70519
|
}
|
|
69704
70520
|
function buildStatusSummary(state, quorumLoop) {
|
|
69705
|
-
let lines = [];
|
|
70521
|
+
let lines = [], described = typeof quorumLoop?.describeTasks == "function" ? quorumLoop.describeTasks() : [];
|
|
70522
|
+
if (described.length > 0) {
|
|
70523
|
+
let awaiting = /* @__PURE__ */ new Set();
|
|
70524
|
+
for (let entry of state.conversation)
|
|
70525
|
+
entry.kind === "gate-prompt" && !entry.final && awaiting.add(entry.envelope.taskId);
|
|
70526
|
+
for (let taskId of state.pendingApplyConflicts.keys()) awaiting.add(taskId);
|
|
70527
|
+
lines.push("Tasks this session:");
|
|
70528
|
+
for (let t of described) {
|
|
70529
|
+
let stateLabel = t.outcome === "running" ? awaiting.has(t.taskId) ? "awaiting you" : t.implementorActive ? "implementing" : "in review" : t.outcome === "applied" ? "done" : t.outcome === "apply_failed" ? awaiting.has(t.taskId) ? "apply failed \u2014 awaiting you" : "apply failed" : t.outcome === "unresolved" ? "apply failed \xB7 retained" : t.outcome === "rejected" ? "not started" : "discarded", retry = t.retryOf ? ` \xB7 retry of ${taskLabel(state, t.retryOf)}` : "";
|
|
70530
|
+
lines.push(
|
|
70531
|
+
` ${t.label} \xB7 ${t.agent} \xB7 ${stateLabel}${retry} \xB7 ${truncate(sanitizeForTerminal(t.brief.replace(/\s+/g, " ").trim()), 60)}` + (t.focused ? " \u25C0 focused" : "")
|
|
70532
|
+
);
|
|
70533
|
+
}
|
|
70534
|
+
}
|
|
69706
70535
|
if (state.team) {
|
|
69707
70536
|
let t = state.team, indices = [...t.tracks.keys()].sort((a, b) => a - b), n = indices.length, trackParts = indices.map((i) => {
|
|
69708
70537
|
let e = t.tracks.get(i), details = [e.agent, e.taskId ? truncate(e.taskId, 12) : null].filter(
|
|
@@ -69717,21 +70546,27 @@ function buildStatusSummary(state, quorumLoop) {
|
|
|
69717
70546
|
let teamLine = `Agent Teams group${groupRef} \u2014 ${n} ${n === 1 ? "track" : "tracks"} (MergeGate: ${MERGE_GATE_STATUS_LABEL[t.mergeGate]}${mergeElapsedStr})`;
|
|
69718
70547
|
n > 0 && (teamLine += `: ${trackParts.join(", ")}`), teamLine += ".", t.groupResolved ? teamLine += t.outcome === "complete" ? " Team complete." : ` Team halted \u2014 ${t.outcome ?? t.haltReason ?? "unknown"}.` : t.haltReason && (teamLine += ` Halted: ${t.haltReason}.`), lines.push(teamLine);
|
|
69719
70548
|
}
|
|
69720
|
-
let hasRunning = hasRunningSingleTask(state),
|
|
70549
|
+
let hasRunning = hasRunningSingleTask(state), startingTasks = (typeof quorumLoop?.knownSingleTasks == "function" ? quorumLoop.knownSingleTasks() : quorumLoop?.activeTask ? [quorumLoop.activeTask] : []).filter((id) => !state.sessionTasks.has(id)), activeTask = startingTasks.length > 0 ? startingTasks[startingTasks.length - 1] : null, teamActive = state.team !== null && !state.team.groupResolved && state.team.haltReason === null, taskStarting = !hasRunning && !teamActive && activeTask !== null;
|
|
69721
70550
|
if (state.progress) {
|
|
69722
70551
|
let startedAt = state.progress.startedAt, elapsed = !startedAt || isNaN(Date.parse(startedAt)) ? null : formatElapsed(Date.now() - Date.parse(startedAt)), tokens = state.progress.tokens, tokenStr = tokens && tokens > 0 ? ` \xB7 \u2193 ${formatTokens(tokens)}` : "";
|
|
69723
70552
|
lines.push(
|
|
69724
70553
|
`Current task: ${state.progress.text}${elapsed ? ` \xB7 ${elapsed}` : ""}${tokenStr}.`
|
|
69725
70554
|
);
|
|
69726
|
-
} else hasRunning ? lines.push("A task is running.") : taskStarting && lines.push(
|
|
70555
|
+
} else hasRunning ? lines.push("A task is running.") : taskStarting && lines.push(
|
|
70556
|
+
`A task is starting (${startingTasks.map((id) => numberedLabel(state, id) ? `${numberedLabel(state, id)} ${id}` : id).join(", ")}).`
|
|
70557
|
+
);
|
|
69727
70558
|
!state.progress && state.lastReviewerProgress && lines.push(`Reviewers: ${state.lastReviewerProgress.text}.`);
|
|
69728
70559
|
for (let entry of state.conversation) {
|
|
69729
70560
|
if (entry.kind !== "gate-prompt" || entry.final) continue;
|
|
69730
70561
|
let label = GATE_PROMPT_KIND_LABEL[entry.envelope.promptKind] ?? entry.envelope.promptKind, queued = entry.queue.length > 0 ? `; ${entry.queue.length} more queued` : "";
|
|
69731
70562
|
lines.push(
|
|
69732
|
-
`Awaiting your decision: ${label} for task ${entry.envelope.taskId} \u2014 reply with an option number (1-${entry.envelope.options.length})${queued}.`
|
|
70563
|
+
`Awaiting your decision: ${label} for task ${numberedLabel(state, entry.envelope.taskId) ? `${numberedLabel(state, entry.envelope.taskId)} (${entry.envelope.taskId})` : entry.envelope.taskId} \u2014 reply with an option number (1-${entry.envelope.options.length})${queued}.`
|
|
69733
70564
|
);
|
|
69734
70565
|
}
|
|
70566
|
+
for (let menu of state.pendingApplyConflicts.values())
|
|
70567
|
+
lines.push(
|
|
70568
|
+
`Awaiting your decision: task ${menu.taskLabel} could not be applied \u2014 ` + (state.pendingApplyConflicts.size === 1 ? "reply 1 to retry as a new task or 2 to discard (or /task retry|discard <n>)." : `/task retry ${menu.taskLabel.replace(/^#/, "").split(" ")[0]} or /task discard ${menu.taskLabel.replace(/^#/, "").split(" ")[0]}.`)
|
|
70569
|
+
);
|
|
69735
70570
|
let rawOutcome = quorumLoop?.getLastWorkspaceOutcome() ?? null, outcome = null;
|
|
69736
70571
|
if (rawOutcome !== null && !teamActive)
|
|
69737
70572
|
if (rawOutcome.taskId) {
|
|
@@ -74926,14 +75761,10 @@ function buildAndArmQuorumLoop(args) {
|
|
|
74926
75761
|
// manifest BEFORE the tree mutates (the C1 capture is already gated on
|
|
74927
75762
|
// `this.deps.durableStore` inside `promoteShadowLocked`).
|
|
74928
75763
|
...args.durableStore ? { durableStore: args.durableStore } : {},
|
|
74929
|
-
//
|
|
74930
|
-
//
|
|
74931
|
-
//
|
|
74932
|
-
//
|
|
74933
|
-
// rejection by the loop. This is the SAME `taskId` state TaskAuthorized later
|
|
74934
|
-
// advances; arming it early closes the round-0 window where the implementor
|
|
74935
|
-
// would otherwise spawn unbadged/unsandboxed.
|
|
74936
|
-
armLeTaskId: (taskId) => localExecutor.setActiveTaskId(taskId),
|
|
75764
|
+
// P46a B2 — the executor keeps no singleton task id any more (the CP-7
|
|
75765
|
+
// `armLeTaskId` arming is gone): every implementor spawn carries its own
|
|
75766
|
+
// `taskId`, which engages the CP-7 substrate and resolves the per-task
|
|
75767
|
+
// authority scope, single tasks included.
|
|
74937
75768
|
// CP-7 §8 (Stage-1-resolved) — wire the REVIEWER substrate engager so each
|
|
74938
75769
|
// Trusted-agent reviewer seat runs INSIDE the CP-7 sandbox + broker (no
|
|
74939
75770
|
// ambient vendor creds; model call via the loopback broker), exactly like
|