@defend-tech/opencode-optima 0.1.49 → 0.1.51
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 +77 -24
- package/dist/sanitize_cli.js +77 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7938,6 +7938,7 @@ var CLICKUP_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
|
|
|
7938
7938
|
var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
|
|
7939
7939
|
var CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT = 50;
|
|
7940
7940
|
var CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT = 100;
|
|
7941
|
+
var CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS = 3e4;
|
|
7941
7942
|
var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
|
|
7942
7943
|
var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
|
|
7943
7944
|
var DISCUSSION_BACKFILL_FETCH_LIMIT = 100;
|
|
@@ -9013,6 +9014,11 @@ function normalizeOpenCodeBaseUrl(value, defaultValue = "http://127.0.0.1:3001")
|
|
|
9013
9014
|
return candidate.replace(/\/+$/, "");
|
|
9014
9015
|
}
|
|
9015
9016
|
}
|
|
9017
|
+
function normalizeNonNegativeInteger(value, defaultValue) {
|
|
9018
|
+
if (value === void 0 || value === null || value === "") return defaultValue;
|
|
9019
|
+
const number = Number(value);
|
|
9020
|
+
return Number.isInteger(number) && number >= 0 ? number : defaultValue;
|
|
9021
|
+
}
|
|
9016
9022
|
function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd()) {
|
|
9017
9023
|
const raw = isPlainObject(rawClickUp) ? rawClickUp : {};
|
|
9018
9024
|
const webhook = isPlainObject(raw.webhook) ? raw.webhook : {};
|
|
@@ -9028,7 +9034,11 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
9028
9034
|
apiToken: String(raw.api_token || raw.apiToken || "").trim(),
|
|
9029
9035
|
log: normalizeClickUpWebhookLogLevel(raw.log),
|
|
9030
9036
|
opencode: {
|
|
9031
|
-
baseUrl: normalizeOpenCodeBaseUrl(opencode.base_url || opencode.baseUrl || raw.opencode_base_url || raw.opencodeBaseUrl)
|
|
9037
|
+
baseUrl: normalizeOpenCodeBaseUrl(opencode.base_url || opencode.baseUrl || raw.opencode_base_url || raw.opencodeBaseUrl),
|
|
9038
|
+
startupReconciliationDelayMs: normalizeNonNegativeInteger(
|
|
9039
|
+
opencode.startup_reconciliation_delay_ms ?? opencode.startupReconciliationDelayMs ?? raw.startup_reconciliation_delay_ms ?? raw.startupReconciliationDelayMs,
|
|
9040
|
+
CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS
|
|
9041
|
+
)
|
|
9032
9042
|
},
|
|
9033
9043
|
webhook: {
|
|
9034
9044
|
publicUrl: String(webhook.public_url || webhook.publicUrl || "").trim(),
|
|
@@ -9709,7 +9719,13 @@ async function readOpenCodeJsonResponse(response, endpointName) {
|
|
|
9709
9719
|
return { raw };
|
|
9710
9720
|
}
|
|
9711
9721
|
}
|
|
9712
|
-
|
|
9722
|
+
function appendDirectoryQuery(url, directory) {
|
|
9723
|
+
const value = String(directory || "").trim();
|
|
9724
|
+
if (!value) return url;
|
|
9725
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
9726
|
+
return `${url}${separator}directory=${encodeURIComponent(value)}`;
|
|
9727
|
+
}
|
|
9728
|
+
async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, directory, fetchImpl = globalThis.fetch, legacyOnly = false } = {}) {
|
|
9713
9729
|
if (typeof fetchImpl !== "function") throw new Error("OpenCode direct prompt delivery requires fetch.");
|
|
9714
9730
|
const root = normalizeOpenCodeBaseUrl(baseUrl, "");
|
|
9715
9731
|
if (!root) throw new Error("OpenCode direct prompt delivery requires a base URL.");
|
|
@@ -9717,7 +9733,7 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
|
|
|
9717
9733
|
const attempts = [
|
|
9718
9734
|
legacyOnly ? null : {
|
|
9719
9735
|
name: "v2 prompt",
|
|
9720
|
-
url: `${root}/api/session/${encodedSession}/prompt`,
|
|
9736
|
+
url: appendDirectoryQuery(`${root}/api/session/${encodedSession}/prompt`, directory),
|
|
9721
9737
|
body: { prompt: { text }, delivery: "queue", resume: true },
|
|
9722
9738
|
accept: (response, data) => {
|
|
9723
9739
|
if (!response.ok) return null;
|
|
@@ -9727,7 +9743,7 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
|
|
|
9727
9743
|
},
|
|
9728
9744
|
{
|
|
9729
9745
|
name: "legacy async prompt",
|
|
9730
|
-
url: `${root}/session/${encodedSession}/prompt_async`,
|
|
9746
|
+
url: appendDirectoryQuery(`${root}/session/${encodedSession}/prompt_async`, directory),
|
|
9731
9747
|
body: { agent, parts: [{ type: "text", text }] },
|
|
9732
9748
|
accept: (response, data) => {
|
|
9733
9749
|
if (response.status !== 204 && !response.ok) return null;
|
|
@@ -9792,7 +9808,7 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
|
|
|
9792
9808
|
firstError ??= error;
|
|
9793
9809
|
}
|
|
9794
9810
|
}
|
|
9795
|
-
if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, fetchImpl, legacyOnly });
|
|
9811
|
+
if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, directory, fetchImpl, legacyOnly });
|
|
9796
9812
|
if (firstError) throw firstError;
|
|
9797
9813
|
throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
|
|
9798
9814
|
}
|
|
@@ -9827,6 +9843,11 @@ function openCodeMessageText(message) {
|
|
|
9827
9843
|
for (const part of partList) parts.push(part?.text, part?.content);
|
|
9828
9844
|
return parts.filter((value) => typeof value === "string").join("\n");
|
|
9829
9845
|
}
|
|
9846
|
+
function openCodeMessageStableKey(message, index = 0) {
|
|
9847
|
+
const id = message?.id || message?.messageID || message?.messageId || message?.data?.id || null;
|
|
9848
|
+
if (id) return `id:${id}`;
|
|
9849
|
+
return `idx:${index}:text:${openCodeMessageText(message)}`;
|
|
9850
|
+
}
|
|
9830
9851
|
async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 8, delayMs = 250 } = {}) {
|
|
9831
9852
|
let lastError = "message_verification_unavailable";
|
|
9832
9853
|
for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
|
|
@@ -9834,13 +9855,15 @@ async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMes
|
|
|
9834
9855
|
const afterMessages = await readOpenCodeSessionMessages(client, { sessionId, limit: 50 });
|
|
9835
9856
|
if (!afterMessages) return { ok: false, reason: "message_verification_unavailable" };
|
|
9836
9857
|
const beforeCount = Array.isArray(beforeMessages) ? beforeMessages.length : null;
|
|
9837
|
-
const
|
|
9858
|
+
const beforeKeys = Array.isArray(beforeMessages) ? new Set(beforeMessages.map(openCodeMessageStableKey)) : null;
|
|
9859
|
+
const newMessages = beforeKeys ? afterMessages.filter((message, index) => !beforeKeys.has(openCodeMessageStableKey(message, index))) : afterMessages;
|
|
9860
|
+
const lastMessage = newMessages.at(-1) || afterMessages.at(-1) || null;
|
|
9838
9861
|
const lastMessageId = lastMessage?.id || lastMessage?.messageID || lastMessage?.messageId || null;
|
|
9839
9862
|
if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9840
9863
|
const textNeedles = [expectedText, ...markers].map((value) => String(value || "").trim()).filter(Boolean);
|
|
9841
|
-
const haystack =
|
|
9864
|
+
const haystack = newMessages.slice(-20).map(openCodeMessageText).join("\n");
|
|
9842
9865
|
const matched = textNeedles.find((needle) => haystack.includes(needle));
|
|
9843
|
-
if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9866
|
+
if (matched && (!beforeKeys || newMessages.length > 0)) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9844
9867
|
lastError = "message_not_visible";
|
|
9845
9868
|
} catch (error) {
|
|
9846
9869
|
lastError = error.message || "message_verification_failed";
|
|
@@ -10259,11 +10282,44 @@ async function routeClickUpWebhookEvent(options = {}) {
|
|
|
10259
10282
|
const taskId = clickUpTaskIdFromPayload(options.payload || {});
|
|
10260
10283
|
return withClickUpTaskRouteLock(taskId, () => routeClickUpWebhookEventUnlocked(options));
|
|
10261
10284
|
}
|
|
10285
|
+
function defaultStartupReconciliationScheduler(run, delayMs) {
|
|
10286
|
+
return setTimeout(run, delayMs);
|
|
10287
|
+
}
|
|
10288
|
+
function scheduleClickUpStartupReconciliation({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, scheduler = defaultStartupReconciliationScheduler, delayMs = config?.opencode?.startupReconciliationDelayMs ?? CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS, saveState = null, reconcile = reconcileClickUpStartup } = {}) {
|
|
10289
|
+
const normalizedDelayMs = normalizeNonNegativeInteger(delayMs, CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS);
|
|
10290
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_scheduled", delayMs: normalizedDelayMs, webhookId: state?.webhookId || null });
|
|
10291
|
+
const run = () => {
|
|
10292
|
+
Promise.resolve().then(() => reconcile({
|
|
10293
|
+
config,
|
|
10294
|
+
state,
|
|
10295
|
+
worktree,
|
|
10296
|
+
clickupClient,
|
|
10297
|
+
openCodeClient,
|
|
10298
|
+
saveState
|
|
10299
|
+
})).catch((error) => {
|
|
10300
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_failed", message: error.message });
|
|
10301
|
+
appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_failed", message: error.message });
|
|
10302
|
+
});
|
|
10303
|
+
};
|
|
10304
|
+
const timer = scheduler(run, normalizedDelayMs);
|
|
10305
|
+
timer?.unref?.();
|
|
10306
|
+
return { scheduled: true, delayMs: normalizedDelayMs, timer };
|
|
10307
|
+
}
|
|
10262
10308
|
async function reconcileClickUpStartup({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, waitForReadiness = waitForOpenCodeReadiness, saveState = null, now = () => /* @__PURE__ */ new Date(), limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
|
|
10263
|
-
|
|
10309
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_started", lastWebhookAt: state.lastWebhookAt || null });
|
|
10310
|
+
if (!config || !clickupClient?.listAssignedTasks) {
|
|
10311
|
+
const skipped = { ok: true, skipped: true, reason: "clickup_task_listing_unavailable", assigned: 0, comments: 0 };
|
|
10312
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...skipped });
|
|
10313
|
+
return skipped;
|
|
10314
|
+
}
|
|
10264
10315
|
const readiness = await waitForReadiness(openCodeClient, { worktree, now });
|
|
10265
10316
|
appendClickUpWebhookLocalLog(worktree, { type: readiness.ok ? "startup_reconciliation_readiness_ready" : "startup_reconciliation_readiness_failed", ...readiness });
|
|
10266
|
-
if (!readiness.ok)
|
|
10317
|
+
if (!readiness.ok) {
|
|
10318
|
+
const failed = { ok: false, skipped: true, reason: "opencode_not_ready", readiness, assigned: 0, comments: 0, ignored: 0, errors: 1, undelivered: 1, tasks: [], validation: { undelivered: 1, emptyPromptSessions: [] } };
|
|
10319
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_failed", reason: failed.reason, readiness });
|
|
10320
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...failed });
|
|
10321
|
+
return failed;
|
|
10322
|
+
}
|
|
10267
10323
|
let authorizedUserId = "";
|
|
10268
10324
|
if (clickupClient?.getAuthorizedUser) {
|
|
10269
10325
|
try {
|
|
@@ -10281,7 +10337,6 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
10281
10337
|
const lastWebhookMs = clickUpTimestampMs(mutableState.lastWebhookAt);
|
|
10282
10338
|
const ignored = new Set((config.routing?.ignoredStatuses || CLICKUP_WEBHOOK_TERMINAL_STATUSES).map(normalizeClickUpStatus));
|
|
10283
10339
|
const routed = { assigned: 0, comments: 0, ignored: 0, errors: 0, undelivered: 0, tasks: [] };
|
|
10284
|
-
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_started", lastWebhookAt: state.lastWebhookAt || null });
|
|
10285
10340
|
const listed = await clickupClient.listAssignedTasks({ assigneeId: config.routing.productManagerAssigneeId, limit });
|
|
10286
10341
|
const tasks = clickUpTaskListItems(listed).slice(0, limit);
|
|
10287
10342
|
for (const task of tasks) {
|
|
@@ -11599,18 +11654,16 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11599
11654
|
listener: { bindHost: clickUpWebhookValidation.config.webhook.bindHost, bindPort: clickUpWebhookValidation.config.webhook.bindPort, startedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
11600
11655
|
}, clickUpWebhookValidation.config);
|
|
11601
11656
|
registerClickUpWebhookLifecycle({ config: clickUpWebhookValidation.config, state: activeState, worktree, clickupClient: lifecycleClickUpClient, listener: readyListener, listenerRegistry });
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
}
|
|
11612
|
-
appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_failed", message: error.message });
|
|
11613
|
-
}
|
|
11657
|
+
scheduleClickUpStartupReconciliation({
|
|
11658
|
+
config: clickUpWebhookValidation.config,
|
|
11659
|
+
state: activeState,
|
|
11660
|
+
worktree,
|
|
11661
|
+
clickupClient: lifecycleClickUpClient,
|
|
11662
|
+
openCodeClient: input.client,
|
|
11663
|
+
scheduler: input.startupReconciliationScheduler,
|
|
11664
|
+
delayMs: input.startupReconciliationDelayMs,
|
|
11665
|
+
saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, clickUpWebhookValidation.config)
|
|
11666
|
+
});
|
|
11614
11667
|
} else {
|
|
11615
11668
|
clickUpWebhookLifecycleLog(worktree, { type: "remote_webhook_preserved", reason: readyListener.reason || "listener_unavailable", webhookId: listenerState.webhookId });
|
|
11616
11669
|
markClickUpWebhookInactive(worktree, listenerState, clickUpWebhookValidation.config);
|
|
@@ -12185,7 +12238,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
12185
12238
|
}
|
|
12186
12239
|
};
|
|
12187
12240
|
}
|
|
12188
|
-
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, createOpenCodeSession, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionMessages, reconcileClickUpStartup, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
12241
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, createOpenCodeSession, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
12189
12242
|
export {
|
|
12190
12243
|
OptimaPlugin as default
|
|
12191
12244
|
};
|
package/dist/sanitize_cli.js
CHANGED
|
@@ -7945,6 +7945,7 @@ var CLICKUP_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
|
|
|
7945
7945
|
var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
|
|
7946
7946
|
var CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT = 50;
|
|
7947
7947
|
var CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT = 100;
|
|
7948
|
+
var CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS = 3e4;
|
|
7948
7949
|
var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
|
|
7949
7950
|
var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
|
|
7950
7951
|
var DISCUSSION_BACKFILL_FETCH_LIMIT = 100;
|
|
@@ -9020,6 +9021,11 @@ function normalizeOpenCodeBaseUrl(value, defaultValue = "http://127.0.0.1:3001")
|
|
|
9020
9021
|
return candidate.replace(/\/+$/, "");
|
|
9021
9022
|
}
|
|
9022
9023
|
}
|
|
9024
|
+
function normalizeNonNegativeInteger(value, defaultValue) {
|
|
9025
|
+
if (value === void 0 || value === null || value === "") return defaultValue;
|
|
9026
|
+
const number = Number(value);
|
|
9027
|
+
return Number.isInteger(number) && number >= 0 ? number : defaultValue;
|
|
9028
|
+
}
|
|
9023
9029
|
function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd()) {
|
|
9024
9030
|
const raw = isPlainObject(rawClickUp) ? rawClickUp : {};
|
|
9025
9031
|
const webhook = isPlainObject(raw.webhook) ? raw.webhook : {};
|
|
@@ -9035,7 +9041,11 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
9035
9041
|
apiToken: String(raw.api_token || raw.apiToken || "").trim(),
|
|
9036
9042
|
log: normalizeClickUpWebhookLogLevel(raw.log),
|
|
9037
9043
|
opencode: {
|
|
9038
|
-
baseUrl: normalizeOpenCodeBaseUrl(opencode.base_url || opencode.baseUrl || raw.opencode_base_url || raw.opencodeBaseUrl)
|
|
9044
|
+
baseUrl: normalizeOpenCodeBaseUrl(opencode.base_url || opencode.baseUrl || raw.opencode_base_url || raw.opencodeBaseUrl),
|
|
9045
|
+
startupReconciliationDelayMs: normalizeNonNegativeInteger(
|
|
9046
|
+
opencode.startup_reconciliation_delay_ms ?? opencode.startupReconciliationDelayMs ?? raw.startup_reconciliation_delay_ms ?? raw.startupReconciliationDelayMs,
|
|
9047
|
+
CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS
|
|
9048
|
+
)
|
|
9039
9049
|
},
|
|
9040
9050
|
webhook: {
|
|
9041
9051
|
publicUrl: String(webhook.public_url || webhook.publicUrl || "").trim(),
|
|
@@ -9716,7 +9726,13 @@ async function readOpenCodeJsonResponse(response, endpointName) {
|
|
|
9716
9726
|
return { raw };
|
|
9717
9727
|
}
|
|
9718
9728
|
}
|
|
9719
|
-
|
|
9729
|
+
function appendDirectoryQuery(url, directory) {
|
|
9730
|
+
const value = String(directory || "").trim();
|
|
9731
|
+
if (!value) return url;
|
|
9732
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
9733
|
+
return `${url}${separator}directory=${encodeURIComponent(value)}`;
|
|
9734
|
+
}
|
|
9735
|
+
async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, directory, fetchImpl = globalThis.fetch, legacyOnly = false } = {}) {
|
|
9720
9736
|
if (typeof fetchImpl !== "function") throw new Error("OpenCode direct prompt delivery requires fetch.");
|
|
9721
9737
|
const root = normalizeOpenCodeBaseUrl(baseUrl, "");
|
|
9722
9738
|
if (!root) throw new Error("OpenCode direct prompt delivery requires a base URL.");
|
|
@@ -9724,7 +9740,7 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
|
|
|
9724
9740
|
const attempts = [
|
|
9725
9741
|
legacyOnly ? null : {
|
|
9726
9742
|
name: "v2 prompt",
|
|
9727
|
-
url: `${root}/api/session/${encodedSession}/prompt`,
|
|
9743
|
+
url: appendDirectoryQuery(`${root}/api/session/${encodedSession}/prompt`, directory),
|
|
9728
9744
|
body: { prompt: { text }, delivery: "queue", resume: true },
|
|
9729
9745
|
accept: (response, data) => {
|
|
9730
9746
|
if (!response.ok) return null;
|
|
@@ -9734,7 +9750,7 @@ async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent,
|
|
|
9734
9750
|
},
|
|
9735
9751
|
{
|
|
9736
9752
|
name: "legacy async prompt",
|
|
9737
|
-
url: `${root}/session/${encodedSession}/prompt_async`,
|
|
9753
|
+
url: appendDirectoryQuery(`${root}/session/${encodedSession}/prompt_async`, directory),
|
|
9738
9754
|
body: { agent, parts: [{ type: "text", text }] },
|
|
9739
9755
|
accept: (response, data) => {
|
|
9740
9756
|
if (response.status !== 204 && !response.ok) return null;
|
|
@@ -9799,7 +9815,7 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
|
|
|
9799
9815
|
firstError ??= error;
|
|
9800
9816
|
}
|
|
9801
9817
|
}
|
|
9802
|
-
if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, fetchImpl, legacyOnly });
|
|
9818
|
+
if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, directory, fetchImpl, legacyOnly });
|
|
9803
9819
|
if (firstError) throw firstError;
|
|
9804
9820
|
throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
|
|
9805
9821
|
}
|
|
@@ -9834,6 +9850,11 @@ function openCodeMessageText(message) {
|
|
|
9834
9850
|
for (const part of partList) parts.push(part?.text, part?.content);
|
|
9835
9851
|
return parts.filter((value) => typeof value === "string").join("\n");
|
|
9836
9852
|
}
|
|
9853
|
+
function openCodeMessageStableKey(message, index = 0) {
|
|
9854
|
+
const id = message?.id || message?.messageID || message?.messageId || message?.data?.id || null;
|
|
9855
|
+
if (id) return `id:${id}`;
|
|
9856
|
+
return `idx:${index}:text:${openCodeMessageText(message)}`;
|
|
9857
|
+
}
|
|
9837
9858
|
async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMessages = null, expectedText = "", markers = [], attempts = 8, delayMs = 250 } = {}) {
|
|
9838
9859
|
let lastError = "message_verification_unavailable";
|
|
9839
9860
|
for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
|
|
@@ -9841,13 +9862,15 @@ async function verifyOpenCodeSessionEventDelivery(client, { sessionId, beforeMes
|
|
|
9841
9862
|
const afterMessages = await readOpenCodeSessionMessages(client, { sessionId, limit: 50 });
|
|
9842
9863
|
if (!afterMessages) return { ok: false, reason: "message_verification_unavailable" };
|
|
9843
9864
|
const beforeCount = Array.isArray(beforeMessages) ? beforeMessages.length : null;
|
|
9844
|
-
const
|
|
9865
|
+
const beforeKeys = Array.isArray(beforeMessages) ? new Set(beforeMessages.map(openCodeMessageStableKey)) : null;
|
|
9866
|
+
const newMessages = beforeKeys ? afterMessages.filter((message, index) => !beforeKeys.has(openCodeMessageStableKey(message, index))) : afterMessages;
|
|
9867
|
+
const lastMessage = newMessages.at(-1) || afterMessages.at(-1) || null;
|
|
9845
9868
|
const lastMessageId = lastMessage?.id || lastMessage?.messageID || lastMessage?.messageId || null;
|
|
9846
9869
|
if (beforeCount !== null && afterMessages.length > beforeCount) return { ok: true, method: "message_count", beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9847
9870
|
const textNeedles = [expectedText, ...markers].map((value) => String(value || "").trim()).filter(Boolean);
|
|
9848
|
-
const haystack =
|
|
9871
|
+
const haystack = newMessages.slice(-20).map(openCodeMessageText).join("\n");
|
|
9849
9872
|
const matched = textNeedles.find((needle) => haystack.includes(needle));
|
|
9850
|
-
if (matched) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9873
|
+
if (matched && (!beforeKeys || newMessages.length > 0)) return { ok: true, method: "message_text", marker: matched, beforeCount, afterCount: afterMessages.length, lastMessageId };
|
|
9851
9874
|
lastError = "message_not_visible";
|
|
9852
9875
|
} catch (error) {
|
|
9853
9876
|
lastError = error.message || "message_verification_failed";
|
|
@@ -10266,11 +10289,44 @@ async function routeClickUpWebhookEvent(options = {}) {
|
|
|
10266
10289
|
const taskId = clickUpTaskIdFromPayload(options.payload || {});
|
|
10267
10290
|
return withClickUpTaskRouteLock(taskId, () => routeClickUpWebhookEventUnlocked(options));
|
|
10268
10291
|
}
|
|
10292
|
+
function defaultStartupReconciliationScheduler(run, delayMs) {
|
|
10293
|
+
return setTimeout(run, delayMs);
|
|
10294
|
+
}
|
|
10295
|
+
function scheduleClickUpStartupReconciliation({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, scheduler = defaultStartupReconciliationScheduler, delayMs = config?.opencode?.startupReconciliationDelayMs ?? CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS, saveState = null, reconcile = reconcileClickUpStartup } = {}) {
|
|
10296
|
+
const normalizedDelayMs = normalizeNonNegativeInteger(delayMs, CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS);
|
|
10297
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_scheduled", delayMs: normalizedDelayMs, webhookId: state?.webhookId || null });
|
|
10298
|
+
const run = () => {
|
|
10299
|
+
Promise.resolve().then(() => reconcile({
|
|
10300
|
+
config,
|
|
10301
|
+
state,
|
|
10302
|
+
worktree,
|
|
10303
|
+
clickupClient,
|
|
10304
|
+
openCodeClient,
|
|
10305
|
+
saveState
|
|
10306
|
+
})).catch((error) => {
|
|
10307
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_failed", message: error.message });
|
|
10308
|
+
appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_failed", message: error.message });
|
|
10309
|
+
});
|
|
10310
|
+
};
|
|
10311
|
+
const timer = scheduler(run, normalizedDelayMs);
|
|
10312
|
+
timer?.unref?.();
|
|
10313
|
+
return { scheduled: true, delayMs: normalizedDelayMs, timer };
|
|
10314
|
+
}
|
|
10269
10315
|
async function reconcileClickUpStartup({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, waitForReadiness = waitForOpenCodeReadiness, saveState = null, now = () => /* @__PURE__ */ new Date(), limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
|
|
10270
|
-
|
|
10316
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_started", lastWebhookAt: state.lastWebhookAt || null });
|
|
10317
|
+
if (!config || !clickupClient?.listAssignedTasks) {
|
|
10318
|
+
const skipped = { ok: true, skipped: true, reason: "clickup_task_listing_unavailable", assigned: 0, comments: 0 };
|
|
10319
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...skipped });
|
|
10320
|
+
return skipped;
|
|
10321
|
+
}
|
|
10271
10322
|
const readiness = await waitForReadiness(openCodeClient, { worktree, now });
|
|
10272
10323
|
appendClickUpWebhookLocalLog(worktree, { type: readiness.ok ? "startup_reconciliation_readiness_ready" : "startup_reconciliation_readiness_failed", ...readiness });
|
|
10273
|
-
if (!readiness.ok)
|
|
10324
|
+
if (!readiness.ok) {
|
|
10325
|
+
const failed = { ok: false, skipped: true, reason: "opencode_not_ready", readiness, assigned: 0, comments: 0, ignored: 0, errors: 1, undelivered: 1, tasks: [], validation: { undelivered: 1, emptyPromptSessions: [] } };
|
|
10326
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_failed", reason: failed.reason, readiness });
|
|
10327
|
+
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...failed });
|
|
10328
|
+
return failed;
|
|
10329
|
+
}
|
|
10274
10330
|
let authorizedUserId = "";
|
|
10275
10331
|
if (clickupClient?.getAuthorizedUser) {
|
|
10276
10332
|
try {
|
|
@@ -10288,7 +10344,6 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
10288
10344
|
const lastWebhookMs = clickUpTimestampMs(mutableState.lastWebhookAt);
|
|
10289
10345
|
const ignored = new Set((config.routing?.ignoredStatuses || CLICKUP_WEBHOOK_TERMINAL_STATUSES).map(normalizeClickUpStatus));
|
|
10290
10346
|
const routed = { assigned: 0, comments: 0, ignored: 0, errors: 0, undelivered: 0, tasks: [] };
|
|
10291
|
-
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_started", lastWebhookAt: state.lastWebhookAt || null });
|
|
10292
10347
|
const listed = await clickupClient.listAssignedTasks({ assigneeId: config.routing.productManagerAssigneeId, limit });
|
|
10293
10348
|
const tasks = clickUpTaskListItems(listed).slice(0, limit);
|
|
10294
10349
|
for (const task of tasks) {
|
|
@@ -11606,18 +11661,16 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11606
11661
|
listener: { bindHost: clickUpWebhookValidation.config.webhook.bindHost, bindPort: clickUpWebhookValidation.config.webhook.bindPort, startedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
11607
11662
|
}, clickUpWebhookValidation.config);
|
|
11608
11663
|
registerClickUpWebhookLifecycle({ config: clickUpWebhookValidation.config, state: activeState, worktree, clickupClient: lifecycleClickUpClient, listener: readyListener, listenerRegistry });
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
}
|
|
11619
|
-
appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_failed", message: error.message });
|
|
11620
|
-
}
|
|
11664
|
+
scheduleClickUpStartupReconciliation({
|
|
11665
|
+
config: clickUpWebhookValidation.config,
|
|
11666
|
+
state: activeState,
|
|
11667
|
+
worktree,
|
|
11668
|
+
clickupClient: lifecycleClickUpClient,
|
|
11669
|
+
openCodeClient: input.client,
|
|
11670
|
+
scheduler: input.startupReconciliationScheduler,
|
|
11671
|
+
delayMs: input.startupReconciliationDelayMs,
|
|
11672
|
+
saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, clickUpWebhookValidation.config)
|
|
11673
|
+
});
|
|
11621
11674
|
} else {
|
|
11622
11675
|
clickUpWebhookLifecycleLog(worktree, { type: "remote_webhook_preserved", reason: readyListener.reason || "listener_unavailable", webhookId: listenerState.webhookId });
|
|
11623
11676
|
markClickUpWebhookInactive(worktree, listenerState, clickUpWebhookValidation.config);
|
|
@@ -12192,7 +12245,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
12192
12245
|
}
|
|
12193
12246
|
};
|
|
12194
12247
|
}
|
|
12195
|
-
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, createOpenCodeSession, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionMessages, reconcileClickUpStartup, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
12248
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, createOpenCodeSession, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
12196
12249
|
|
|
12197
12250
|
// src/sanitize_cli.js
|
|
12198
12251
|
var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
|