@defend-tech/opencode-optima 0.1.37 → 0.1.39
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 +106 -64
- package/dist/sanitize_cli.js +106 -64
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7932,6 +7932,7 @@ var CLICKUP_WEBHOOK_EVENTS = ["taskStatusUpdated", "taskAssigneeUpdated", "taskC
|
|
|
7932
7932
|
var CLICKUP_WEBHOOK_TERMINAL_STATUSES = ["backlog", "done", "completed", "closed"];
|
|
7933
7933
|
var CLICKUP_PM_METADATA_KEY = "optima.sessions.product_manager";
|
|
7934
7934
|
var CLICKUP_PM_MENTION_NAME = "Defend Tech Product Manager";
|
|
7935
|
+
var CLICKUP_BLOCKER_TAG_NAME = "BLOCKER";
|
|
7935
7936
|
var CLICKUP_WEBHOOK_RUNTIME_VERSION = 1;
|
|
7936
7937
|
var CLICKUP_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
|
|
7937
7938
|
var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
|
|
@@ -9156,6 +9157,12 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
|
|
|
9156
9157
|
body: JSON.stringify({ comment_text: comment })
|
|
9157
9158
|
});
|
|
9158
9159
|
},
|
|
9160
|
+
async addTaskTag({ taskId, tagName = CLICKUP_BLOCKER_TAG_NAME } = {}) {
|
|
9161
|
+
return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}/tag/${encodeURIComponent(tagName)}`, {
|
|
9162
|
+
method: "POST",
|
|
9163
|
+
body: JSON.stringify({})
|
|
9164
|
+
});
|
|
9165
|
+
},
|
|
9159
9166
|
async listAssignedTasks({ assigneeId, statuses = [], limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
|
|
9160
9167
|
const params = new URLSearchParams();
|
|
9161
9168
|
if (assigneeId) params.append("assignees[]", assigneeId);
|
|
@@ -9600,6 +9607,88 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
|
|
|
9600
9607
|
if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, fetchImpl });
|
|
9601
9608
|
throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
|
|
9602
9609
|
}
|
|
9610
|
+
function normalizeOpenCodeSessionMessages(result) {
|
|
9611
|
+
const data = result?.data ?? result;
|
|
9612
|
+
if (Array.isArray(data)) return [...data];
|
|
9613
|
+
if (Array.isArray(data?.messages)) return [...data.messages];
|
|
9614
|
+
if (Array.isArray(data?.items)) return [...data.items];
|
|
9615
|
+
return [];
|
|
9616
|
+
}
|
|
9617
|
+
async function readOpenCodeSessionMessages(client, { sessionId, limit = 20 } = {}) {
|
|
9618
|
+
if (typeof client?.session?.messages !== "function") return null;
|
|
9619
|
+
const attempts = [
|
|
9620
|
+
{ path: { id: sessionId }, query: { limit } },
|
|
9621
|
+
{ path: { sessionID: sessionId }, query: { limit } },
|
|
9622
|
+
{ id: sessionId, limit },
|
|
9623
|
+
{ sessionID: sessionId, limit }
|
|
9624
|
+
];
|
|
9625
|
+
let firstError = null;
|
|
9626
|
+
for (const attempt of attempts) {
|
|
9627
|
+
try {
|
|
9628
|
+
return normalizeOpenCodeSessionMessages(await client.session.messages(attempt));
|
|
9629
|
+
} catch (error) {
|
|
9630
|
+
firstError ??= error;
|
|
9631
|
+
}
|
|
9632
|
+
}
|
|
9633
|
+
throw firstError;
|
|
9634
|
+
}
|
|
9635
|
+
function openCodeMessageText(message) {
|
|
9636
|
+
const parts = [message?.text, message?.content, message?.message, message?.body?.text, message?.data?.text];
|
|
9637
|
+
const partList = Array.isArray(message?.parts) ? message.parts : Array.isArray(message?.body?.parts) ? message.body.parts : [];
|
|
9638
|
+
for (const part of partList) parts.push(part?.text, part?.content);
|
|
9639
|
+
return parts.filter((value) => typeof value === "string").join("\n");
|
|
9640
|
+
}
|
|
9641
|
+
async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 3, delayMs = 25 } = {}) {
|
|
9642
|
+
let lastError = "message_verification_unavailable";
|
|
9643
|
+
for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
|
|
9644
|
+
try {
|
|
9645
|
+
const afterMessages = await readOpenCodeSessionMessages(client, { sessionId, limit: 50 });
|
|
9646
|
+
if (!afterMessages) return { ok: true, method: "verification_unavailable", skipped: true };
|
|
9647
|
+
const beforeCount = Array.isArray(beforeMessages) ? beforeMessages.length : null;
|
|
9648
|
+
if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length };
|
|
9649
|
+
const textNeedles = [expectedText, ...markers].map((value) => String(value || "").trim()).filter(Boolean);
|
|
9650
|
+
const haystack = afterMessages.slice(-20).map(openCodeMessageText).join("\n");
|
|
9651
|
+
const matched = textNeedles.find((needle) => haystack.includes(needle));
|
|
9652
|
+
if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length };
|
|
9653
|
+
lastError = "message_not_visible";
|
|
9654
|
+
} catch (error) {
|
|
9655
|
+
lastError = error.message || "message_verification_failed";
|
|
9656
|
+
}
|
|
9657
|
+
if (attempt < attempts - 1 && delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
9658
|
+
}
|
|
9659
|
+
return { ok: false, reason: lastError };
|
|
9660
|
+
}
|
|
9661
|
+
async function applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source, tagName = CLICKUP_BLOCKER_TAG_NAME } = {}) {
|
|
9662
|
+
if (typeof clickupClient?.addTaskTag !== "function") {
|
|
9663
|
+
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_unavailable", taskId, reason, source, tagName });
|
|
9664
|
+
return { ok: false, skipped: true, reason: "add_task_tag_unavailable", tagName };
|
|
9665
|
+
}
|
|
9666
|
+
try {
|
|
9667
|
+
await clickupClient.addTaskTag({ taskId, tagName });
|
|
9668
|
+
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_applied", taskId, reason, source, tagName });
|
|
9669
|
+
return { ok: true, tagName };
|
|
9670
|
+
} catch (error) {
|
|
9671
|
+
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_failed", taskId, reason, source, tagName, message: error.message });
|
|
9672
|
+
return { ok: false, error: error.message, tagName };
|
|
9673
|
+
}
|
|
9674
|
+
}
|
|
9675
|
+
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery } = {}) {
|
|
9676
|
+
const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => null);
|
|
9677
|
+
await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl });
|
|
9678
|
+
let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages, expectedText: text, markers: eventMarkers });
|
|
9679
|
+
if (verification?.ok) return { ok: true, verification, fallback: false };
|
|
9680
|
+
const canFallbackDirect = Boolean(opencodeBaseUrl);
|
|
9681
|
+
if (canFallbackDirect) {
|
|
9682
|
+
const retryBeforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => beforeMessages);
|
|
9683
|
+
await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true });
|
|
9684
|
+
verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages: retryBeforeMessages, expectedText: text, markers: eventMarkers });
|
|
9685
|
+
if (verification?.ok) return { ok: true, verification, fallback: true };
|
|
9686
|
+
}
|
|
9687
|
+
const reason = verification?.reason || "message_delivery_failed";
|
|
9688
|
+
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason, fallbackAttempted: canFallbackDirect });
|
|
9689
|
+
const blocker = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source: canFallbackDirect ? "delivery_fallback_failed" : "delivery_verification_failed" });
|
|
9690
|
+
return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: canFallbackDirect, blockerTag: blocker };
|
|
9691
|
+
}
|
|
9603
9692
|
function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", worktree = "", deliveryEvidencePath = "" }) {
|
|
9604
9693
|
const comment = clickUpCommentFromPayload(payload);
|
|
9605
9694
|
const commentText = clickUpCommentText(comment).trim();
|
|
@@ -9616,45 +9705,6 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
|
|
|
9616
9705
|
deliveryEvidencePath ? `Final merge-trackable evidence must be written under ${deliveryEvidencePath}; use .optima only as local mirror/staging.` : null
|
|
9617
9706
|
].filter((part) => part !== null).join("\n");
|
|
9618
9707
|
}
|
|
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
|
-
}
|
|
9658
9708
|
function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
9659
9709
|
const logPath = clickUpWebhookLogPath(worktree);
|
|
9660
9710
|
fs2.mkdirSync(path2.dirname(logPath), { recursive: true });
|
|
@@ -9831,7 +9881,7 @@ async function withClickUpTaskRouteLock(taskId, operation) {
|
|
|
9831
9881
|
if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
|
|
9832
9882
|
}
|
|
9833
9883
|
}
|
|
9834
|
-
async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
|
|
9884
|
+
async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
|
|
9835
9885
|
const eventType = clickUpEventType(payload);
|
|
9836
9886
|
const eventKey = clickUpWebhookEventKey(payload);
|
|
9837
9887
|
const remembered = rememberClickUpWebhookEvent(state, eventKey);
|
|
@@ -9890,8 +9940,8 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
9890
9940
|
} catch (error) {
|
|
9891
9941
|
const message = `Optima webhook could not create or reuse a task worktree for ${taskId}: ${error.message}`;
|
|
9892
9942
|
appendClickUpWebhookLocalLog(worktree, { type: "task_worktree_failed", taskId, message });
|
|
9893
|
-
|
|
9894
|
-
return { ok: false, action: "error", reason: "task_worktree_failed", taskId, message };
|
|
9943
|
+
const blockerTag2 = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: "task_worktree_failed", source: "route_worktree" });
|
|
9944
|
+
return { ok: false, action: "error", reason: "task_worktree_failed", taskId, message, blockerTag: blockerTag2 };
|
|
9895
9945
|
}
|
|
9896
9946
|
const deliveryEvidencePath = deliveryEvidencePathForClickUpTask(taskId);
|
|
9897
9947
|
const routingMetadata = {
|
|
@@ -9921,24 +9971,25 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
9921
9971
|
appendClickUpWebhookLocalLog(worktree, { type: "pending_session_metadata_failed", taskId, sessionId: pendingSessionId, message: error.message });
|
|
9922
9972
|
throw error;
|
|
9923
9973
|
}
|
|
9924
|
-
await
|
|
9974
|
+
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
|
|
9975
|
+
if (!delivery.ok) return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
|
|
9925
9976
|
const nextMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(pendingMetadata, config.routing.metadataKey, pendingSessionId), config.routing.metadataKey);
|
|
9926
9977
|
await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
|
|
9927
9978
|
const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
|
|
9928
9979
|
stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
|
|
9929
|
-
return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
|
|
9980
|
+
return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, deliveryVerification: delivery.verification, deliveryFallback: delivery.fallback });
|
|
9930
9981
|
}
|
|
9931
9982
|
const sessionId = String(existingSessionId);
|
|
9932
9983
|
if (await sessionExists(openCodeClient, sessionId)) {
|
|
9933
9984
|
if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
|
|
9934
|
-
await
|
|
9935
|
-
return finish({
|
|
9985
|
+
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
|
|
9986
|
+
if (!delivery.ok) return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
|
|
9987
|
+
return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, deliveryVerification: delivery.verification, deliveryFallback: delivery.fallback });
|
|
9936
9988
|
}
|
|
9937
9989
|
const at = now().toISOString();
|
|
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.`;
|
|
9939
9990
|
appendClickUpWebhookLocalLog(worktree, { type: "missing_session", taskId, sessionId, host, at });
|
|
9940
|
-
await
|
|
9941
|
-
return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
|
|
9991
|
+
const blockerTag = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: "missing_session", source: "route_existing_session" });
|
|
9992
|
+
return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, blockerTag });
|
|
9942
9993
|
}
|
|
9943
9994
|
async function routeClickUpWebhookEvent(options = {}) {
|
|
9944
9995
|
const taskId = clickUpTaskIdFromPayload(options.payload || {});
|
|
@@ -9996,24 +10047,14 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
9996
10047
|
});
|
|
9997
10048
|
if (result?.ok && result.action !== "ignored") {
|
|
9998
10049
|
routed.assigned += 1;
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
taskId,
|
|
10003
|
-
type: "resume",
|
|
10004
|
-
comment: buildClickUpStartupResumeComment({ taskId, result })
|
|
10005
|
-
});
|
|
10050
|
+
} else if (result?.ok === false) {
|
|
10051
|
+
routed.errors += 1;
|
|
10052
|
+
if (!result.blockerTag) await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: result.reason || "startup_reconciliation_route_failed", source: "startup_reconciliation_task" });
|
|
10006
10053
|
}
|
|
10007
10054
|
} catch (error) {
|
|
10008
10055
|
routed.errors += 1;
|
|
10009
10056
|
appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_failed", taskId, message: error.message });
|
|
10010
|
-
await
|
|
10011
|
-
clickupClient,
|
|
10012
|
-
worktree,
|
|
10013
|
-
taskId,
|
|
10014
|
-
type: "blocker",
|
|
10015
|
-
comment: buildClickUpStartupBlockerComment({ taskId, error })
|
|
10016
|
-
});
|
|
10057
|
+
await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: "startup_reconciliation_task_failed", source: "startup_reconciliation_task" });
|
|
10017
10058
|
}
|
|
10018
10059
|
if (!lastWebhookMs || !clickupClient?.getTaskComments) continue;
|
|
10019
10060
|
try {
|
|
@@ -10043,6 +10084,7 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
10043
10084
|
} catch (error) {
|
|
10044
10085
|
routed.errors += 1;
|
|
10045
10086
|
appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comments_failed", taskId, message: error.message });
|
|
10087
|
+
await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: "startup_reconciliation_comments_failed", source: "startup_reconciliation_comments" });
|
|
10046
10088
|
}
|
|
10047
10089
|
}
|
|
10048
10090
|
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...routed });
|
|
@@ -11827,7 +11869,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
11827
11869
|
}
|
|
11828
11870
|
};
|
|
11829
11871
|
}
|
|
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 };
|
|
11872
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, reconcileClickUpStartup, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
11831
11873
|
export {
|
|
11832
11874
|
OptimaPlugin as default
|
|
11833
11875
|
};
|
package/dist/sanitize_cli.js
CHANGED
|
@@ -7939,6 +7939,7 @@ var CLICKUP_WEBHOOK_EVENTS = ["taskStatusUpdated", "taskAssigneeUpdated", "taskC
|
|
|
7939
7939
|
var CLICKUP_WEBHOOK_TERMINAL_STATUSES = ["backlog", "done", "completed", "closed"];
|
|
7940
7940
|
var CLICKUP_PM_METADATA_KEY = "optima.sessions.product_manager";
|
|
7941
7941
|
var CLICKUP_PM_MENTION_NAME = "Defend Tech Product Manager";
|
|
7942
|
+
var CLICKUP_BLOCKER_TAG_NAME = "BLOCKER";
|
|
7942
7943
|
var CLICKUP_WEBHOOK_RUNTIME_VERSION = 1;
|
|
7943
7944
|
var CLICKUP_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
|
|
7944
7945
|
var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
|
|
@@ -9163,6 +9164,12 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
|
|
|
9163
9164
|
body: JSON.stringify({ comment_text: comment })
|
|
9164
9165
|
});
|
|
9165
9166
|
},
|
|
9167
|
+
async addTaskTag({ taskId, tagName = CLICKUP_BLOCKER_TAG_NAME } = {}) {
|
|
9168
|
+
return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}/tag/${encodeURIComponent(tagName)}`, {
|
|
9169
|
+
method: "POST",
|
|
9170
|
+
body: JSON.stringify({})
|
|
9171
|
+
});
|
|
9172
|
+
},
|
|
9166
9173
|
async listAssignedTasks({ assigneeId, statuses = [], limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
|
|
9167
9174
|
const params = new URLSearchParams();
|
|
9168
9175
|
if (assigneeId) params.append("assignees[]", assigneeId);
|
|
@@ -9607,6 +9614,88 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
|
|
|
9607
9614
|
if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, fetchImpl });
|
|
9608
9615
|
throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
|
|
9609
9616
|
}
|
|
9617
|
+
function normalizeOpenCodeSessionMessages(result) {
|
|
9618
|
+
const data = result?.data ?? result;
|
|
9619
|
+
if (Array.isArray(data)) return [...data];
|
|
9620
|
+
if (Array.isArray(data?.messages)) return [...data.messages];
|
|
9621
|
+
if (Array.isArray(data?.items)) return [...data.items];
|
|
9622
|
+
return [];
|
|
9623
|
+
}
|
|
9624
|
+
async function readOpenCodeSessionMessages(client, { sessionId, limit = 20 } = {}) {
|
|
9625
|
+
if (typeof client?.session?.messages !== "function") return null;
|
|
9626
|
+
const attempts = [
|
|
9627
|
+
{ path: { id: sessionId }, query: { limit } },
|
|
9628
|
+
{ path: { sessionID: sessionId }, query: { limit } },
|
|
9629
|
+
{ id: sessionId, limit },
|
|
9630
|
+
{ sessionID: sessionId, limit }
|
|
9631
|
+
];
|
|
9632
|
+
let firstError = null;
|
|
9633
|
+
for (const attempt of attempts) {
|
|
9634
|
+
try {
|
|
9635
|
+
return normalizeOpenCodeSessionMessages(await client.session.messages(attempt));
|
|
9636
|
+
} catch (error) {
|
|
9637
|
+
firstError ??= error;
|
|
9638
|
+
}
|
|
9639
|
+
}
|
|
9640
|
+
throw firstError;
|
|
9641
|
+
}
|
|
9642
|
+
function openCodeMessageText(message) {
|
|
9643
|
+
const parts = [message?.text, message?.content, message?.message, message?.body?.text, message?.data?.text];
|
|
9644
|
+
const partList = Array.isArray(message?.parts) ? message.parts : Array.isArray(message?.body?.parts) ? message.body.parts : [];
|
|
9645
|
+
for (const part of partList) parts.push(part?.text, part?.content);
|
|
9646
|
+
return parts.filter((value) => typeof value === "string").join("\n");
|
|
9647
|
+
}
|
|
9648
|
+
async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 3, delayMs = 25 } = {}) {
|
|
9649
|
+
let lastError = "message_verification_unavailable";
|
|
9650
|
+
for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
|
|
9651
|
+
try {
|
|
9652
|
+
const afterMessages = await readOpenCodeSessionMessages(client, { sessionId, limit: 50 });
|
|
9653
|
+
if (!afterMessages) return { ok: true, method: "verification_unavailable", skipped: true };
|
|
9654
|
+
const beforeCount = Array.isArray(beforeMessages) ? beforeMessages.length : null;
|
|
9655
|
+
if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length };
|
|
9656
|
+
const textNeedles = [expectedText, ...markers].map((value) => String(value || "").trim()).filter(Boolean);
|
|
9657
|
+
const haystack = afterMessages.slice(-20).map(openCodeMessageText).join("\n");
|
|
9658
|
+
const matched = textNeedles.find((needle) => haystack.includes(needle));
|
|
9659
|
+
if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length };
|
|
9660
|
+
lastError = "message_not_visible";
|
|
9661
|
+
} catch (error) {
|
|
9662
|
+
lastError = error.message || "message_verification_failed";
|
|
9663
|
+
}
|
|
9664
|
+
if (attempt < attempts - 1 && delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
9665
|
+
}
|
|
9666
|
+
return { ok: false, reason: lastError };
|
|
9667
|
+
}
|
|
9668
|
+
async function applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source, tagName = CLICKUP_BLOCKER_TAG_NAME } = {}) {
|
|
9669
|
+
if (typeof clickupClient?.addTaskTag !== "function") {
|
|
9670
|
+
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_unavailable", taskId, reason, source, tagName });
|
|
9671
|
+
return { ok: false, skipped: true, reason: "add_task_tag_unavailable", tagName };
|
|
9672
|
+
}
|
|
9673
|
+
try {
|
|
9674
|
+
await clickupClient.addTaskTag({ taskId, tagName });
|
|
9675
|
+
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_applied", taskId, reason, source, tagName });
|
|
9676
|
+
return { ok: true, tagName };
|
|
9677
|
+
} catch (error) {
|
|
9678
|
+
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_failed", taskId, reason, source, tagName, message: error.message });
|
|
9679
|
+
return { ok: false, error: error.message, tagName };
|
|
9680
|
+
}
|
|
9681
|
+
}
|
|
9682
|
+
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery } = {}) {
|
|
9683
|
+
const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => null);
|
|
9684
|
+
await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl });
|
|
9685
|
+
let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages, expectedText: text, markers: eventMarkers });
|
|
9686
|
+
if (verification?.ok) return { ok: true, verification, fallback: false };
|
|
9687
|
+
const canFallbackDirect = Boolean(opencodeBaseUrl);
|
|
9688
|
+
if (canFallbackDirect) {
|
|
9689
|
+
const retryBeforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, limit: 50 }).catch(() => beforeMessages);
|
|
9690
|
+
await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: true });
|
|
9691
|
+
verification = await verifySessionEventDelivery(openCodeClient, { sessionId, beforeMessages: retryBeforeMessages, expectedText: text, markers: eventMarkers });
|
|
9692
|
+
if (verification?.ok) return { ok: true, verification, fallback: true };
|
|
9693
|
+
}
|
|
9694
|
+
const reason = verification?.reason || "message_delivery_failed";
|
|
9695
|
+
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason, fallbackAttempted: canFallbackDirect });
|
|
9696
|
+
const blocker = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source: canFallbackDirect ? "delivery_fallback_failed" : "delivery_verification_failed" });
|
|
9697
|
+
return { ok: false, action: "message_delivery_failed", reason, taskId, sessionId, fallbackAttempted: canFallbackDirect, blockerTag: blocker };
|
|
9698
|
+
}
|
|
9610
9699
|
function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", worktree = "", deliveryEvidencePath = "" }) {
|
|
9611
9700
|
const comment = clickUpCommentFromPayload(payload);
|
|
9612
9701
|
const commentText = clickUpCommentText(comment).trim();
|
|
@@ -9623,45 +9712,6 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
|
|
|
9623
9712
|
deliveryEvidencePath ? `Final merge-trackable evidence must be written under ${deliveryEvidencePath}; use .optima only as local mirror/staging.` : null
|
|
9624
9713
|
].filter((part) => part !== null).join("\n");
|
|
9625
9714
|
}
|
|
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
|
-
}
|
|
9665
9715
|
function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
9666
9716
|
const logPath = clickUpWebhookLogPath(worktree);
|
|
9667
9717
|
fs2.mkdirSync(path2.dirname(logPath), { recursive: true });
|
|
@@ -9838,7 +9888,7 @@ async function withClickUpTaskRouteLock(taskId, operation) {
|
|
|
9838
9888
|
if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
|
|
9839
9889
|
}
|
|
9840
9890
|
}
|
|
9841
|
-
async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
|
|
9891
|
+
async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
|
|
9842
9892
|
const eventType = clickUpEventType(payload);
|
|
9843
9893
|
const eventKey = clickUpWebhookEventKey(payload);
|
|
9844
9894
|
const remembered = rememberClickUpWebhookEvent(state, eventKey);
|
|
@@ -9897,8 +9947,8 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
9897
9947
|
} catch (error) {
|
|
9898
9948
|
const message = `Optima webhook could not create or reuse a task worktree for ${taskId}: ${error.message}`;
|
|
9899
9949
|
appendClickUpWebhookLocalLog(worktree, { type: "task_worktree_failed", taskId, message });
|
|
9900
|
-
|
|
9901
|
-
return { ok: false, action: "error", reason: "task_worktree_failed", taskId, message };
|
|
9950
|
+
const blockerTag2 = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: "task_worktree_failed", source: "route_worktree" });
|
|
9951
|
+
return { ok: false, action: "error", reason: "task_worktree_failed", taskId, message, blockerTag: blockerTag2 };
|
|
9902
9952
|
}
|
|
9903
9953
|
const deliveryEvidencePath = deliveryEvidencePathForClickUpTask(taskId);
|
|
9904
9954
|
const routingMetadata = {
|
|
@@ -9928,24 +9978,25 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
9928
9978
|
appendClickUpWebhookLocalLog(worktree, { type: "pending_session_metadata_failed", taskId, sessionId: pendingSessionId, message: error.message });
|
|
9929
9979
|
throw error;
|
|
9930
9980
|
}
|
|
9931
|
-
await
|
|
9981
|
+
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
|
|
9982
|
+
if (!delivery.ok) return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
|
|
9932
9983
|
const nextMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(pendingMetadata, config.routing.metadataKey, pendingSessionId), config.routing.metadataKey);
|
|
9933
9984
|
await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
|
|
9934
9985
|
const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
|
|
9935
9986
|
stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
|
|
9936
|
-
return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
|
|
9987
|
+
return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, deliveryVerification: delivery.verification, deliveryFallback: delivery.fallback });
|
|
9937
9988
|
}
|
|
9938
9989
|
const sessionId = String(existingSessionId);
|
|
9939
9990
|
if (await sessionExists(openCodeClient, sessionId)) {
|
|
9940
9991
|
if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
|
|
9941
|
-
await
|
|
9942
|
-
return finish({
|
|
9992
|
+
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
|
|
9993
|
+
if (!delivery.ok) return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
|
|
9994
|
+
return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, deliveryVerification: delivery.verification, deliveryFallback: delivery.fallback });
|
|
9943
9995
|
}
|
|
9944
9996
|
const at = now().toISOString();
|
|
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.`;
|
|
9946
9997
|
appendClickUpWebhookLocalLog(worktree, { type: "missing_session", taskId, sessionId, host, at });
|
|
9947
|
-
await
|
|
9948
|
-
return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
|
|
9998
|
+
const blockerTag = await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: "missing_session", source: "route_existing_session" });
|
|
9999
|
+
return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, blockerTag });
|
|
9949
10000
|
}
|
|
9950
10001
|
async function routeClickUpWebhookEvent(options = {}) {
|
|
9951
10002
|
const taskId = clickUpTaskIdFromPayload(options.payload || {});
|
|
@@ -10003,24 +10054,14 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
10003
10054
|
});
|
|
10004
10055
|
if (result?.ok && result.action !== "ignored") {
|
|
10005
10056
|
routed.assigned += 1;
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
taskId,
|
|
10010
|
-
type: "resume",
|
|
10011
|
-
comment: buildClickUpStartupResumeComment({ taskId, result })
|
|
10012
|
-
});
|
|
10057
|
+
} else if (result?.ok === false) {
|
|
10058
|
+
routed.errors += 1;
|
|
10059
|
+
if (!result.blockerTag) await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: result.reason || "startup_reconciliation_route_failed", source: "startup_reconciliation_task" });
|
|
10013
10060
|
}
|
|
10014
10061
|
} catch (error) {
|
|
10015
10062
|
routed.errors += 1;
|
|
10016
10063
|
appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_failed", taskId, message: error.message });
|
|
10017
|
-
await
|
|
10018
|
-
clickupClient,
|
|
10019
|
-
worktree,
|
|
10020
|
-
taskId,
|
|
10021
|
-
type: "blocker",
|
|
10022
|
-
comment: buildClickUpStartupBlockerComment({ taskId, error })
|
|
10023
|
-
});
|
|
10064
|
+
await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: "startup_reconciliation_task_failed", source: "startup_reconciliation_task" });
|
|
10024
10065
|
}
|
|
10025
10066
|
if (!lastWebhookMs || !clickupClient?.getTaskComments) continue;
|
|
10026
10067
|
try {
|
|
@@ -10050,6 +10091,7 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
10050
10091
|
} catch (error) {
|
|
10051
10092
|
routed.errors += 1;
|
|
10052
10093
|
appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comments_failed", taskId, message: error.message });
|
|
10094
|
+
await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: "startup_reconciliation_comments_failed", source: "startup_reconciliation_comments" });
|
|
10053
10095
|
}
|
|
10054
10096
|
}
|
|
10055
10097
|
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...routed });
|
|
@@ -11834,7 +11876,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
11834
11876
|
}
|
|
11835
11877
|
};
|
|
11836
11878
|
}
|
|
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 };
|
|
11879
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, reconcileClickUpStartup, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
11838
11880
|
|
|
11839
11881
|
// src/sanitize_cli.js
|
|
11840
11882
|
var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
|