@defend-tech/opencode-optima 0.1.34 → 0.1.36

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
@@ -7935,6 +7935,8 @@ var CLICKUP_PM_MENTION_NAME = "Defend Tech Product Manager";
7935
7935
  var CLICKUP_WEBHOOK_RUNTIME_VERSION = 1;
7936
7936
  var CLICKUP_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
7937
7937
  var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
7938
+ var CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT = 50;
7939
+ var CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT = 100;
7938
7940
  var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
7939
7941
  var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
7940
7942
  var DISCUSSION_BACKFILL_FETCH_LIMIT = 100;
@@ -9053,6 +9055,7 @@ function sanitizeClickUpWebhookState(state = {}, config = null) {
9053
9055
  lastValidatedAt: state.lastValidatedAt || null,
9054
9056
  listener: isPlainObject(state.listener) ? state.listener : {},
9055
9057
  pendingSessions: isPlainObject(state.pendingSessions) ? state.pendingSessions : {},
9058
+ lastWebhookAt: state.lastWebhookAt || state.last_webhook_at || null,
9056
9059
  recentEventKeys
9057
9060
  };
9058
9061
  }
@@ -9135,6 +9138,9 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
9135
9138
  async deleteWebhook(webhookId) {
9136
9139
  return request(`https://api.clickup.com/api/v2/webhook/${encodeURIComponent(webhookId)}`, { method: "DELETE" });
9137
9140
  },
9141
+ async getAuthorizedUser() {
9142
+ return request("https://api.clickup.com/api/v2/user");
9143
+ },
9138
9144
  async getTask(taskId) {
9139
9145
  return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}`);
9140
9146
  },
@@ -9149,6 +9155,20 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
9149
9155
  method: "POST",
9150
9156
  body: JSON.stringify({ comment_text: comment })
9151
9157
  });
9158
+ },
9159
+ async listAssignedTasks({ assigneeId, statuses = [], limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
9160
+ const params = new URLSearchParams();
9161
+ if (assigneeId) params.append("assignees[]", assigneeId);
9162
+ for (const status of statuses) if (status) params.append("statuses[]", status);
9163
+ if (limit) params.append("limit", String(limit));
9164
+ return request(`https://api.clickup.com/api/v2/team/${encodeURIComponent(config.teamId)}/task?${params.toString()}`);
9165
+ },
9166
+ async getTaskComments({ taskId, start, limit = CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT } = {}) {
9167
+ const params = new URLSearchParams();
9168
+ if (start) params.set("start", String(start));
9169
+ if (limit) params.set("limit", String(limit));
9170
+ const suffix = params.toString() ? `?${params.toString()}` : "";
9171
+ return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}/comment${suffix}`);
9152
9172
  }
9153
9173
  };
9154
9174
  }
@@ -9194,7 +9214,7 @@ async function findReusableClickUpWebhook(config, clickupClient = null) {
9194
9214
  }
9195
9215
  return null;
9196
9216
  }
9197
- async function validateClickUpWebhookState(state, config, clickupClient = null) {
9217
+ async function validateClickUpWebhookState(state, config, clickupClient = null, { allowRemoteUnhealthyLocalRecovery = false } = {}) {
9198
9218
  if (!isClickUpWebhookStateActive(state, config)) return { valid: false, reason: "state_incomplete" };
9199
9219
  if (clickupClient?.listWebhooks) {
9200
9220
  const listed = await clickupClient.listWebhooks({ teamId: config.teamId });
@@ -9202,10 +9222,22 @@ async function validateClickUpWebhookState(state, config, clickupClient = null)
9202
9222
  if (!match) return { valid: false, reason: "remote_state_missing" };
9203
9223
  const remote = normalizeClickUpWebhookApiResponse(match, config);
9204
9224
  const remoteWithLocalSecret = { ...remote, secret: state.secret };
9205
- if (!isClickUpWebhookStateActive(remoteWithLocalSecret, config) || remote.webhookId !== state.webhookId) {
9206
- return { valid: false, reason: "remote_state_mismatch", remote };
9225
+ if (isClickUpWebhookStateActive(remoteWithLocalSecret, config) && remote.webhookId === state.webhookId) {
9226
+ return { valid: true, mode: "remote", state: { ...remoteWithLocalSecret, recentEventKeys: state.recentEventKeys || [], lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() } };
9227
+ }
9228
+ const localStateValidation = await validateClickUpWebhookState(state, config, null);
9229
+ if (allowRemoteUnhealthyLocalRecovery && localStateValidation.valid && remote.webhookId === state.webhookId) {
9230
+ const remoteConfigMatches = remote.publicUrl === config.webhook.publicUrl && [...new Set(config.webhook.events || [])].every((event) => new Set(remote.events || []).has(event));
9231
+ if (remoteConfigMatches) {
9232
+ return {
9233
+ ...localStateValidation,
9234
+ mode: "local_state_remote_unhealthy",
9235
+ limitation: "ClickUp remote webhook is present but unhealthy; Optima starts the local listener from matching local id/secret/config so delivery can recover.",
9236
+ remote
9237
+ };
9238
+ }
9207
9239
  }
9208
- return { valid: true, mode: "remote", state: { ...remoteWithLocalSecret, recentEventKeys: state.recentEventKeys || [], lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() } };
9240
+ return { valid: false, reason: "remote_state_mismatch", remote };
9209
9241
  }
9210
9242
  return { valid: true, mode: "local_state", state: { ...state, lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() }, limitation: "ClickUp API validation unavailable; Optima trusts ignored local runtime state only after id/secret/config checks." };
9211
9243
  }
@@ -9215,7 +9247,7 @@ async function ensureClickUpWebhookSubscription({ validation, worktree, clickupC
9215
9247
  }
9216
9248
  const { config } = validation;
9217
9249
  const existing = readClickUpWebhookState(worktree, config);
9218
- const existingValidation = await validateClickUpWebhookState(existing, config, clickupClient);
9250
+ const existingValidation = await validateClickUpWebhookState(existing, config, clickupClient, { allowRemoteUnhealthyLocalRecovery: true });
9219
9251
  if (existingValidation.valid) {
9220
9252
  const state = writeClickUpWebhookState(worktree, { ...existingValidation.state, recentEventKeys: existing.recentEventKeys || [] }, config);
9221
9253
  clickUpWebhookLifecycleLog(worktree, { type: "remote_webhook_reused", webhookId: state.webhookId, mode: existingValidation.mode });
@@ -9366,6 +9398,31 @@ function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventTy
9366
9398
  function clickUpTaskIdFromPayload(payload = {}) {
9367
9399
  return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
9368
9400
  }
9401
+ function clickUpTaskListItems(response = {}) {
9402
+ if (Array.isArray(response)) return response;
9403
+ if (Array.isArray(response.tasks)) return response.tasks;
9404
+ if (Array.isArray(response.data)) return response.data;
9405
+ if (Array.isArray(response?.data?.tasks)) return response.data.tasks;
9406
+ return [];
9407
+ }
9408
+ function clickUpCommentListItems(response = {}) {
9409
+ if (Array.isArray(response)) return response;
9410
+ if (Array.isArray(response.comments)) return response.comments;
9411
+ if (Array.isArray(response.data)) return response.data;
9412
+ if (Array.isArray(response?.data?.comments)) return response.data.comments;
9413
+ return [];
9414
+ }
9415
+ function clickUpTimestampMs(value) {
9416
+ if (value === null || value === void 0 || value === "") return 0;
9417
+ if (value instanceof Date) return value.getTime();
9418
+ const numeric = Number(value);
9419
+ if (Number.isFinite(numeric) && numeric > 0) return numeric < 1e10 ? numeric * 1e3 : numeric;
9420
+ const parsed = Date.parse(String(value));
9421
+ return Number.isFinite(parsed) ? parsed : 0;
9422
+ }
9423
+ function clickUpCommentUpdatedMs(comment = {}) {
9424
+ return clickUpTimestampMs(comment.date_updated || comment.dateUpdated || comment.updated_at || comment.updatedAt || comment.date || comment.date_created || comment.created_at);
9425
+ }
9369
9426
  function clickUpTaskName(task = {}) {
9370
9427
  return String(task?.name || "").trim();
9371
9428
  }
@@ -9758,12 +9815,18 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9758
9815
  if (!taskId) return { ok: false, action: "error", reason: "missing_task_id" };
9759
9816
  let task = payload.task || (clickupClient?.getTask ? await clickupClient.getTask(taskId) : null);
9760
9817
  if (!task) return { ok: false, action: "error", reason: "task_unavailable", taskId };
9818
+ if (clickupClient?.getTask) {
9819
+ const latestTask = await clickupClient.getTask(taskId);
9820
+ if (latestTask) task = latestTask;
9821
+ }
9761
9822
  const isCommentEvent = eventType === "taskCommentPosted" || eventType === "taskCommentUpdated";
9762
9823
  if (isCommentEvent) {
9763
9824
  const comment = clickUpCommentFromPayload(payload);
9764
9825
  const authorId = clickUpCommentAuthorId(comment);
9765
9826
  if (authorId && authorId === config.routing.ignoredCommentAuthorId) return finish({ ok: true, action: "ignored", reason: "self_authored_comment", taskId });
9766
- if (!clickUpCommentMentionsProductManager(comment, config.routing)) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention", taskId });
9827
+ const mentionsProductManager = clickUpCommentMentionsProductManager(comment, config.routing);
9828
+ const assignedToProductManager = isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId);
9829
+ if (!mentionsProductManager && !assignedToProductManager) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention_or_assignment", taskId });
9767
9830
  commentLedgerKey = clickUpCommentLedgerKey({ taskId, eventType, payload });
9768
9831
  try {
9769
9832
  if (isClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, worktree })) {
@@ -9775,10 +9838,6 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9775
9838
  }
9776
9839
  if (!isCommentEvent && !isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) return finish({ ok: true, action: "ignored", reason: "not_assigned_to_product_manager", taskId });
9777
9840
  if (isClickUpTaskTerminal(task, payload, config.routing.ignoredStatuses)) return finish({ ok: true, action: "ignored", reason: "terminal_status", taskId });
9778
- if (clickupClient?.getTask) {
9779
- const latestTask = await clickupClient.getTask(taskId);
9780
- if (latestTask) task = latestTask;
9781
- }
9782
9841
  const sessionTitle = formatClickUpSessionTitle({ taskId, payloadTask: payload.task, task });
9783
9842
  const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
9784
9843
  const metadata = normalizeAgentMetadataJson(existingMetadata);
@@ -9846,6 +9905,94 @@ async function routeClickUpWebhookEvent(options = {}) {
9846
9905
  const taskId = clickUpTaskIdFromPayload(options.payload || {});
9847
9906
  return withClickUpTaskRouteLock(taskId, () => routeClickUpWebhookEventUnlocked(options));
9848
9907
  }
9908
+ async function reconcileClickUpStartup({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
9909
+ if (!config || !clickupClient?.listAssignedTasks) return { ok: true, skipped: true, reason: "clickup_task_listing_unavailable", assigned: 0, comments: 0 };
9910
+ let authorizedUserId = "";
9911
+ if (clickupClient?.getAuthorizedUser) {
9912
+ try {
9913
+ const userResponse = await clickupClient.getAuthorizedUser();
9914
+ authorizedUserId = String(userResponse?.user?.id || userResponse?.id || "").trim();
9915
+ } catch (error) {
9916
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_user_lookup_failed", message: error.message });
9917
+ }
9918
+ }
9919
+ let mutableState = state;
9920
+ const persistState = (nextState) => {
9921
+ mutableState = nextState;
9922
+ if (saveState) saveState(nextState);
9923
+ };
9924
+ const lastWebhookMs = clickUpTimestampMs(mutableState.lastWebhookAt);
9925
+ const ignored = new Set((config.routing?.ignoredStatuses || CLICKUP_WEBHOOK_TERMINAL_STATUSES).map(normalizeClickUpStatus));
9926
+ const routed = { assigned: 0, comments: 0, ignored: 0, errors: 0 };
9927
+ clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_started", lastWebhookAt: state.lastWebhookAt || null });
9928
+ const listed = await clickupClient.listAssignedTasks({ assigneeId: config.routing.productManagerAssigneeId, limit });
9929
+ const tasks = clickUpTaskListItems(listed).slice(0, limit);
9930
+ for (const task of tasks) {
9931
+ const taskId = String(task?.id || task?.task_id || task?.taskId || "").trim();
9932
+ if (!taskId) {
9933
+ routed.ignored += 1;
9934
+ continue;
9935
+ }
9936
+ if (ignored.has(normalizeClickUpStatus(clickUpTaskStatus(task)))) {
9937
+ routed.ignored += 1;
9938
+ continue;
9939
+ }
9940
+ if (!isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) {
9941
+ routed.ignored += 1;
9942
+ continue;
9943
+ }
9944
+ try {
9945
+ const result = await routeClickUpWebhookEvent({
9946
+ payload: { webhook_id: mutableState.webhookId || "startup", event: "taskAssigneeUpdated", task_id: taskId, task, startup_reconciliation: true },
9947
+ config,
9948
+ state: mutableState,
9949
+ worktree,
9950
+ clickupClient,
9951
+ openCodeClient,
9952
+ sessionExists,
9953
+ createSession,
9954
+ sendSessionEvent,
9955
+ saveState: persistState,
9956
+ now
9957
+ });
9958
+ if (result?.ok && result.action !== "ignored") routed.assigned += 1;
9959
+ } catch (error) {
9960
+ routed.errors += 1;
9961
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_failed", taskId, message: error.message });
9962
+ }
9963
+ if (!lastWebhookMs || !clickupClient?.getTaskComments) continue;
9964
+ try {
9965
+ const commentsResponse = await clickupClient.getTaskComments({ taskId, start: lastWebhookMs, limit: CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT });
9966
+ for (const comment of clickUpCommentListItems(commentsResponse).slice(0, CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT)) {
9967
+ const commentMs = clickUpCommentUpdatedMs(comment);
9968
+ if (!commentMs || commentMs <= lastWebhookMs) continue;
9969
+ const authorId = clickUpCommentAuthorId(comment);
9970
+ if (authorId && (authorId === config.routing.ignoredCommentAuthorId || authorId === authorizedUserId)) continue;
9971
+ if (!clickUpCommentMentionsProductManager(comment, config.routing)) continue;
9972
+ const event = comment.date_updated || comment.dateUpdated || comment.updated_at || comment.updatedAt ? "taskCommentUpdated" : "taskCommentPosted";
9973
+ const result = await routeClickUpWebhookEvent({
9974
+ payload: { webhook_id: mutableState.webhookId || "startup", event, task_id: taskId, task, comment, history_item: { id: `startup-${taskId}-${comment.id || commentMs}` }, startup_reconciliation: true },
9975
+ config,
9976
+ state: mutableState,
9977
+ worktree,
9978
+ clickupClient,
9979
+ openCodeClient,
9980
+ sessionExists,
9981
+ createSession,
9982
+ sendSessionEvent,
9983
+ saveState: persistState,
9984
+ now
9985
+ });
9986
+ if (result?.ok && result.action !== "ignored") routed.comments += 1;
9987
+ }
9988
+ } catch (error) {
9989
+ routed.errors += 1;
9990
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comments_failed", taskId, message: error.message });
9991
+ }
9992
+ }
9993
+ clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...routed });
9994
+ return { ok: routed.errors === 0, ...routed };
9995
+ }
9849
9996
  function clickUpWebhookExpectedPath(config) {
9850
9997
  try {
9851
9998
  return new URL(config?.webhook?.publicUrl).pathname || "/";
@@ -9856,9 +10003,14 @@ function clickUpWebhookExpectedPath(config) {
9856
10003
  async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState, now = () => /* @__PURE__ */ new Date() } = {}) {
9857
10004
  let payload = null;
9858
10005
  let handled = null;
10006
+ let authenticatedWebhook = false;
10007
+ let receivedAt = null;
9859
10008
  const finish = (result) => {
9860
10009
  handled = result;
9861
- writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, payload, at: now() });
10010
+ receivedAt ??= now();
10011
+ const auditState = authenticatedWebhook ? { ...state, lastWebhookAt: receivedAt.toISOString() } : state;
10012
+ if (saveState && authenticatedWebhook) saveState(auditState);
10013
+ writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state: auditState, handled, payload, at: receivedAt });
9862
10014
  return result;
9863
10015
  };
9864
10016
  try {
@@ -9872,12 +10024,15 @@ async function handleClickUpWebhookRequest({ method = "POST", url = null, header
9872
10024
  if (bodyBytes > maxBodyBytes) return finish({ ok: false, status: 413, reason: "body_too_large" });
9873
10025
  const signature = headers["x-signature"] || headers["X-Signature"];
9874
10026
  if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return finish({ ok: false, status: 401, reason: "invalid_signature" });
10027
+ authenticatedWebhook = true;
10028
+ receivedAt = now();
9875
10029
  try {
9876
10030
  payload = JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody));
9877
10031
  } catch {
9878
10032
  return finish({ ok: false, status: 400, reason: "invalid_json" });
9879
10033
  }
9880
- const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
10034
+ const receivedState = { ...state, lastWebhookAt: receivedAt.toISOString() };
10035
+ const result = await routeClickUpWebhookEvent({ payload, config, state: receivedState, worktree, clickupClient, openCodeClient, saveState });
9881
10036
  return finish({ ok: result.ok, status: result.ok ? 200 : 422, result });
9882
10037
  } catch (error) {
9883
10038
  writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, error, payload, at: now() });
@@ -11031,6 +11186,18 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
11031
11186
  listener: { bindHost: clickUpWebhookValidation.config.webhook.bindHost, bindPort: clickUpWebhookValidation.config.webhook.bindPort, startedAt: (/* @__PURE__ */ new Date()).toISOString() }
11032
11187
  }, clickUpWebhookValidation.config);
11033
11188
  registerClickUpWebhookLifecycle({ config: clickUpWebhookValidation.config, state: activeState, worktree, clickupClient: lifecycleClickUpClient, listener: readyListener, listenerRegistry });
11189
+ try {
11190
+ await reconcileClickUpStartup({
11191
+ config: clickUpWebhookValidation.config,
11192
+ state: activeState,
11193
+ worktree,
11194
+ clickupClient: lifecycleClickUpClient,
11195
+ openCodeClient: input.client,
11196
+ saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, clickUpWebhookValidation.config)
11197
+ });
11198
+ } catch (error) {
11199
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_failed", message: error.message });
11200
+ }
11034
11201
  } else {
11035
11202
  clickUpWebhookLifecycleLog(worktree, { type: "remote_webhook_preserved", reason: readyListener.reason || "listener_unavailable", webhookId: listenerState.webhookId });
11036
11203
  markClickUpWebhookInactive(worktree, listenerState, clickUpWebhookValidation.config);
@@ -11605,7 +11772,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
11605
11772
  }
11606
11773
  };
11607
11774
  }
11608
- 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, 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 };
11775
+ 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 };
11609
11776
  export {
11610
11777
  OptimaPlugin as default
11611
11778
  };
@@ -7942,6 +7942,8 @@ var CLICKUP_PM_MENTION_NAME = "Defend Tech Product Manager";
7942
7942
  var CLICKUP_WEBHOOK_RUNTIME_VERSION = 1;
7943
7943
  var CLICKUP_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
7944
7944
  var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
7945
+ var CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT = 50;
7946
+ var CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT = 100;
7945
7947
  var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
7946
7948
  var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
7947
7949
  var DISCUSSION_BACKFILL_FETCH_LIMIT = 100;
@@ -9060,6 +9062,7 @@ function sanitizeClickUpWebhookState(state = {}, config = null) {
9060
9062
  lastValidatedAt: state.lastValidatedAt || null,
9061
9063
  listener: isPlainObject(state.listener) ? state.listener : {},
9062
9064
  pendingSessions: isPlainObject(state.pendingSessions) ? state.pendingSessions : {},
9065
+ lastWebhookAt: state.lastWebhookAt || state.last_webhook_at || null,
9063
9066
  recentEventKeys
9064
9067
  };
9065
9068
  }
@@ -9142,6 +9145,9 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
9142
9145
  async deleteWebhook(webhookId) {
9143
9146
  return request(`https://api.clickup.com/api/v2/webhook/${encodeURIComponent(webhookId)}`, { method: "DELETE" });
9144
9147
  },
9148
+ async getAuthorizedUser() {
9149
+ return request("https://api.clickup.com/api/v2/user");
9150
+ },
9145
9151
  async getTask(taskId) {
9146
9152
  return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}`);
9147
9153
  },
@@ -9156,6 +9162,20 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
9156
9162
  method: "POST",
9157
9163
  body: JSON.stringify({ comment_text: comment })
9158
9164
  });
9165
+ },
9166
+ async listAssignedTasks({ assigneeId, statuses = [], limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
9167
+ const params = new URLSearchParams();
9168
+ if (assigneeId) params.append("assignees[]", assigneeId);
9169
+ for (const status of statuses) if (status) params.append("statuses[]", status);
9170
+ if (limit) params.append("limit", String(limit));
9171
+ return request(`https://api.clickup.com/api/v2/team/${encodeURIComponent(config.teamId)}/task?${params.toString()}`);
9172
+ },
9173
+ async getTaskComments({ taskId, start, limit = CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT } = {}) {
9174
+ const params = new URLSearchParams();
9175
+ if (start) params.set("start", String(start));
9176
+ if (limit) params.set("limit", String(limit));
9177
+ const suffix = params.toString() ? `?${params.toString()}` : "";
9178
+ return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}/comment${suffix}`);
9159
9179
  }
9160
9180
  };
9161
9181
  }
@@ -9201,7 +9221,7 @@ async function findReusableClickUpWebhook(config, clickupClient = null) {
9201
9221
  }
9202
9222
  return null;
9203
9223
  }
9204
- async function validateClickUpWebhookState(state, config, clickupClient = null) {
9224
+ async function validateClickUpWebhookState(state, config, clickupClient = null, { allowRemoteUnhealthyLocalRecovery = false } = {}) {
9205
9225
  if (!isClickUpWebhookStateActive(state, config)) return { valid: false, reason: "state_incomplete" };
9206
9226
  if (clickupClient?.listWebhooks) {
9207
9227
  const listed = await clickupClient.listWebhooks({ teamId: config.teamId });
@@ -9209,10 +9229,22 @@ async function validateClickUpWebhookState(state, config, clickupClient = null)
9209
9229
  if (!match) return { valid: false, reason: "remote_state_missing" };
9210
9230
  const remote = normalizeClickUpWebhookApiResponse(match, config);
9211
9231
  const remoteWithLocalSecret = { ...remote, secret: state.secret };
9212
- if (!isClickUpWebhookStateActive(remoteWithLocalSecret, config) || remote.webhookId !== state.webhookId) {
9213
- return { valid: false, reason: "remote_state_mismatch", remote };
9232
+ if (isClickUpWebhookStateActive(remoteWithLocalSecret, config) && remote.webhookId === state.webhookId) {
9233
+ return { valid: true, mode: "remote", state: { ...remoteWithLocalSecret, recentEventKeys: state.recentEventKeys || [], lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() } };
9234
+ }
9235
+ const localStateValidation = await validateClickUpWebhookState(state, config, null);
9236
+ if (allowRemoteUnhealthyLocalRecovery && localStateValidation.valid && remote.webhookId === state.webhookId) {
9237
+ const remoteConfigMatches = remote.publicUrl === config.webhook.publicUrl && [...new Set(config.webhook.events || [])].every((event) => new Set(remote.events || []).has(event));
9238
+ if (remoteConfigMatches) {
9239
+ return {
9240
+ ...localStateValidation,
9241
+ mode: "local_state_remote_unhealthy",
9242
+ limitation: "ClickUp remote webhook is present but unhealthy; Optima starts the local listener from matching local id/secret/config so delivery can recover.",
9243
+ remote
9244
+ };
9245
+ }
9214
9246
  }
9215
- return { valid: true, mode: "remote", state: { ...remoteWithLocalSecret, recentEventKeys: state.recentEventKeys || [], lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() } };
9247
+ return { valid: false, reason: "remote_state_mismatch", remote };
9216
9248
  }
9217
9249
  return { valid: true, mode: "local_state", state: { ...state, lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() }, limitation: "ClickUp API validation unavailable; Optima trusts ignored local runtime state only after id/secret/config checks." };
9218
9250
  }
@@ -9222,7 +9254,7 @@ async function ensureClickUpWebhookSubscription({ validation, worktree, clickupC
9222
9254
  }
9223
9255
  const { config } = validation;
9224
9256
  const existing = readClickUpWebhookState(worktree, config);
9225
- const existingValidation = await validateClickUpWebhookState(existing, config, clickupClient);
9257
+ const existingValidation = await validateClickUpWebhookState(existing, config, clickupClient, { allowRemoteUnhealthyLocalRecovery: true });
9226
9258
  if (existingValidation.valid) {
9227
9259
  const state = writeClickUpWebhookState(worktree, { ...existingValidation.state, recentEventKeys: existing.recentEventKeys || [] }, config);
9228
9260
  clickUpWebhookLifecycleLog(worktree, { type: "remote_webhook_reused", webhookId: state.webhookId, mode: existingValidation.mode });
@@ -9373,6 +9405,31 @@ function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventTy
9373
9405
  function clickUpTaskIdFromPayload(payload = {}) {
9374
9406
  return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
9375
9407
  }
9408
+ function clickUpTaskListItems(response = {}) {
9409
+ if (Array.isArray(response)) return response;
9410
+ if (Array.isArray(response.tasks)) return response.tasks;
9411
+ if (Array.isArray(response.data)) return response.data;
9412
+ if (Array.isArray(response?.data?.tasks)) return response.data.tasks;
9413
+ return [];
9414
+ }
9415
+ function clickUpCommentListItems(response = {}) {
9416
+ if (Array.isArray(response)) return response;
9417
+ if (Array.isArray(response.comments)) return response.comments;
9418
+ if (Array.isArray(response.data)) return response.data;
9419
+ if (Array.isArray(response?.data?.comments)) return response.data.comments;
9420
+ return [];
9421
+ }
9422
+ function clickUpTimestampMs(value) {
9423
+ if (value === null || value === void 0 || value === "") return 0;
9424
+ if (value instanceof Date) return value.getTime();
9425
+ const numeric = Number(value);
9426
+ if (Number.isFinite(numeric) && numeric > 0) return numeric < 1e10 ? numeric * 1e3 : numeric;
9427
+ const parsed = Date.parse(String(value));
9428
+ return Number.isFinite(parsed) ? parsed : 0;
9429
+ }
9430
+ function clickUpCommentUpdatedMs(comment = {}) {
9431
+ return clickUpTimestampMs(comment.date_updated || comment.dateUpdated || comment.updated_at || comment.updatedAt || comment.date || comment.date_created || comment.created_at);
9432
+ }
9376
9433
  function clickUpTaskName(task = {}) {
9377
9434
  return String(task?.name || "").trim();
9378
9435
  }
@@ -9765,12 +9822,18 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9765
9822
  if (!taskId) return { ok: false, action: "error", reason: "missing_task_id" };
9766
9823
  let task = payload.task || (clickupClient?.getTask ? await clickupClient.getTask(taskId) : null);
9767
9824
  if (!task) return { ok: false, action: "error", reason: "task_unavailable", taskId };
9825
+ if (clickupClient?.getTask) {
9826
+ const latestTask = await clickupClient.getTask(taskId);
9827
+ if (latestTask) task = latestTask;
9828
+ }
9768
9829
  const isCommentEvent = eventType === "taskCommentPosted" || eventType === "taskCommentUpdated";
9769
9830
  if (isCommentEvent) {
9770
9831
  const comment = clickUpCommentFromPayload(payload);
9771
9832
  const authorId = clickUpCommentAuthorId(comment);
9772
9833
  if (authorId && authorId === config.routing.ignoredCommentAuthorId) return finish({ ok: true, action: "ignored", reason: "self_authored_comment", taskId });
9773
- if (!clickUpCommentMentionsProductManager(comment, config.routing)) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention", taskId });
9834
+ const mentionsProductManager = clickUpCommentMentionsProductManager(comment, config.routing);
9835
+ const assignedToProductManager = isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId);
9836
+ if (!mentionsProductManager && !assignedToProductManager) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention_or_assignment", taskId });
9774
9837
  commentLedgerKey = clickUpCommentLedgerKey({ taskId, eventType, payload });
9775
9838
  try {
9776
9839
  if (isClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, worktree })) {
@@ -9782,10 +9845,6 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9782
9845
  }
9783
9846
  if (!isCommentEvent && !isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) return finish({ ok: true, action: "ignored", reason: "not_assigned_to_product_manager", taskId });
9784
9847
  if (isClickUpTaskTerminal(task, payload, config.routing.ignoredStatuses)) return finish({ ok: true, action: "ignored", reason: "terminal_status", taskId });
9785
- if (clickupClient?.getTask) {
9786
- const latestTask = await clickupClient.getTask(taskId);
9787
- if (latestTask) task = latestTask;
9788
- }
9789
9848
  const sessionTitle = formatClickUpSessionTitle({ taskId, payloadTask: payload.task, task });
9790
9849
  const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
9791
9850
  const metadata = normalizeAgentMetadataJson(existingMetadata);
@@ -9853,6 +9912,94 @@ async function routeClickUpWebhookEvent(options = {}) {
9853
9912
  const taskId = clickUpTaskIdFromPayload(options.payload || {});
9854
9913
  return withClickUpTaskRouteLock(taskId, () => routeClickUpWebhookEventUnlocked(options));
9855
9914
  }
9915
+ async function reconcileClickUpStartup({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
9916
+ if (!config || !clickupClient?.listAssignedTasks) return { ok: true, skipped: true, reason: "clickup_task_listing_unavailable", assigned: 0, comments: 0 };
9917
+ let authorizedUserId = "";
9918
+ if (clickupClient?.getAuthorizedUser) {
9919
+ try {
9920
+ const userResponse = await clickupClient.getAuthorizedUser();
9921
+ authorizedUserId = String(userResponse?.user?.id || userResponse?.id || "").trim();
9922
+ } catch (error) {
9923
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_user_lookup_failed", message: error.message });
9924
+ }
9925
+ }
9926
+ let mutableState = state;
9927
+ const persistState = (nextState) => {
9928
+ mutableState = nextState;
9929
+ if (saveState) saveState(nextState);
9930
+ };
9931
+ const lastWebhookMs = clickUpTimestampMs(mutableState.lastWebhookAt);
9932
+ const ignored = new Set((config.routing?.ignoredStatuses || CLICKUP_WEBHOOK_TERMINAL_STATUSES).map(normalizeClickUpStatus));
9933
+ const routed = { assigned: 0, comments: 0, ignored: 0, errors: 0 };
9934
+ clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_started", lastWebhookAt: state.lastWebhookAt || null });
9935
+ const listed = await clickupClient.listAssignedTasks({ assigneeId: config.routing.productManagerAssigneeId, limit });
9936
+ const tasks = clickUpTaskListItems(listed).slice(0, limit);
9937
+ for (const task of tasks) {
9938
+ const taskId = String(task?.id || task?.task_id || task?.taskId || "").trim();
9939
+ if (!taskId) {
9940
+ routed.ignored += 1;
9941
+ continue;
9942
+ }
9943
+ if (ignored.has(normalizeClickUpStatus(clickUpTaskStatus(task)))) {
9944
+ routed.ignored += 1;
9945
+ continue;
9946
+ }
9947
+ if (!isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) {
9948
+ routed.ignored += 1;
9949
+ continue;
9950
+ }
9951
+ try {
9952
+ const result = await routeClickUpWebhookEvent({
9953
+ payload: { webhook_id: mutableState.webhookId || "startup", event: "taskAssigneeUpdated", task_id: taskId, task, startup_reconciliation: true },
9954
+ config,
9955
+ state: mutableState,
9956
+ worktree,
9957
+ clickupClient,
9958
+ openCodeClient,
9959
+ sessionExists,
9960
+ createSession,
9961
+ sendSessionEvent,
9962
+ saveState: persistState,
9963
+ now
9964
+ });
9965
+ if (result?.ok && result.action !== "ignored") routed.assigned += 1;
9966
+ } catch (error) {
9967
+ routed.errors += 1;
9968
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_failed", taskId, message: error.message });
9969
+ }
9970
+ if (!lastWebhookMs || !clickupClient?.getTaskComments) continue;
9971
+ try {
9972
+ const commentsResponse = await clickupClient.getTaskComments({ taskId, start: lastWebhookMs, limit: CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT });
9973
+ for (const comment of clickUpCommentListItems(commentsResponse).slice(0, CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT)) {
9974
+ const commentMs = clickUpCommentUpdatedMs(comment);
9975
+ if (!commentMs || commentMs <= lastWebhookMs) continue;
9976
+ const authorId = clickUpCommentAuthorId(comment);
9977
+ if (authorId && (authorId === config.routing.ignoredCommentAuthorId || authorId === authorizedUserId)) continue;
9978
+ if (!clickUpCommentMentionsProductManager(comment, config.routing)) continue;
9979
+ const event = comment.date_updated || comment.dateUpdated || comment.updated_at || comment.updatedAt ? "taskCommentUpdated" : "taskCommentPosted";
9980
+ const result = await routeClickUpWebhookEvent({
9981
+ payload: { webhook_id: mutableState.webhookId || "startup", event, task_id: taskId, task, comment, history_item: { id: `startup-${taskId}-${comment.id || commentMs}` }, startup_reconciliation: true },
9982
+ config,
9983
+ state: mutableState,
9984
+ worktree,
9985
+ clickupClient,
9986
+ openCodeClient,
9987
+ sessionExists,
9988
+ createSession,
9989
+ sendSessionEvent,
9990
+ saveState: persistState,
9991
+ now
9992
+ });
9993
+ if (result?.ok && result.action !== "ignored") routed.comments += 1;
9994
+ }
9995
+ } catch (error) {
9996
+ routed.errors += 1;
9997
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comments_failed", taskId, message: error.message });
9998
+ }
9999
+ }
10000
+ clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...routed });
10001
+ return { ok: routed.errors === 0, ...routed };
10002
+ }
9856
10003
  function clickUpWebhookExpectedPath(config) {
9857
10004
  try {
9858
10005
  return new URL(config?.webhook?.publicUrl).pathname || "/";
@@ -9863,9 +10010,14 @@ function clickUpWebhookExpectedPath(config) {
9863
10010
  async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState, now = () => /* @__PURE__ */ new Date() } = {}) {
9864
10011
  let payload = null;
9865
10012
  let handled = null;
10013
+ let authenticatedWebhook = false;
10014
+ let receivedAt = null;
9866
10015
  const finish = (result) => {
9867
10016
  handled = result;
9868
- writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, payload, at: now() });
10017
+ receivedAt ??= now();
10018
+ const auditState = authenticatedWebhook ? { ...state, lastWebhookAt: receivedAt.toISOString() } : state;
10019
+ if (saveState && authenticatedWebhook) saveState(auditState);
10020
+ writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state: auditState, handled, payload, at: receivedAt });
9869
10021
  return result;
9870
10022
  };
9871
10023
  try {
@@ -9879,12 +10031,15 @@ async function handleClickUpWebhookRequest({ method = "POST", url = null, header
9879
10031
  if (bodyBytes > maxBodyBytes) return finish({ ok: false, status: 413, reason: "body_too_large" });
9880
10032
  const signature = headers["x-signature"] || headers["X-Signature"];
9881
10033
  if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return finish({ ok: false, status: 401, reason: "invalid_signature" });
10034
+ authenticatedWebhook = true;
10035
+ receivedAt = now();
9882
10036
  try {
9883
10037
  payload = JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody));
9884
10038
  } catch {
9885
10039
  return finish({ ok: false, status: 400, reason: "invalid_json" });
9886
10040
  }
9887
- const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
10041
+ const receivedState = { ...state, lastWebhookAt: receivedAt.toISOString() };
10042
+ const result = await routeClickUpWebhookEvent({ payload, config, state: receivedState, worktree, clickupClient, openCodeClient, saveState });
9888
10043
  return finish({ ok: result.ok, status: result.ok ? 200 : 422, result });
9889
10044
  } catch (error) {
9890
10045
  writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, error, payload, at: now() });
@@ -11038,6 +11193,18 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
11038
11193
  listener: { bindHost: clickUpWebhookValidation.config.webhook.bindHost, bindPort: clickUpWebhookValidation.config.webhook.bindPort, startedAt: (/* @__PURE__ */ new Date()).toISOString() }
11039
11194
  }, clickUpWebhookValidation.config);
11040
11195
  registerClickUpWebhookLifecycle({ config: clickUpWebhookValidation.config, state: activeState, worktree, clickupClient: lifecycleClickUpClient, listener: readyListener, listenerRegistry });
11196
+ try {
11197
+ await reconcileClickUpStartup({
11198
+ config: clickUpWebhookValidation.config,
11199
+ state: activeState,
11200
+ worktree,
11201
+ clickupClient: lifecycleClickUpClient,
11202
+ openCodeClient: input.client,
11203
+ saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, clickUpWebhookValidation.config)
11204
+ });
11205
+ } catch (error) {
11206
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_failed", message: error.message });
11207
+ }
11041
11208
  } else {
11042
11209
  clickUpWebhookLifecycleLog(worktree, { type: "remote_webhook_preserved", reason: readyListener.reason || "listener_unavailable", webhookId: listenerState.webhookId });
11043
11210
  markClickUpWebhookInactive(worktree, listenerState, clickUpWebhookValidation.config);
@@ -11612,7 +11779,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
11612
11779
  }
11613
11780
  };
11614
11781
  }
11615
- 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, 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 };
11782
+ 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 };
11616
11783
 
11617
11784
  // src/sanitize_cli.js
11618
11785
  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.34",
3
+ "version": "0.1.36",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"