@defend-tech/opencode-optima 0.1.50 → 0.1.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +60 -39
- package/dist/sanitize_cli.js +60 -39
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9719,7 +9719,13 @@ async function readOpenCodeJsonResponse(response, endpointName) {
|
|
|
9719
9719
|
return { raw };
|
|
9720
9720
|
}
|
|
9721
9721
|
}
|
|
9722
|
-
|
|
9722
|
+
function appendDirectoryQuery(url, directory) {
|
|
9723
|
+
const value = String(directory || "").trim();
|
|
9724
|
+
if (!value) return url;
|
|
9725
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
9726
|
+
return `${url}${separator}directory=${encodeURIComponent(value)}`;
|
|
9727
|
+
}
|
|
9728
|
+
async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, directory, fetchImpl = globalThis.fetch, legacyOnly = false } = {}) {
|
|
9723
9729
|
if (typeof fetchImpl !== "function") throw new Error("OpenCode direct prompt delivery requires fetch.");
|
|
9724
9730
|
const root = normalizeOpenCodeBaseUrl(baseUrl, "");
|
|
9725
9731
|
if (!root) throw new Error("OpenCode direct prompt delivery requires a base URL.");
|
|
@@ -9727,7 +9733,7 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
|
|
|
9727
9733
|
const attempts = [
|
|
9728
9734
|
legacyOnly ? null : {
|
|
9729
9735
|
name: "v2 prompt",
|
|
9730
|
-
url: `${root}/api/session/${encodedSession}/prompt`,
|
|
9736
|
+
url: appendDirectoryQuery(`${root}/api/session/${encodedSession}/prompt`, directory),
|
|
9731
9737
|
body: { prompt: { text }, delivery: "queue", resume: true },
|
|
9732
9738
|
accept: (response, data) => {
|
|
9733
9739
|
if (!response.ok) return null;
|
|
@@ -9737,7 +9743,7 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
|
|
|
9737
9743
|
},
|
|
9738
9744
|
{
|
|
9739
9745
|
name: "legacy async prompt",
|
|
9740
|
-
url: `${root}/session/${encodedSession}/prompt_async`,
|
|
9746
|
+
url: appendDirectoryQuery(`${root}/session/${encodedSession}/prompt_async`, directory),
|
|
9741
9747
|
body: { agent, parts: [{ type: "text", text }] },
|
|
9742
9748
|
accept: (response, data) => {
|
|
9743
9749
|
if (response.status !== 204 && !response.ok) return null;
|
|
@@ -9761,7 +9767,17 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
|
|
|
9761
9767
|
}
|
|
9762
9768
|
throw firstError || new Error("OpenCode direct prompt delivery failed.");
|
|
9763
9769
|
}
|
|
9764
|
-
|
|
9770
|
+
function tagOpenCodePromptResult(result, deliveryMethod) {
|
|
9771
|
+
if (result && typeof result === "object") {
|
|
9772
|
+
try {
|
|
9773
|
+
Object.defineProperty(result, "__optimaPromptDelivery", { value: deliveryMethod, enumerable: false, configurable: true });
|
|
9774
|
+
} catch {
|
|
9775
|
+
return { result, __optimaPromptDelivery: deliveryMethod };
|
|
9776
|
+
}
|
|
9777
|
+
}
|
|
9778
|
+
return result;
|
|
9779
|
+
}
|
|
9780
|
+
async function callOpenCodePromptWithFallbacks(method, sessionId, flatPayload, structuredPayload, deliveryMethod) {
|
|
9765
9781
|
const attempts = [
|
|
9766
9782
|
{ ...structuredPayload, path: { id: sessionId } },
|
|
9767
9783
|
{ ...structuredPayload, path: { sessionID: sessionId } },
|
|
@@ -9771,14 +9787,14 @@ async function callOpenCodePromptWithFallbacks(method, sessionId, flatPayload, s
|
|
|
9771
9787
|
let firstError = null;
|
|
9772
9788
|
for (const attempt of attempts) {
|
|
9773
9789
|
try {
|
|
9774
|
-
return assertOpenCodePromptAccepted(await method(attempt));
|
|
9790
|
+
return tagOpenCodePromptResult(assertOpenCodePromptAccepted(await method(attempt)), deliveryMethod);
|
|
9775
9791
|
} catch (error) {
|
|
9776
9792
|
firstError ??= error;
|
|
9777
9793
|
}
|
|
9778
9794
|
}
|
|
9779
9795
|
throw firstError;
|
|
9780
9796
|
}
|
|
9781
|
-
async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false, legacyOnly = false } = {}) {
|
|
9797
|
+
async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false, legacyOnly = false, allowDirectFallback = true } = {}) {
|
|
9782
9798
|
const directBaseUrl = opencodeBaseUrl || baseUrl;
|
|
9783
9799
|
const parts = [{ type: "text", text }];
|
|
9784
9800
|
const flatPayload = { directory, agent, parts };
|
|
@@ -9788,21 +9804,21 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
|
|
|
9788
9804
|
body: { agent, parts }
|
|
9789
9805
|
};
|
|
9790
9806
|
let firstError = null;
|
|
9791
|
-
if (!direct && typeof client?.session?.
|
|
9807
|
+
if (!direct && typeof client?.session?.prompt === "function") {
|
|
9792
9808
|
try {
|
|
9793
|
-
return await callOpenCodePromptWithFallbacks(client.session.
|
|
9809
|
+
return await callOpenCodePromptWithFallbacks(client.session.prompt.bind(client.session), sessionId, flatPayload, structuredPayload, "prompt");
|
|
9794
9810
|
} catch (error) {
|
|
9795
9811
|
firstError ??= error;
|
|
9796
9812
|
}
|
|
9797
9813
|
}
|
|
9798
|
-
if (!direct && typeof client?.session?.
|
|
9814
|
+
if (!direct && typeof client?.session?.promptAsync === "function") {
|
|
9799
9815
|
try {
|
|
9800
|
-
return await callOpenCodePromptWithFallbacks(client.session.
|
|
9816
|
+
return await callOpenCodePromptWithFallbacks(client.session.promptAsync.bind(client.session), sessionId, flatPayload, structuredPayload, "prompt_async");
|
|
9801
9817
|
} catch (error) {
|
|
9802
9818
|
firstError ??= error;
|
|
9803
9819
|
}
|
|
9804
9820
|
}
|
|
9805
|
-
if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, fetchImpl, legacyOnly });
|
|
9821
|
+
if (allowDirectFallback && directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, directory, fetchImpl, legacyOnly });
|
|
9806
9822
|
if (firstError) throw firstError;
|
|
9807
9823
|
throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
|
|
9808
9824
|
}
|
|
@@ -9837,6 +9853,11 @@ function openCodeMessageText(message) {
|
|
|
9837
9853
|
for (const part of partList) parts.push(part?.text, part?.content);
|
|
9838
9854
|
return parts.filter((value) => typeof value === "string").join("\n");
|
|
9839
9855
|
}
|
|
9856
|
+
function openCodeMessageStableKey(message, index = 0) {
|
|
9857
|
+
const id = message?.id || message?.messageID || message?.messageId || message?.data?.id || null;
|
|
9858
|
+
if (id) return `id:${id}`;
|
|
9859
|
+
return `idx:${index}:text:${openCodeMessageText(message)}`;
|
|
9860
|
+
}
|
|
9840
9861
|
async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 8, delayMs = 250 } = {}) {
|
|
9841
9862
|
let lastError = "message_verification_unavailable";
|
|
9842
9863
|
for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
|
|
@@ -9844,13 +9865,15 @@ async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMes
|
|
|
9844
9865
|
const afterMessages = await readOpenCodeSessionMessages(client, { sessionId, limit: 50 });
|
|
9845
9866
|
if (!afterMessages) return { ok: false, reason: "message_verification_unavailable" };
|
|
9846
9867
|
const beforeCount = Array.isArray(beforeMessages) ? beforeMessages.length : null;
|
|
9847
|
-
const
|
|
9868
|
+
const beforeKeys = Array.isArray(beforeMessages) ? new Set(beforeMessages.map(openCodeMessageStableKey)) : null;
|
|
9869
|
+
const newMessages = beforeKeys ? afterMessages.filter((message, index) => !beforeKeys.has(openCodeMessageStableKey(message, index))) : afterMessages;
|
|
9870
|
+
const lastMessage = newMessages.at(-1) || afterMessages.at(-1) || null;
|
|
9848
9871
|
const lastMessageId = lastMessage?.id || lastMessage?.messageID || lastMessage?.messageId || null;
|
|
9849
9872
|
if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9850
9873
|
const textNeedles = [expectedText, ...markers].map((value) => String(value || "").trim()).filter(Boolean);
|
|
9851
|
-
const haystack =
|
|
9874
|
+
const haystack = newMessages.slice(-20).map(openCodeMessageText).join("\n");
|
|
9852
9875
|
const matched = textNeedles.find((needle) => haystack.includes(needle));
|
|
9853
|
-
if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9876
|
+
if (matched && (!beforeKeys || newMessages.length > 0)) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9854
9877
|
lastError = "message_not_visible";
|
|
9855
9878
|
} catch (error) {
|
|
9856
9879
|
lastError = error.message || "message_verification_failed";
|
|
@@ -9873,46 +9896,44 @@ async function applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason,
|
|
|
9873
9896
|
return { ok: false, error: error.message, tagName };
|
|
9874
9897
|
}
|
|
9875
9898
|
}
|
|
9899
|
+
function openCodeBlockingPromptVerification(result, sessionId) {
|
|
9900
|
+
if (result?.__optimaPromptDelivery !== "prompt") return null;
|
|
9901
|
+
const response = result?.response ?? result;
|
|
9902
|
+
const data = response?.data ?? result?.data ?? null;
|
|
9903
|
+
const parts = normalizePromptResponseParts(result);
|
|
9904
|
+
const messageId = data?.id ?? response?.id ?? result?.id ?? data?.messageID ?? response?.messageID ?? result?.messageID;
|
|
9905
|
+
const deliveredSessionId = response?.sessionID ?? response?.sessionId ?? data?.sessionID ?? data?.sessionId ?? result?.sessionID ?? result?.sessionId;
|
|
9906
|
+
if (deliveredSessionId && String(deliveredSessionId) !== String(sessionId)) {
|
|
9907
|
+
throw new Error(`OpenCode blocking prompt targeted foreign session ${deliveredSessionId}.`);
|
|
9908
|
+
}
|
|
9909
|
+
if (parts.length > 0 || messageId) return { ok: true, method: parts.length > 0 ? "blocking_prompt_parts" : "blocking_prompt_message", messageId: messageId ? String(messageId) : null, sessionId: deliveredSessionId ? String(deliveredSessionId) : String(sessionId), parts: parts.length };
|
|
9910
|
+
return null;
|
|
9911
|
+
}
|
|
9876
9912
|
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
|
|
9877
9913
|
const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => null);
|
|
9878
|
-
const sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl });
|
|
9914
|
+
const sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, allowDirectFallback: false });
|
|
9915
|
+
let blockingPromptVerification = null;
|
|
9879
9916
|
let admissionVerification = null;
|
|
9880
9917
|
try {
|
|
9881
|
-
|
|
9918
|
+
blockingPromptVerification = openCodeBlockingPromptVerification(sendResult, sessionId);
|
|
9919
|
+
if (!blockingPromptVerification) admissionVerification = openCodePromptAdmissionVerification(sendResult, sessionId);
|
|
9882
9920
|
} catch (error) {
|
|
9883
9921
|
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason: error.message, fallbackAttempted: false });
|
|
9884
9922
|
if (!applyBlockerOnFailure) return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false };
|
|
9885
9923
|
const blocker2 = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: error.message, source: "delivery_admission_failed" });
|
|
9886
9924
|
return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false, blockerTag: blocker2 };
|
|
9887
9925
|
}
|
|
9926
|
+
if (blockingPromptVerification) return { ok: true, verification: blockingPromptVerification, admissionVerification: null, fallback: false };
|
|
9888
9927
|
let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages, expectedText: text, markers: eventMarkers });
|
|
9889
9928
|
if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: false };
|
|
9890
|
-
if (verification?.reason === "message_verification_unavailable" && !admissionVerification) {
|
|
9891
|
-
return { ok: true, verification: { ok: true, method: "legacy_prompt_accepted", skipped: true }, fallback: false };
|
|
9892
|
-
}
|
|
9893
9929
|
if (admissionVerification) {
|
|
9894
9930
|
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_admitted_but_invisible", taskId, sessionId, admission: admissionVerification, reason: verification?.reason || "message_not_visible" });
|
|
9895
9931
|
}
|
|
9896
|
-
const
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
admissionVerification = openCodePromptAdmissionVerification(retrySendResult, sessionId);
|
|
9902
|
-
} catch (error) {
|
|
9903
|
-
verification = { ok: false, reason: error.message };
|
|
9904
|
-
}
|
|
9905
|
-
verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages: retryBeforeMessages, expectedText: text, markers: eventMarkers });
|
|
9906
|
-
if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: true };
|
|
9907
|
-
if (verification?.reason === "message_verification_unavailable" && !admissionVerification) {
|
|
9908
|
-
return { ok: true, verification: { ok: true, method: "legacy_prompt_accepted", skipped: true }, fallback: true };
|
|
9909
|
-
}
|
|
9910
|
-
}
|
|
9911
|
-
const reason = verification?.reason || "message_delivery_failed";
|
|
9912
|
-
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason, fallbackAttempted: canFallbackDirect });
|
|
9913
|
-
if (!applyBlockerOnFailure) return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: canFallbackDirect };
|
|
9914
|
-
const blocker = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source: canFallbackDirect ? "delivery_fallback_failed" : "delivery_verification_failed" });
|
|
9915
|
-
return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: canFallbackDirect, blockerTag: blocker };
|
|
9932
|
+
const reason = verification?.reason || (admissionVerification ? "prompt_admission_not_delivered" : "message_delivery_failed");
|
|
9933
|
+
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason, fallbackAttempted: false, httpFallbackDisabled: Boolean(opencodeBaseUrl) });
|
|
9934
|
+
if (!applyBlockerOnFailure) return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: false };
|
|
9935
|
+
const blocker = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source: "delivery_verification_failed" });
|
|
9936
|
+
return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: false, blockerTag: blocker };
|
|
9916
9937
|
}
|
|
9917
9938
|
async function recoverClickUpPmSession({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, staleSessionId, sessionTitle, taskRoute, metadataWithRouting, config, prompt, eventMarkers = [], deliveryEvidencePath, evidencePath, eventKey, createSession, verifySessionEventDelivery } = {}) {
|
|
9918
9939
|
appendClickUpWebhookLocalLog(worktree, { type: "pm_session_recovery_started", taskId, staleSessionId, worktree: taskRoute?.worktree });
|
package/dist/sanitize_cli.js
CHANGED
|
@@ -9726,7 +9726,13 @@ async function readOpenCodeJsonResponse(response, endpointName) {
|
|
|
9726
9726
|
return { raw };
|
|
9727
9727
|
}
|
|
9728
9728
|
}
|
|
9729
|
-
|
|
9729
|
+
function appendDirectoryQuery(url, directory) {
|
|
9730
|
+
const value = String(directory || "").trim();
|
|
9731
|
+
if (!value) return url;
|
|
9732
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
9733
|
+
return `${url}${separator}directory=${encodeURIComponent(value)}`;
|
|
9734
|
+
}
|
|
9735
|
+
async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, directory, fetchImpl = globalThis.fetch, legacyOnly = false } = {}) {
|
|
9730
9736
|
if (typeof fetchImpl !== "function") throw new Error("OpenCode direct prompt delivery requires fetch.");
|
|
9731
9737
|
const root = normalizeOpenCodeBaseUrl(baseUrl, "");
|
|
9732
9738
|
if (!root) throw new Error("OpenCode direct prompt delivery requires a base URL.");
|
|
@@ -9734,7 +9740,7 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
|
|
|
9734
9740
|
const attempts = [
|
|
9735
9741
|
legacyOnly ? null : {
|
|
9736
9742
|
name: "v2 prompt",
|
|
9737
|
-
url: `${root}/api/session/${encodedSession}/prompt`,
|
|
9743
|
+
url: appendDirectoryQuery(`${root}/api/session/${encodedSession}/prompt`, directory),
|
|
9738
9744
|
body: { prompt: { text }, delivery: "queue", resume: true },
|
|
9739
9745
|
accept: (response, data) => {
|
|
9740
9746
|
if (!response.ok) return null;
|
|
@@ -9744,7 +9750,7 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
|
|
|
9744
9750
|
},
|
|
9745
9751
|
{
|
|
9746
9752
|
name: "legacy async prompt",
|
|
9747
|
-
url: `${root}/session/${encodedSession}/prompt_async`,
|
|
9753
|
+
url: appendDirectoryQuery(`${root}/session/${encodedSession}/prompt_async`, directory),
|
|
9748
9754
|
body: { agent, parts: [{ type: "text", text }] },
|
|
9749
9755
|
accept: (response, data) => {
|
|
9750
9756
|
if (response.status !== 204 && !response.ok) return null;
|
|
@@ -9768,7 +9774,17 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
|
|
|
9768
9774
|
}
|
|
9769
9775
|
throw firstError || new Error("OpenCode direct prompt delivery failed.");
|
|
9770
9776
|
}
|
|
9771
|
-
|
|
9777
|
+
function tagOpenCodePromptResult(result, deliveryMethod) {
|
|
9778
|
+
if (result && typeof result === "object") {
|
|
9779
|
+
try {
|
|
9780
|
+
Object.defineProperty(result, "__optimaPromptDelivery", { value: deliveryMethod, enumerable: false, configurable: true });
|
|
9781
|
+
} catch {
|
|
9782
|
+
return { result, __optimaPromptDelivery: deliveryMethod };
|
|
9783
|
+
}
|
|
9784
|
+
}
|
|
9785
|
+
return result;
|
|
9786
|
+
}
|
|
9787
|
+
async function callOpenCodePromptWithFallbacks(method, sessionId, flatPayload, structuredPayload, deliveryMethod) {
|
|
9772
9788
|
const attempts = [
|
|
9773
9789
|
{ ...structuredPayload, path: { id: sessionId } },
|
|
9774
9790
|
{ ...structuredPayload, path: { sessionID: sessionId } },
|
|
@@ -9778,14 +9794,14 @@ async function callOpenCodePromptWithFallbacks(method, sessionId, flatPayload, s
|
|
|
9778
9794
|
let firstError = null;
|
|
9779
9795
|
for (const attempt of attempts) {
|
|
9780
9796
|
try {
|
|
9781
|
-
return assertOpenCodePromptAccepted(await method(attempt));
|
|
9797
|
+
return tagOpenCodePromptResult(assertOpenCodePromptAccepted(await method(attempt)), deliveryMethod);
|
|
9782
9798
|
} catch (error) {
|
|
9783
9799
|
firstError ??= error;
|
|
9784
9800
|
}
|
|
9785
9801
|
}
|
|
9786
9802
|
throw firstError;
|
|
9787
9803
|
}
|
|
9788
|
-
async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false, legacyOnly = false } = {}) {
|
|
9804
|
+
async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false, legacyOnly = false, allowDirectFallback = true } = {}) {
|
|
9789
9805
|
const directBaseUrl = opencodeBaseUrl || baseUrl;
|
|
9790
9806
|
const parts = [{ type: "text", text }];
|
|
9791
9807
|
const flatPayload = { directory, agent, parts };
|
|
@@ -9795,21 +9811,21 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
|
|
|
9795
9811
|
body: { agent, parts }
|
|
9796
9812
|
};
|
|
9797
9813
|
let firstError = null;
|
|
9798
|
-
if (!direct && typeof client?.session?.
|
|
9814
|
+
if (!direct && typeof client?.session?.prompt === "function") {
|
|
9799
9815
|
try {
|
|
9800
|
-
return await callOpenCodePromptWithFallbacks(client.session.
|
|
9816
|
+
return await callOpenCodePromptWithFallbacks(client.session.prompt.bind(client.session), sessionId, flatPayload, structuredPayload, "prompt");
|
|
9801
9817
|
} catch (error) {
|
|
9802
9818
|
firstError ??= error;
|
|
9803
9819
|
}
|
|
9804
9820
|
}
|
|
9805
|
-
if (!direct && typeof client?.session?.
|
|
9821
|
+
if (!direct && typeof client?.session?.promptAsync === "function") {
|
|
9806
9822
|
try {
|
|
9807
|
-
return await callOpenCodePromptWithFallbacks(client.session.
|
|
9823
|
+
return await callOpenCodePromptWithFallbacks(client.session.promptAsync.bind(client.session), sessionId, flatPayload, structuredPayload, "prompt_async");
|
|
9808
9824
|
} catch (error) {
|
|
9809
9825
|
firstError ??= error;
|
|
9810
9826
|
}
|
|
9811
9827
|
}
|
|
9812
|
-
if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, fetchImpl, legacyOnly });
|
|
9828
|
+
if (allowDirectFallback && directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, directory, fetchImpl, legacyOnly });
|
|
9813
9829
|
if (firstError) throw firstError;
|
|
9814
9830
|
throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
|
|
9815
9831
|
}
|
|
@@ -9844,6 +9860,11 @@ function openCodeMessageText(message) {
|
|
|
9844
9860
|
for (const part of partList) parts.push(part?.text, part?.content);
|
|
9845
9861
|
return parts.filter((value) => typeof value === "string").join("\n");
|
|
9846
9862
|
}
|
|
9863
|
+
function openCodeMessageStableKey(message, index = 0) {
|
|
9864
|
+
const id = message?.id || message?.messageID || message?.messageId || message?.data?.id || null;
|
|
9865
|
+
if (id) return `id:${id}`;
|
|
9866
|
+
return `idx:${index}:text:${openCodeMessageText(message)}`;
|
|
9867
|
+
}
|
|
9847
9868
|
async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 8, delayMs = 250 } = {}) {
|
|
9848
9869
|
let lastError = "message_verification_unavailable";
|
|
9849
9870
|
for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
|
|
@@ -9851,13 +9872,15 @@ async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMes
|
|
|
9851
9872
|
const afterMessages = await readOpenCodeSessionMessages(client, { sessionId, limit: 50 });
|
|
9852
9873
|
if (!afterMessages) return { ok: false, reason: "message_verification_unavailable" };
|
|
9853
9874
|
const beforeCount = Array.isArray(beforeMessages) ? beforeMessages.length : null;
|
|
9854
|
-
const
|
|
9875
|
+
const beforeKeys = Array.isArray(beforeMessages) ? new Set(beforeMessages.map(openCodeMessageStableKey)) : null;
|
|
9876
|
+
const newMessages = beforeKeys ? afterMessages.filter((message, index) => !beforeKeys.has(openCodeMessageStableKey(message, index))) : afterMessages;
|
|
9877
|
+
const lastMessage = newMessages.at(-1) || afterMessages.at(-1) || null;
|
|
9855
9878
|
const lastMessageId = lastMessage?.id || lastMessage?.messageID || lastMessage?.messageId || null;
|
|
9856
9879
|
if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9857
9880
|
const textNeedles = [expectedText, ...markers].map((value) => String(value || "").trim()).filter(Boolean);
|
|
9858
|
-
const haystack =
|
|
9881
|
+
const haystack = newMessages.slice(-20).map(openCodeMessageText).join("\n");
|
|
9859
9882
|
const matched = textNeedles.find((needle) => haystack.includes(needle));
|
|
9860
|
-
if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9883
|
+
if (matched && (!beforeKeys || newMessages.length > 0)) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9861
9884
|
lastError = "message_not_visible";
|
|
9862
9885
|
} catch (error) {
|
|
9863
9886
|
lastError = error.message || "message_verification_failed";
|
|
@@ -9880,46 +9903,44 @@ async function applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason,
|
|
|
9880
9903
|
return { ok: false, error: error.message, tagName };
|
|
9881
9904
|
}
|
|
9882
9905
|
}
|
|
9906
|
+
function openCodeBlockingPromptVerification(result, sessionId) {
|
|
9907
|
+
if (result?.__optimaPromptDelivery !== "prompt") return null;
|
|
9908
|
+
const response = result?.response ?? result;
|
|
9909
|
+
const data = response?.data ?? result?.data ?? null;
|
|
9910
|
+
const parts = normalizePromptResponseParts(result);
|
|
9911
|
+
const messageId = data?.id ?? response?.id ?? result?.id ?? data?.messageID ?? response?.messageID ?? result?.messageID;
|
|
9912
|
+
const deliveredSessionId = response?.sessionID ?? response?.sessionId ?? data?.sessionID ?? data?.sessionId ?? result?.sessionID ?? result?.sessionId;
|
|
9913
|
+
if (deliveredSessionId && String(deliveredSessionId) !== String(sessionId)) {
|
|
9914
|
+
throw new Error(`OpenCode blocking prompt targeted foreign session ${deliveredSessionId}.`);
|
|
9915
|
+
}
|
|
9916
|
+
if (parts.length > 0 || messageId) return { ok: true, method: parts.length > 0 ? "blocking_prompt_parts" : "blocking_prompt_message", messageId: messageId ? String(messageId) : null, sessionId: deliveredSessionId ? String(deliveredSessionId) : String(sessionId), parts: parts.length };
|
|
9917
|
+
return null;
|
|
9918
|
+
}
|
|
9883
9919
|
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
|
|
9884
9920
|
const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => null);
|
|
9885
|
-
const sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl });
|
|
9921
|
+
const sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, allowDirectFallback: false });
|
|
9922
|
+
let blockingPromptVerification = null;
|
|
9886
9923
|
let admissionVerification = null;
|
|
9887
9924
|
try {
|
|
9888
|
-
|
|
9925
|
+
blockingPromptVerification = openCodeBlockingPromptVerification(sendResult, sessionId);
|
|
9926
|
+
if (!blockingPromptVerification) admissionVerification = openCodePromptAdmissionVerification(sendResult, sessionId);
|
|
9889
9927
|
} catch (error) {
|
|
9890
9928
|
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason: error.message, fallbackAttempted: false });
|
|
9891
9929
|
if (!applyBlockerOnFailure) return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false };
|
|
9892
9930
|
const blocker2 = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: error.message, source: "delivery_admission_failed" });
|
|
9893
9931
|
return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false, blockerTag: blocker2 };
|
|
9894
9932
|
}
|
|
9933
|
+
if (blockingPromptVerification) return { ok: true, verification: blockingPromptVerification, admissionVerification: null, fallback: false };
|
|
9895
9934
|
let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages, expectedText: text, markers: eventMarkers });
|
|
9896
9935
|
if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: false };
|
|
9897
|
-
if (verification?.reason === "message_verification_unavailable" && !admissionVerification) {
|
|
9898
|
-
return { ok: true, verification: { ok: true, method: "legacy_prompt_accepted", skipped: true }, fallback: false };
|
|
9899
|
-
}
|
|
9900
9936
|
if (admissionVerification) {
|
|
9901
9937
|
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_admitted_but_invisible", taskId, sessionId, admission: admissionVerification, reason: verification?.reason || "message_not_visible" });
|
|
9902
9938
|
}
|
|
9903
|
-
const
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
admissionVerification = openCodePromptAdmissionVerification(retrySendResult, sessionId);
|
|
9909
|
-
} catch (error) {
|
|
9910
|
-
verification = { ok: false, reason: error.message };
|
|
9911
|
-
}
|
|
9912
|
-
verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages: retryBeforeMessages, expectedText: text, markers: eventMarkers });
|
|
9913
|
-
if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: true };
|
|
9914
|
-
if (verification?.reason === "message_verification_unavailable" && !admissionVerification) {
|
|
9915
|
-
return { ok: true, verification: { ok: true, method: "legacy_prompt_accepted", skipped: true }, fallback: true };
|
|
9916
|
-
}
|
|
9917
|
-
}
|
|
9918
|
-
const reason = verification?.reason || "message_delivery_failed";
|
|
9919
|
-
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason, fallbackAttempted: canFallbackDirect });
|
|
9920
|
-
if (!applyBlockerOnFailure) return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: canFallbackDirect };
|
|
9921
|
-
const blocker = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source: canFallbackDirect ? "delivery_fallback_failed" : "delivery_verification_failed" });
|
|
9922
|
-
return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: canFallbackDirect, blockerTag: blocker };
|
|
9939
|
+
const reason = verification?.reason || (admissionVerification ? "prompt_admission_not_delivered" : "message_delivery_failed");
|
|
9940
|
+
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason, fallbackAttempted: false, httpFallbackDisabled: Boolean(opencodeBaseUrl) });
|
|
9941
|
+
if (!applyBlockerOnFailure) return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: false };
|
|
9942
|
+
const blocker = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source: "delivery_verification_failed" });
|
|
9943
|
+
return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: false, blockerTag: blocker };
|
|
9923
9944
|
}
|
|
9924
9945
|
async function recoverClickUpPmSession({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, staleSessionId, sessionTitle, taskRoute, metadataWithRouting, config, prompt, eventMarkers = [], deliveryEvidencePath, evidencePath, eventKey, createSession, verifySessionEventDelivery } = {}) {
|
|
9925
9946
|
appendClickUpWebhookLocalLog(worktree, { type: "pm_session_recovery_started", taskId, staleSessionId, worktree: taskRoute?.worktree });
|