@defend-tech/opencode-optima 0.1.64 → 0.1.66
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 +62 -30
- package/dist/sanitize_cli.js +62 -30
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9337,7 +9337,7 @@ async function syncOpenChamberWorktreeVisibility({ openchamberBaseUrl, opencodeB
|
|
|
9337
9337
|
gitWorktree,
|
|
9338
9338
|
projectId: project.id
|
|
9339
9339
|
};
|
|
9340
|
-
if (!visibility.worktree || !visibility.
|
|
9340
|
+
if (!visibility.worktree || !visibility.projectDirectory) {
|
|
9341
9341
|
throw new Error(`OpenCode visibility verification failed for ${worktreePath}: ${JSON.stringify(visibility)}`);
|
|
9342
9342
|
}
|
|
9343
9343
|
return visibility;
|
|
@@ -9978,17 +9978,11 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
|
|
|
9978
9978
|
body: JSON.stringify({ value })
|
|
9979
9979
|
});
|
|
9980
9980
|
},
|
|
9981
|
-
async postTaskComment(
|
|
9982
|
-
|
|
9983
|
-
method: "POST",
|
|
9984
|
-
body: JSON.stringify({ comment_text: comment })
|
|
9985
|
-
});
|
|
9981
|
+
async postTaskComment() {
|
|
9982
|
+
throw new Error("Optima runtime does not post ClickUp task comments; use local logs for process failures.");
|
|
9986
9983
|
},
|
|
9987
|
-
async addTaskTag(
|
|
9988
|
-
|
|
9989
|
-
method: "POST",
|
|
9990
|
-
body: JSON.stringify({})
|
|
9991
|
-
});
|
|
9984
|
+
async addTaskTag() {
|
|
9985
|
+
throw new Error("Optima runtime does not add ClickUp task tags; use local logs for process failures.");
|
|
9992
9986
|
},
|
|
9993
9987
|
async listAssignedTasks({ assigneeId, statuses = [], limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
|
|
9994
9988
|
const params = new URLSearchParams();
|
|
@@ -10548,15 +10542,54 @@ async function createOpenCodeSession(client, { title, directory, agent } = {}) {
|
|
|
10548
10542
|
throw error || new Error("OpenCode session create failed.");
|
|
10549
10543
|
}
|
|
10550
10544
|
}
|
|
10551
|
-
|
|
10552
|
-
if (
|
|
10545
|
+
function normalizeOpenCodeSessionCollection(value) {
|
|
10546
|
+
if (Array.isArray(value)) return value;
|
|
10547
|
+
if (!isPlainObject(value)) return [];
|
|
10548
|
+
for (const key of ["sessions", "items", "data", "result", "results"]) {
|
|
10549
|
+
const nested = value[key];
|
|
10550
|
+
if (Array.isArray(nested)) return nested;
|
|
10551
|
+
if (isPlainObject(nested)) {
|
|
10552
|
+
const normalized = normalizeOpenCodeSessionCollection(nested);
|
|
10553
|
+
if (normalized.length > 0) return normalized;
|
|
10554
|
+
}
|
|
10555
|
+
}
|
|
10556
|
+
return [];
|
|
10557
|
+
}
|
|
10558
|
+
async function listOpenCodeSessionsReadiness(client, { directory = "" } = {}) {
|
|
10559
|
+
if (typeof client?.session?.list !== "function") return null;
|
|
10560
|
+
const attempts = [
|
|
10561
|
+
{ query: directory ? { directory } : {} },
|
|
10562
|
+
directory ? { directory } : {}
|
|
10563
|
+
];
|
|
10564
|
+
for (const attempt of attempts) {
|
|
10565
|
+
try {
|
|
10566
|
+
const result = await client.session.list(attempt);
|
|
10567
|
+
return { ok: true, method: "session_list", sessions: normalizeOpenCodeSessionCollection(result?.data ?? result).length };
|
|
10568
|
+
} catch {
|
|
10569
|
+
}
|
|
10570
|
+
}
|
|
10571
|
+
return null;
|
|
10572
|
+
}
|
|
10573
|
+
async function listOpenCodeSessionsReadinessHttp({ baseUrl, directory = "", fetchImpl = globalThis.fetch } = {}) {
|
|
10574
|
+
const root = normalizeOpenCodeBaseUrl(baseUrl, "");
|
|
10575
|
+
if (!root || typeof fetchImpl !== "function") return null;
|
|
10576
|
+
const url = openChamberUrl(root, "/session", directory ? { directory } : {});
|
|
10577
|
+
const response = await fetchImpl(url, { method: "GET" });
|
|
10578
|
+
const contentType = response.headers?.get?.("content-type") || "";
|
|
10579
|
+
const raw = await response.text();
|
|
10580
|
+
if (!response.ok) throw new Error(`OpenCode session list readiness failed with status ${response.status}.`);
|
|
10581
|
+
if (responseLooksLikeHtml(contentType, raw)) throw new Error("OpenCode session list readiness returned an HTML app shell.");
|
|
10582
|
+
const data = raw.trim() ? JSON.parse(raw) : [];
|
|
10583
|
+
return { ok: true, method: "session_list_http", sessions: normalizeOpenCodeSessionCollection(data).length };
|
|
10584
|
+
}
|
|
10585
|
+
async function waitForOpenCodeReadiness(client, { worktree = process.cwd(), attempts = 10, delayMs = 500, opencodeBaseUrl = "", fetchImpl = globalThis.fetch } = {}) {
|
|
10553
10586
|
let lastError = "opencode_not_ready";
|
|
10554
10587
|
const maxAttempts = Math.max(1, Number(attempts) || 1);
|
|
10555
10588
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
10556
10589
|
try {
|
|
10557
|
-
const
|
|
10558
|
-
if (
|
|
10559
|
-
|
|
10590
|
+
const listed = await listOpenCodeSessionsReadiness(client, { directory: worktree }) || await listOpenCodeSessionsReadinessHttp({ baseUrl: opencodeBaseUrl, directory: worktree, fetchImpl });
|
|
10591
|
+
if (listed?.ok) return { ...listed, attempts: attempt };
|
|
10592
|
+
return { ok: true, skipped: true, reason: "session_list_probe_unavailable", attempts: attempt };
|
|
10560
10593
|
} catch (error) {
|
|
10561
10594
|
lastError = error.message || "opencode_not_ready";
|
|
10562
10595
|
}
|
|
@@ -10929,18 +10962,9 @@ async function postClickUpLaunchFailureComment({ clickupClient, worktree, taskId
|
|
|
10929
10962
|
return { ok: false, skipped: true, reason: "local_log_only" };
|
|
10930
10963
|
}
|
|
10931
10964
|
async function applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source, tagName = CLICKUP_BLOCKER_TAG_NAME } = {}) {
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
}
|
|
10936
|
-
try {
|
|
10937
|
-
await clickupClient.addTaskTag({ taskId, tagName });
|
|
10938
|
-
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_applied", taskId, reason, source, tagName });
|
|
10939
|
-
return { ok: true, tagName };
|
|
10940
|
-
} catch (error) {
|
|
10941
|
-
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_failed", taskId, reason, source, tagName, message: error.message });
|
|
10942
|
-
return { ok: false, error: error.message, tagName };
|
|
10943
|
-
}
|
|
10965
|
+
void clickupClient;
|
|
10966
|
+
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_skipped", taskId, reason, source, tagName, policy: "metadata_only_task_writes" });
|
|
10967
|
+
return { ok: false, skipped: true, reason: "metadata_only_task_writes", tagName };
|
|
10944
10968
|
}
|
|
10945
10969
|
function openCodeBlockingPromptVerification(result, sessionId) {
|
|
10946
10970
|
if (result?.__optimaPromptDelivery !== "prompt") return null;
|
|
@@ -11287,7 +11311,9 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
11287
11311
|
const sessionTitle = formatClickUpSessionTitle({ taskId, payloadTask: payload.task, task });
|
|
11288
11312
|
const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
|
|
11289
11313
|
const metadata = normalizeAgentMetadataJson(existingMetadata);
|
|
11290
|
-
const
|
|
11314
|
+
const rawExistingSessionId = getNestedMetadataValue(metadata, config.routing.metadataKey);
|
|
11315
|
+
const existingSessionId = typeof rawExistingSessionId === "string" && rawExistingSessionId.startsWith("ses_") ? rawExistingSessionId : "";
|
|
11316
|
+
if (rawExistingSessionId !== void 0 && !existingSessionId) appendClickUpWebhookLocalLog(worktree, { type: "pm_session_metadata_ignored", taskId, valueType: typeof rawExistingSessionId, reason: "not_session_id" });
|
|
11291
11317
|
const taskType = clickUpTaskType(task);
|
|
11292
11318
|
const parentTaskId = clickUpParentTaskId(task) || taskId;
|
|
11293
11319
|
const subtaskId = parentTaskId && parentTaskId !== taskId ? taskId : "";
|
|
@@ -11317,6 +11343,12 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
11317
11343
|
const prompt = formatClickUpWebhookPrompt({ eventType, taskId, payload, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, humanRoleContext });
|
|
11318
11344
|
if (!existingSessionId) {
|
|
11319
11345
|
let pendingSessionId = getNestedMetadataValue(metadata, clickUpPendingSessionKey(config.routing.metadataKey)) || state.pendingSessions?.[taskId];
|
|
11346
|
+
if (pendingSessionId && !await sessionExists(openCodeClient, pendingSessionId, { directory: taskRoute.worktree })) {
|
|
11347
|
+
appendClickUpWebhookLocalLog(worktree, { type: "pending_session_stale", taskId, sessionId: pendingSessionId, worktree: taskRoute.worktree });
|
|
11348
|
+
const { [taskId]: _stalePending, ...remainingPending2 } = stateToPersist.pendingSessions || {};
|
|
11349
|
+
stateToPersist = { ...stateToPersist, pendingSessions: remainingPending2 };
|
|
11350
|
+
pendingSessionId = "";
|
|
11351
|
+
}
|
|
11320
11352
|
if (!pendingSessionId) {
|
|
11321
11353
|
try {
|
|
11322
11354
|
pendingSessionId = await createSession(openCodeClient, { title: sessionTitle, directory: taskRoute.worktree, agent: config.routing.targetAgent });
|
|
@@ -11422,7 +11454,7 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
11422
11454
|
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...skipped });
|
|
11423
11455
|
return skipped;
|
|
11424
11456
|
}
|
|
11425
|
-
const readiness = await waitForReadiness(openCodeClient, { worktree, now });
|
|
11457
|
+
const readiness = await waitForReadiness(openCodeClient, { worktree, now, opencodeBaseUrl: config.opencode?.baseUrl });
|
|
11426
11458
|
appendClickUpWebhookLocalLog(worktree, { type: readiness.ok ? "startup_reconciliation_readiness_ready" : "startup_reconciliation_readiness_failed", ...readiness });
|
|
11427
11459
|
if (!readiness.ok) {
|
|
11428
11460
|
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: [] } };
|
package/dist/sanitize_cli.js
CHANGED
|
@@ -9344,7 +9344,7 @@ async function syncOpenChamberWorktreeVisibility({ openchamberBaseUrl, opencodeB
|
|
|
9344
9344
|
gitWorktree,
|
|
9345
9345
|
projectId: project.id
|
|
9346
9346
|
};
|
|
9347
|
-
if (!visibility.worktree || !visibility.
|
|
9347
|
+
if (!visibility.worktree || !visibility.projectDirectory) {
|
|
9348
9348
|
throw new Error(`OpenCode visibility verification failed for ${worktreePath}: ${JSON.stringify(visibility)}`);
|
|
9349
9349
|
}
|
|
9350
9350
|
return visibility;
|
|
@@ -9985,17 +9985,11 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
|
|
|
9985
9985
|
body: JSON.stringify({ value })
|
|
9986
9986
|
});
|
|
9987
9987
|
},
|
|
9988
|
-
async postTaskComment(
|
|
9989
|
-
|
|
9990
|
-
method: "POST",
|
|
9991
|
-
body: JSON.stringify({ comment_text: comment })
|
|
9992
|
-
});
|
|
9988
|
+
async postTaskComment() {
|
|
9989
|
+
throw new Error("Optima runtime does not post ClickUp task comments; use local logs for process failures.");
|
|
9993
9990
|
},
|
|
9994
|
-
async addTaskTag(
|
|
9995
|
-
|
|
9996
|
-
method: "POST",
|
|
9997
|
-
body: JSON.stringify({})
|
|
9998
|
-
});
|
|
9991
|
+
async addTaskTag() {
|
|
9992
|
+
throw new Error("Optima runtime does not add ClickUp task tags; use local logs for process failures.");
|
|
9999
9993
|
},
|
|
10000
9994
|
async listAssignedTasks({ assigneeId, statuses = [], limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
|
|
10001
9995
|
const params = new URLSearchParams();
|
|
@@ -10555,15 +10549,54 @@ async function createOpenCodeSession(client, { title, directory, agent } = {}) {
|
|
|
10555
10549
|
throw error || new Error("OpenCode session create failed.");
|
|
10556
10550
|
}
|
|
10557
10551
|
}
|
|
10558
|
-
|
|
10559
|
-
if (
|
|
10552
|
+
function normalizeOpenCodeSessionCollection(value) {
|
|
10553
|
+
if (Array.isArray(value)) return value;
|
|
10554
|
+
if (!isPlainObject(value)) return [];
|
|
10555
|
+
for (const key of ["sessions", "items", "data", "result", "results"]) {
|
|
10556
|
+
const nested = value[key];
|
|
10557
|
+
if (Array.isArray(nested)) return nested;
|
|
10558
|
+
if (isPlainObject(nested)) {
|
|
10559
|
+
const normalized = normalizeOpenCodeSessionCollection(nested);
|
|
10560
|
+
if (normalized.length > 0) return normalized;
|
|
10561
|
+
}
|
|
10562
|
+
}
|
|
10563
|
+
return [];
|
|
10564
|
+
}
|
|
10565
|
+
async function listOpenCodeSessionsReadiness(client, { directory = "" } = {}) {
|
|
10566
|
+
if (typeof client?.session?.list !== "function") return null;
|
|
10567
|
+
const attempts = [
|
|
10568
|
+
{ query: directory ? { directory } : {} },
|
|
10569
|
+
directory ? { directory } : {}
|
|
10570
|
+
];
|
|
10571
|
+
for (const attempt of attempts) {
|
|
10572
|
+
try {
|
|
10573
|
+
const result = await client.session.list(attempt);
|
|
10574
|
+
return { ok: true, method: "session_list", sessions: normalizeOpenCodeSessionCollection(result?.data ?? result).length };
|
|
10575
|
+
} catch {
|
|
10576
|
+
}
|
|
10577
|
+
}
|
|
10578
|
+
return null;
|
|
10579
|
+
}
|
|
10580
|
+
async function listOpenCodeSessionsReadinessHttp({ baseUrl, directory = "", fetchImpl = globalThis.fetch } = {}) {
|
|
10581
|
+
const root = normalizeOpenCodeBaseUrl(baseUrl, "");
|
|
10582
|
+
if (!root || typeof fetchImpl !== "function") return null;
|
|
10583
|
+
const url = openChamberUrl(root, "/session", directory ? { directory } : {});
|
|
10584
|
+
const response = await fetchImpl(url, { method: "GET" });
|
|
10585
|
+
const contentType = response.headers?.get?.("content-type") || "";
|
|
10586
|
+
const raw = await response.text();
|
|
10587
|
+
if (!response.ok) throw new Error(`OpenCode session list readiness failed with status ${response.status}.`);
|
|
10588
|
+
if (responseLooksLikeHtml(contentType, raw)) throw new Error("OpenCode session list readiness returned an HTML app shell.");
|
|
10589
|
+
const data = raw.trim() ? JSON.parse(raw) : [];
|
|
10590
|
+
return { ok: true, method: "session_list_http", sessions: normalizeOpenCodeSessionCollection(data).length };
|
|
10591
|
+
}
|
|
10592
|
+
async function waitForOpenCodeReadiness(client, { worktree = process.cwd(), attempts = 10, delayMs = 500, opencodeBaseUrl = "", fetchImpl = globalThis.fetch } = {}) {
|
|
10560
10593
|
let lastError = "opencode_not_ready";
|
|
10561
10594
|
const maxAttempts = Math.max(1, Number(attempts) || 1);
|
|
10562
10595
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
10563
10596
|
try {
|
|
10564
|
-
const
|
|
10565
|
-
if (
|
|
10566
|
-
|
|
10597
|
+
const listed = await listOpenCodeSessionsReadiness(client, { directory: worktree }) || await listOpenCodeSessionsReadinessHttp({ baseUrl: opencodeBaseUrl, directory: worktree, fetchImpl });
|
|
10598
|
+
if (listed?.ok) return { ...listed, attempts: attempt };
|
|
10599
|
+
return { ok: true, skipped: true, reason: "session_list_probe_unavailable", attempts: attempt };
|
|
10567
10600
|
} catch (error) {
|
|
10568
10601
|
lastError = error.message || "opencode_not_ready";
|
|
10569
10602
|
}
|
|
@@ -10936,18 +10969,9 @@ async function postClickUpLaunchFailureComment({ clickupClient, worktree, taskId
|
|
|
10936
10969
|
return { ok: false, skipped: true, reason: "local_log_only" };
|
|
10937
10970
|
}
|
|
10938
10971
|
async function applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source, tagName = CLICKUP_BLOCKER_TAG_NAME } = {}) {
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
}
|
|
10943
|
-
try {
|
|
10944
|
-
await clickupClient.addTaskTag({ taskId, tagName });
|
|
10945
|
-
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_applied", taskId, reason, source, tagName });
|
|
10946
|
-
return { ok: true, tagName };
|
|
10947
|
-
} catch (error) {
|
|
10948
|
-
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_failed", taskId, reason, source, tagName, message: error.message });
|
|
10949
|
-
return { ok: false, error: error.message, tagName };
|
|
10950
|
-
}
|
|
10972
|
+
void clickupClient;
|
|
10973
|
+
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_skipped", taskId, reason, source, tagName, policy: "metadata_only_task_writes" });
|
|
10974
|
+
return { ok: false, skipped: true, reason: "metadata_only_task_writes", tagName };
|
|
10951
10975
|
}
|
|
10952
10976
|
function openCodeBlockingPromptVerification(result, sessionId) {
|
|
10953
10977
|
if (result?.__optimaPromptDelivery !== "prompt") return null;
|
|
@@ -11294,7 +11318,9 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
11294
11318
|
const sessionTitle = formatClickUpSessionTitle({ taskId, payloadTask: payload.task, task });
|
|
11295
11319
|
const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
|
|
11296
11320
|
const metadata = normalizeAgentMetadataJson(existingMetadata);
|
|
11297
|
-
const
|
|
11321
|
+
const rawExistingSessionId = getNestedMetadataValue(metadata, config.routing.metadataKey);
|
|
11322
|
+
const existingSessionId = typeof rawExistingSessionId === "string" && rawExistingSessionId.startsWith("ses_") ? rawExistingSessionId : "";
|
|
11323
|
+
if (rawExistingSessionId !== void 0 && !existingSessionId) appendClickUpWebhookLocalLog(worktree, { type: "pm_session_metadata_ignored", taskId, valueType: typeof rawExistingSessionId, reason: "not_session_id" });
|
|
11298
11324
|
const taskType = clickUpTaskType(task);
|
|
11299
11325
|
const parentTaskId = clickUpParentTaskId(task) || taskId;
|
|
11300
11326
|
const subtaskId = parentTaskId && parentTaskId !== taskId ? taskId : "";
|
|
@@ -11324,6 +11350,12 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
11324
11350
|
const prompt = formatClickUpWebhookPrompt({ eventType, taskId, payload, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, humanRoleContext });
|
|
11325
11351
|
if (!existingSessionId) {
|
|
11326
11352
|
let pendingSessionId = getNestedMetadataValue(metadata, clickUpPendingSessionKey(config.routing.metadataKey)) || state.pendingSessions?.[taskId];
|
|
11353
|
+
if (pendingSessionId && !await sessionExists(openCodeClient, pendingSessionId, { directory: taskRoute.worktree })) {
|
|
11354
|
+
appendClickUpWebhookLocalLog(worktree, { type: "pending_session_stale", taskId, sessionId: pendingSessionId, worktree: taskRoute.worktree });
|
|
11355
|
+
const { [taskId]: _stalePending, ...remainingPending2 } = stateToPersist.pendingSessions || {};
|
|
11356
|
+
stateToPersist = { ...stateToPersist, pendingSessions: remainingPending2 };
|
|
11357
|
+
pendingSessionId = "";
|
|
11358
|
+
}
|
|
11327
11359
|
if (!pendingSessionId) {
|
|
11328
11360
|
try {
|
|
11329
11361
|
pendingSessionId = await createSession(openCodeClient, { title: sessionTitle, directory: taskRoute.worktree, agent: config.routing.targetAgent });
|
|
@@ -11429,7 +11461,7 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
11429
11461
|
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...skipped });
|
|
11430
11462
|
return skipped;
|
|
11431
11463
|
}
|
|
11432
|
-
const readiness = await waitForReadiness(openCodeClient, { worktree, now });
|
|
11464
|
+
const readiness = await waitForReadiness(openCodeClient, { worktree, now, opencodeBaseUrl: config.opencode?.baseUrl });
|
|
11433
11465
|
appendClickUpWebhookLocalLog(worktree, { type: readiness.ok ? "startup_reconciliation_readiness_ready" : "startup_reconciliation_readiness_failed", ...readiness });
|
|
11434
11466
|
if (!readiness.ok) {
|
|
11435
11467
|
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: [] } };
|