@defend-tech/opencode-optima 0.1.69 → 0.1.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8530,6 +8530,7 @@ var CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT = 100;
8530
8530
  var CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS = 3e4;
8531
8531
  var CLICKUP_WEBHOOK_STARTUP_COMMENT_LOOKBACK_MS = 6 * 60 * 60 * 1e3;
8532
8532
  var CLICKUP_ASSIGNMENT_WATCHDOG_INTERVAL_MS = 5 * 60 * 1e3;
8533
+ var CLICKUP_ASSIGNMENT_WATCHDOG_RUNNING_GRACE_MS = 15 * 60 * 1e3;
8533
8534
  var CLICKUP_WORKTREE_FAILURE_COMMENT_DEDUPE_MS = 10 * 60 * 1e3;
8534
8535
  var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
8535
8536
  var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
@@ -9816,6 +9817,10 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
9816
9817
  assignmentWatchdogIntervalMs: normalizeNonNegativeInteger(
9817
9818
  opencode.assignment_watchdog_interval_ms ?? opencode.assignmentWatchdogIntervalMs ?? raw.assignment_watchdog_interval_ms ?? raw.assignmentWatchdogIntervalMs,
9818
9819
  CLICKUP_ASSIGNMENT_WATCHDOG_INTERVAL_MS
9820
+ ),
9821
+ assignmentWatchdogRunningGraceMs: normalizeNonNegativeInteger(
9822
+ opencode.assignment_watchdog_running_grace_ms ?? opencode.assignmentWatchdogRunningGraceMs ?? raw.assignment_watchdog_running_grace_ms ?? raw.assignmentWatchdogRunningGraceMs,
9823
+ CLICKUP_ASSIGNMENT_WATCHDOG_RUNNING_GRACE_MS
9819
9824
  )
9820
9825
  },
9821
9826
  openchamber: {
@@ -10771,6 +10776,22 @@ async function readOpenCodeSessionMessages(client, { sessionId, directory, limit
10771
10776
  if (directory) query.directory = directory;
10772
10777
  return normalizeOpenCodeSessionMessages(await client.session.messages({ path: { id: sessionId }, query }));
10773
10778
  }
10779
+ async function readOpenCodeChildSessions(client, { sessionId, directory } = {}) {
10780
+ if (typeof client?.session?.children !== "function") return [];
10781
+ const attempts = [
10782
+ { path: { id: sessionId }, query: directory ? { directory } : {} },
10783
+ { path: { sessionID: sessionId }, query: directory ? { directory } : {} },
10784
+ { sessionID: sessionId, ...directory ? { directory } : {} }
10785
+ ];
10786
+ for (const attempt of attempts) {
10787
+ try {
10788
+ const result = await client.session.children(attempt);
10789
+ return normalizeOpenCodeSessionCollection(result?.data ?? result);
10790
+ } catch {
10791
+ }
10792
+ }
10793
+ return [];
10794
+ }
10774
10795
  function openCodeResultSummary(result) {
10775
10796
  const data = result?.data ?? result;
10776
10797
  return {
@@ -10831,13 +10852,28 @@ function openCodeMessageTimestampMs(message = {}, key = "updated") {
10831
10852
  const number = Number(value);
10832
10853
  return Number.isFinite(number) && number > 0 ? number : 0;
10833
10854
  }
10834
- function isOpenCodeAssistantMessageRunning(message = {}) {
10855
+ function openCodeSessionTimestampMs(session = {}, key = "updated") {
10856
+ const time = session?.time || session?.info?.time || {};
10857
+ const value = key === "created" ? session.time_created ?? session.timeCreated ?? time.created : session.time_updated ?? session.timeUpdated ?? time.updated ?? session.time_created ?? session.timeCreated ?? time.created;
10858
+ const number = Number(value);
10859
+ return Number.isFinite(number) && number > 0 ? number : 0;
10860
+ }
10861
+ function openCodeNowMs(now = /* @__PURE__ */ new Date()) {
10862
+ const value = typeof now === "function" ? now() : now;
10863
+ const number = Number(value instanceof Date ? value.getTime() : new Date(value).getTime());
10864
+ return Number.isFinite(number) && number > 0 ? number : 0;
10865
+ }
10866
+ function isOpenCodeAssistantMessageRunning(message = {}, { latestActivityAt = 0, now = /* @__PURE__ */ new Date(), runningGraceMs = CLICKUP_ASSIGNMENT_WATCHDOG_RUNNING_GRACE_MS } = {}) {
10835
10867
  if (normalizeLooseToken(normalizeOpenCodeMessageRole(message)) !== "assistant") return false;
10836
10868
  const started = openCodeMessageTimestampMs(message, "created") > 0;
10837
10869
  if (!started) return false;
10838
- return openCodeMessageTimestampMs(message, "completed") === 0;
10870
+ if (openCodeMessageTimestampMs(message, "completed") !== 0) return false;
10871
+ const nowMs = openCodeNowMs(now);
10872
+ const activityAt = Number(latestActivityAt) || openCodeMessageTimestampMs(message, "updated") || openCodeMessageTimestampMs(message, "created");
10873
+ if (!Number.isFinite(nowMs) || nowMs <= 0 || !Number.isFinite(activityAt) || activityAt <= 0) return false;
10874
+ return nowMs - activityAt <= Math.max(0, Number(runningGraceMs) || 0);
10839
10875
  }
10840
- async function inspectOpenCodeSessionActivity(client, { sessionId, directory, limit = 10 } = {}) {
10876
+ async function inspectOpenCodeSessionActivity(client, { sessionId, directory, limit = 10, now = /* @__PURE__ */ new Date(), runningGraceMs = CLICKUP_ASSIGNMENT_WATCHDOG_RUNNING_GRACE_MS } = {}) {
10841
10877
  const messages = await readOpenCodeSessionMessages(client, { sessionId, directory, limit });
10842
10878
  if (!messages) return { ok: false, reason: "message_inspection_unavailable", sessionId, directory };
10843
10879
  const enriched = messages.map((message, index) => ({
@@ -10847,20 +10883,45 @@ async function inspectOpenCodeSessionActivity(client, { sessionId, directory, li
10847
10883
  createdAt: openCodeMessageTimestampMs(message, "created"),
10848
10884
  completedAt: openCodeMessageTimestampMs(message, "completed")
10849
10885
  })).sort((a, b) => a.sortTime - b.sortTime || a.index - b.index);
10886
+ const childSessions = await readOpenCodeChildSessions(client, { sessionId, directory });
10887
+ const childActivities = childSessions.map((session, index) => ({
10888
+ session,
10889
+ index,
10890
+ id: session.id || session.sessionID || session.sessionId || null,
10891
+ agent: session.agent || session.mode?.agent || session.metadata?.agent || "",
10892
+ updatedAt: openCodeSessionTimestampMs(session, "updated")
10893
+ })).filter((entry) => entry.updatedAt > 0).sort((a, b) => a.updatedAt - b.updatedAt || a.index - b.index);
10850
10894
  const assistantMessages = enriched.filter((entry) => normalizeLooseToken(normalizeOpenCodeMessageRole(entry.message)) === "assistant");
10851
10895
  const latestAssistant = assistantMessages.at(-1) || null;
10852
10896
  const latest = enriched.at(-1) || null;
10853
- const running = latestAssistant ? isOpenCodeAssistantMessageRunning(latestAssistant.message) : false;
10897
+ const latestChild = childActivities.at(-1) || null;
10898
+ const latestMessageActivityAt = latest?.sortTime || 0;
10899
+ const latestChildActivityAt = latestChild?.updatedAt || 0;
10900
+ const latestActivityAt = Math.max(latestMessageActivityAt, latestChildActivityAt);
10901
+ const nowMs = openCodeNowMs(now);
10902
+ const latestActivityAgeMs = Number.isFinite(nowMs) && latestActivityAt > 0 ? Math.max(0, nowMs - latestActivityAt) : null;
10903
+ const assistantRunning = latestAssistant ? isOpenCodeAssistantMessageRunning(latestAssistant.message, { latestActivityAt, now, runningGraceMs }) : false;
10904
+ const childRunning = latestChildActivityAt > 0 && latestActivityAgeMs !== null && latestActivityAgeMs <= Math.max(0, Number(runningGraceMs) || 0);
10905
+ const running = assistantRunning || childRunning;
10906
+ const runningReason = running ? childRunning && latestChildActivityAt >= latestMessageActivityAt ? "recent_child_session_activity" : "recent_incomplete_assistant" : latestAssistant && latestAssistant.completedAt === 0 ? "stale_incomplete_assistant" : "not_running";
10854
10907
  return {
10855
10908
  ok: true,
10856
10909
  sessionId,
10857
10910
  directory,
10858
10911
  count: messages.length,
10859
10912
  running,
10913
+ runningReason,
10914
+ runningGraceMs,
10915
+ latestActivityAt,
10916
+ latestActivityAgeMs,
10860
10917
  latestMessageId: latest ? normalizeOpenCodeMessageId(latest.message) : null,
10861
10918
  latestAssistantMessageId: latestAssistant ? normalizeOpenCodeMessageId(latestAssistant.message) : null,
10862
10919
  latestAssistantCreatedAt: latestAssistant?.createdAt || 0,
10863
- latestAssistantCompletedAt: latestAssistant?.completedAt || 0
10920
+ latestAssistantCompletedAt: latestAssistant?.completedAt || 0,
10921
+ latestChildSessionId: latestChild?.id || null,
10922
+ latestChildAgent: latestChild?.agent || null,
10923
+ latestChildUpdatedAt: latestChild?.updatedAt || 0,
10924
+ childSessionCount: childSessions.length
10864
10925
  };
10865
10926
  }
10866
10927
  function summarizeOpenCodeMessages(messages = [], { snippetLength = 160, maxMessages = 50 } = {}) {
@@ -11620,7 +11681,12 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
11620
11681
  const directory = String(getNestedMetadataValue(metadata, "task.worktree") || "").trim();
11621
11682
  if (sessionId) {
11622
11683
  try {
11623
- const activity = await inspectOpenCodeSessionActivity(openCodeClient, { sessionId, directory });
11684
+ const activity = await inspectOpenCodeSessionActivity(openCodeClient, {
11685
+ sessionId,
11686
+ directory,
11687
+ now,
11688
+ runningGraceMs: config.opencode.assignmentWatchdogRunningGraceMs
11689
+ });
11624
11690
  appendClickUpWebhookLocalLog(worktree, { type: "assignment_watchdog_session_activity", taskId, sessionId, directory: directory || null, ...activity });
11625
11691
  if (activity.running) {
11626
11692
  routed.ignored += 1;
@@ -8537,6 +8537,7 @@ var CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT = 100;
8537
8537
  var CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS = 3e4;
8538
8538
  var CLICKUP_WEBHOOK_STARTUP_COMMENT_LOOKBACK_MS = 6 * 60 * 60 * 1e3;
8539
8539
  var CLICKUP_ASSIGNMENT_WATCHDOG_INTERVAL_MS = 5 * 60 * 1e3;
8540
+ var CLICKUP_ASSIGNMENT_WATCHDOG_RUNNING_GRACE_MS = 15 * 60 * 1e3;
8540
8541
  var CLICKUP_WORKTREE_FAILURE_COMMENT_DEDUPE_MS = 10 * 60 * 1e3;
8541
8542
  var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
8542
8543
  var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
@@ -9823,6 +9824,10 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
9823
9824
  assignmentWatchdogIntervalMs: normalizeNonNegativeInteger(
9824
9825
  opencode.assignment_watchdog_interval_ms ?? opencode.assignmentWatchdogIntervalMs ?? raw.assignment_watchdog_interval_ms ?? raw.assignmentWatchdogIntervalMs,
9825
9826
  CLICKUP_ASSIGNMENT_WATCHDOG_INTERVAL_MS
9827
+ ),
9828
+ assignmentWatchdogRunningGraceMs: normalizeNonNegativeInteger(
9829
+ opencode.assignment_watchdog_running_grace_ms ?? opencode.assignmentWatchdogRunningGraceMs ?? raw.assignment_watchdog_running_grace_ms ?? raw.assignmentWatchdogRunningGraceMs,
9830
+ CLICKUP_ASSIGNMENT_WATCHDOG_RUNNING_GRACE_MS
9826
9831
  )
9827
9832
  },
9828
9833
  openchamber: {
@@ -10778,6 +10783,22 @@ async function readOpenCodeSessionMessages(client, { sessionId, directory, limit
10778
10783
  if (directory) query.directory = directory;
10779
10784
  return normalizeOpenCodeSessionMessages(await client.session.messages({ path: { id: sessionId }, query }));
10780
10785
  }
10786
+ async function readOpenCodeChildSessions(client, { sessionId, directory } = {}) {
10787
+ if (typeof client?.session?.children !== "function") return [];
10788
+ const attempts = [
10789
+ { path: { id: sessionId }, query: directory ? { directory } : {} },
10790
+ { path: { sessionID: sessionId }, query: directory ? { directory } : {} },
10791
+ { sessionID: sessionId, ...directory ? { directory } : {} }
10792
+ ];
10793
+ for (const attempt of attempts) {
10794
+ try {
10795
+ const result = await client.session.children(attempt);
10796
+ return normalizeOpenCodeSessionCollection(result?.data ?? result);
10797
+ } catch {
10798
+ }
10799
+ }
10800
+ return [];
10801
+ }
10781
10802
  function openCodeResultSummary(result) {
10782
10803
  const data = result?.data ?? result;
10783
10804
  return {
@@ -10838,13 +10859,28 @@ function openCodeMessageTimestampMs(message = {}, key = "updated") {
10838
10859
  const number = Number(value);
10839
10860
  return Number.isFinite(number) && number > 0 ? number : 0;
10840
10861
  }
10841
- function isOpenCodeAssistantMessageRunning(message = {}) {
10862
+ function openCodeSessionTimestampMs(session = {}, key = "updated") {
10863
+ const time = session?.time || session?.info?.time || {};
10864
+ const value = key === "created" ? session.time_created ?? session.timeCreated ?? time.created : session.time_updated ?? session.timeUpdated ?? time.updated ?? session.time_created ?? session.timeCreated ?? time.created;
10865
+ const number = Number(value);
10866
+ return Number.isFinite(number) && number > 0 ? number : 0;
10867
+ }
10868
+ function openCodeNowMs(now = /* @__PURE__ */ new Date()) {
10869
+ const value = typeof now === "function" ? now() : now;
10870
+ const number = Number(value instanceof Date ? value.getTime() : new Date(value).getTime());
10871
+ return Number.isFinite(number) && number > 0 ? number : 0;
10872
+ }
10873
+ function isOpenCodeAssistantMessageRunning(message = {}, { latestActivityAt = 0, now = /* @__PURE__ */ new Date(), runningGraceMs = CLICKUP_ASSIGNMENT_WATCHDOG_RUNNING_GRACE_MS } = {}) {
10842
10874
  if (normalizeLooseToken(normalizeOpenCodeMessageRole(message)) !== "assistant") return false;
10843
10875
  const started = openCodeMessageTimestampMs(message, "created") > 0;
10844
10876
  if (!started) return false;
10845
- return openCodeMessageTimestampMs(message, "completed") === 0;
10877
+ if (openCodeMessageTimestampMs(message, "completed") !== 0) return false;
10878
+ const nowMs = openCodeNowMs(now);
10879
+ const activityAt = Number(latestActivityAt) || openCodeMessageTimestampMs(message, "updated") || openCodeMessageTimestampMs(message, "created");
10880
+ if (!Number.isFinite(nowMs) || nowMs <= 0 || !Number.isFinite(activityAt) || activityAt <= 0) return false;
10881
+ return nowMs - activityAt <= Math.max(0, Number(runningGraceMs) || 0);
10846
10882
  }
10847
- async function inspectOpenCodeSessionActivity(client, { sessionId, directory, limit = 10 } = {}) {
10883
+ async function inspectOpenCodeSessionActivity(client, { sessionId, directory, limit = 10, now = /* @__PURE__ */ new Date(), runningGraceMs = CLICKUP_ASSIGNMENT_WATCHDOG_RUNNING_GRACE_MS } = {}) {
10848
10884
  const messages = await readOpenCodeSessionMessages(client, { sessionId, directory, limit });
10849
10885
  if (!messages) return { ok: false, reason: "message_inspection_unavailable", sessionId, directory };
10850
10886
  const enriched = messages.map((message, index) => ({
@@ -10854,20 +10890,45 @@ async function inspectOpenCodeSessionActivity(client, { sessionId, directory, li
10854
10890
  createdAt: openCodeMessageTimestampMs(message, "created"),
10855
10891
  completedAt: openCodeMessageTimestampMs(message, "completed")
10856
10892
  })).sort((a, b) => a.sortTime - b.sortTime || a.index - b.index);
10893
+ const childSessions = await readOpenCodeChildSessions(client, { sessionId, directory });
10894
+ const childActivities = childSessions.map((session, index) => ({
10895
+ session,
10896
+ index,
10897
+ id: session.id || session.sessionID || session.sessionId || null,
10898
+ agent: session.agent || session.mode?.agent || session.metadata?.agent || "",
10899
+ updatedAt: openCodeSessionTimestampMs(session, "updated")
10900
+ })).filter((entry) => entry.updatedAt > 0).sort((a, b) => a.updatedAt - b.updatedAt || a.index - b.index);
10857
10901
  const assistantMessages = enriched.filter((entry) => normalizeLooseToken(normalizeOpenCodeMessageRole(entry.message)) === "assistant");
10858
10902
  const latestAssistant = assistantMessages.at(-1) || null;
10859
10903
  const latest = enriched.at(-1) || null;
10860
- const running = latestAssistant ? isOpenCodeAssistantMessageRunning(latestAssistant.message) : false;
10904
+ const latestChild = childActivities.at(-1) || null;
10905
+ const latestMessageActivityAt = latest?.sortTime || 0;
10906
+ const latestChildActivityAt = latestChild?.updatedAt || 0;
10907
+ const latestActivityAt = Math.max(latestMessageActivityAt, latestChildActivityAt);
10908
+ const nowMs = openCodeNowMs(now);
10909
+ const latestActivityAgeMs = Number.isFinite(nowMs) && latestActivityAt > 0 ? Math.max(0, nowMs - latestActivityAt) : null;
10910
+ const assistantRunning = latestAssistant ? isOpenCodeAssistantMessageRunning(latestAssistant.message, { latestActivityAt, now, runningGraceMs }) : false;
10911
+ const childRunning = latestChildActivityAt > 0 && latestActivityAgeMs !== null && latestActivityAgeMs <= Math.max(0, Number(runningGraceMs) || 0);
10912
+ const running = assistantRunning || childRunning;
10913
+ const runningReason = running ? childRunning && latestChildActivityAt >= latestMessageActivityAt ? "recent_child_session_activity" : "recent_incomplete_assistant" : latestAssistant && latestAssistant.completedAt === 0 ? "stale_incomplete_assistant" : "not_running";
10861
10914
  return {
10862
10915
  ok: true,
10863
10916
  sessionId,
10864
10917
  directory,
10865
10918
  count: messages.length,
10866
10919
  running,
10920
+ runningReason,
10921
+ runningGraceMs,
10922
+ latestActivityAt,
10923
+ latestActivityAgeMs,
10867
10924
  latestMessageId: latest ? normalizeOpenCodeMessageId(latest.message) : null,
10868
10925
  latestAssistantMessageId: latestAssistant ? normalizeOpenCodeMessageId(latestAssistant.message) : null,
10869
10926
  latestAssistantCreatedAt: latestAssistant?.createdAt || 0,
10870
- latestAssistantCompletedAt: latestAssistant?.completedAt || 0
10927
+ latestAssistantCompletedAt: latestAssistant?.completedAt || 0,
10928
+ latestChildSessionId: latestChild?.id || null,
10929
+ latestChildAgent: latestChild?.agent || null,
10930
+ latestChildUpdatedAt: latestChild?.updatedAt || 0,
10931
+ childSessionCount: childSessions.length
10871
10932
  };
10872
10933
  }
10873
10934
  function summarizeOpenCodeMessages(messages = [], { snippetLength = 160, maxMessages = 50 } = {}) {
@@ -11627,7 +11688,12 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
11627
11688
  const directory = String(getNestedMetadataValue(metadata, "task.worktree") || "").trim();
11628
11689
  if (sessionId) {
11629
11690
  try {
11630
- const activity = await inspectOpenCodeSessionActivity(openCodeClient, { sessionId, directory });
11691
+ const activity = await inspectOpenCodeSessionActivity(openCodeClient, {
11692
+ sessionId,
11693
+ directory,
11694
+ now,
11695
+ runningGraceMs: config.opencode.assignmentWatchdogRunningGraceMs
11696
+ });
11631
11697
  appendClickUpWebhookLocalLog(worktree, { type: "assignment_watchdog_session_activity", taskId, sessionId, directory: directory || null, ...activity });
11632
11698
  if (activity.running) {
11633
11699
  routed.ignored += 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defend-tech/opencode-optima",
3
- "version": "0.1.69",
3
+ "version": "0.1.70",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"