oh-my-opencode 3.0.0-beta.1 → 3.0.0-beta.3

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
@@ -19567,6 +19567,18 @@ async function runBunInstallWithDetails() {
19567
19567
 
19568
19568
  // src/hooks/auto-update-checker/index.ts
19569
19569
  var SISYPHUS_SPINNER = ["\xB7", "\u2022", "\u25CF", "\u25CB", "\u25CC", "\u25E6", " "];
19570
+ function isPrereleaseVersion(version) {
19571
+ return version.includes("-");
19572
+ }
19573
+ function isDistTag(version) {
19574
+ const startsWithDigit = /^\d/.test(version);
19575
+ return !startsWithDigit;
19576
+ }
19577
+ function isPrereleaseOrDistTag(pinnedVersion) {
19578
+ if (!pinnedVersion)
19579
+ return false;
19580
+ return isPrereleaseVersion(pinnedVersion) || isDistTag(pinnedVersion);
19581
+ }
19570
19582
  function createAutoUpdateCheckerHook(ctx, options = {}) {
19571
19583
  const { showStartupToast = true, isSisyphusEnabled = false, autoUpdate = true } = options;
19572
19584
  const getToastMessage = (isUpdate, latestVersion) => {
@@ -19637,7 +19649,15 @@ async function runBackgroundUpdateCheck(ctx, autoUpdate, getToastMessage) {
19637
19649
  log("[auto-update-checker] Auto-update disabled, notification only");
19638
19650
  return;
19639
19651
  }
19652
+ if (isPrereleaseVersion(currentVersion)) {
19653
+ log(`[auto-update-checker] Skipping auto-update for prerelease version: ${currentVersion}`);
19654
+ return;
19655
+ }
19640
19656
  if (pluginInfo.isPinned) {
19657
+ if (isPrereleaseOrDistTag(pluginInfo.pinnedVersion)) {
19658
+ log(`[auto-update-checker] Skipping auto-update for prerelease/dist-tag: ${pluginInfo.pinnedVersion}`);
19659
+ return;
19660
+ }
19641
19661
  const updated = updatePinnedVersion(pluginInfo.configPath, pluginInfo.entry, latestVersion);
19642
19662
  if (!updated) {
19643
19663
  await showUpdateAvailableToast(ctx, latestVersion, getToastMessage);
@@ -19755,6 +19775,8 @@ var TARGET_TOOLS = new Set([
19755
19775
  "safe_glob",
19756
19776
  "webfetch",
19757
19777
  "context7_resolve-library-id",
19778
+ "context7_query-docs",
19779
+ "websearch_web_search_exa",
19758
19780
  "context7_get-library-docs",
19759
19781
  "grep_app_searchgithub"
19760
19782
  ]);
@@ -44131,8 +44153,8 @@ Session ID: ${task.sessionID}
44131
44153
 
44132
44154
  (No messages found)`;
44133
44155
  }
44134
- const assistantMessages = messages.filter((m2) => m2.info?.role === "assistant");
44135
- if (assistantMessages.length === 0) {
44156
+ const relevantMessages = messages.filter((m2) => m2.info?.role === "assistant" || m2.info?.role === "tool");
44157
+ if (relevantMessages.length === 0) {
44136
44158
  return `Task Result
44137
44159
 
44138
44160
  Task ID: ${task.id}
@@ -44142,11 +44164,34 @@ Session ID: ${task.sessionID}
44142
44164
 
44143
44165
  ---
44144
44166
 
44145
- (No assistant response found)`;
44167
+ (No assistant or tool response found)`;
44146
44168
  }
44147
- const lastMessage = assistantMessages[assistantMessages.length - 1];
44148
- const textParts = lastMessage?.parts?.filter((p2) => p2.type === "text") ?? [];
44149
- const textContent = textParts.map((p2) => p2.text ?? "").filter((text) => text.length > 0).join(`
44169
+ const sortedMessages = [...relevantMessages].sort((a, b3) => {
44170
+ const timeA = String(a.info?.time ?? "");
44171
+ const timeB = String(b3.info?.time ?? "");
44172
+ return timeA.localeCompare(timeB);
44173
+ });
44174
+ const extractedContent = [];
44175
+ for (const message of sortedMessages) {
44176
+ for (const part of message.parts ?? []) {
44177
+ if ((part.type === "text" || part.type === "reasoning") && part.text) {
44178
+ extractedContent.push(part.text);
44179
+ } else if (part.type === "tool_result") {
44180
+ const toolResult = part;
44181
+ if (typeof toolResult.content === "string" && toolResult.content) {
44182
+ extractedContent.push(toolResult.content);
44183
+ } else if (Array.isArray(toolResult.content)) {
44184
+ for (const block of toolResult.content) {
44185
+ if ((block.type === "text" || block.type === "reasoning") && block.text) {
44186
+ extractedContent.push(block.text);
44187
+ }
44188
+ }
44189
+ }
44190
+ }
44191
+ }
44192
+ }
44193
+ const textContent = extractedContent.filter((text) => text.length > 0).join(`
44194
+
44150
44195
  `);
44151
44196
  const duration3 = formatDuration(task.startedAt, task.completedAt);
44152
44197
  return `Task Result
@@ -44409,19 +44454,43 @@ session_id: ${sessionID}
44409
44454
  }
44410
44455
  const messages = messagesResult.data;
44411
44456
  log(`[call_omo_agent] Got ${messages.length} messages`);
44412
- const lastAssistantMessage = messages.filter((m2) => m2.info.role === "assistant").sort((a, b3) => (b3.info.time?.created || 0) - (a.info.time?.created || 0))[0];
44413
- if (!lastAssistantMessage) {
44414
- log(`[call_omo_agent] No assistant message found`);
44457
+ const relevantMessages = messages.filter((m2) => m2.info?.role === "assistant" || m2.info?.role === "tool");
44458
+ if (relevantMessages.length === 0) {
44459
+ log(`[call_omo_agent] No assistant or tool messages found`);
44415
44460
  log(`[call_omo_agent] All messages:`, JSON.stringify(messages, null, 2));
44416
- return `Error: No assistant response found
44461
+ return `Error: No assistant or tool response found
44417
44462
 
44418
44463
  <task_metadata>
44419
44464
  session_id: ${sessionID}
44420
44465
  </task_metadata>`;
44421
44466
  }
44422
- log(`[call_omo_agent] Found assistant message with ${lastAssistantMessage.parts.length} parts`);
44423
- const textParts = lastAssistantMessage.parts.filter((p2) => p2.type === "text");
44424
- const responseText = textParts.map((p2) => p2.text).join(`
44467
+ log(`[call_omo_agent] Found ${relevantMessages.length} relevant messages`);
44468
+ const sortedMessages = [...relevantMessages].sort((a, b3) => {
44469
+ const timeA = a.info?.time?.created ?? 0;
44470
+ const timeB = b3.info?.time?.created ?? 0;
44471
+ return timeA - timeB;
44472
+ });
44473
+ const extractedContent = [];
44474
+ for (const message of sortedMessages) {
44475
+ for (const part of message.parts ?? []) {
44476
+ if ((part.type === "text" || part.type === "reasoning") && part.text) {
44477
+ extractedContent.push(part.text);
44478
+ } else if (part.type === "tool_result") {
44479
+ const toolResult = part;
44480
+ if (typeof toolResult.content === "string" && toolResult.content) {
44481
+ extractedContent.push(toolResult.content);
44482
+ } else if (Array.isArray(toolResult.content)) {
44483
+ for (const block of toolResult.content) {
44484
+ if ((block.type === "text" || block.type === "reasoning") && block.text) {
44485
+ extractedContent.push(block.text);
44486
+ }
44487
+ }
44488
+ }
44489
+ }
44490
+ }
44491
+ }
44492
+ const responseText = extractedContent.filter((text) => text.length > 0).join(`
44493
+
44425
44494
  `);
44426
44495
  log(`[call_omo_agent] Got response, length: ${responseText.length}`);
44427
44496
  const output = responseText + `
@@ -44856,6 +44925,29 @@ Use \`background_output\` with task_id="${task.id}" to check progress.`;
44856
44925
 
44857
44926
  Session ID: ${args.resume}`;
44858
44927
  }
44928
+ const POLL_INTERVAL_MS = 500;
44929
+ const MIN_STABILITY_TIME_MS = 5000;
44930
+ const STABILITY_POLLS_REQUIRED = 3;
44931
+ const pollStart = Date.now();
44932
+ let lastMsgCount = 0;
44933
+ let stablePolls = 0;
44934
+ while (Date.now() - pollStart < 60000) {
44935
+ await new Promise((resolve8) => setTimeout(resolve8, POLL_INTERVAL_MS));
44936
+ const elapsed = Date.now() - pollStart;
44937
+ if (elapsed < MIN_STABILITY_TIME_MS)
44938
+ continue;
44939
+ const messagesCheck = await client2.session.messages({ path: { id: args.resume } });
44940
+ const msgs = messagesCheck.data ?? messagesCheck;
44941
+ const currentMsgCount = msgs.length;
44942
+ if (currentMsgCount > 0 && currentMsgCount === lastMsgCount) {
44943
+ stablePolls++;
44944
+ if (stablePolls >= STABILITY_POLLS_REQUIRED)
44945
+ break;
44946
+ } else {
44947
+ stablePolls = 0;
44948
+ lastMsgCount = currentMsgCount;
44949
+ }
44950
+ }
44859
44951
  const messagesResult = await client2.session.messages({
44860
44952
  path: { id: args.resume }
44861
44953
  });
@@ -44878,7 +44970,7 @@ Session ID: ${args.resume}`;
44878
44970
 
44879
44971
  Session ID: ${args.resume}`;
44880
44972
  }
44881
- const textParts = lastMessage?.parts?.filter((p2) => p2.type === "text") ?? [];
44973
+ const textParts = lastMessage?.parts?.filter((p2) => p2.type === "text" || p2.type === "reasoning") ?? [];
44882
44974
  const textContent = textParts.map((p2) => p2.text ?? "").filter(Boolean).join(`
44883
44975
  `);
44884
44976
  const duration3 = formatDuration2(startTime);
@@ -44992,11 +45084,10 @@ System notifies on completion. Use \`background_output\` with task_id="${task.id
44992
45084
  metadata: { sessionId: sessionID, category: args.category, sync: true }
44993
45085
  });
44994
45086
  let promptError;
44995
- await client2.session.promptAsync({
45087
+ client2.session.prompt({
44996
45088
  path: { id: sessionID },
44997
45089
  body: {
44998
45090
  agent: agentToUse,
44999
- model: categoryModel,
45000
45091
  system: systemContent,
45001
45092
  tools: {
45002
45093
  task: false,
@@ -45007,6 +45098,7 @@ System notifies on completion. Use \`background_output\` with task_id="${task.id
45007
45098
  }).catch((error45) => {
45008
45099
  promptError = error45 instanceof Error ? error45 : new Error(String(error45));
45009
45100
  });
45101
+ await new Promise((resolve8) => setTimeout(resolve8, 100));
45010
45102
  if (promptError) {
45011
45103
  if (toastManager && taskId !== undefined) {
45012
45104
  toastManager.removeTask(taskId);
@@ -45023,14 +45115,51 @@ Session ID: ${sessionID}`;
45023
45115
  }
45024
45116
  const POLL_INTERVAL_MS = 500;
45025
45117
  const MAX_POLL_TIME_MS = 10 * 60 * 1000;
45118
+ const MIN_STABILITY_TIME_MS = 1e4;
45119
+ const STABILITY_POLLS_REQUIRED = 3;
45026
45120
  const pollStart = Date.now();
45121
+ let lastMsgCount = 0;
45122
+ let stablePolls = 0;
45027
45123
  while (Date.now() - pollStart < MAX_POLL_TIME_MS) {
45028
45124
  await new Promise((resolve8) => setTimeout(resolve8, POLL_INTERVAL_MS));
45125
+ const asyncError = promptError;
45126
+ if (asyncError) {
45127
+ if (toastManager && taskId !== undefined) {
45128
+ toastManager.removeTask(taskId);
45129
+ }
45130
+ const errorMessage = asyncError.message;
45131
+ if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
45132
+ return `\u274C Agent "${agentToUse}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.
45133
+
45134
+ Session ID: ${sessionID}`;
45135
+ }
45136
+ return `\u274C Failed to send prompt: ${errorMessage}
45137
+
45138
+ Session ID: ${sessionID}`;
45139
+ }
45029
45140
  const statusResult = await client2.session.status();
45030
45141
  const allStatuses = statusResult.data ?? {};
45031
45142
  const sessionStatus = allStatuses[sessionID];
45032
- if (!sessionStatus || sessionStatus.type === "idle") {
45033
- break;
45143
+ if (sessionStatus && sessionStatus.type !== "idle") {
45144
+ stablePolls = 0;
45145
+ lastMsgCount = 0;
45146
+ continue;
45147
+ }
45148
+ const elapsed = Date.now() - pollStart;
45149
+ if (elapsed < MIN_STABILITY_TIME_MS) {
45150
+ continue;
45151
+ }
45152
+ const messagesCheck = await client2.session.messages({ path: { id: sessionID } });
45153
+ const msgs = messagesCheck.data ?? messagesCheck;
45154
+ const currentMsgCount = msgs.length;
45155
+ if (currentMsgCount > 0 && currentMsgCount === lastMsgCount) {
45156
+ stablePolls++;
45157
+ if (stablePolls >= STABILITY_POLLS_REQUIRED) {
45158
+ break;
45159
+ }
45160
+ } else {
45161
+ stablePolls = 0;
45162
+ lastMsgCount = currentMsgCount;
45034
45163
  }
45035
45164
  }
45036
45165
  const messagesResult = await client2.session.messages({
@@ -45049,7 +45178,7 @@ Session ID: ${sessionID}`;
45049
45178
 
45050
45179
  Session ID: ${sessionID}`;
45051
45180
  }
45052
- const textParts = lastMessage?.parts?.filter((p2) => p2.type === "text") ?? [];
45181
+ const textParts = lastMessage?.parts?.filter((p2) => p2.type === "text" || p2.type === "reasoning") ?? [];
45053
45182
  const textContent = textParts.map((p2) => p2.text ?? "").filter(Boolean).join(`
45054
45183
  `);
45055
45184
  const duration3 = formatDuration2(startTime);
@@ -45171,10 +45300,12 @@ class ConcurrencyManager {
45171
45300
 
45172
45301
  // src/features/background-agent/manager.ts
45173
45302
  var TASK_TTL_MS = 30 * 60 * 1000;
45303
+ var MIN_STABILITY_TIME_MS = 10 * 1000;
45174
45304
 
45175
45305
  class BackgroundManager {
45176
45306
  tasks;
45177
45307
  notifications;
45308
+ pendingByParent;
45178
45309
  client;
45179
45310
  directory;
45180
45311
  pollingInterval;
@@ -45182,11 +45313,18 @@ class BackgroundManager {
45182
45313
  constructor(ctx, config3) {
45183
45314
  this.tasks = new Map;
45184
45315
  this.notifications = new Map;
45316
+ this.pendingByParent = new Map;
45185
45317
  this.client = ctx.client;
45186
45318
  this.directory = ctx.directory;
45187
45319
  this.concurrencyManager = new ConcurrencyManager(config3);
45188
45320
  }
45189
45321
  async launch(input) {
45322
+ log("[background-agent] launch() called with:", {
45323
+ agent: input.agent,
45324
+ model: input.model,
45325
+ description: input.description,
45326
+ parentSessionID: input.parentSessionID
45327
+ });
45190
45328
  if (!input.agent || input.agent.trim() === "") {
45191
45329
  throw new Error("Agent parameter is required");
45192
45330
  }
@@ -45228,6 +45366,9 @@ class BackgroundManager {
45228
45366
  };
45229
45367
  this.tasks.set(task.id, task);
45230
45368
  this.startPolling();
45369
+ const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set;
45370
+ pending.add(task.id);
45371
+ this.pendingByParent.set(input.parentSessionID, pending);
45231
45372
  log("[background-agent] Launching task:", { taskId: task.id, sessionID, agent: input.agent });
45232
45373
  const toastManager = getTaskToastManager();
45233
45374
  if (toastManager) {
@@ -45239,10 +45380,18 @@ class BackgroundManager {
45239
45380
  skills: input.skills
45240
45381
  });
45241
45382
  }
45242
- this.client.session.promptAsync({
45383
+ log("[background-agent] Calling prompt (fire-and-forget) for launch with:", {
45384
+ sessionID,
45385
+ agent: input.agent,
45386
+ model: input.model,
45387
+ hasSkillContent: !!input.skillContent,
45388
+ promptLength: input.prompt.length
45389
+ });
45390
+ this.client.session.prompt({
45243
45391
  path: { id: sessionID },
45244
45392
  body: {
45245
45393
  agent: input.agent,
45394
+ ...input.model ? { model: input.model } : {},
45246
45395
  system: input.skillContent,
45247
45396
  tools: {
45248
45397
  task: false,
@@ -45266,7 +45415,9 @@ class BackgroundManager {
45266
45415
  this.concurrencyManager.release(existingTask.concurrencyKey);
45267
45416
  }
45268
45417
  this.markForNotification(existingTask);
45269
- this.notifyParentSession(existingTask);
45418
+ this.notifyParentSession(existingTask).catch((err) => {
45419
+ log("[background-agent] Failed to notify on error:", err);
45420
+ });
45270
45421
  }
45271
45422
  });
45272
45423
  return task;
@@ -45315,11 +45466,15 @@ class BackgroundManager {
45315
45466
  progress: {
45316
45467
  toolCalls: 0,
45317
45468
  lastUpdate: new Date
45318
- }
45469
+ },
45470
+ parentAgent: input.parentAgent
45319
45471
  };
45320
45472
  this.tasks.set(task.id, task);
45321
45473
  subagentSessions.add(input.sessionID);
45322
45474
  this.startPolling();
45475
+ const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set;
45476
+ pending.add(task.id);
45477
+ this.pendingByParent.set(input.parentSessionID, pending);
45323
45478
  log("[background-agent] Registered external task:", { taskId: task.id, sessionID: input.sessionID });
45324
45479
  return task;
45325
45480
  }
@@ -45341,6 +45496,9 @@ class BackgroundManager {
45341
45496
  };
45342
45497
  this.startPolling();
45343
45498
  subagentSessions.add(existingTask.sessionID);
45499
+ const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set;
45500
+ pending.add(existingTask.id);
45501
+ this.pendingByParent.set(input.parentSessionID, pending);
45344
45502
  const toastManager = getTaskToastManager();
45345
45503
  if (toastManager) {
45346
45504
  toastManager.addTask({
@@ -45351,7 +45509,12 @@ class BackgroundManager {
45351
45509
  });
45352
45510
  }
45353
45511
  log("[background-agent] Resuming task:", { taskId: existingTask.id, sessionID: existingTask.sessionID });
45354
- this.client.session.promptAsync({
45512
+ log("[background-agent] Resuming task - calling prompt (fire-and-forget) with:", {
45513
+ sessionID: existingTask.sessionID,
45514
+ agent: existingTask.agent,
45515
+ promptLength: input.prompt.length
45516
+ });
45517
+ this.client.session.prompt({
45355
45518
  path: { id: existingTask.sessionID },
45356
45519
  body: {
45357
45520
  agent: existingTask.agent,
@@ -45362,13 +45525,15 @@ class BackgroundManager {
45362
45525
  parts: [{ type: "text", text: input.prompt }]
45363
45526
  }
45364
45527
  }).catch((error45) => {
45365
- log("[background-agent] resume promptAsync error:", error45);
45528
+ log("[background-agent] resume prompt error:", error45);
45366
45529
  existingTask.status = "error";
45367
45530
  const errorMessage = error45 instanceof Error ? error45.message : String(error45);
45368
45531
  existingTask.error = errorMessage;
45369
45532
  existingTask.completedAt = new Date;
45370
45533
  this.markForNotification(existingTask);
45371
- this.notifyParentSession(existingTask);
45534
+ this.notifyParentSession(existingTask).catch((err) => {
45535
+ log("[background-agent] Failed to notify on resume error:", err);
45536
+ });
45372
45537
  });
45373
45538
  return existingTask;
45374
45539
  }
@@ -45417,7 +45582,18 @@ class BackgroundManager {
45417
45582
  const task = this.findBySession(sessionID);
45418
45583
  if (!task || task.status !== "running")
45419
45584
  return;
45420
- this.checkSessionTodos(sessionID).then((hasIncompleteTodos2) => {
45585
+ const elapsedMs = Date.now() - task.startedAt.getTime();
45586
+ const MIN_IDLE_TIME_MS = 5000;
45587
+ if (elapsedMs < MIN_IDLE_TIME_MS) {
45588
+ log("[background-agent] Ignoring early session.idle, elapsed:", { elapsedMs, taskId: task.id });
45589
+ return;
45590
+ }
45591
+ this.validateSessionHasOutput(sessionID).then(async (hasValidOutput) => {
45592
+ if (!hasValidOutput) {
45593
+ log("[background-agent] Session.idle but no valid output yet, waiting:", task.id);
45594
+ return;
45595
+ }
45596
+ const hasIncompleteTodos2 = await this.checkSessionTodos(sessionID);
45421
45597
  if (hasIncompleteTodos2) {
45422
45598
  log("[background-agent] Task has incomplete todos, waiting for todo-continuation:", task.id);
45423
45599
  return;
@@ -45425,8 +45601,10 @@ class BackgroundManager {
45425
45601
  task.status = "completed";
45426
45602
  task.completedAt = new Date;
45427
45603
  this.markForNotification(task);
45428
- this.notifyParentSession(task);
45604
+ await this.notifyParentSession(task);
45429
45605
  log("[background-agent] Task completed via session.idle event:", task.id);
45606
+ }).catch((err) => {
45607
+ log("[background-agent] Error in session.idle handler:", err);
45430
45608
  });
45431
45609
  }
45432
45610
  if (event.type === "session.deleted") {
@@ -45461,6 +45639,33 @@ class BackgroundManager {
45461
45639
  clearNotifications(sessionID) {
45462
45640
  this.notifications.delete(sessionID);
45463
45641
  }
45642
+ async validateSessionHasOutput(sessionID) {
45643
+ try {
45644
+ const response2 = await this.client.session.messages({
45645
+ path: { id: sessionID }
45646
+ });
45647
+ const messages = response2.data ?? [];
45648
+ const hasAssistantOrToolMessage = messages.some((m2) => m2.info?.role === "assistant" || m2.info?.role === "tool");
45649
+ if (!hasAssistantOrToolMessage) {
45650
+ log("[background-agent] No assistant/tool messages found in session:", sessionID);
45651
+ return false;
45652
+ }
45653
+ const hasContent2 = messages.some((m2) => {
45654
+ if (m2.info?.role !== "assistant" && m2.info?.role !== "tool")
45655
+ return false;
45656
+ const parts = m2.parts ?? [];
45657
+ return parts.some((p2) => p2.type === "text" && p2.text && p2.text.trim().length > 0 || p2.type === "reasoning" && p2.text && p2.text.trim().length > 0 || p2.type === "tool" || p2.type === "tool_result" && p2.content && (typeof p2.content === "string" ? p2.content.trim().length > 0 : p2.content.length > 0));
45658
+ });
45659
+ if (!hasContent2) {
45660
+ log("[background-agent] Messages exist but no content found in session:", sessionID);
45661
+ return false;
45662
+ }
45663
+ return true;
45664
+ } catch (error45) {
45665
+ log("[background-agent] Error validating session output:", error45);
45666
+ return true;
45667
+ }
45668
+ }
45464
45669
  clearNotificationsForTask(taskId) {
45465
45670
  for (const [sessionID, tasks] of this.notifications.entries()) {
45466
45671
  const filtered = tasks.filter((t) => t.id !== taskId);
@@ -45489,8 +45694,15 @@ class BackgroundManager {
45489
45694
  this.stopPolling();
45490
45695
  this.tasks.clear();
45491
45696
  this.notifications.clear();
45697
+ this.pendingByParent.clear();
45698
+ }
45699
+ getRunningTasks() {
45700
+ return Array.from(this.tasks.values()).filter((t) => t.status === "running");
45492
45701
  }
45493
- notifyParentSession(task) {
45702
+ getCompletedTasks() {
45703
+ return Array.from(this.tasks.values()).filter((t) => t.status !== "running");
45704
+ }
45705
+ async notifyParentSession(task) {
45494
45706
  const duration3 = this.formatDuration(task.startedAt, task.completedAt);
45495
45707
  log("[background-agent] notifyParentSession called for task:", task.id);
45496
45708
  const toastManager = getTaskToastManager();
@@ -45501,33 +45713,70 @@ class BackgroundManager {
45501
45713
  duration: duration3
45502
45714
  });
45503
45715
  }
45504
- const message = `[BACKGROUND TASK COMPLETED] Task "${task.description}" finished in ${duration3}. Use background_output with task_id="${task.id}" to get results.`;
45505
- log("[background-agent] Sending notification to parent session:", { parentSessionID: task.parentSessionID });
45716
+ const pendingSet = this.pendingByParent.get(task.parentSessionID);
45717
+ if (pendingSet) {
45718
+ pendingSet.delete(task.id);
45719
+ if (pendingSet.size === 0) {
45720
+ this.pendingByParent.delete(task.parentSessionID);
45721
+ }
45722
+ }
45723
+ const allComplete = !pendingSet || pendingSet.size === 0;
45724
+ const remainingCount = pendingSet?.size ?? 0;
45725
+ const statusText = task.status === "error" ? "FAILED" : "COMPLETED";
45726
+ const errorInfo = task.error ? `
45727
+ **Error:** ${task.error}` : "";
45728
+ let notification;
45729
+ if (allComplete) {
45730
+ const completedTasks = Array.from(this.tasks.values()).filter((t) => t.parentSessionID === task.parentSessionID && t.status !== "running").map((t) => `- \`${t.id}\`: ${t.description}`).join(`
45731
+ `);
45732
+ notification = `<system-reminder>
45733
+ [ALL BACKGROUND TASKS COMPLETE]
45734
+
45735
+ **Completed:**
45736
+ ${completedTasks || `- \`${task.id}\`: ${task.description}`}
45737
+
45738
+ Use \`background_output(task_id="<id>")\` to retrieve each result.
45739
+ </system-reminder>`;
45740
+ } else {
45741
+ notification = `<system-reminder>
45742
+ [BACKGROUND TASK ${statusText}]
45743
+ **ID:** \`${task.id}\`
45744
+ **Description:** ${task.description}
45745
+ **Duration:** ${duration3}${errorInfo}
45746
+
45747
+ **${remainingCount} task${remainingCount === 1 ? "" : "s"} still in progress.** You WILL be notified when ALL complete.
45748
+ Do NOT poll - continue productive work.
45749
+
45750
+ Use \`background_output(task_id="${task.id}")\` to retrieve this result when ready.
45751
+ </system-reminder>`;
45752
+ }
45753
+ try {
45754
+ await this.client.session.prompt({
45755
+ path: { id: task.parentSessionID },
45756
+ body: {
45757
+ noReply: !allComplete,
45758
+ agent: task.parentAgent,
45759
+ parts: [{ type: "text", text: notification }]
45760
+ }
45761
+ });
45762
+ log("[background-agent] Sent notification to parent session:", {
45763
+ taskId: task.id,
45764
+ allComplete,
45765
+ noReply: !allComplete
45766
+ });
45767
+ } catch (error45) {
45768
+ log("[background-agent] Failed to send notification:", error45);
45769
+ }
45506
45770
  const taskId = task.id;
45507
- setTimeout(async () => {
45771
+ setTimeout(() => {
45508
45772
  if (task.concurrencyKey) {
45509
45773
  this.concurrencyManager.release(task.concurrencyKey);
45774
+ task.concurrencyKey = undefined;
45510
45775
  }
45511
- try {
45512
- const modelField = task.parentModel?.providerID && task.parentModel?.modelID ? { providerID: task.parentModel.providerID, modelID: task.parentModel.modelID } : undefined;
45513
- await this.client.session.prompt({
45514
- path: { id: task.parentSessionID },
45515
- body: {
45516
- agent: task.parentAgent,
45517
- model: modelField,
45518
- parts: [{ type: "text", text: message }]
45519
- },
45520
- query: { directory: this.directory }
45521
- });
45522
- log("[background-agent] Successfully sent prompt to parent session:", { parentSessionID: task.parentSessionID });
45523
- } catch (error45) {
45524
- log("[background-agent] prompt failed:", String(error45));
45525
- } finally {
45526
- this.clearNotificationsForTask(taskId);
45527
- this.tasks.delete(taskId);
45528
- log("[background-agent] Removed completed task from memory:", taskId);
45529
- }
45530
- }, 200);
45776
+ this.clearNotificationsForTask(taskId);
45777
+ this.tasks.delete(taskId);
45778
+ log("[background-agent] Removed completed task from memory:", taskId);
45779
+ }, 5 * 60 * 1000);
45531
45780
  }
45532
45781
  formatDuration(start, end) {
45533
45782
  const duration3 = (end ?? new Date).getTime() - start.getTime();
@@ -45590,11 +45839,12 @@ class BackgroundManager {
45590
45839
  continue;
45591
45840
  try {
45592
45841
  const sessionStatus = allStatuses[task.sessionID];
45593
- if (!sessionStatus) {
45594
- log("[background-agent] Session not found in status:", task.sessionID);
45595
- continue;
45596
- }
45597
- if (sessionStatus.type === "idle") {
45842
+ if (sessionStatus?.type === "idle") {
45843
+ const hasValidOutput = await this.validateSessionHasOutput(task.sessionID);
45844
+ if (!hasValidOutput) {
45845
+ log("[background-agent] Polling idle but no valid output yet, waiting:", task.id);
45846
+ continue;
45847
+ }
45598
45848
  const hasIncompleteTodos2 = await this.checkSessionTodos(task.sessionID);
45599
45849
  if (hasIncompleteTodos2) {
45600
45850
  log("[background-agent] Task has incomplete todos via polling, waiting:", task.id);
@@ -45603,7 +45853,7 @@ class BackgroundManager {
45603
45853
  task.status = "completed";
45604
45854
  task.completedAt = new Date;
45605
45855
  this.markForNotification(task);
45606
- this.notifyParentSession(task);
45856
+ await this.notifyParentSession(task);
45607
45857
  log("[background-agent] Task completed via polling:", task.id);
45608
45858
  continue;
45609
45859
  }
@@ -45638,6 +45888,32 @@ class BackgroundManager {
45638
45888
  task.progress.lastMessage = lastMessage;
45639
45889
  task.progress.lastMessageAt = new Date;
45640
45890
  }
45891
+ const currentMsgCount = messages.length;
45892
+ const elapsedMs = Date.now() - task.startedAt.getTime();
45893
+ if (elapsedMs >= MIN_STABILITY_TIME_MS) {
45894
+ if (task.lastMsgCount === currentMsgCount) {
45895
+ task.stablePolls = (task.stablePolls ?? 0) + 1;
45896
+ if (task.stablePolls >= 3) {
45897
+ const hasValidOutput = await this.validateSessionHasOutput(task.sessionID);
45898
+ if (!hasValidOutput) {
45899
+ log("[background-agent] Stability reached but no valid output, waiting:", task.id);
45900
+ continue;
45901
+ }
45902
+ const hasIncompleteTodos2 = await this.checkSessionTodos(task.sessionID);
45903
+ if (!hasIncompleteTodos2) {
45904
+ task.status = "completed";
45905
+ task.completedAt = new Date;
45906
+ this.markForNotification(task);
45907
+ await this.notifyParentSession(task);
45908
+ log("[background-agent] Task completed via stability detection:", task.id);
45909
+ continue;
45910
+ }
45911
+ }
45912
+ } else {
45913
+ task.stablePolls = 0;
45914
+ }
45915
+ }
45916
+ task.lastMsgCount = currentMsgCount;
45641
45917
  }
45642
45918
  } catch (error45) {
45643
45919
  log("[background-agent] Poll error for task:", { taskId: task.id, error: error45 });
@@ -48454,7 +48730,9 @@ var BuiltinAgentNameSchema = exports_external.enum([
48454
48730
  "frontend-ui-ux-engineer",
48455
48731
  "document-writer",
48456
48732
  "multimodal-looker",
48457
- "Metis (Plan Consultant)"
48733
+ "Metis (Plan Consultant)",
48734
+ "Momus (Plan Reviewer)",
48735
+ "orchestrator-sisyphus"
48458
48736
  ]);
48459
48737
  var BuiltinSkillNameSchema = exports_external.enum([
48460
48738
  "playwright",
@@ -48468,12 +48746,14 @@ var OverridableAgentNameSchema = exports_external.enum([
48468
48746
  "OpenCode-Builder",
48469
48747
  "Prometheus (Planner)",
48470
48748
  "Metis (Plan Consultant)",
48749
+ "Momus (Plan Reviewer)",
48471
48750
  "oracle",
48472
48751
  "librarian",
48473
48752
  "explore",
48474
48753
  "frontend-ui-ux-engineer",
48475
48754
  "document-writer",
48476
- "multimodal-looker"
48755
+ "multimodal-looker",
48756
+ "orchestrator-sisyphus"
48477
48757
  ]);
48478
48758
  var HookNameSchema = exports_external.enum([
48479
48759
  "todo-continuation-enforcer",
@@ -48534,12 +48814,14 @@ var AgentOverridesSchema = exports_external.object({
48534
48814
  "OpenCode-Builder": AgentOverrideConfigSchema.optional(),
48535
48815
  "Prometheus (Planner)": AgentOverrideConfigSchema.optional(),
48536
48816
  "Metis (Plan Consultant)": AgentOverrideConfigSchema.optional(),
48817
+ "Momus (Plan Reviewer)": AgentOverrideConfigSchema.optional(),
48537
48818
  oracle: AgentOverrideConfigSchema.optional(),
48538
48819
  librarian: AgentOverrideConfigSchema.optional(),
48539
48820
  explore: AgentOverrideConfigSchema.optional(),
48540
48821
  "frontend-ui-ux-engineer": AgentOverrideConfigSchema.optional(),
48541
48822
  "document-writer": AgentOverrideConfigSchema.optional(),
48542
- "multimodal-looker": AgentOverrideConfigSchema.optional()
48823
+ "multimodal-looker": AgentOverrideConfigSchema.optional(),
48824
+ "orchestrator-sisyphus": AgentOverrideConfigSchema.optional()
48543
48825
  });
48544
48826
  var ClaudeCodeConfigSchema = exports_external.object({
48545
48827
  mcp: exports_external.boolean().optional(),
@@ -49056,7 +49338,6 @@ ${patterns.join(`
49056
49338
  var DEFAULT_MODEL = "anthropic/claude-opus-4-5";
49057
49339
  var SISYPHUS_ROLE_SECTION = `<Role>
49058
49340
  You are "Sisyphus" - Powerful AI Agent with orchestration capabilities from OhMyOpenCode.
49059
- Named by [YeonGyu Kim](https://github.com/code-yeongyu).
49060
49341
 
49061
49342
  **Why Sisyphus?**: Humans roll their boulder every day. So do you. We're not so different\u2014your code should be indistinguishable from a senior engineer's.
49062
49343
 
@@ -49777,24 +50058,17 @@ var LIBRARIAN_PROMPT_METADATA = {
49777
50058
  ]
49778
50059
  };
49779
50060
  function createLibrarianAgent(model = DEFAULT_MODEL3) {
49780
- const restrictions = createAgentToolRestrictions([
49781
- "write",
49782
- "edit",
49783
- "task",
49784
- "sisyphus_task",
49785
- "call_omo_agent"
49786
- ]);
49787
50061
  return {
49788
50062
  description: "Specialized codebase understanding agent for multi-repository analysis, searching remote codebases, retrieving official documentation, and finding implementation examples using GitHub CLI, Context7, and Web Search. MUST BE USED when users ask to look up code in remote repositories, explain library internals, or find usage examples in open source.",
49789
50063
  mode: "subagent",
49790
50064
  model,
49791
50065
  temperature: 0.1,
49792
- ...restrictions,
50066
+ tools: { write: false, edit: false, background_task: false },
49793
50067
  prompt: `# THE LIBRARIAN
49794
50068
 
49795
50069
  You are **THE LIBRARIAN**, a specialized open-source codebase understanding agent.
49796
50070
 
49797
- Your job: Answer questions about open-source libraries. Provide **EVIDENCE** with **GitHub permalinks** when the question requires verification, implementation details, or current/version-specific information. For well-known APIs and stable concepts, answer directly from knowledge.
50071
+ Your job: Answer questions about open-source libraries by finding **EVIDENCE** with **GitHub permalinks**.
49798
50072
 
49799
50073
  ## CRITICAL: DATE AWARENESS
49800
50074
 
@@ -49806,20 +50080,64 @@ Your job: Answer questions about open-source libraries. Provide **EVIDENCE** wit
49806
50080
 
49807
50081
  ---
49808
50082
 
49809
- ## PHASE 0: ASSESS BEFORE SEARCHING
49810
-
49811
- **First**: Can you answer confidently from training knowledge? If yes, answer directly.
50083
+ ## PHASE 0: REQUEST CLASSIFICATION (MANDATORY FIRST STEP)
49812
50084
 
49813
- **Search when**: version-specific info, implementation internals, recent changes, unfamiliar libraries, user explicitly requests source/examples.
49814
-
49815
- **If search needed**, classify into:
50085
+ Classify EVERY request into one of these categories before taking action:
49816
50086
 
49817
50087
  | Type | Trigger Examples | Tools |
49818
50088
  |------|------------------|-------|
49819
- | **TYPE A: CONCEPTUAL** | "How do I use X?", "Best practice for Y?" | context7 + web search (if available) in parallel |
50089
+ | **TYPE A: CONCEPTUAL** | "How do I use X?", "Best practice for Y?" | Doc Discovery \u2192 context7 + websearch |
49820
50090
  | **TYPE B: IMPLEMENTATION** | "How does X implement Y?", "Show me source of Z" | gh clone + read + blame |
49821
- | **TYPE C: CONTEXT** | "Why was this changed?", "What's the history?", "Related issues/PRs?" | gh issues/prs + git log/blame |
49822
- | **TYPE D: COMPREHENSIVE** | Complex/ambiguous requests | ALL available tools in parallel |
50091
+ | **TYPE C: CONTEXT** | "Why was this changed?", "History of X?" | gh issues/prs + git log/blame |
50092
+ | **TYPE D: COMPREHENSIVE** | Complex/ambiguous requests | Doc Discovery \u2192 ALL tools |
50093
+
50094
+ ---
50095
+
50096
+ ## PHASE 0.5: DOCUMENTATION DISCOVERY (FOR TYPE A & D)
50097
+
50098
+ **When to execute**: Before TYPE A or TYPE D investigations involving external libraries/frameworks.
50099
+
50100
+ ### Step 1: Find Official Documentation
50101
+ \`\`\`
50102
+ websearch("library-name official documentation site")
50103
+ \`\`\`
50104
+ - Identify the **official documentation URL** (not blogs, not tutorials)
50105
+ - Note the base URL (e.g., \`https://docs.example.com\`)
50106
+
50107
+ ### Step 2: Version Check (if version specified)
50108
+ If user mentions a specific version (e.g., "React 18", "Next.js 14", "v2.x"):
50109
+ \`\`\`
50110
+ websearch("library-name v{version} documentation")
50111
+ // OR check if docs have version selector:
50112
+ webfetch(official_docs_url + "/versions")
50113
+ // or
50114
+ webfetch(official_docs_url + "/v{version}")
50115
+ \`\`\`
50116
+ - Confirm you're looking at the **correct version's documentation**
50117
+ - Many docs have versioned URLs: \`/docs/v2/\`, \`/v14/\`, etc.
50118
+
50119
+ ### Step 3: Sitemap Discovery (understand doc structure)
50120
+ \`\`\`
50121
+ webfetch(official_docs_base_url + "/sitemap.xml")
50122
+ // Fallback options:
50123
+ webfetch(official_docs_base_url + "/sitemap-0.xml")
50124
+ webfetch(official_docs_base_url + "/docs/sitemap.xml")
50125
+ \`\`\`
50126
+ - Parse sitemap to understand documentation structure
50127
+ - Identify relevant sections for the user's question
50128
+ - This prevents random searching\u2014you now know WHERE to look
50129
+
50130
+ ### Step 4: Targeted Investigation
50131
+ With sitemap knowledge, fetch the SPECIFIC documentation pages relevant to the query:
50132
+ \`\`\`
50133
+ webfetch(specific_doc_page_from_sitemap)
50134
+ context7_query-docs(libraryId: id, query: "specific topic")
50135
+ \`\`\`
50136
+
50137
+ **Skip Doc Discovery when**:
50138
+ - TYPE B (implementation) - you're cloning repos anyway
50139
+ - TYPE C (context/history) - you're looking at issues/PRs
50140
+ - Library has no official docs (rare OSS projects)
49823
50141
 
49824
50142
  ---
49825
50143
 
@@ -49828,15 +50146,15 @@ Your job: Answer questions about open-source libraries. Provide **EVIDENCE** wit
49828
50146
  ### TYPE A: CONCEPTUAL QUESTION
49829
50147
  **Trigger**: "How do I...", "What is...", "Best practice for...", rough/general questions
49830
50148
 
49831
- **If searching**, use tools as needed:
50149
+ **Execute Documentation Discovery FIRST (Phase 0.5)**, then:
49832
50150
  \`\`\`
49833
50151
  Tool 1: context7_resolve-library-id("library-name")
49834
- \u2192 then context7_get-library-docs(id, topic: "specific-topic")
49835
- Tool 2: grep_app_searchGitHub(query: "usage pattern", language: ["TypeScript"])
49836
- Tool 3 (optional): If web search is available, search "library-name topic 2025"
50152
+ \u2192 then context7_query-docs(libraryId: id, query: "specific-topic")
50153
+ Tool 2: webfetch(relevant_pages_from_sitemap) // Targeted, not random
50154
+ Tool 3: grep_app_searchGitHub(query: "usage pattern", language: ["TypeScript"])
49837
50155
  \`\`\`
49838
50156
 
49839
- **Output**: Summarize findings with links to official docs and real-world examples.
50157
+ **Output**: Summarize findings with links to official docs (versioned if applicable) and real-world examples.
49840
50158
 
49841
50159
  ---
49842
50160
 
@@ -49847,20 +50165,20 @@ Tool 3 (optional): If web search is available, search "library-name topic 2025"
49847
50165
  \`\`\`
49848
50166
  Step 1: Clone to temp directory
49849
50167
  gh repo clone owner/repo \${TMPDIR:-/tmp}/repo-name -- --depth 1
49850
-
50168
+
49851
50169
  Step 2: Get commit SHA for permalinks
49852
50170
  cd \${TMPDIR:-/tmp}/repo-name && git rev-parse HEAD
49853
-
50171
+
49854
50172
  Step 3: Find the implementation
49855
50173
  - grep/ast_grep_search for function/class
49856
50174
  - read the specific file
49857
50175
  - git blame for context if needed
49858
-
50176
+
49859
50177
  Step 4: Construct permalink
49860
50178
  https://github.com/owner/repo/blob/<sha>/path/to/file#L10-L20
49861
50179
  \`\`\`
49862
50180
 
49863
- **For faster results, parallelize**:
50181
+ **Parallel acceleration (4+ calls)**:
49864
50182
  \`\`\`
49865
50183
  Tool 1: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
49866
50184
  Tool 2: grep_app_searchGitHub(query: "function_name", repo: "owner/repo")
@@ -49873,7 +50191,7 @@ Tool 4: context7_get-library-docs(id, topic: "relevant-api")
49873
50191
  ### TYPE C: CONTEXT & HISTORY
49874
50192
  **Trigger**: "Why was this changed?", "What's the history?", "Related issues/PRs?"
49875
50193
 
49876
- **Tools to use**:
50194
+ **Execute in parallel (4+ calls)**:
49877
50195
  \`\`\`
49878
50196
  Tool 1: gh search issues "keyword" --repo owner/repo --state all --limit 10
49879
50197
  Tool 2: gh search prs "keyword" --repo owner/repo --state merged --limit 10
@@ -49895,22 +50213,21 @@ gh api repos/owner/repo/pulls/<number>/files
49895
50213
  ### TYPE D: COMPREHENSIVE RESEARCH
49896
50214
  **Trigger**: Complex questions, ambiguous requests, "deep dive into..."
49897
50215
 
49898
- **Use multiple tools as needed**:
50216
+ **Execute Documentation Discovery FIRST (Phase 0.5)**, then execute in parallel (6+ calls):
49899
50217
  \`\`\`
49900
- // Documentation
49901
- Tool 1: context7_resolve-library-id \u2192 context7_get-library-docs
50218
+ // Documentation (informed by sitemap discovery)
50219
+ Tool 1: context7_resolve-library-id \u2192 context7_query-docs
50220
+ Tool 2: webfetch(targeted_doc_pages_from_sitemap)
49902
50221
 
49903
50222
  // Code Search
49904
- Tool 2: grep_app_searchGitHub(query: "pattern1", language: [...])
49905
- Tool 3: grep_app_searchGitHub(query: "pattern2", useRegexp: true)
50223
+ Tool 3: grep_app_searchGitHub(query: "pattern1", language: [...])
50224
+ Tool 4: grep_app_searchGitHub(query: "pattern2", useRegexp: true)
49906
50225
 
49907
50226
  // Source Analysis
49908
- Tool 4: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
50227
+ Tool 5: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
49909
50228
 
49910
50229
  // Context
49911
- Tool 5: gh search issues "topic" --repo owner/repo
49912
-
49913
- // Optional: If web search is available, search for recent updates
50230
+ Tool 6: gh search issues "topic" --repo owner/repo
49914
50231
  \`\`\`
49915
50232
 
49916
50233
  ---
@@ -49955,7 +50272,11 @@ https://github.com/tanstack/query/blob/abc123def/packages/react-query/src/useQue
49955
50272
 
49956
50273
  | Purpose | Tool | Command/Usage |
49957
50274
  |---------|------|---------------|
49958
- | **Official Docs** | context7 | \`context7_resolve-library-id\` \u2192 \`context7_get-library-docs\` |
50275
+ | **Official Docs** | context7 | \`context7_resolve-library-id\` \u2192 \`context7_query-docs\` |
50276
+ | **Find Docs URL** | websearch_exa | \`websearch_exa_web_search_exa("library official documentation")\` |
50277
+ | **Sitemap Discovery** | webfetch | \`webfetch(docs_url + "/sitemap.xml")\` to understand doc structure |
50278
+ | **Read Doc Page** | webfetch | \`webfetch(specific_doc_page)\` for targeted documentation |
50279
+ | **Latest Info** | websearch_exa | \`websearch_exa_web_search_exa("query 2025")\` |
49959
50280
  | **Fast Code Search** | grep_app | \`grep_app_searchGitHub(query, language, useRegexp)\` |
49960
50281
  | **Deep Code Search** | gh CLI | \`gh search code "query" --repo owner/repo\` |
49961
50282
  | **Clone Repo** | gh CLI | \`gh repo clone owner/repo \${TMPDIR:-/tmp}/name -- --depth 1\` |
@@ -49963,8 +50284,6 @@ https://github.com/tanstack/query/blob/abc123def/packages/react-query/src/useQue
49963
50284
  | **View Issue/PR** | gh CLI | \`gh issue/pr view <num> --repo owner/repo --comments\` |
49964
50285
  | **Release Info** | gh CLI | \`gh api repos/owner/repo/releases/latest\` |
49965
50286
  | **Git History** | git | \`git log\`, \`git blame\`, \`git show\` |
49966
- | **Read URL** | webfetch | \`webfetch(url)\` for blog posts, SO threads |
49967
- | **Web Search** | (if available) | Use any available web search tool for latest info |
49968
50287
 
49969
50288
  ### Temp Directory
49970
50289
 
@@ -49981,16 +50300,18 @@ Use OS-appropriate temp directory:
49981
50300
 
49982
50301
  ---
49983
50302
 
49984
- ## PARALLEL EXECUTION GUIDANCE
50303
+ ## PARALLEL EXECUTION REQUIREMENTS
49985
50304
 
49986
- When searching is needed, scale effort to question complexity:
49987
-
49988
- | Request Type | Suggested Calls |
50305
+ | Request Type | Suggested Calls | Doc Discovery Required |
49989
50306
  |--------------|----------------|
49990
- | TYPE A (Conceptual) | 1-2 |
49991
- | TYPE B (Implementation) | 2-3 |
49992
- | TYPE C (Context) | 2-3 |
49993
- | TYPE D (Comprehensive) | 3-5 |
50307
+ | TYPE A (Conceptual) | 1-2 | YES (Phase 0.5 first) |
50308
+ | TYPE B (Implementation) | 2-3 NO |
50309
+ | TYPE C (Context) | 2-3 NO |
50310
+ | TYPE D (Comprehensive) | 3-5 | YES (Phase 0.5 first) |
50311
+ | Request Type | Minimum Parallel Calls
50312
+
50313
+ **Doc Discovery is SEQUENTIAL** (websearch \u2192 version check \u2192 sitemap \u2192 investigate).
50314
+ **Main phase is PARALLEL** once you know where to look.
49994
50315
 
49995
50316
  **Always vary queries** when using grep_app:
49996
50317
  \`\`\`
@@ -50014,6 +50335,8 @@ grep_app_searchGitHub(query: "useQuery")
50014
50335
  | grep_app no results | Broaden query, try concept instead of exact name |
50015
50336
  | gh API rate limit | Use cloned repo in temp directory |
50016
50337
  | Repo not found | Search for forks or mirrors |
50338
+ | Sitemap not found | Try \`/sitemap-0.xml\`, \`/sitemap_index.xml\`, or fetch docs index page and parse navigation |
50339
+ | Versioned docs not found | Fall back to latest version, note this in response |
50017
50340
  | Uncertain | **STATE YOUR UNCERTAINTY**, propose hypothesis |
50018
50341
 
50019
50342
  ---
@@ -50021,7 +50344,7 @@ grep_app_searchGitHub(query: "useQuery")
50021
50344
  ## COMMUNICATION RULES
50022
50345
 
50023
50346
  1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use grep_app"
50024
- 2. **NO PREAMBLE**: Answer directly, skip "I'll help you with..."
50347
+ 2. **NO PREAMBLE**: Answer directly, skip "I'll help you with..."
50025
50348
  3. **ALWAYS CITE**: Every code claim needs a permalink
50026
50349
  4. **USE MARKDOWN**: Code blocks with language identifiers
50027
50350
  5. **BE CONCISE**: Facts > opinions, evidence > speculation
@@ -50781,15 +51104,19 @@ var metisRestrictions = createAgentToolRestrictions([
50781
51104
  "task",
50782
51105
  "sisyphus_task"
50783
51106
  ]);
50784
- var metisAgent = {
50785
- description: "Pre-planning consultant that analyzes requests to identify hidden intentions, ambiguities, and AI failure points.",
50786
- mode: "subagent",
50787
- model: "anthropic/claude-opus-4-5",
50788
- temperature: 0.3,
50789
- ...metisRestrictions,
50790
- prompt: METIS_SYSTEM_PROMPT,
50791
- thinking: { type: "enabled", budgetTokens: 32000 }
50792
- };
51107
+ var DEFAULT_MODEL8 = "anthropic/claude-opus-4-5";
51108
+ function createMetisAgent(model = DEFAULT_MODEL8) {
51109
+ return {
51110
+ description: "Pre-planning consultant that analyzes requests to identify hidden intentions, ambiguities, and AI failure points.",
51111
+ mode: "subagent",
51112
+ model,
51113
+ temperature: 0.3,
51114
+ ...metisRestrictions,
51115
+ prompt: METIS_SYSTEM_PROMPT,
51116
+ thinking: { type: "enabled", budgetTokens: 32000 }
51117
+ };
51118
+ }
51119
+ var metisAgent = createMetisAgent();
50793
51120
 
50794
51121
  // src/agents/orchestrator-sisyphus.ts
50795
51122
  init_constants();
@@ -50903,7 +51230,6 @@ ${rows.join(`
50903
51230
  **NEVER provide both category AND agent - they are mutually exclusive.**`;
50904
51231
  }
50905
51232
  var ORCHESTRATOR_SISYPHUS_SYSTEM_PROMPT = `You are "Sisyphus" - Powerful AI Agent with orchestration capabilities from OhMyOpenCode.
50906
- Named by [YeonGyu Kim](https://github.com/code-yeongyu).
50907
51233
 
50908
51234
  **Why Sisyphus?**: Humans roll their boulder every day. So do you. We're not so different\u2014your code should be indistinguishable from a senior engineer's.
50909
51235
 
@@ -52196,6 +52522,7 @@ function buildDynamicOrchestratorPrompt(ctx) {
52196
52522
  const skillsSection = buildSkillsSection(skills);
52197
52523
  return ORCHESTRATOR_SISYPHUS_SYSTEM_PROMPT.replace("{CATEGORY_SECTION}", categorySection).replace("{AGENT_SECTION}", agentSection).replace("{DECISION_MATRIX}", decisionMatrix).replace("{SKILLS_SECTION}", skillsSection);
52198
52524
  }
52525
+ var DEFAULT_MODEL9 = "anthropic/claude-sonnet-4-5";
52199
52526
  function createOrchestratorSisyphusAgent(ctx) {
52200
52527
  const restrictions = createAgentToolRestrictions([
52201
52528
  "task",
@@ -52204,7 +52531,7 @@ function createOrchestratorSisyphusAgent(ctx) {
52204
52531
  return {
52205
52532
  description: "Orchestrates work via sisyphus_task() to complete ALL tasks in a todo list until fully done",
52206
52533
  mode: "primary",
52207
- model: "anthropic/claude-sonnet-4-5",
52534
+ model: ctx?.model ?? DEFAULT_MODEL9,
52208
52535
  temperature: 0.1,
52209
52536
  prompt: buildDynamicOrchestratorPrompt(ctx),
52210
52537
  thinking: { type: "enabled", budgetTokens: 32000 },
@@ -52214,7 +52541,7 @@ function createOrchestratorSisyphusAgent(ctx) {
52214
52541
  var orchestratorSisyphusAgent = createOrchestratorSisyphusAgent();
52215
52542
 
52216
52543
  // src/agents/momus.ts
52217
- var DEFAULT_MODEL8 = "openai/gpt-5.2";
52544
+ var DEFAULT_MODEL10 = "openai/gpt-5.2";
52218
52545
  var MOMUS_SYSTEM_PROMPT = `You are a work plan review expert. You review the provided work plan (.sisyphus/plans/{name}.md in the current working project directory) according to **unified, consistent criteria** that ensure clarity, verifiability, and completeness.
52219
52546
 
52220
52547
  **CRITICAL FIRST RULE**:
@@ -52544,7 +52871,7 @@ Use structured format, **in the same language as the work plan**.
52544
52871
 
52545
52872
  **Strike the right balance**: Prevent critical failures while empowering developer autonomy.
52546
52873
  `;
52547
- function createMomusAgent(model = DEFAULT_MODEL8) {
52874
+ function createMomusAgent(model = DEFAULT_MODEL10) {
52548
52875
  const restrictions = createAgentToolRestrictions([
52549
52876
  "write",
52550
52877
  "edit",
@@ -52575,8 +52902,8 @@ var agentSources = {
52575
52902
  "frontend-ui-ux-engineer": createFrontendUiUxEngineerAgent,
52576
52903
  "document-writer": createDocumentWriterAgent,
52577
52904
  "multimodal-looker": createMultimodalLookerAgent,
52578
- "Metis (Plan Consultant)": metisAgent,
52579
- "Momus (Plan Reviewer)": momusAgent,
52905
+ "Metis (Plan Consultant)": createMetisAgent,
52906
+ "Momus (Plan Reviewer)": createMomusAgent,
52580
52907
  "orchestrator-sisyphus": orchestratorSisyphusAgent
52581
52908
  };
52582
52909
  var agentMetadata = {
@@ -52689,7 +53016,11 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
52689
53016
  }
52690
53017
  if (!disabledAgents.includes("orchestrator-sisyphus")) {
52691
53018
  const orchestratorOverride = agentOverrides["orchestrator-sisyphus"];
52692
- let orchestratorConfig = createOrchestratorSisyphusAgent({ availableAgents });
53019
+ const orchestratorModel = orchestratorOverride?.model;
53020
+ let orchestratorConfig = createOrchestratorSisyphusAgent({
53021
+ model: orchestratorModel,
53022
+ availableAgents
53023
+ });
52693
53024
  if (orchestratorOverride) {
52694
53025
  orchestratorConfig = mergeAgentConfig(orchestratorConfig, orchestratorOverride);
52695
53026
  }