@defend-tech/opencode-optima 0.1.42 → 0.1.44

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 CHANGED
@@ -9592,31 +9592,76 @@ function assertOpenCodePromptAccepted(result) {
9592
9592
  }
9593
9593
  return result;
9594
9594
  }
9595
- async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, fetchImpl = globalThis.fetch } = {}) {
9595
+ function openCodePromptAdmissionVerification(result, sessionId) {
9596
+ const response = result?.response ?? result;
9597
+ const data = response?.data ?? result?.data ?? null;
9598
+ const promptId = data?.id ?? response?.id ?? result?.id;
9599
+ const admittedSessionId = response?.sessionID ?? response?.sessionId ?? data?.sessionID ?? data?.sessionId ?? result?.sessionID ?? result?.sessionId;
9600
+ const admittedSeq = response?.admittedSeq ?? data?.admittedSeq ?? result?.admittedSeq;
9601
+ if (!promptId || admittedSeq === void 0 || admittedSeq === null || !admittedSessionId) return null;
9602
+ if (String(admittedSessionId) !== String(sessionId)) {
9603
+ throw new Error(`OpenCode prompt admission targeted foreign session ${admittedSessionId}.`);
9604
+ }
9605
+ return { ok: true, method: "prompt_admission", promptId: String(promptId), sessionId: String(admittedSessionId), admittedSeq };
9606
+ }
9607
+ function responseLooksLikeHtml(contentType = "", raw = "") {
9608
+ return String(contentType || "").toLowerCase().includes("text/html") || /^\s*<!doctype\s+html\b/i.test(raw) || /^\s*<html\b/i.test(raw);
9609
+ }
9610
+ async function readOpenCodeJsonResponse(response, endpointName) {
9611
+ const contentType = response.headers?.get?.("content-type") || "";
9612
+ const raw = await response.text();
9613
+ if (responseLooksLikeHtml(contentType, raw)) {
9614
+ throw new Error(`OpenCode ${endpointName} returned the HTML app shell instead of an API response.`);
9615
+ }
9616
+ if (!raw) return null;
9617
+ try {
9618
+ return JSON.parse(raw);
9619
+ } catch (error) {
9620
+ if (response.ok) throw new Error(`OpenCode ${endpointName} response was not JSON: ${error.message}`);
9621
+ return { raw };
9622
+ }
9623
+ }
9624
+ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, fetchImpl = globalThis.fetch } = {}) {
9596
9625
  if (typeof fetchImpl !== "function") throw new Error("OpenCode direct prompt delivery requires fetch.");
9597
9626
  const root = normalizeOpenCodeBaseUrl(baseUrl, "");
9598
9627
  if (!root) throw new Error("OpenCode direct prompt delivery requires a base URL.");
9599
- const url = `${root}/api/session/${encodeURIComponent(sessionId)}/prompt`;
9600
- const response = await fetchImpl(url, {
9601
- method: "POST",
9602
- headers: { "content-type": "application/json" },
9603
- body: JSON.stringify({ prompt: { text }, delivery: "queue", resume: true })
9604
- });
9605
- const raw = await response.text();
9606
- let data = null;
9607
- if (raw) {
9608
- try {
9609
- data = JSON.parse(raw);
9610
- } catch (error) {
9611
- if (response.ok) throw new Error(`OpenCode prompt response was not JSON: ${error.message}`);
9628
+ const encodedSession = encodeURIComponent(sessionId);
9629
+ const attempts = [
9630
+ {
9631
+ name: "v2 prompt",
9632
+ url: `${root}/api/session/${encodedSession}/prompt`,
9633
+ body: { prompt: { text }, delivery: "queue", resume: true },
9634
+ accept: (response, data) => {
9635
+ if (!response.ok) return null;
9636
+ if (!openCodePromptAdmissionVerification(data, sessionId)) throw new Error("OpenCode v2 prompt response did not include a valid prompt admission.");
9637
+ return { ok: true, method: "http", endpoint: "/api/session/{sessionID}/prompt", status: response.status, data: data.data, response: data };
9638
+ }
9639
+ },
9640
+ {
9641
+ name: "legacy async prompt",
9642
+ url: `${root}/session/${encodedSession}/prompt_async`,
9643
+ body: { agent, parts: [{ type: "text", text }] },
9644
+ accept: (response, data) => {
9645
+ if (response.status !== 204 && !response.ok) return null;
9646
+ return { ok: true, method: "http", endpoint: "/session/{sessionID}/prompt_async", status: response.status, data: data?.data || null, response: data };
9647
+ }
9612
9648
  }
9649
+ ];
9650
+ let firstError = null;
9651
+ for (const attempt of attempts) {
9652
+ const response = await fetchImpl(attempt.url, {
9653
+ method: "POST",
9654
+ headers: { "content-type": "application/json" },
9655
+ body: JSON.stringify(attempt.body)
9656
+ });
9657
+ const data = await readOpenCodeJsonResponse(response, attempt.name);
9658
+ const accepted = attempt.accept(response, data);
9659
+ if (accepted) return accepted;
9660
+ const message = data?.error?.message || data?.message || data?.raw || `OpenCode ${attempt.name} request failed with status ${response.status}.`;
9661
+ firstError ??= new Error(message);
9662
+ if (![404, 405].includes(Number(response.status))) break;
9613
9663
  }
9614
- if (!response.ok) {
9615
- const message = data?.error?.message || data?.message || raw || `OpenCode prompt request failed with status ${response.status}.`;
9616
- throw new Error(message);
9617
- }
9618
- if (!data?.data?.id) throw new Error("OpenCode prompt response did not include data.id.");
9619
- return { ok: true, method: "http", status: response.status, data: data.data, response: data };
9664
+ throw firstError || new Error("OpenCode direct prompt delivery failed.");
9620
9665
  }
9621
9666
  async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false } = {}) {
9622
9667
  const directBaseUrl = opencodeBaseUrl || baseUrl;
@@ -9632,7 +9677,7 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
9632
9677
  if (!direct && typeof client?.session?.prompt === "function") {
9633
9678
  return assertOpenCodePromptAccepted(await client.session.prompt(structuredPayload));
9634
9679
  }
9635
- if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, fetchImpl });
9680
+ if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, fetchImpl });
9636
9681
  throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
9637
9682
  }
9638
9683
  function normalizeOpenCodeSessionMessages(result) {
@@ -9702,13 +9747,29 @@ async function applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason,
9702
9747
  }
9703
9748
  async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
9704
9749
  const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => null);
9705
- await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl });
9750
+ const sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl });
9751
+ let admissionVerification = null;
9752
+ try {
9753
+ admissionVerification = openCodePromptAdmissionVerification(sendResult, sessionId);
9754
+ } catch (error) {
9755
+ appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason: error.message, fallbackAttempted: false });
9756
+ if (!applyBlockerOnFailure) return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false };
9757
+ const blocker2 = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: error.message, source: "delivery_admission_failed" });
9758
+ return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false, blockerTag: blocker2 };
9759
+ }
9760
+ if (admissionVerification) return { ok: true, verification: admissionVerification, fallback: false };
9706
9761
  let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages, expectedText: text, markers: eventMarkers });
9707
9762
  if (verification?.ok) return { ok: true, verification, fallback: false };
9708
9763
  const canFallbackDirect = Boolean(opencodeBaseUrl);
9709
9764
  if (canFallbackDirect) {
9710
9765
  const retryBeforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => beforeMessages);
9711
- await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true });
9766
+ const retrySendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true });
9767
+ try {
9768
+ admissionVerification = openCodePromptAdmissionVerification(retrySendResult, sessionId);
9769
+ } catch (error) {
9770
+ verification = { ok: false, reason: error.message };
9771
+ }
9772
+ if (admissionVerification) return { ok: true, verification: admissionVerification, fallback: true };
9712
9773
  verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages: retryBeforeMessages, expectedText: text, markers: eventMarkers });
9713
9774
  if (verification?.ok) return { ok: true, verification, fallback: true };
9714
9775
  }
@@ -9599,31 +9599,76 @@ function assertOpenCodePromptAccepted(result) {
9599
9599
  }
9600
9600
  return result;
9601
9601
  }
9602
- async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, fetchImpl = globalThis.fetch } = {}) {
9602
+ function openCodePromptAdmissionVerification(result, sessionId) {
9603
+ const response = result?.response ?? result;
9604
+ const data = response?.data ?? result?.data ?? null;
9605
+ const promptId = data?.id ?? response?.id ?? result?.id;
9606
+ const admittedSessionId = response?.sessionID ?? response?.sessionId ?? data?.sessionID ?? data?.sessionId ?? result?.sessionID ?? result?.sessionId;
9607
+ const admittedSeq = response?.admittedSeq ?? data?.admittedSeq ?? result?.admittedSeq;
9608
+ if (!promptId || admittedSeq === void 0 || admittedSeq === null || !admittedSessionId) return null;
9609
+ if (String(admittedSessionId) !== String(sessionId)) {
9610
+ throw new Error(`OpenCode prompt admission targeted foreign session ${admittedSessionId}.`);
9611
+ }
9612
+ return { ok: true, method: "prompt_admission", promptId: String(promptId), sessionId: String(admittedSessionId), admittedSeq };
9613
+ }
9614
+ function responseLooksLikeHtml(contentType = "", raw = "") {
9615
+ return String(contentType || "").toLowerCase().includes("text/html") || /^\s*<!doctype\s+html\b/i.test(raw) || /^\s*<html\b/i.test(raw);
9616
+ }
9617
+ async function readOpenCodeJsonResponse(response, endpointName) {
9618
+ const contentType = response.headers?.get?.("content-type") || "";
9619
+ const raw = await response.text();
9620
+ if (responseLooksLikeHtml(contentType, raw)) {
9621
+ throw new Error(`OpenCode ${endpointName} returned the HTML app shell instead of an API response.`);
9622
+ }
9623
+ if (!raw) return null;
9624
+ try {
9625
+ return JSON.parse(raw);
9626
+ } catch (error) {
9627
+ if (response.ok) throw new Error(`OpenCode ${endpointName} response was not JSON: ${error.message}`);
9628
+ return { raw };
9629
+ }
9630
+ }
9631
+ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, fetchImpl = globalThis.fetch } = {}) {
9603
9632
  if (typeof fetchImpl !== "function") throw new Error("OpenCode direct prompt delivery requires fetch.");
9604
9633
  const root = normalizeOpenCodeBaseUrl(baseUrl, "");
9605
9634
  if (!root) throw new Error("OpenCode direct prompt delivery requires a base URL.");
9606
- const url = `${root}/api/session/${encodeURIComponent(sessionId)}/prompt`;
9607
- const response = await fetchImpl(url, {
9608
- method: "POST",
9609
- headers: { "content-type": "application/json" },
9610
- body: JSON.stringify({ prompt: { text }, delivery: "queue", resume: true })
9611
- });
9612
- const raw = await response.text();
9613
- let data = null;
9614
- if (raw) {
9615
- try {
9616
- data = JSON.parse(raw);
9617
- } catch (error) {
9618
- if (response.ok) throw new Error(`OpenCode prompt response was not JSON: ${error.message}`);
9635
+ const encodedSession = encodeURIComponent(sessionId);
9636
+ const attempts = [
9637
+ {
9638
+ name: "v2 prompt",
9639
+ url: `${root}/api/session/${encodedSession}/prompt`,
9640
+ body: { prompt: { text }, delivery: "queue", resume: true },
9641
+ accept: (response, data) => {
9642
+ if (!response.ok) return null;
9643
+ if (!openCodePromptAdmissionVerification(data, sessionId)) throw new Error("OpenCode v2 prompt response did not include a valid prompt admission.");
9644
+ return { ok: true, method: "http", endpoint: "/api/session/{sessionID}/prompt", status: response.status, data: data.data, response: data };
9645
+ }
9646
+ },
9647
+ {
9648
+ name: "legacy async prompt",
9649
+ url: `${root}/session/${encodedSession}/prompt_async`,
9650
+ body: { agent, parts: [{ type: "text", text }] },
9651
+ accept: (response, data) => {
9652
+ if (response.status !== 204 && !response.ok) return null;
9653
+ return { ok: true, method: "http", endpoint: "/session/{sessionID}/prompt_async", status: response.status, data: data?.data || null, response: data };
9654
+ }
9619
9655
  }
9656
+ ];
9657
+ let firstError = null;
9658
+ for (const attempt of attempts) {
9659
+ const response = await fetchImpl(attempt.url, {
9660
+ method: "POST",
9661
+ headers: { "content-type": "application/json" },
9662
+ body: JSON.stringify(attempt.body)
9663
+ });
9664
+ const data = await readOpenCodeJsonResponse(response, attempt.name);
9665
+ const accepted = attempt.accept(response, data);
9666
+ if (accepted) return accepted;
9667
+ const message = data?.error?.message || data?.message || data?.raw || `OpenCode ${attempt.name} request failed with status ${response.status}.`;
9668
+ firstError ??= new Error(message);
9669
+ if (![404, 405].includes(Number(response.status))) break;
9620
9670
  }
9621
- if (!response.ok) {
9622
- const message = data?.error?.message || data?.message || raw || `OpenCode prompt request failed with status ${response.status}.`;
9623
- throw new Error(message);
9624
- }
9625
- if (!data?.data?.id) throw new Error("OpenCode prompt response did not include data.id.");
9626
- return { ok: true, method: "http", status: response.status, data: data.data, response: data };
9671
+ throw firstError || new Error("OpenCode direct prompt delivery failed.");
9627
9672
  }
9628
9673
  async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false } = {}) {
9629
9674
  const directBaseUrl = opencodeBaseUrl || baseUrl;
@@ -9639,7 +9684,7 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
9639
9684
  if (!direct && typeof client?.session?.prompt === "function") {
9640
9685
  return assertOpenCodePromptAccepted(await client.session.prompt(structuredPayload));
9641
9686
  }
9642
- if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, fetchImpl });
9687
+ if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, fetchImpl });
9643
9688
  throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
9644
9689
  }
9645
9690
  function normalizeOpenCodeSessionMessages(result) {
@@ -9709,13 +9754,29 @@ async function applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason,
9709
9754
  }
9710
9755
  async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
9711
9756
  const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => null);
9712
- await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl });
9757
+ const sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl });
9758
+ let admissionVerification = null;
9759
+ try {
9760
+ admissionVerification = openCodePromptAdmissionVerification(sendResult, sessionId);
9761
+ } catch (error) {
9762
+ appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason: error.message, fallbackAttempted: false });
9763
+ if (!applyBlockerOnFailure) return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false };
9764
+ const blocker2 = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: error.message, source: "delivery_admission_failed" });
9765
+ return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false, blockerTag: blocker2 };
9766
+ }
9767
+ if (admissionVerification) return { ok: true, verification: admissionVerification, fallback: false };
9713
9768
  let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages, expectedText: text, markers: eventMarkers });
9714
9769
  if (verification?.ok) return { ok: true, verification, fallback: false };
9715
9770
  const canFallbackDirect = Boolean(opencodeBaseUrl);
9716
9771
  if (canFallbackDirect) {
9717
9772
  const retryBeforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => beforeMessages);
9718
- await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true });
9773
+ const retrySendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true });
9774
+ try {
9775
+ admissionVerification = openCodePromptAdmissionVerification(retrySendResult, sessionId);
9776
+ } catch (error) {
9777
+ verification = { ok: false, reason: error.message };
9778
+ }
9779
+ if (admissionVerification) return { ok: true, verification: admissionVerification, fallback: true };
9719
9780
  verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages: retryBeforeMessages, expectedText: text, markers: eventMarkers });
9720
9781
  if (verification?.ok) return { ok: true, verification, fallback: true };
9721
9782
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defend-tech/opencode-optima",
3
- "version": "0.1.42",
3
+ "version": "0.1.44",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"