@defend-tech/opencode-optima 0.1.48 → 0.1.49

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
@@ -8163,6 +8163,7 @@ function hasActionableClickUpRoute(result = {}) {
8163
8163
  if (!result.sessionId) return false;
8164
8164
  if (result.action === "message_delivery_failed" || result.action === "error") return false;
8165
8165
  if (result.deliveryVerification?.ok === false) return false;
8166
+ if (result.deliveryVerification?.method === "prompt_admission") return false;
8166
8167
  return true;
8167
8168
  }
8168
8169
  function determineClickUpMergeAuthority({ isSubtask = false, clickupStatus = "", validationPassed = false, mergeFailed = false, finalApprovalRoles = CLICKUP_FINAL_APPROVER_ROLES, humansRegistry } = {}) {
@@ -9655,6 +9656,22 @@ async function createOpenCodeSession(client, { title, directory, agent } = {}) {
9655
9656
  }
9656
9657
  throw firstError || new Error("OpenCode session create failed.");
9657
9658
  }
9659
+ async function waitForOpenCodeReadiness(client, { worktree = process.cwd(), attempts = 10, delayMs = 500, now = () => /* @__PURE__ */ new Date() } = {}) {
9660
+ if (typeof client?.session?.create !== "function") return { ok: true, skipped: true, reason: "session_create_probe_unavailable" };
9661
+ let lastError = "opencode_not_ready";
9662
+ const maxAttempts = Math.max(1, Number(attempts) || 1);
9663
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
9664
+ try {
9665
+ const sessionId = await createOpenCodeSession(client, { title: `Optima startup readiness probe ${now().toISOString()}`, directory: worktree });
9666
+ if (await openCodeSessionExists(client, sessionId)) return { ok: true, method: "session_create_probe", sessionId, attempts: attempt };
9667
+ lastError = "readiness_probe_session_not_visible";
9668
+ } catch (error) {
9669
+ lastError = error.message || "opencode_not_ready";
9670
+ }
9671
+ if (attempt < maxAttempts && delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
9672
+ }
9673
+ return { ok: false, reason: lastError, attempts: maxAttempts };
9674
+ }
9658
9675
  function assertOpenCodePromptAccepted(result) {
9659
9676
  const status = Number(result?.status || result?.response?.status || result?.error?.status || 0);
9660
9677
  if (status >= 400 || result?.ok === false || result?.error) {
@@ -9692,13 +9709,13 @@ async function readOpenCodeJsonResponse(response, endpointName) {
9692
9709
  return { raw };
9693
9710
  }
9694
9711
  }
9695
- async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, fetchImpl = globalThis.fetch } = {}) {
9712
+ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, fetchImpl = globalThis.fetch, legacyOnly = false } = {}) {
9696
9713
  if (typeof fetchImpl !== "function") throw new Error("OpenCode direct prompt delivery requires fetch.");
9697
9714
  const root = normalizeOpenCodeBaseUrl(baseUrl, "");
9698
9715
  if (!root) throw new Error("OpenCode direct prompt delivery requires a base URL.");
9699
9716
  const encodedSession = encodeURIComponent(sessionId);
9700
9717
  const attempts = [
9701
- {
9718
+ legacyOnly ? null : {
9702
9719
  name: "v2 prompt",
9703
9720
  url: `${root}/api/session/${encodedSession}/prompt`,
9704
9721
  body: { prompt: { text }, delivery: "queue", resume: true },
@@ -9717,7 +9734,7 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
9717
9734
  return { ok: true, method: "http", endpoint: "/session/{sessionID}/prompt_async", status: response.status, data: data?.data || null, response: data };
9718
9735
  }
9719
9736
  }
9720
- ];
9737
+ ].filter(Boolean);
9721
9738
  let firstError = null;
9722
9739
  for (const attempt of attempts) {
9723
9740
  const response = await fetchImpl(attempt.url, {
@@ -9751,7 +9768,7 @@ async function callOpenCodePromptWithFallbacks(method, sessionId, flatPayload, s
9751
9768
  }
9752
9769
  throw firstError;
9753
9770
  }
9754
- async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false } = {}) {
9771
+ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false, legacyOnly = false } = {}) {
9755
9772
  const directBaseUrl = opencodeBaseUrl || baseUrl;
9756
9773
  const parts = [{ type: "text", text }];
9757
9774
  const flatPayload = { directory, agent, parts };
@@ -9775,7 +9792,7 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
9775
9792
  firstError ??= error;
9776
9793
  }
9777
9794
  }
9778
- if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, fetchImpl });
9795
+ if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, fetchImpl, legacyOnly });
9779
9796
  if (firstError) throw firstError;
9780
9797
  throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
9781
9798
  }
@@ -9810,18 +9827,20 @@ function openCodeMessageText(message) {
9810
9827
  for (const part of partList) parts.push(part?.text, part?.content);
9811
9828
  return parts.filter((value) => typeof value === "string").join("\n");
9812
9829
  }
9813
- async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 3, delayMs = 25 } = {}) {
9830
+ async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 8, delayMs = 250 } = {}) {
9814
9831
  let lastError = "message_verification_unavailable";
9815
9832
  for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
9816
9833
  try {
9817
9834
  const afterMessages = await readOpenCodeSessionMessages(client, { sessionId, limit: 50 });
9818
- if (!afterMessages) return { ok: true, method: "verification_unavailable", skipped: true };
9835
+ if (!afterMessages) return { ok: false, reason: "message_verification_unavailable" };
9819
9836
  const beforeCount = Array.isArray(beforeMessages) ? beforeMessages.length : null;
9820
- if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length };
9837
+ const lastMessage = afterMessages.at(-1) || null;
9838
+ const lastMessageId = lastMessage?.id || lastMessage?.messageID || lastMessage?.messageId || null;
9839
+ if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length, lastMessageId };
9821
9840
  const textNeedles = [expectedText, ...markers].map((value) => String(value || "").trim()).filter(Boolean);
9822
9841
  const haystack = afterMessages.slice(-20).map(openCodeMessageText).join("\n");
9823
9842
  const matched = textNeedles.find((needle) => haystack.includes(needle));
9824
- if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length };
9843
+ if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length, lastMessageId };
9825
9844
  lastError = "message_not_visible";
9826
9845
  } catch (error) {
9827
9846
  lastError = error.message || "message_verification_failed";
@@ -9856,21 +9875,28 @@ async function deliverClickUpSessionEventWithVerification({ openCodeClient, send
9856
9875
  const blocker2 = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: error.message, source: "delivery_admission_failed" });
9857
9876
  return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false, blockerTag: blocker2 };
9858
9877
  }
9859
- if (admissionVerification) return { ok: true, verification: admissionVerification, fallback: false };
9860
9878
  let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages, expectedText: text, markers: eventMarkers });
9861
- if (verification?.ok) return { ok: true, verification, fallback: false };
9879
+ if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: false };
9880
+ if (verification?.reason === "message_verification_unavailable" && !admissionVerification) {
9881
+ return { ok: true, verification: { ok: true, method: "legacy_prompt_accepted", skipped: true }, fallback: false };
9882
+ }
9883
+ if (admissionVerification) {
9884
+ appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_admitted_but_invisible", taskId, sessionId, admission: admissionVerification, reason: verification?.reason || "message_not_visible" });
9885
+ }
9862
9886
  const canFallbackDirect = Boolean(opencodeBaseUrl);
9863
9887
  if (canFallbackDirect) {
9864
9888
  const retryBeforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => beforeMessages);
9865
- const retrySendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true });
9889
+ const retrySendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true, legacyOnly: Boolean(admissionVerification) });
9866
9890
  try {
9867
9891
  admissionVerification = openCodePromptAdmissionVerification(retrySendResult, sessionId);
9868
9892
  } catch (error) {
9869
9893
  verification = { ok: false, reason: error.message };
9870
9894
  }
9871
- if (admissionVerification) return { ok: true, verification: admissionVerification, fallback: true };
9872
9895
  verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages: retryBeforeMessages, expectedText: text, markers: eventMarkers });
9873
- if (verification?.ok) return { ok: true, verification, fallback: true };
9896
+ if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: true };
9897
+ if (verification?.reason === "message_verification_unavailable" && !admissionVerification) {
9898
+ return { ok: true, verification: { ok: true, method: "legacy_prompt_accepted", skipped: true }, fallback: true };
9899
+ }
9874
9900
  }
9875
9901
  const reason = verification?.reason || "message_delivery_failed";
9876
9902
  appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason, fallbackAttempted: canFallbackDirect });
@@ -9916,7 +9942,7 @@ async function recoverClickUpPmSession({ openCodeClient, sendSessionEvent, click
9916
9942
  const replacementMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(metadataWithRouting, config.routing.metadataKey, replacementSessionId), config.routing.metadataKey);
9917
9943
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: replacementMetadata });
9918
9944
  appendClickUpWebhookLocalLog(worktree, { type: "pm_session_recovery_succeeded", taskId, staleSessionId, replacementSessionId });
9919
- return { ok: true, action: "sent_to_replacement_session", taskId, sessionId: replacementSessionId, staleSessionId, replacementAttempted: true, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath, deliveryVerification: replacementDelivery.verification, deliveryFallback: replacementDelivery.fallback };
9945
+ return { ok: true, action: "sent_to_replacement_session", taskId, sessionId: replacementSessionId, staleSessionId, replacementAttempted: true, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath, deliveryVerification: replacementDelivery.verification, deliveryAdmission: replacementDelivery.admissionVerification, deliveryFallback: replacementDelivery.fallback, deliveryAttempts: replacementDelivery.fallback ? 2 : 1 };
9920
9946
  }
9921
9947
  function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", worktree = "", deliveryEvidencePath = "", humanRoleContext = [] }) {
9922
9948
  const comment = clickUpCommentFromPayload(payload);
@@ -10212,7 +10238,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
10212
10238
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
10213
10239
  const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
10214
10240
  stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
10215
- 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 });
10241
+ 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, deliveryAdmission: delivery.admissionVerification, deliveryFallback: delivery.fallback, deliveryAttempts: delivery.fallback ? 2 : 1 });
10216
10242
  }
10217
10243
  const sessionId = String(existingSessionId);
10218
10244
  if (await sessionExists(openCodeClient, sessionId)) {
@@ -10222,7 +10248,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
10222
10248
  const recovery2 = await recoverClickUpPmSession({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, staleSessionId: sessionId, sessionTitle, taskRoute, metadataWithRouting, config, prompt, eventMarkers: [taskId, eventType], deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, eventKey, createSession, verifySessionEventDelivery });
10223
10249
  return finish(recovery2);
10224
10250
  }
10225
- 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 });
10251
+ 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, deliveryAdmission: delivery.admissionVerification, deliveryFallback: delivery.fallback, deliveryAttempts: delivery.fallback ? 2 : 1 });
10226
10252
  }
10227
10253
  const at = now().toISOString();
10228
10254
  appendClickUpWebhookLocalLog(worktree, { type: "missing_session", taskId, sessionId, host, at });
@@ -10233,8 +10259,11 @@ async function routeClickUpWebhookEvent(options = {}) {
10233
10259
  const taskId = clickUpTaskIdFromPayload(options.payload || {});
10234
10260
  return withClickUpTaskRouteLock(taskId, () => routeClickUpWebhookEventUnlocked(options));
10235
10261
  }
10236
- async function reconcileClickUpStartup({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
10262
+ async function reconcileClickUpStartup({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, waitForReadiness = waitForOpenCodeReadiness, saveState = null, now = () => /* @__PURE__ */ new Date(), limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
10237
10263
  if (!config || !clickupClient?.listAssignedTasks) return { ok: true, skipped: true, reason: "clickup_task_listing_unavailable", assigned: 0, comments: 0 };
10264
+ const readiness = await waitForReadiness(openCodeClient, { worktree, now });
10265
+ appendClickUpWebhookLocalLog(worktree, { type: readiness.ok ? "startup_reconciliation_readiness_ready" : "startup_reconciliation_readiness_failed", ...readiness });
10266
+ if (!readiness.ok) return { ok: false, skipped: true, reason: "opencode_not_ready", readiness, assigned: 0, comments: 0, ignored: 0, errors: 1, undelivered: 1, tasks: [], validation: { undelivered: 1, emptyPromptSessions: [] } };
10238
10267
  let authorizedUserId = "";
10239
10268
  if (clickupClient?.getAuthorizedUser) {
10240
10269
  try {
@@ -10292,7 +10321,10 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
10292
10321
  branch: result?.branch || null,
10293
10322
  worktree: result?.worktree || null,
10294
10323
  verification: result?.deliveryVerification?.method || result?.deliveryVerification?.reason || null,
10295
- delivered: hasActionableClickUpRoute(result)
10324
+ delivered: hasActionableClickUpRoute(result),
10325
+ attempts: result?.deliveryAttempts || null,
10326
+ finalMessageCount: result?.deliveryVerification?.afterCount ?? null,
10327
+ finalMessageId: result?.deliveryVerification?.lastMessageId || null
10296
10328
  };
10297
10329
  routed.tasks.push(routeSummary);
10298
10330
  appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_routed", ...routeSummary });
@@ -12153,7 +12185,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
12153
12185
  }
12154
12186
  };
12155
12187
  }
12156
- 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, createOpenCodeSession, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionMessages, reconcileClickUpStartup, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, 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 };
12188
+ 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, createOpenCodeSession, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionMessages, reconcileClickUpStartup, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, 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 };
12157
12189
  export {
12158
12190
  OptimaPlugin as default
12159
12191
  };
@@ -8170,6 +8170,7 @@ function hasActionableClickUpRoute(result = {}) {
8170
8170
  if (!result.sessionId) return false;
8171
8171
  if (result.action === "message_delivery_failed" || result.action === "error") return false;
8172
8172
  if (result.deliveryVerification?.ok === false) return false;
8173
+ if (result.deliveryVerification?.method === "prompt_admission") return false;
8173
8174
  return true;
8174
8175
  }
8175
8176
  function determineClickUpMergeAuthority({ isSubtask = false, clickupStatus = "", validationPassed = false, mergeFailed = false, finalApprovalRoles = CLICKUP_FINAL_APPROVER_ROLES, humansRegistry } = {}) {
@@ -9662,6 +9663,22 @@ async function createOpenCodeSession(client, { title, directory, agent } = {}) {
9662
9663
  }
9663
9664
  throw firstError || new Error("OpenCode session create failed.");
9664
9665
  }
9666
+ async function waitForOpenCodeReadiness(client, { worktree = process.cwd(), attempts = 10, delayMs = 500, now = () => /* @__PURE__ */ new Date() } = {}) {
9667
+ if (typeof client?.session?.create !== "function") return { ok: true, skipped: true, reason: "session_create_probe_unavailable" };
9668
+ let lastError = "opencode_not_ready";
9669
+ const maxAttempts = Math.max(1, Number(attempts) || 1);
9670
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
9671
+ try {
9672
+ const sessionId = await createOpenCodeSession(client, { title: `Optima startup readiness probe ${now().toISOString()}`, directory: worktree });
9673
+ if (await openCodeSessionExists(client, sessionId)) return { ok: true, method: "session_create_probe", sessionId, attempts: attempt };
9674
+ lastError = "readiness_probe_session_not_visible";
9675
+ } catch (error) {
9676
+ lastError = error.message || "opencode_not_ready";
9677
+ }
9678
+ if (attempt < maxAttempts && delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
9679
+ }
9680
+ return { ok: false, reason: lastError, attempts: maxAttempts };
9681
+ }
9665
9682
  function assertOpenCodePromptAccepted(result) {
9666
9683
  const status = Number(result?.status || result?.response?.status || result?.error?.status || 0);
9667
9684
  if (status >= 400 || result?.ok === false || result?.error) {
@@ -9699,13 +9716,13 @@ async function readOpenCodeJsonResponse(response, endpointName) {
9699
9716
  return { raw };
9700
9717
  }
9701
9718
  }
9702
- async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, fetchImpl = globalThis.fetch } = {}) {
9719
+ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, fetchImpl = globalThis.fetch, legacyOnly = false } = {}) {
9703
9720
  if (typeof fetchImpl !== "function") throw new Error("OpenCode direct prompt delivery requires fetch.");
9704
9721
  const root = normalizeOpenCodeBaseUrl(baseUrl, "");
9705
9722
  if (!root) throw new Error("OpenCode direct prompt delivery requires a base URL.");
9706
9723
  const encodedSession = encodeURIComponent(sessionId);
9707
9724
  const attempts = [
9708
- {
9725
+ legacyOnly ? null : {
9709
9726
  name: "v2 prompt",
9710
9727
  url: `${root}/api/session/${encodedSession}/prompt`,
9711
9728
  body: { prompt: { text }, delivery: "queue", resume: true },
@@ -9724,7 +9741,7 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
9724
9741
  return { ok: true, method: "http", endpoint: "/session/{sessionID}/prompt_async", status: response.status, data: data?.data || null, response: data };
9725
9742
  }
9726
9743
  }
9727
- ];
9744
+ ].filter(Boolean);
9728
9745
  let firstError = null;
9729
9746
  for (const attempt of attempts) {
9730
9747
  const response = await fetchImpl(attempt.url, {
@@ -9758,7 +9775,7 @@ async function callOpenCodePromptWithFallbacks(method, sessionId, flatPayload, s
9758
9775
  }
9759
9776
  throw firstError;
9760
9777
  }
9761
- async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false } = {}) {
9778
+ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false, legacyOnly = false } = {}) {
9762
9779
  const directBaseUrl = opencodeBaseUrl || baseUrl;
9763
9780
  const parts = [{ type: "text", text }];
9764
9781
  const flatPayload = { directory, agent, parts };
@@ -9782,7 +9799,7 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
9782
9799
  firstError ??= error;
9783
9800
  }
9784
9801
  }
9785
- if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, fetchImpl });
9802
+ if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, fetchImpl, legacyOnly });
9786
9803
  if (firstError) throw firstError;
9787
9804
  throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
9788
9805
  }
@@ -9817,18 +9834,20 @@ function openCodeMessageText(message) {
9817
9834
  for (const part of partList) parts.push(part?.text, part?.content);
9818
9835
  return parts.filter((value) => typeof value === "string").join("\n");
9819
9836
  }
9820
- async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 3, delayMs = 25 } = {}) {
9837
+ async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 8, delayMs = 250 } = {}) {
9821
9838
  let lastError = "message_verification_unavailable";
9822
9839
  for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
9823
9840
  try {
9824
9841
  const afterMessages = await readOpenCodeSessionMessages(client, { sessionId, limit: 50 });
9825
- if (!afterMessages) return { ok: true, method: "verification_unavailable", skipped: true };
9842
+ if (!afterMessages) return { ok: false, reason: "message_verification_unavailable" };
9826
9843
  const beforeCount = Array.isArray(beforeMessages) ? beforeMessages.length : null;
9827
- if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length };
9844
+ const lastMessage = afterMessages.at(-1) || null;
9845
+ const lastMessageId = lastMessage?.id || lastMessage?.messageID || lastMessage?.messageId || null;
9846
+ if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length, lastMessageId };
9828
9847
  const textNeedles = [expectedText, ...markers].map((value) => String(value || "").trim()).filter(Boolean);
9829
9848
  const haystack = afterMessages.slice(-20).map(openCodeMessageText).join("\n");
9830
9849
  const matched = textNeedles.find((needle) => haystack.includes(needle));
9831
- if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length };
9850
+ if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length, lastMessageId };
9832
9851
  lastError = "message_not_visible";
9833
9852
  } catch (error) {
9834
9853
  lastError = error.message || "message_verification_failed";
@@ -9863,21 +9882,28 @@ async function deliverClickUpSessionEventWithVerification({ openCodeClient, send
9863
9882
  const blocker2 = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: error.message, source: "delivery_admission_failed" });
9864
9883
  return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false, blockerTag: blocker2 };
9865
9884
  }
9866
- if (admissionVerification) return { ok: true, verification: admissionVerification, fallback: false };
9867
9885
  let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages, expectedText: text, markers: eventMarkers });
9868
- if (verification?.ok) return { ok: true, verification, fallback: false };
9886
+ if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: false };
9887
+ if (verification?.reason === "message_verification_unavailable" && !admissionVerification) {
9888
+ return { ok: true, verification: { ok: true, method: "legacy_prompt_accepted", skipped: true }, fallback: false };
9889
+ }
9890
+ if (admissionVerification) {
9891
+ appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_admitted_but_invisible", taskId, sessionId, admission: admissionVerification, reason: verification?.reason || "message_not_visible" });
9892
+ }
9869
9893
  const canFallbackDirect = Boolean(opencodeBaseUrl);
9870
9894
  if (canFallbackDirect) {
9871
9895
  const retryBeforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => beforeMessages);
9872
- const retrySendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true });
9896
+ const retrySendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true, legacyOnly: Boolean(admissionVerification) });
9873
9897
  try {
9874
9898
  admissionVerification = openCodePromptAdmissionVerification(retrySendResult, sessionId);
9875
9899
  } catch (error) {
9876
9900
  verification = { ok: false, reason: error.message };
9877
9901
  }
9878
- if (admissionVerification) return { ok: true, verification: admissionVerification, fallback: true };
9879
9902
  verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages: retryBeforeMessages, expectedText: text, markers: eventMarkers });
9880
- if (verification?.ok) return { ok: true, verification, fallback: true };
9903
+ if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: true };
9904
+ if (verification?.reason === "message_verification_unavailable" && !admissionVerification) {
9905
+ return { ok: true, verification: { ok: true, method: "legacy_prompt_accepted", skipped: true }, fallback: true };
9906
+ }
9881
9907
  }
9882
9908
  const reason = verification?.reason || "message_delivery_failed";
9883
9909
  appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason, fallbackAttempted: canFallbackDirect });
@@ -9923,7 +9949,7 @@ async function recoverClickUpPmSession({ openCodeClient, sendSessionEvent, click
9923
9949
  const replacementMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(metadataWithRouting, config.routing.metadataKey, replacementSessionId), config.routing.metadataKey);
9924
9950
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: replacementMetadata });
9925
9951
  appendClickUpWebhookLocalLog(worktree, { type: "pm_session_recovery_succeeded", taskId, staleSessionId, replacementSessionId });
9926
- return { ok: true, action: "sent_to_replacement_session", taskId, sessionId: replacementSessionId, staleSessionId, replacementAttempted: true, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath, deliveryVerification: replacementDelivery.verification, deliveryFallback: replacementDelivery.fallback };
9952
+ return { ok: true, action: "sent_to_replacement_session", taskId, sessionId: replacementSessionId, staleSessionId, replacementAttempted: true, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath, deliveryVerification: replacementDelivery.verification, deliveryAdmission: replacementDelivery.admissionVerification, deliveryFallback: replacementDelivery.fallback, deliveryAttempts: replacementDelivery.fallback ? 2 : 1 };
9927
9953
  }
9928
9954
  function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", worktree = "", deliveryEvidencePath = "", humanRoleContext = [] }) {
9929
9955
  const comment = clickUpCommentFromPayload(payload);
@@ -10219,7 +10245,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
10219
10245
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
10220
10246
  const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
10221
10247
  stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
10222
- 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 });
10248
+ 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, deliveryAdmission: delivery.admissionVerification, deliveryFallback: delivery.fallback, deliveryAttempts: delivery.fallback ? 2 : 1 });
10223
10249
  }
10224
10250
  const sessionId = String(existingSessionId);
10225
10251
  if (await sessionExists(openCodeClient, sessionId)) {
@@ -10229,7 +10255,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
10229
10255
  const recovery2 = await recoverClickUpPmSession({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, staleSessionId: sessionId, sessionTitle, taskRoute, metadataWithRouting, config, prompt, eventMarkers: [taskId, eventType], deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, eventKey, createSession, verifySessionEventDelivery });
10230
10256
  return finish(recovery2);
10231
10257
  }
10232
- 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 });
10258
+ 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, deliveryAdmission: delivery.admissionVerification, deliveryFallback: delivery.fallback, deliveryAttempts: delivery.fallback ? 2 : 1 });
10233
10259
  }
10234
10260
  const at = now().toISOString();
10235
10261
  appendClickUpWebhookLocalLog(worktree, { type: "missing_session", taskId, sessionId, host, at });
@@ -10240,8 +10266,11 @@ async function routeClickUpWebhookEvent(options = {}) {
10240
10266
  const taskId = clickUpTaskIdFromPayload(options.payload || {});
10241
10267
  return withClickUpTaskRouteLock(taskId, () => routeClickUpWebhookEventUnlocked(options));
10242
10268
  }
10243
- async function reconcileClickUpStartup({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
10269
+ async function reconcileClickUpStartup({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, waitForReadiness = waitForOpenCodeReadiness, saveState = null, now = () => /* @__PURE__ */ new Date(), limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
10244
10270
  if (!config || !clickupClient?.listAssignedTasks) return { ok: true, skipped: true, reason: "clickup_task_listing_unavailable", assigned: 0, comments: 0 };
10271
+ const readiness = await waitForReadiness(openCodeClient, { worktree, now });
10272
+ appendClickUpWebhookLocalLog(worktree, { type: readiness.ok ? "startup_reconciliation_readiness_ready" : "startup_reconciliation_readiness_failed", ...readiness });
10273
+ if (!readiness.ok) return { ok: false, skipped: true, reason: "opencode_not_ready", readiness, assigned: 0, comments: 0, ignored: 0, errors: 1, undelivered: 1, tasks: [], validation: { undelivered: 1, emptyPromptSessions: [] } };
10245
10274
  let authorizedUserId = "";
10246
10275
  if (clickupClient?.getAuthorizedUser) {
10247
10276
  try {
@@ -10299,7 +10328,10 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
10299
10328
  branch: result?.branch || null,
10300
10329
  worktree: result?.worktree || null,
10301
10330
  verification: result?.deliveryVerification?.method || result?.deliveryVerification?.reason || null,
10302
- delivered: hasActionableClickUpRoute(result)
10331
+ delivered: hasActionableClickUpRoute(result),
10332
+ attempts: result?.deliveryAttempts || null,
10333
+ finalMessageCount: result?.deliveryVerification?.afterCount ?? null,
10334
+ finalMessageId: result?.deliveryVerification?.lastMessageId || null
10303
10335
  };
10304
10336
  routed.tasks.push(routeSummary);
10305
10337
  appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_routed", ...routeSummary });
@@ -12160,7 +12192,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
12160
12192
  }
12161
12193
  };
12162
12194
  }
12163
- 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, createOpenCodeSession, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionMessages, reconcileClickUpStartup, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, 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 };
12195
+ 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, createOpenCodeSession, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionMessages, reconcileClickUpStartup, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, 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 };
12164
12196
 
12165
12197
  // src/sanitize_cli.js
12166
12198
  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.48",
3
+ "version": "0.1.49",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"