@defend-tech/opencode-optima 0.1.37 → 0.1.38

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
@@ -9600,6 +9600,96 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
9600
9600
  if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, fetchImpl });
9601
9601
  throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
9602
9602
  }
9603
+ function normalizeOpenCodeSessionMessages(result) {
9604
+ const data = result?.data ?? result;
9605
+ if (Array.isArray(data)) return [...data];
9606
+ if (Array.isArray(data?.messages)) return [...data.messages];
9607
+ if (Array.isArray(data?.items)) return [...data.items];
9608
+ return [];
9609
+ }
9610
+ async function readOpenCodeSessionMessages(client, { sessionId, limit = 20 } = {}) {
9611
+ if (typeof client?.session?.messages !== "function") return null;
9612
+ const attempts = [
9613
+ { path: { id: sessionId }, query: { limit } },
9614
+ { path: { sessionID: sessionId }, query: { limit } },
9615
+ { id: sessionId, limit },
9616
+ { sessionID: sessionId, limit }
9617
+ ];
9618
+ let firstError = null;
9619
+ for (const attempt of attempts) {
9620
+ try {
9621
+ return normalizeOpenCodeSessionMessages(await client.session.messages(attempt));
9622
+ } catch (error) {
9623
+ firstError ??= error;
9624
+ }
9625
+ }
9626
+ throw firstError;
9627
+ }
9628
+ function openCodeMessageText(message) {
9629
+ const parts = [message?.text, message?.content, message?.message, message?.body?.text, message?.data?.text];
9630
+ const partList = Array.isArray(message?.parts) ? message.parts : Array.isArray(message?.body?.parts) ? message.body.parts : [];
9631
+ for (const part of partList) parts.push(part?.text, part?.content);
9632
+ return parts.filter((value) => typeof value === "string").join("\n");
9633
+ }
9634
+ async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 3, delayMs = 25 } = {}) {
9635
+ let lastError = "message_verification_unavailable";
9636
+ for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
9637
+ try {
9638
+ const afterMessages = await readOpenCodeSessionMessages(client, { sessionId, limit: 50 });
9639
+ if (!afterMessages) return { ok: true, method: "verification_unavailable", skipped: true };
9640
+ const beforeCount = Array.isArray(beforeMessages) ? beforeMessages.length : null;
9641
+ if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length };
9642
+ const textNeedles = [expectedText, ...markers].map((value) => String(value || "").trim()).filter(Boolean);
9643
+ const haystack = afterMessages.slice(-20).map(openCodeMessageText).join("\n");
9644
+ const matched = textNeedles.find((needle) => haystack.includes(needle));
9645
+ if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length };
9646
+ lastError = "message_not_visible";
9647
+ } catch (error) {
9648
+ lastError = error.message || "message_verification_failed";
9649
+ }
9650
+ if (attempt < attempts - 1 && delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
9651
+ }
9652
+ return { ok: false, reason: lastError };
9653
+ }
9654
+ function buildClickUpMessageDeliveryBlockerComment({ taskId, sessionId, reason, action } = {}) {
9655
+ return [
9656
+ `Optima blocker: could not deliver ClickUp task ${taskId} to OpenCode session ${sessionId}.`,
9657
+ `Delivery verification failed after send${action ? ` (${action})` : ""}: ${reason || "message_delivery_failed"}.`,
9658
+ "The webhook did not mark this event as successfully routed; please inspect/replay the event or route the task manually.",
9659
+ clickUpNoAbandonmentRuleText()
9660
+ ].join("\n");
9661
+ }
9662
+ async function postClickUpMessageDeliveryBlocker({ clickupClient, worktree, taskId, sessionId, reason, action } = {}) {
9663
+ const comment = buildClickUpMessageDeliveryBlockerComment({ taskId, sessionId, reason, action });
9664
+ if (typeof clickupClient?.postTaskComment !== "function") {
9665
+ appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_blocker_comment_unavailable", taskId, sessionId, reason, action });
9666
+ return { ok: false, skipped: true, reason: "post_task_comment_unavailable", comment };
9667
+ }
9668
+ try {
9669
+ await clickupClient.postTaskComment({ taskId, comment });
9670
+ return { ok: true, comment };
9671
+ } catch (error) {
9672
+ appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_blocker_comment_failed", taskId, sessionId, reason, action, message: error.message });
9673
+ return { ok: false, error: error.message, comment };
9674
+ }
9675
+ }
9676
+ async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery } = {}) {
9677
+ const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => null);
9678
+ await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl });
9679
+ let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages, expectedText: text, markers: eventMarkers });
9680
+ if (verification?.ok) return { ok: true, verification, fallback: false };
9681
+ const canFallbackDirect = Boolean(opencodeBaseUrl);
9682
+ if (canFallbackDirect) {
9683
+ const retryBeforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => beforeMessages);
9684
+ await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true });
9685
+ verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages: retryBeforeMessages, expectedText: text, markers: eventMarkers });
9686
+ if (verification?.ok) return { ok: true, verification, fallback: true };
9687
+ }
9688
+ const reason = verification?.reason || "message_delivery_failed";
9689
+ appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason, fallbackAttempted: canFallbackDirect });
9690
+ const blocker = await postClickUpMessageDeliveryBlocker({ clickupClient, worktree, taskId, sessionId, reason, action: canFallbackDirect ? "fallback_failed" : "verification_failed" });
9691
+ return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: canFallbackDirect, blockerComment: blocker };
9692
+ }
9603
9693
  function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", worktree = "", deliveryEvidencePath = "" }) {
9604
9694
  const comment = clickUpCommentFromPayload(payload);
9605
9695
  const commentText = clickUpCommentText(comment).trim();
@@ -9831,7 +9921,7 @@ async function withClickUpTaskRouteLock(taskId, operation) {
9831
9921
  if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
9832
9922
  }
9833
9923
  }
9834
- async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
9924
+ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
9835
9925
  const eventType = clickUpEventType(payload);
9836
9926
  const eventKey = clickUpWebhookEventKey(payload);
9837
9927
  const remembered = rememberClickUpWebhookEvent(state, eventKey);
@@ -9921,18 +10011,20 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9921
10011
  appendClickUpWebhookLocalLog(worktree, { type: "pending_session_metadata_failed", taskId, sessionId: pendingSessionId, message: error.message });
9922
10012
  throw error;
9923
10013
  }
9924
- await sendSessionEvent(openCodeClient, { sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
10014
+ const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
10015
+ if (!delivery.ok) return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9925
10016
  const nextMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(pendingMetadata, config.routing.metadataKey, pendingSessionId), config.routing.metadataKey);
9926
10017
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
9927
10018
  const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
9928
10019
  stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
9929
- return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
10020
+ return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, deliveryVerification: delivery.verification, deliveryFallback: delivery.fallback });
9930
10021
  }
9931
10022
  const sessionId = String(existingSessionId);
9932
10023
  if (await sessionExists(openCodeClient, sessionId)) {
9933
10024
  if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
9934
- await sendSessionEvent(openCodeClient, { sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
9935
- return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
10025
+ const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
10026
+ if (!delivery.ok) return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
10027
+ return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, deliveryVerification: delivery.verification, deliveryFallback: delivery.fallback });
9936
10028
  }
9937
10029
  const at = now().toISOString();
9938
10030
  const incidentComment = `Optima webhook could not route this ClickUp event because OpenCode session ${sessionId} is missing on host ${host} at ${at}. No replacement session was created automatically.`;
@@ -11827,7 +11919,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
11827
11919
  }
11828
11920
  };
11829
11921
  }
11830
- OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, reconcileClickUpStartup, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11922
+ OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, reconcileClickUpStartup, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11831
11923
  export {
11832
11924
  OptimaPlugin as default
11833
11925
  };
@@ -9607,6 +9607,96 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
9607
9607
  if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, fetchImpl });
9608
9608
  throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
9609
9609
  }
9610
+ function normalizeOpenCodeSessionMessages(result) {
9611
+ const data = result?.data ?? result;
9612
+ if (Array.isArray(data)) return [...data];
9613
+ if (Array.isArray(data?.messages)) return [...data.messages];
9614
+ if (Array.isArray(data?.items)) return [...data.items];
9615
+ return [];
9616
+ }
9617
+ async function readOpenCodeSessionMessages(client, { sessionId, limit = 20 } = {}) {
9618
+ if (typeof client?.session?.messages !== "function") return null;
9619
+ const attempts = [
9620
+ { path: { id: sessionId }, query: { limit } },
9621
+ { path: { sessionID: sessionId }, query: { limit } },
9622
+ { id: sessionId, limit },
9623
+ { sessionID: sessionId, limit }
9624
+ ];
9625
+ let firstError = null;
9626
+ for (const attempt of attempts) {
9627
+ try {
9628
+ return normalizeOpenCodeSessionMessages(await client.session.messages(attempt));
9629
+ } catch (error) {
9630
+ firstError ??= error;
9631
+ }
9632
+ }
9633
+ throw firstError;
9634
+ }
9635
+ function openCodeMessageText(message) {
9636
+ const parts = [message?.text, message?.content, message?.message, message?.body?.text, message?.data?.text];
9637
+ const partList = Array.isArray(message?.parts) ? message.parts : Array.isArray(message?.body?.parts) ? message.body.parts : [];
9638
+ for (const part of partList) parts.push(part?.text, part?.content);
9639
+ return parts.filter((value) => typeof value === "string").join("\n");
9640
+ }
9641
+ async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 3, delayMs = 25 } = {}) {
9642
+ let lastError = "message_verification_unavailable";
9643
+ for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
9644
+ try {
9645
+ const afterMessages = await readOpenCodeSessionMessages(client, { sessionId, limit: 50 });
9646
+ if (!afterMessages) return { ok: true, method: "verification_unavailable", skipped: true };
9647
+ const beforeCount = Array.isArray(beforeMessages) ? beforeMessages.length : null;
9648
+ if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length };
9649
+ const textNeedles = [expectedText, ...markers].map((value) => String(value || "").trim()).filter(Boolean);
9650
+ const haystack = afterMessages.slice(-20).map(openCodeMessageText).join("\n");
9651
+ const matched = textNeedles.find((needle) => haystack.includes(needle));
9652
+ if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length };
9653
+ lastError = "message_not_visible";
9654
+ } catch (error) {
9655
+ lastError = error.message || "message_verification_failed";
9656
+ }
9657
+ if (attempt < attempts - 1 && delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
9658
+ }
9659
+ return { ok: false, reason: lastError };
9660
+ }
9661
+ function buildClickUpMessageDeliveryBlockerComment({ taskId, sessionId, reason, action } = {}) {
9662
+ return [
9663
+ `Optima blocker: could not deliver ClickUp task ${taskId} to OpenCode session ${sessionId}.`,
9664
+ `Delivery verification failed after send${action ? ` (${action})` : ""}: ${reason || "message_delivery_failed"}.`,
9665
+ "The webhook did not mark this event as successfully routed; please inspect/replay the event or route the task manually.",
9666
+ clickUpNoAbandonmentRuleText()
9667
+ ].join("\n");
9668
+ }
9669
+ async function postClickUpMessageDeliveryBlocker({ clickupClient, worktree, taskId, sessionId, reason, action } = {}) {
9670
+ const comment = buildClickUpMessageDeliveryBlockerComment({ taskId, sessionId, reason, action });
9671
+ if (typeof clickupClient?.postTaskComment !== "function") {
9672
+ appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_blocker_comment_unavailable", taskId, sessionId, reason, action });
9673
+ return { ok: false, skipped: true, reason: "post_task_comment_unavailable", comment };
9674
+ }
9675
+ try {
9676
+ await clickupClient.postTaskComment({ taskId, comment });
9677
+ return { ok: true, comment };
9678
+ } catch (error) {
9679
+ appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_blocker_comment_failed", taskId, sessionId, reason, action, message: error.message });
9680
+ return { ok: false, error: error.message, comment };
9681
+ }
9682
+ }
9683
+ async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery } = {}) {
9684
+ const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => null);
9685
+ await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl });
9686
+ let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages, expectedText: text, markers: eventMarkers });
9687
+ if (verification?.ok) return { ok: true, verification, fallback: false };
9688
+ const canFallbackDirect = Boolean(opencodeBaseUrl);
9689
+ if (canFallbackDirect) {
9690
+ const retryBeforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => beforeMessages);
9691
+ await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true });
9692
+ verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages: retryBeforeMessages, expectedText: text, markers: eventMarkers });
9693
+ if (verification?.ok) return { ok: true, verification, fallback: true };
9694
+ }
9695
+ const reason = verification?.reason || "message_delivery_failed";
9696
+ appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason, fallbackAttempted: canFallbackDirect });
9697
+ const blocker = await postClickUpMessageDeliveryBlocker({ clickupClient, worktree, taskId, sessionId, reason, action: canFallbackDirect ? "fallback_failed" : "verification_failed" });
9698
+ return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: canFallbackDirect, blockerComment: blocker };
9699
+ }
9610
9700
  function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", worktree = "", deliveryEvidencePath = "" }) {
9611
9701
  const comment = clickUpCommentFromPayload(payload);
9612
9702
  const commentText = clickUpCommentText(comment).trim();
@@ -9838,7 +9928,7 @@ async function withClickUpTaskRouteLock(taskId, operation) {
9838
9928
  if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
9839
9929
  }
9840
9930
  }
9841
- async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
9931
+ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
9842
9932
  const eventType = clickUpEventType(payload);
9843
9933
  const eventKey = clickUpWebhookEventKey(payload);
9844
9934
  const remembered = rememberClickUpWebhookEvent(state, eventKey);
@@ -9928,18 +10018,20 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9928
10018
  appendClickUpWebhookLocalLog(worktree, { type: "pending_session_metadata_failed", taskId, sessionId: pendingSessionId, message: error.message });
9929
10019
  throw error;
9930
10020
  }
9931
- await sendSessionEvent(openCodeClient, { sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
10021
+ const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
10022
+ if (!delivery.ok) return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9932
10023
  const nextMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(pendingMetadata, config.routing.metadataKey, pendingSessionId), config.routing.metadataKey);
9933
10024
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
9934
10025
  const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
9935
10026
  stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
9936
- return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
10027
+ return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, deliveryVerification: delivery.verification, deliveryFallback: delivery.fallback });
9937
10028
  }
9938
10029
  const sessionId = String(existingSessionId);
9939
10030
  if (await sessionExists(openCodeClient, sessionId)) {
9940
10031
  if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
9941
- await sendSessionEvent(openCodeClient, { sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
9942
- return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
10032
+ const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
10033
+ if (!delivery.ok) return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
10034
+ return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, deliveryVerification: delivery.verification, deliveryFallback: delivery.fallback });
9943
10035
  }
9944
10036
  const at = now().toISOString();
9945
10037
  const incidentComment = `Optima webhook could not route this ClickUp event because OpenCode session ${sessionId} is missing on host ${host} at ${at}. No replacement session was created automatically.`;
@@ -11834,7 +11926,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
11834
11926
  }
11835
11927
  };
11836
11928
  }
11837
- OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, reconcileClickUpStartup, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11929
+ OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, reconcileClickUpStartup, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11838
11930
 
11839
11931
  // src/sanitize_cli.js
11840
11932
  var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defend-tech/opencode-optima",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"