@defend-tech/opencode-optima 0.1.35 → 0.1.37

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
  }
@@ -9378,6 +9398,31 @@ function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventTy
9378
9398
  function clickUpTaskIdFromPayload(payload = {}) {
9379
9399
  return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
9380
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
+ }
9381
9426
  function clickUpTaskName(task = {}) {
9382
9427
  return String(task?.name || "").trim();
9383
9428
  }
@@ -9571,6 +9616,45 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
9571
9616
  deliveryEvidencePath ? `Final merge-trackable evidence must be written under ${deliveryEvidencePath}; use .optima only as local mirror/staging.` : null
9572
9617
  ].filter((part) => part !== null).join("\n");
9573
9618
  }
9619
+ function clickUpNoAbandonmentRuleText() {
9620
+ return "No-abandonment rule: PM must keep working, or before stopping must post a ClickUp comment, hand off, remove PM as assignee, and assign the next owner.";
9621
+ }
9622
+ function buildClickUpStartupResumeComment({ taskId, result = {} } = {}) {
9623
+ const contextParts = [
9624
+ result.branch ? `- Branch: ${result.branch}` : null,
9625
+ result.worktree ? `- Worktree: ${result.worktree}` : null,
9626
+ result.deliveryEvidencePath ? `- Delivery evidence path: ${result.deliveryEvidencePath}` : null,
9627
+ result.evidencePath ? `- Local evidence path: ${result.evidencePath}` : null
9628
+ ].filter(Boolean);
9629
+ return [
9630
+ `Startup reconciliation resumed ClickUp task ${taskId}.`,
9631
+ `Route result: ${result.action || "unknown"}${result.ok === false ? " (failed)" : ""}.`,
9632
+ result.sessionId ? `Session id: ${result.sessionId}.` : "Session id: unavailable.",
9633
+ contextParts.length ? contextParts.join("\n") : "Worktree/branch/delivery evidence path: unavailable.",
9634
+ clickUpNoAbandonmentRuleText()
9635
+ ].join("\n");
9636
+ }
9637
+ function buildClickUpStartupBlockerComment({ taskId, error } = {}) {
9638
+ const summary = error?.message || String(error || "unknown error");
9639
+ return [
9640
+ `Startup reconciliation blocker for ClickUp task ${taskId}: ${summary}`,
9641
+ "Optima skipped this task for this startup pass and continued with the next PM-assigned task.",
9642
+ clickUpNoAbandonmentRuleText()
9643
+ ].join("\n");
9644
+ }
9645
+ async function postClickUpStartupComment({ clickupClient, worktree, taskId, comment, type } = {}) {
9646
+ if (typeof clickupClient?.postTaskComment !== "function") {
9647
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comment_unavailable", taskId, commentType: type });
9648
+ return { ok: false, skipped: true, reason: "post_task_comment_unavailable" };
9649
+ }
9650
+ try {
9651
+ await clickupClient.postTaskComment({ taskId, comment });
9652
+ return { ok: true };
9653
+ } catch (error) {
9654
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comment_failed", taskId, commentType: type, message: error.message });
9655
+ return { ok: false, error: error.message };
9656
+ }
9657
+ }
9574
9658
  function appendClickUpWebhookLocalLog(worktree, entry) {
9575
9659
  const logPath = clickUpWebhookLogPath(worktree);
9576
9660
  fs2.mkdirSync(path2.dirname(logPath), { recursive: true });
@@ -9842,24 +9926,128 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9842
9926
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
9843
9927
  const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
9844
9928
  stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
9845
- return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey });
9929
+ return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9846
9930
  }
9847
9931
  const sessionId = String(existingSessionId);
9848
9932
  if (await sessionExists(openCodeClient, sessionId)) {
9849
9933
  if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
9850
9934
  await sendSessionEvent(openCodeClient, { sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
9851
- return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey });
9935
+ return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9852
9936
  }
9853
9937
  const at = now().toISOString();
9854
9938
  const incidentComment = `Optima webhook could not route this ClickUp event because OpenCode session ${sessionId} is missing on host ${host} at ${at}. No replacement session was created automatically.`;
9855
9939
  appendClickUpWebhookLocalLog(worktree, { type: "missing_session", taskId, sessionId, host, at });
9856
9940
  await clickupClient.postTaskComment({ taskId, comment: incidentComment });
9857
- return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey });
9941
+ return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9858
9942
  }
9859
9943
  async function routeClickUpWebhookEvent(options = {}) {
9860
9944
  const taskId = clickUpTaskIdFromPayload(options.payload || {});
9861
9945
  return withClickUpTaskRouteLock(taskId, () => routeClickUpWebhookEventUnlocked(options));
9862
9946
  }
9947
+ 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 } = {}) {
9948
+ if (!config || !clickupClient?.listAssignedTasks) return { ok: true, skipped: true, reason: "clickup_task_listing_unavailable", assigned: 0, comments: 0 };
9949
+ let authorizedUserId = "";
9950
+ if (clickupClient?.getAuthorizedUser) {
9951
+ try {
9952
+ const userResponse = await clickupClient.getAuthorizedUser();
9953
+ authorizedUserId = String(userResponse?.user?.id || userResponse?.id || "").trim();
9954
+ } catch (error) {
9955
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_user_lookup_failed", message: error.message });
9956
+ }
9957
+ }
9958
+ let mutableState = state;
9959
+ const persistState = (nextState) => {
9960
+ mutableState = nextState;
9961
+ if (saveState) saveState(nextState);
9962
+ };
9963
+ const lastWebhookMs = clickUpTimestampMs(mutableState.lastWebhookAt);
9964
+ const ignored = new Set((config.routing?.ignoredStatuses || CLICKUP_WEBHOOK_TERMINAL_STATUSES).map(normalizeClickUpStatus));
9965
+ const routed = { assigned: 0, comments: 0, ignored: 0, errors: 0 };
9966
+ clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_started", lastWebhookAt: state.lastWebhookAt || null });
9967
+ const listed = await clickupClient.listAssignedTasks({ assigneeId: config.routing.productManagerAssigneeId, limit });
9968
+ const tasks = clickUpTaskListItems(listed).slice(0, limit);
9969
+ for (const task of tasks) {
9970
+ const taskId = String(task?.id || task?.task_id || task?.taskId || "").trim();
9971
+ if (!taskId) {
9972
+ routed.ignored += 1;
9973
+ continue;
9974
+ }
9975
+ if (ignored.has(normalizeClickUpStatus(clickUpTaskStatus(task)))) {
9976
+ routed.ignored += 1;
9977
+ continue;
9978
+ }
9979
+ if (!isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) {
9980
+ routed.ignored += 1;
9981
+ continue;
9982
+ }
9983
+ try {
9984
+ const result = await routeClickUpWebhookEvent({
9985
+ payload: { webhook_id: mutableState.webhookId || "startup", event: "taskAssigneeUpdated", task_id: taskId, task, startup_reconciliation: true },
9986
+ config,
9987
+ state: mutableState,
9988
+ worktree,
9989
+ clickupClient,
9990
+ openCodeClient,
9991
+ sessionExists,
9992
+ createSession,
9993
+ sendSessionEvent,
9994
+ saveState: persistState,
9995
+ now
9996
+ });
9997
+ if (result?.ok && result.action !== "ignored") {
9998
+ routed.assigned += 1;
9999
+ await postClickUpStartupComment({
10000
+ clickupClient,
10001
+ worktree,
10002
+ taskId,
10003
+ type: "resume",
10004
+ comment: buildClickUpStartupResumeComment({ taskId, result })
10005
+ });
10006
+ }
10007
+ } catch (error) {
10008
+ routed.errors += 1;
10009
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_failed", taskId, message: error.message });
10010
+ await postClickUpStartupComment({
10011
+ clickupClient,
10012
+ worktree,
10013
+ taskId,
10014
+ type: "blocker",
10015
+ comment: buildClickUpStartupBlockerComment({ taskId, error })
10016
+ });
10017
+ }
10018
+ if (!lastWebhookMs || !clickupClient?.getTaskComments) continue;
10019
+ try {
10020
+ const commentsResponse = await clickupClient.getTaskComments({ taskId, start: lastWebhookMs, limit: CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT });
10021
+ for (const comment of clickUpCommentListItems(commentsResponse).slice(0, CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT)) {
10022
+ const commentMs = clickUpCommentUpdatedMs(comment);
10023
+ if (!commentMs || commentMs <= lastWebhookMs) continue;
10024
+ const authorId = clickUpCommentAuthorId(comment);
10025
+ if (authorId && (authorId === config.routing.ignoredCommentAuthorId || authorId === authorizedUserId)) continue;
10026
+ if (!clickUpCommentMentionsProductManager(comment, config.routing)) continue;
10027
+ const event = comment.date_updated || comment.dateUpdated || comment.updated_at || comment.updatedAt ? "taskCommentUpdated" : "taskCommentPosted";
10028
+ const result = await routeClickUpWebhookEvent({
10029
+ payload: { webhook_id: mutableState.webhookId || "startup", event, task_id: taskId, task, comment, history_item: { id: `startup-${taskId}-${comment.id || commentMs}` }, startup_reconciliation: true },
10030
+ config,
10031
+ state: mutableState,
10032
+ worktree,
10033
+ clickupClient,
10034
+ openCodeClient,
10035
+ sessionExists,
10036
+ createSession,
10037
+ sendSessionEvent,
10038
+ saveState: persistState,
10039
+ now
10040
+ });
10041
+ if (result?.ok && result.action !== "ignored") routed.comments += 1;
10042
+ }
10043
+ } catch (error) {
10044
+ routed.errors += 1;
10045
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comments_failed", taskId, message: error.message });
10046
+ }
10047
+ }
10048
+ clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...routed });
10049
+ return { ok: routed.errors === 0, ...routed };
10050
+ }
9863
10051
  function clickUpWebhookExpectedPath(config) {
9864
10052
  try {
9865
10053
  return new URL(config?.webhook?.publicUrl).pathname || "/";
@@ -9870,9 +10058,14 @@ function clickUpWebhookExpectedPath(config) {
9870
10058
  async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState, now = () => /* @__PURE__ */ new Date() } = {}) {
9871
10059
  let payload = null;
9872
10060
  let handled = null;
10061
+ let authenticatedWebhook = false;
10062
+ let receivedAt = null;
9873
10063
  const finish = (result) => {
9874
10064
  handled = result;
9875
- writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, payload, at: now() });
10065
+ receivedAt ??= now();
10066
+ const auditState = authenticatedWebhook ? { ...state, lastWebhookAt: receivedAt.toISOString() } : state;
10067
+ if (saveState && authenticatedWebhook) saveState(auditState);
10068
+ writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state: auditState, handled, payload, at: receivedAt });
9876
10069
  return result;
9877
10070
  };
9878
10071
  try {
@@ -9886,12 +10079,15 @@ async function handleClickUpWebhookRequest({ method = "POST", url = null, header
9886
10079
  if (bodyBytes > maxBodyBytes) return finish({ ok: false, status: 413, reason: "body_too_large" });
9887
10080
  const signature = headers["x-signature"] || headers["X-Signature"];
9888
10081
  if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return finish({ ok: false, status: 401, reason: "invalid_signature" });
10082
+ authenticatedWebhook = true;
10083
+ receivedAt = now();
9889
10084
  try {
9890
10085
  payload = JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody));
9891
10086
  } catch {
9892
10087
  return finish({ ok: false, status: 400, reason: "invalid_json" });
9893
10088
  }
9894
- const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
10089
+ const receivedState = { ...state, lastWebhookAt: receivedAt.toISOString() };
10090
+ const result = await routeClickUpWebhookEvent({ payload, config, state: receivedState, worktree, clickupClient, openCodeClient, saveState });
9895
10091
  return finish({ ok: result.ok, status: result.ok ? 200 : 422, result });
9896
10092
  } catch (error) {
9897
10093
  writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, error, payload, at: now() });
@@ -11045,6 +11241,18 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
11045
11241
  listener: { bindHost: clickUpWebhookValidation.config.webhook.bindHost, bindPort: clickUpWebhookValidation.config.webhook.bindPort, startedAt: (/* @__PURE__ */ new Date()).toISOString() }
11046
11242
  }, clickUpWebhookValidation.config);
11047
11243
  registerClickUpWebhookLifecycle({ config: clickUpWebhookValidation.config, state: activeState, worktree, clickupClient: lifecycleClickUpClient, listener: readyListener, listenerRegistry });
11244
+ try {
11245
+ await reconcileClickUpStartup({
11246
+ config: clickUpWebhookValidation.config,
11247
+ state: activeState,
11248
+ worktree,
11249
+ clickupClient: lifecycleClickUpClient,
11250
+ openCodeClient: input.client,
11251
+ saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, clickUpWebhookValidation.config)
11252
+ });
11253
+ } catch (error) {
11254
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_failed", message: error.message });
11255
+ }
11048
11256
  } else {
11049
11257
  clickUpWebhookLifecycleLog(worktree, { type: "remote_webhook_preserved", reason: readyListener.reason || "listener_unavailable", webhookId: listenerState.webhookId });
11050
11258
  markClickUpWebhookInactive(worktree, listenerState, clickUpWebhookValidation.config);
@@ -11619,7 +11827,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
11619
11827
  }
11620
11828
  };
11621
11829
  }
11622
- 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 };
11830
+ OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, reconcileClickUpStartup, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11623
11831
  export {
11624
11832
  OptimaPlugin as default
11625
11833
  };
@@ -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
  }
@@ -9385,6 +9405,31 @@ function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventTy
9385
9405
  function clickUpTaskIdFromPayload(payload = {}) {
9386
9406
  return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
9387
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
+ }
9388
9433
  function clickUpTaskName(task = {}) {
9389
9434
  return String(task?.name || "").trim();
9390
9435
  }
@@ -9578,6 +9623,45 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
9578
9623
  deliveryEvidencePath ? `Final merge-trackable evidence must be written under ${deliveryEvidencePath}; use .optima only as local mirror/staging.` : null
9579
9624
  ].filter((part) => part !== null).join("\n");
9580
9625
  }
9626
+ function clickUpNoAbandonmentRuleText() {
9627
+ return "No-abandonment rule: PM must keep working, or before stopping must post a ClickUp comment, hand off, remove PM as assignee, and assign the next owner.";
9628
+ }
9629
+ function buildClickUpStartupResumeComment({ taskId, result = {} } = {}) {
9630
+ const contextParts = [
9631
+ result.branch ? `- Branch: ${result.branch}` : null,
9632
+ result.worktree ? `- Worktree: ${result.worktree}` : null,
9633
+ result.deliveryEvidencePath ? `- Delivery evidence path: ${result.deliveryEvidencePath}` : null,
9634
+ result.evidencePath ? `- Local evidence path: ${result.evidencePath}` : null
9635
+ ].filter(Boolean);
9636
+ return [
9637
+ `Startup reconciliation resumed ClickUp task ${taskId}.`,
9638
+ `Route result: ${result.action || "unknown"}${result.ok === false ? " (failed)" : ""}.`,
9639
+ result.sessionId ? `Session id: ${result.sessionId}.` : "Session id: unavailable.",
9640
+ contextParts.length ? contextParts.join("\n") : "Worktree/branch/delivery evidence path: unavailable.",
9641
+ clickUpNoAbandonmentRuleText()
9642
+ ].join("\n");
9643
+ }
9644
+ function buildClickUpStartupBlockerComment({ taskId, error } = {}) {
9645
+ const summary = error?.message || String(error || "unknown error");
9646
+ return [
9647
+ `Startup reconciliation blocker for ClickUp task ${taskId}: ${summary}`,
9648
+ "Optima skipped this task for this startup pass and continued with the next PM-assigned task.",
9649
+ clickUpNoAbandonmentRuleText()
9650
+ ].join("\n");
9651
+ }
9652
+ async function postClickUpStartupComment({ clickupClient, worktree, taskId, comment, type } = {}) {
9653
+ if (typeof clickupClient?.postTaskComment !== "function") {
9654
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comment_unavailable", taskId, commentType: type });
9655
+ return { ok: false, skipped: true, reason: "post_task_comment_unavailable" };
9656
+ }
9657
+ try {
9658
+ await clickupClient.postTaskComment({ taskId, comment });
9659
+ return { ok: true };
9660
+ } catch (error) {
9661
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comment_failed", taskId, commentType: type, message: error.message });
9662
+ return { ok: false, error: error.message };
9663
+ }
9664
+ }
9581
9665
  function appendClickUpWebhookLocalLog(worktree, entry) {
9582
9666
  const logPath = clickUpWebhookLogPath(worktree);
9583
9667
  fs2.mkdirSync(path2.dirname(logPath), { recursive: true });
@@ -9849,24 +9933,128 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9849
9933
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
9850
9934
  const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
9851
9935
  stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
9852
- return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey });
9936
+ return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9853
9937
  }
9854
9938
  const sessionId = String(existingSessionId);
9855
9939
  if (await sessionExists(openCodeClient, sessionId)) {
9856
9940
  if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
9857
9941
  await sendSessionEvent(openCodeClient, { sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
9858
- return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey });
9942
+ return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9859
9943
  }
9860
9944
  const at = now().toISOString();
9861
9945
  const incidentComment = `Optima webhook could not route this ClickUp event because OpenCode session ${sessionId} is missing on host ${host} at ${at}. No replacement session was created automatically.`;
9862
9946
  appendClickUpWebhookLocalLog(worktree, { type: "missing_session", taskId, sessionId, host, at });
9863
9947
  await clickupClient.postTaskComment({ taskId, comment: incidentComment });
9864
- return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey });
9948
+ return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9865
9949
  }
9866
9950
  async function routeClickUpWebhookEvent(options = {}) {
9867
9951
  const taskId = clickUpTaskIdFromPayload(options.payload || {});
9868
9952
  return withClickUpTaskRouteLock(taskId, () => routeClickUpWebhookEventUnlocked(options));
9869
9953
  }
9954
+ 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 } = {}) {
9955
+ if (!config || !clickupClient?.listAssignedTasks) return { ok: true, skipped: true, reason: "clickup_task_listing_unavailable", assigned: 0, comments: 0 };
9956
+ let authorizedUserId = "";
9957
+ if (clickupClient?.getAuthorizedUser) {
9958
+ try {
9959
+ const userResponse = await clickupClient.getAuthorizedUser();
9960
+ authorizedUserId = String(userResponse?.user?.id || userResponse?.id || "").trim();
9961
+ } catch (error) {
9962
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_user_lookup_failed", message: error.message });
9963
+ }
9964
+ }
9965
+ let mutableState = state;
9966
+ const persistState = (nextState) => {
9967
+ mutableState = nextState;
9968
+ if (saveState) saveState(nextState);
9969
+ };
9970
+ const lastWebhookMs = clickUpTimestampMs(mutableState.lastWebhookAt);
9971
+ const ignored = new Set((config.routing?.ignoredStatuses || CLICKUP_WEBHOOK_TERMINAL_STATUSES).map(normalizeClickUpStatus));
9972
+ const routed = { assigned: 0, comments: 0, ignored: 0, errors: 0 };
9973
+ clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_started", lastWebhookAt: state.lastWebhookAt || null });
9974
+ const listed = await clickupClient.listAssignedTasks({ assigneeId: config.routing.productManagerAssigneeId, limit });
9975
+ const tasks = clickUpTaskListItems(listed).slice(0, limit);
9976
+ for (const task of tasks) {
9977
+ const taskId = String(task?.id || task?.task_id || task?.taskId || "").trim();
9978
+ if (!taskId) {
9979
+ routed.ignored += 1;
9980
+ continue;
9981
+ }
9982
+ if (ignored.has(normalizeClickUpStatus(clickUpTaskStatus(task)))) {
9983
+ routed.ignored += 1;
9984
+ continue;
9985
+ }
9986
+ if (!isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) {
9987
+ routed.ignored += 1;
9988
+ continue;
9989
+ }
9990
+ try {
9991
+ const result = await routeClickUpWebhookEvent({
9992
+ payload: { webhook_id: mutableState.webhookId || "startup", event: "taskAssigneeUpdated", task_id: taskId, task, startup_reconciliation: true },
9993
+ config,
9994
+ state: mutableState,
9995
+ worktree,
9996
+ clickupClient,
9997
+ openCodeClient,
9998
+ sessionExists,
9999
+ createSession,
10000
+ sendSessionEvent,
10001
+ saveState: persistState,
10002
+ now
10003
+ });
10004
+ if (result?.ok && result.action !== "ignored") {
10005
+ routed.assigned += 1;
10006
+ await postClickUpStartupComment({
10007
+ clickupClient,
10008
+ worktree,
10009
+ taskId,
10010
+ type: "resume",
10011
+ comment: buildClickUpStartupResumeComment({ taskId, result })
10012
+ });
10013
+ }
10014
+ } catch (error) {
10015
+ routed.errors += 1;
10016
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_failed", taskId, message: error.message });
10017
+ await postClickUpStartupComment({
10018
+ clickupClient,
10019
+ worktree,
10020
+ taskId,
10021
+ type: "blocker",
10022
+ comment: buildClickUpStartupBlockerComment({ taskId, error })
10023
+ });
10024
+ }
10025
+ if (!lastWebhookMs || !clickupClient?.getTaskComments) continue;
10026
+ try {
10027
+ const commentsResponse = await clickupClient.getTaskComments({ taskId, start: lastWebhookMs, limit: CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT });
10028
+ for (const comment of clickUpCommentListItems(commentsResponse).slice(0, CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT)) {
10029
+ const commentMs = clickUpCommentUpdatedMs(comment);
10030
+ if (!commentMs || commentMs <= lastWebhookMs) continue;
10031
+ const authorId = clickUpCommentAuthorId(comment);
10032
+ if (authorId && (authorId === config.routing.ignoredCommentAuthorId || authorId === authorizedUserId)) continue;
10033
+ if (!clickUpCommentMentionsProductManager(comment, config.routing)) continue;
10034
+ const event = comment.date_updated || comment.dateUpdated || comment.updated_at || comment.updatedAt ? "taskCommentUpdated" : "taskCommentPosted";
10035
+ const result = await routeClickUpWebhookEvent({
10036
+ payload: { webhook_id: mutableState.webhookId || "startup", event, task_id: taskId, task, comment, history_item: { id: `startup-${taskId}-${comment.id || commentMs}` }, startup_reconciliation: true },
10037
+ config,
10038
+ state: mutableState,
10039
+ worktree,
10040
+ clickupClient,
10041
+ openCodeClient,
10042
+ sessionExists,
10043
+ createSession,
10044
+ sendSessionEvent,
10045
+ saveState: persistState,
10046
+ now
10047
+ });
10048
+ if (result?.ok && result.action !== "ignored") routed.comments += 1;
10049
+ }
10050
+ } catch (error) {
10051
+ routed.errors += 1;
10052
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comments_failed", taskId, message: error.message });
10053
+ }
10054
+ }
10055
+ clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...routed });
10056
+ return { ok: routed.errors === 0, ...routed };
10057
+ }
9870
10058
  function clickUpWebhookExpectedPath(config) {
9871
10059
  try {
9872
10060
  return new URL(config?.webhook?.publicUrl).pathname || "/";
@@ -9877,9 +10065,14 @@ function clickUpWebhookExpectedPath(config) {
9877
10065
  async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState, now = () => /* @__PURE__ */ new Date() } = {}) {
9878
10066
  let payload = null;
9879
10067
  let handled = null;
10068
+ let authenticatedWebhook = false;
10069
+ let receivedAt = null;
9880
10070
  const finish = (result) => {
9881
10071
  handled = result;
9882
- writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, payload, at: now() });
10072
+ receivedAt ??= now();
10073
+ const auditState = authenticatedWebhook ? { ...state, lastWebhookAt: receivedAt.toISOString() } : state;
10074
+ if (saveState && authenticatedWebhook) saveState(auditState);
10075
+ writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state: auditState, handled, payload, at: receivedAt });
9883
10076
  return result;
9884
10077
  };
9885
10078
  try {
@@ -9893,12 +10086,15 @@ async function handleClickUpWebhookRequest({ method = "POST", url = null, header
9893
10086
  if (bodyBytes > maxBodyBytes) return finish({ ok: false, status: 413, reason: "body_too_large" });
9894
10087
  const signature = headers["x-signature"] || headers["X-Signature"];
9895
10088
  if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return finish({ ok: false, status: 401, reason: "invalid_signature" });
10089
+ authenticatedWebhook = true;
10090
+ receivedAt = now();
9896
10091
  try {
9897
10092
  payload = JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody));
9898
10093
  } catch {
9899
10094
  return finish({ ok: false, status: 400, reason: "invalid_json" });
9900
10095
  }
9901
- const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
10096
+ const receivedState = { ...state, lastWebhookAt: receivedAt.toISOString() };
10097
+ const result = await routeClickUpWebhookEvent({ payload, config, state: receivedState, worktree, clickupClient, openCodeClient, saveState });
9902
10098
  return finish({ ok: result.ok, status: result.ok ? 200 : 422, result });
9903
10099
  } catch (error) {
9904
10100
  writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, error, payload, at: now() });
@@ -11052,6 +11248,18 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
11052
11248
  listener: { bindHost: clickUpWebhookValidation.config.webhook.bindHost, bindPort: clickUpWebhookValidation.config.webhook.bindPort, startedAt: (/* @__PURE__ */ new Date()).toISOString() }
11053
11249
  }, clickUpWebhookValidation.config);
11054
11250
  registerClickUpWebhookLifecycle({ config: clickUpWebhookValidation.config, state: activeState, worktree, clickupClient: lifecycleClickUpClient, listener: readyListener, listenerRegistry });
11251
+ try {
11252
+ await reconcileClickUpStartup({
11253
+ config: clickUpWebhookValidation.config,
11254
+ state: activeState,
11255
+ worktree,
11256
+ clickupClient: lifecycleClickUpClient,
11257
+ openCodeClient: input.client,
11258
+ saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, clickUpWebhookValidation.config)
11259
+ });
11260
+ } catch (error) {
11261
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_failed", message: error.message });
11262
+ }
11055
11263
  } else {
11056
11264
  clickUpWebhookLifecycleLog(worktree, { type: "remote_webhook_preserved", reason: readyListener.reason || "listener_unavailable", webhookId: listenerState.webhookId });
11057
11265
  markClickUpWebhookInactive(worktree, listenerState, clickUpWebhookValidation.config);
@@ -11626,7 +11834,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
11626
11834
  }
11627
11835
  };
11628
11836
  }
11629
- 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 };
11837
+ OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, reconcileClickUpStartup, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11630
11838
 
11631
11839
  // src/sanitize_cli.js
11632
11840
  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.35",
3
+ "version": "0.1.37",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"