oh-my-opencode 3.1.2 → 3.1.4

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
@@ -4629,9 +4629,16 @@ function loadOpencodePlugins(directory) {
4629
4629
  function matchesNotificationPlugin(entry) {
4630
4630
  const normalized = entry.toLowerCase();
4631
4631
  for (const known of KNOWN_NOTIFICATION_PLUGINS) {
4632
- if (normalized === known || normalized.startsWith(`${known}@`) || normalized.includes(`/${known}`) || normalized.endsWith(`/${known}`)) {
4632
+ if (normalized === known)
4633
+ return known;
4634
+ if (normalized.startsWith(`${known}@`))
4635
+ return known;
4636
+ if (normalized === `@mohak34/${known}` || normalized.startsWith(`@mohak34/${known}@`))
4637
+ return known;
4638
+ if (normalized === `npm:${known}` || normalized.startsWith(`npm:${known}@`))
4639
+ return known;
4640
+ if (normalized.startsWith("file://") && (normalized.endsWith(`/${known}`) || normalized.endsWith(`\\${known}`)))
4633
4641
  return known;
4634
- }
4635
4642
  }
4636
4643
  return null;
4637
4644
  }
@@ -5323,25 +5330,24 @@ function resolveModelWithFallback(input) {
5323
5330
  if (availableModels.size === 0) {
5324
5331
  const connectedProviders = readConnectedProvidersCache();
5325
5332
  const connectedSet = connectedProviders ? new Set(connectedProviders) : null;
5326
- for (const entry of fallbackChain) {
5327
- for (const provider of entry.providers) {
5328
- if (connectedSet === null || connectedSet.has(provider)) {
5329
- const model2 = `${provider}/${entry.model}`;
5330
- log("Model resolved via fallback chain (no model cache, using connected provider)", {
5331
- provider,
5332
- model: entry.model,
5333
- variant: entry.variant,
5334
- hasConnectedCache: connectedSet !== null
5335
- });
5336
- return { model: model2, source: "provider-fallback", variant: entry.variant };
5333
+ if (connectedSet === null) {
5334
+ log("No cache available, skipping fallback chain to use system default");
5335
+ } else {
5336
+ for (const entry of fallbackChain) {
5337
+ for (const provider of entry.providers) {
5338
+ if (connectedSet.has(provider)) {
5339
+ const model = `${provider}/${entry.model}`;
5340
+ log("Model resolved via fallback chain (no model cache, using connected provider)", {
5341
+ provider,
5342
+ model: entry.model,
5343
+ variant: entry.variant
5344
+ });
5345
+ return { model, source: "provider-fallback", variant: entry.variant };
5346
+ }
5337
5347
  }
5338
5348
  }
5349
+ log("No matching provider in connected cache, falling through to system default");
5339
5350
  }
5340
- const firstEntry = fallbackChain[0];
5341
- const firstProvider = firstEntry.providers[0];
5342
- const model = `${firstProvider}/${firstEntry.model}`;
5343
- log("Model resolved via fallback chain (no cache at all, using first entry)", { provider: firstProvider, model: firstEntry.model, variant: firstEntry.variant });
5344
- return { model, source: "provider-fallback", variant: firstEntry.variant };
5345
5351
  }
5346
5352
  for (const entry of fallbackChain) {
5347
5353
  for (const provider of entry.providers) {
@@ -7267,6 +7273,18 @@ function getCachedVersion() {
7267
7273
  } catch (err) {
7268
7274
  log("[auto-update-checker] Failed to resolve version from current directory:", err);
7269
7275
  }
7276
+ try {
7277
+ const execDir = path5.dirname(fs7.realpathSync(process.execPath));
7278
+ const pkgPath = findPackageJsonUp(execDir);
7279
+ if (pkgPath) {
7280
+ const content = fs7.readFileSync(pkgPath, "utf-8");
7281
+ const pkg = JSON.parse(content);
7282
+ if (pkg.version)
7283
+ return pkg.version;
7284
+ }
7285
+ } catch (err) {
7286
+ log("[auto-update-checker] Failed to resolve version from execPath:", err);
7287
+ }
7270
7288
  return null;
7271
7289
  }
7272
7290
  function updatePinnedVersion(configPath, oldEntry, newVersion) {
@@ -14342,7 +14360,7 @@ var require_formats = __commonJS((exports) => {
14342
14360
  }
14343
14361
  var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
14344
14362
  function getTime(strictTimeZone) {
14345
- return function time(str2) {
14363
+ return function time3(str2) {
14346
14364
  const matches = TIME.exec(str2);
14347
14365
  if (!matches)
14348
14366
  return false;
@@ -17925,6 +17943,15 @@ When summarizing this session, you MUST include the following sections in your s
17925
17943
  - User's explicit restrictions or preferences
17926
17944
  - Anti-patterns identified during the session
17927
17945
 
17946
+ ## 7. Agent Verification State (Critical for Reviewers)
17947
+ - **Current Agent**: What agent is running (momus, oracle, etc.)
17948
+ - **Verification Progress**: Files already verified/validated
17949
+ - **Pending Verifications**: Files still needing verification
17950
+ - **Previous Rejections**: If reviewer agent, what was rejected and why
17951
+ - **Acceptance Status**: Current state of review process
17952
+
17953
+ This section is CRITICAL for reviewer agents (momus, oracle) to maintain continuity.
17954
+
17928
17955
  This context is critical for maintaining continuity after compaction.
17929
17956
  `;
17930
17957
  function createCompactionContextInjector() {
@@ -20142,28 +20169,52 @@ delegate_task(agent="oracle", prompt="Review my approach: [describe plan]")
20142
20169
  YOU MUST LEVERAGE ALL AVAILABLE AGENTS / **CATEGORY + SKILLS** TO THEIR FULLEST POTENTIAL.
20143
20170
  TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
20144
20171
 
20145
- ## MANDATORY: PLAN AGENT INVOCATION (NON-NEGOTIABLE)
20172
+ ## MANDATORY: PROMETHEUS AGENT INVOCATION (NON-NEGOTIABLE)
20146
20173
 
20147
- **YOU MUST ALWAYS INVOKE THE PLAN AGENT FOR ANY NON-TRIVIAL TASK.**
20174
+ **YOU MUST ALWAYS INVOKE PROMETHEUS (THE PLANNER) FOR ANY NON-TRIVIAL TASK.**
20148
20175
 
20149
20176
  | Condition | Action |
20150
20177
  |-----------|--------|
20151
- | Task has 2+ steps | MUST call Plan agent |
20152
- | Task scope unclear | MUST call Plan agent |
20153
- | Implementation required | MUST call Plan agent |
20154
- | Architecture decision needed | MUST call Plan agent |
20178
+ | Task has 2+ steps | MUST call Prometheus |
20179
+ | Task scope unclear | MUST call Prometheus |
20180
+ | Implementation required | MUST call Prometheus |
20181
+ | Architecture decision needed | MUST call Prometheus |
20155
20182
 
20156
20183
  \`\`\`
20157
- delegate_task(subagent_type="plan", prompt="<gathered context + user request>")
20184
+ delegate_task(subagent_type="prometheus", prompt="<gathered context + user request>")
20158
20185
  \`\`\`
20159
20186
 
20160
- **WHY THIS IS MANDATORY:**
20161
- - Plan agent analyzes dependencies and parallel execution opportunities
20162
- - Plan agent recommends CATEGORY + SKILLS for each task
20163
- - Plan agent ensures nothing is missed
20187
+ **WHY PROMETHEUS IS MANDATORY:**
20188
+ - Prometheus analyzes dependencies and parallel execution opportunities
20189
+ - Prometheus recommends CATEGORY + SKILLS for each task (in TL;DR + per-task)
20190
+ - Prometheus ensures nothing is missed with structured work plans
20164
20191
  - YOU are an orchestrator, NOT an implementer
20165
20192
 
20166
- **FAILURE TO CALL PLAN AGENT = INCOMPLETE WORK.**
20193
+ ### SESSION CONTINUITY WITH PROMETHEUS (CRITICAL)
20194
+
20195
+ **Prometheus returns a session_id. USE IT for follow-up interactions.**
20196
+
20197
+ | Scenario | Action |
20198
+ |----------|--------|
20199
+ | Prometheus asks clarifying questions | \`delegate_task(session_id="{returned_session_id}", prompt="<your answer>")\` |
20200
+ | Need to refine the plan | \`delegate_task(session_id="{returned_session_id}", prompt="Please adjust: <feedback>")\` |
20201
+ | Plan needs more detail | \`delegate_task(session_id="{returned_session_id}", prompt="Add more detail to Task N")\` |
20202
+
20203
+ **WHY SESSION_ID IS CRITICAL:**
20204
+ - Prometheus retains FULL conversation context
20205
+ - No repeated exploration or context gathering
20206
+ - Saves 70%+ tokens on follow-ups
20207
+ - Maintains interview continuity until plan is finalized
20208
+
20209
+ \`\`\`
20210
+ // WRONG: Starting fresh loses all context
20211
+ delegate_task(subagent_type="prometheus", prompt="Here's more info...")
20212
+
20213
+ // CORRECT: Resume preserves everything
20214
+ delegate_task(session_id="ses_abc123", prompt="Here's my answer to your question: ...")
20215
+ \`\`\`
20216
+
20217
+ **FAILURE TO CALL PROMETHEUS = INCOMPLETE WORK.**
20167
20218
 
20168
20219
  ---
20169
20220
 
@@ -20235,17 +20286,25 @@ delegate_task(..., run_in_background=true) // task_id_3
20235
20286
  delegate_task(subagent_type="librarian", run_in_background=true, prompt="...")
20236
20287
  \`\`\`
20237
20288
 
20238
- 2. **INVOKE PLAN AGENT** (MANDATORY for non-trivial tasks):
20289
+ 2. **INVOKE PROMETHEUS** (MANDATORY for non-trivial tasks):
20290
+ \`\`\`
20291
+ result = delegate_task(subagent_type="prometheus", prompt="<context + request>")
20292
+ // STORE the session_id for follow-ups!
20293
+ prometheus_session_id = result.session_id
20294
+ \`\`\`
20295
+
20296
+ 3. **ITERATE WITH PROMETHEUS** (if clarification needed):
20239
20297
  \`\`\`
20240
- delegate_task(subagent_type="plan", prompt="<context + request>")
20298
+ // Use session_id to continue the conversation
20299
+ delegate_task(session_id=prometheus_session_id, prompt="<answer to Prometheus's question>")
20241
20300
  \`\`\`
20242
20301
 
20243
- 3. **EXECUTE VIA DELEGATION** (category + skills):
20302
+ 4. **EXECUTE VIA DELEGATION** (category + skills from Prometheus's plan):
20244
20303
  \`\`\`
20245
20304
  delegate_task(category="...", load_skills=[...], prompt="<task from plan>")
20246
20305
  \`\`\`
20247
20306
 
20248
- 4. **VERIFY** against original requirements
20307
+ 5. **VERIFY** against original requirements
20249
20308
 
20250
20309
  ## VERIFICATION GUARANTEE (NON-NEGOTIABLE)
20251
20310
 
@@ -20319,8 +20378,9 @@ Write these criteria explicitly. Share with user if scope is non-trivial.
20319
20378
  THE USER ASKED FOR X. DELIVER EXACTLY X. NOT A SUBSET. NOT A DEMO. NOT A STARTING POINT.
20320
20379
 
20321
20380
  1. EXPLORES + LIBRARIANS (background)
20322
- 2. GATHER -> delegate_task(subagent_type="plan", prompt="<context + request>")
20323
- 3. WORK BY DELEGATING TO CATEGORY + SKILLS AGENTS
20381
+ 2. GATHER -> delegate_task(subagent_type="prometheus", prompt="<context + request>")
20382
+ 3. ITERATE WITH PROMETHEUS (session_id resume) UNTIL PLAN IS FINALIZED
20383
+ 4. WORK BY DELEGATING TO CATEGORY + SKILLS AGENTS (following Prometheus's plan)
20324
20384
 
20325
20385
  NOW.
20326
20386
 
@@ -20394,6 +20454,9 @@ function createKeywordDetectorHook(ctx, collector) {
20394
20454
  }
20395
20455
  const currentAgent = getSessionAgent(input.sessionID) ?? input.agent;
20396
20456
  let detectedKeywords = detectKeywordsWithType(removeCodeBlocks2(promptText), currentAgent);
20457
+ if (isPlannerAgent(currentAgent)) {
20458
+ detectedKeywords = detectedKeywords.filter((k) => k.type !== "ultrawork");
20459
+ }
20397
20460
  if (detectedKeywords.length === 0) {
20398
20461
  return;
20399
20462
  }
@@ -25324,6 +25387,26 @@ function createQuestionLabelTruncatorHook() {
25324
25387
  }
25325
25388
  };
25326
25389
  }
25390
+ // src/hooks/subagent-question-blocker/index.ts
25391
+ init_shared();
25392
+ function createSubagentQuestionBlockerHook() {
25393
+ return {
25394
+ "tool.execute.before": async (input) => {
25395
+ const toolName = input.tool?.toLowerCase();
25396
+ if (toolName !== "question" && toolName !== "askuserquestion") {
25397
+ return;
25398
+ }
25399
+ if (!subagentSessions.has(input.sessionID)) {
25400
+ return;
25401
+ }
25402
+ log("[subagent-question-blocker] Blocking question tool call from subagent session", {
25403
+ sessionID: input.sessionID,
25404
+ tool: input.tool
25405
+ });
25406
+ throw new Error("Question tool is disabled for subagent sessions. " + "Subagents should complete their work autonomously without asking questions to users. " + "If you need clarification, return to the parent agent with your findings and uncertainties.");
25407
+ }
25408
+ };
25409
+ }
25327
25410
  // src/features/context-injector/collector.ts
25328
25411
  var PRIORITY_ORDER = {
25329
25412
  critical: 0,
@@ -43809,10 +43892,32 @@ If the requested information is not found, clearly state what is missing.`;
43809
43892
  }
43810
43893
  // src/tools/delegate-task/tools.ts
43811
43894
  init_constants4();
43812
- init_hook_message_injector();
43813
43895
  import { existsSync as existsSync46, readdirSync as readdirSync15 } from "fs";
43814
43896
  import { join as join55 } from "path";
43815
43897
 
43898
+ // src/tools/delegate-task/timing.ts
43899
+ var POLL_INTERVAL_MS = 500;
43900
+ var MIN_STABILITY_TIME_MS = 1e4;
43901
+ var STABILITY_POLLS_REQUIRED = 3;
43902
+ var WAIT_FOR_SESSION_INTERVAL_MS = 100;
43903
+ var WAIT_FOR_SESSION_TIMEOUT_MS = 30000;
43904
+ var MAX_POLL_TIME_MS = 10 * 60 * 1000;
43905
+ var SESSION_CONTINUATION_STABILITY_MS = 5000;
43906
+ function getTimingConfig() {
43907
+ return {
43908
+ POLL_INTERVAL_MS,
43909
+ MIN_STABILITY_TIME_MS,
43910
+ STABILITY_POLLS_REQUIRED,
43911
+ WAIT_FOR_SESSION_INTERVAL_MS,
43912
+ WAIT_FOR_SESSION_TIMEOUT_MS,
43913
+ MAX_POLL_TIME_MS,
43914
+ SESSION_CONTINUATION_STABILITY_MS
43915
+ };
43916
+ }
43917
+
43918
+ // src/tools/delegate-task/tools.ts
43919
+ init_hook_message_injector();
43920
+
43816
43921
  // src/features/task-toast-manager/manager.ts
43817
43922
  class TaskToastManager {
43818
43923
  tasks = new Map;
@@ -44277,23 +44382,24 @@ Use \`background_output\` with task_id="${task.id}" to check progress.`;
44277
44382
 
44278
44383
  Session ID: ${args.session_id}`;
44279
44384
  }
44280
- const POLL_INTERVAL_MS = 500;
44281
- const MIN_STABILITY_TIME_MS = 5000;
44282
- const STABILITY_POLLS_REQUIRED = 3;
44385
+ const timing = getTimingConfig();
44386
+ const POLL_INTERVAL_MS2 = timing.POLL_INTERVAL_MS;
44387
+ const MIN_STABILITY_TIME_MS2 = timing.SESSION_CONTINUATION_STABILITY_MS;
44388
+ const STABILITY_POLLS_REQUIRED2 = timing.STABILITY_POLLS_REQUIRED;
44283
44389
  const pollStart = Date.now();
44284
44390
  let lastMsgCount = 0;
44285
44391
  let stablePolls = 0;
44286
44392
  while (Date.now() - pollStart < 60000) {
44287
- await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS));
44393
+ await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS2));
44288
44394
  const elapsed = Date.now() - pollStart;
44289
- if (elapsed < MIN_STABILITY_TIME_MS)
44395
+ if (elapsed < MIN_STABILITY_TIME_MS2)
44290
44396
  continue;
44291
44397
  const messagesCheck = await client2.session.messages({ path: { id: args.session_id } });
44292
44398
  const msgs = messagesCheck.data ?? messagesCheck;
44293
44399
  const currentMsgCount = msgs.length;
44294
44400
  if (currentMsgCount > 0 && currentMsgCount === lastMsgCount) {
44295
44401
  stablePolls++;
44296
- if (stablePolls >= STABILITY_POLLS_REQUIRED)
44402
+ if (stablePolls >= STABILITY_POLLS_REQUIRED2)
44297
44403
  break;
44298
44404
  } else {
44299
44405
  stablePolls = 0;
@@ -44412,6 +44518,18 @@ To continue this session: session_id="${args.session_id}"`;
44412
44518
  categoryModel = parsedModel ?? undefined;
44413
44519
  }
44414
44520
  categoryPromptAppend = resolved.promptAppend || undefined;
44521
+ if (!categoryModel && !actualModel) {
44522
+ const categoryNames2 = Object.keys({ ...DEFAULT_CATEGORIES, ...userCategories });
44523
+ return `Model not configured for category "${args.category}".
44524
+
44525
+ Configure in one of:
44526
+ 1. OpenCode: Set "model" in opencode.json
44527
+ 2. Oh-My-OpenCode: Set category model in oh-my-opencode.json
44528
+ 3. Provider: Connect a provider with available models
44529
+
44530
+ Current category: ${args.category}
44531
+ Available categories: ${categoryNames2.join(", ")}`;
44532
+ }
44415
44533
  const isUnstableAgent = resolved.config.is_unstable_agent === true || (actualModel?.toLowerCase().includes("gemini") ?? false);
44416
44534
  const isRunInBackgroundExplicitlyFalse = args.run_in_background === false || args.run_in_background === "false";
44417
44535
  log("[delegate_task] unstable agent detection", {
@@ -44438,16 +44556,16 @@ To continue this session: session_id="${args.session_id}"`;
44438
44556
  skills: args.load_skills.length > 0 ? args.load_skills : undefined,
44439
44557
  skillContent: systemContent2
44440
44558
  });
44441
- const WAIT_FOR_SESSION_INTERVAL_MS = 100;
44442
- const WAIT_FOR_SESSION_TIMEOUT_MS = 30000;
44559
+ const WAIT_FOR_SESSION_INTERVAL_MS2 = 100;
44560
+ const WAIT_FOR_SESSION_TIMEOUT_MS2 = 30000;
44443
44561
  const waitStart = Date.now();
44444
- while (!task.sessionID && Date.now() - waitStart < WAIT_FOR_SESSION_TIMEOUT_MS) {
44562
+ while (!task.sessionID && Date.now() - waitStart < WAIT_FOR_SESSION_TIMEOUT_MS2) {
44445
44563
  if (ctx.abort?.aborted) {
44446
44564
  return `Task aborted while waiting for session to start.
44447
44565
 
44448
44566
  Task ID: ${task.id}`;
44449
44567
  }
44450
- await new Promise((resolve10) => setTimeout(resolve10, WAIT_FOR_SESSION_INTERVAL_MS));
44568
+ await new Promise((resolve10) => setTimeout(resolve10, WAIT_FOR_SESSION_INTERVAL_MS2));
44451
44569
  }
44452
44570
  const sessionID = task.sessionID;
44453
44571
  if (!sessionID) {
@@ -44472,20 +44590,21 @@ Task ID: ${task.id}`;
44472
44590
  }
44473
44591
  });
44474
44592
  const startTime = new Date;
44475
- const POLL_INTERVAL_MS = 500;
44476
- const MAX_POLL_TIME_MS = 10 * 60 * 1000;
44477
- const MIN_STABILITY_TIME_MS = 1e4;
44478
- const STABILITY_POLLS_REQUIRED = 3;
44593
+ const timingCfg = getTimingConfig();
44594
+ const POLL_INTERVAL_MS2 = timingCfg.POLL_INTERVAL_MS;
44595
+ const MAX_POLL_TIME_MS2 = timingCfg.MAX_POLL_TIME_MS;
44596
+ const MIN_STABILITY_TIME_MS2 = timingCfg.MIN_STABILITY_TIME_MS;
44597
+ const STABILITY_POLLS_REQUIRED2 = timingCfg.STABILITY_POLLS_REQUIRED;
44479
44598
  const pollStart = Date.now();
44480
44599
  let lastMsgCount = 0;
44481
44600
  let stablePolls = 0;
44482
- while (Date.now() - pollStart < MAX_POLL_TIME_MS) {
44601
+ while (Date.now() - pollStart < MAX_POLL_TIME_MS2) {
44483
44602
  if (ctx.abort?.aborted) {
44484
44603
  return `Task aborted (was running in background mode).
44485
44604
 
44486
44605
  Session ID: ${sessionID}`;
44487
44606
  }
44488
- await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS));
44607
+ await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS2));
44489
44608
  const statusResult = await client2.session.status();
44490
44609
  const allStatuses = statusResult.data ?? {};
44491
44610
  const sessionStatus = allStatuses[sessionID];
@@ -44494,14 +44613,14 @@ Session ID: ${sessionID}`;
44494
44613
  lastMsgCount = 0;
44495
44614
  continue;
44496
44615
  }
44497
- if (Date.now() - pollStart < MIN_STABILITY_TIME_MS)
44616
+ if (Date.now() - pollStart < MIN_STABILITY_TIME_MS2)
44498
44617
  continue;
44499
44618
  const messagesCheck = await client2.session.messages({ path: { id: sessionID } });
44500
44619
  const msgs = messagesCheck.data ?? messagesCheck;
44501
44620
  const currentMsgCount = msgs.length;
44502
44621
  if (currentMsgCount === lastMsgCount) {
44503
44622
  stablePolls++;
44504
- if (stablePolls >= STABILITY_POLLS_REQUIRED)
44623
+ if (stablePolls >= STABILITY_POLLS_REQUIRED2)
44505
44624
  break;
44506
44625
  } else {
44507
44626
  stablePolls = 0;
@@ -44636,7 +44755,10 @@ To continue this session: session_id="${task.sessionID}"`;
44636
44755
  const createResult = await client2.session.create({
44637
44756
  body: {
44638
44757
  parentID: ctx.sessionID,
44639
- title: `Task: ${args.description}`
44758
+ title: `Task: ${args.description}`,
44759
+ permission: [
44760
+ { permission: "question", action: "deny", pattern: "*" }
44761
+ ]
44640
44762
  },
44641
44763
  query: {
44642
44764
  directory: parentDirectory
@@ -44699,7 +44821,8 @@ To continue this session: session_id="${task.sessionID}"`;
44699
44821
  question: false
44700
44822
  },
44701
44823
  parts: [{ type: "text", text: args.prompt }],
44702
- ...categoryModel ? { model: categoryModel } : {}
44824
+ ...categoryModel ? { model: { providerID: categoryModel.providerID, modelID: categoryModel.modelID } } : {},
44825
+ ...categoryModel?.variant ? { variant: categoryModel.variant } : {}
44703
44826
  }
44704
44827
  });
44705
44828
  } catch (promptError) {
@@ -44724,16 +44847,17 @@ To continue this session: session_id="${task.sessionID}"`;
44724
44847
  category: args.category
44725
44848
  });
44726
44849
  }
44727
- const POLL_INTERVAL_MS = 500;
44728
- const MAX_POLL_TIME_MS = 10 * 60 * 1000;
44729
- const MIN_STABILITY_TIME_MS = 1e4;
44730
- const STABILITY_POLLS_REQUIRED = 3;
44850
+ const syncTiming = getTimingConfig();
44851
+ const POLL_INTERVAL_MS2 = syncTiming.POLL_INTERVAL_MS;
44852
+ const MAX_POLL_TIME_MS2 = syncTiming.MAX_POLL_TIME_MS;
44853
+ const MIN_STABILITY_TIME_MS2 = syncTiming.MIN_STABILITY_TIME_MS;
44854
+ const STABILITY_POLLS_REQUIRED2 = syncTiming.STABILITY_POLLS_REQUIRED;
44731
44855
  const pollStart = Date.now();
44732
44856
  let lastMsgCount = 0;
44733
44857
  let stablePolls = 0;
44734
44858
  let pollCount = 0;
44735
44859
  log("[delegate_task] Starting poll loop", { sessionID, agentToUse });
44736
- while (Date.now() - pollStart < MAX_POLL_TIME_MS) {
44860
+ while (Date.now() - pollStart < MAX_POLL_TIME_MS2) {
44737
44861
  if (ctx.abort?.aborted) {
44738
44862
  log("[delegate_task] Aborted by user", { sessionID });
44739
44863
  if (toastManager && taskId)
@@ -44742,7 +44866,7 @@ To continue this session: session_id="${task.sessionID}"`;
44742
44866
 
44743
44867
  Session ID: ${sessionID}`;
44744
44868
  }
44745
- await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS));
44869
+ await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS2));
44746
44870
  pollCount++;
44747
44871
  const statusResult = await client2.session.status();
44748
44872
  const allStatuses = statusResult.data ?? {};
@@ -44763,7 +44887,7 @@ Session ID: ${sessionID}`;
44763
44887
  continue;
44764
44888
  }
44765
44889
  const elapsed = Date.now() - pollStart;
44766
- if (elapsed < MIN_STABILITY_TIME_MS) {
44890
+ if (elapsed < MIN_STABILITY_TIME_MS2) {
44767
44891
  continue;
44768
44892
  }
44769
44893
  const messagesCheck = await client2.session.messages({ path: { id: sessionID } });
@@ -44771,7 +44895,7 @@ Session ID: ${sessionID}`;
44771
44895
  const currentMsgCount = msgs.length;
44772
44896
  if (currentMsgCount === lastMsgCount) {
44773
44897
  stablePolls++;
44774
- if (stablePolls >= STABILITY_POLLS_REQUIRED) {
44898
+ if (stablePolls >= STABILITY_POLLS_REQUIRED2) {
44775
44899
  log("[delegate_task] Poll complete - messages stable", { sessionID, pollCount, currentMsgCount });
44776
44900
  break;
44777
44901
  }
@@ -44780,7 +44904,7 @@ Session ID: ${sessionID}`;
44780
44904
  lastMsgCount = currentMsgCount;
44781
44905
  }
44782
44906
  }
44783
- if (Date.now() - pollStart >= MAX_POLL_TIME_MS) {
44907
+ if (Date.now() - pollStart >= MAX_POLL_TIME_MS2) {
44784
44908
  log("[delegate_task] Poll timeout reached", { sessionID, pollCount, lastMsgCount, stablePolls });
44785
44909
  }
44786
44910
  const messagesResult = await client2.session.messages({
@@ -44966,7 +45090,7 @@ init_hook_message_injector();
44966
45090
  import { existsSync as existsSync47, readdirSync as readdirSync16 } from "fs";
44967
45091
  import { join as join56 } from "path";
44968
45092
  var TASK_TTL_MS = 30 * 60 * 1000;
44969
- var MIN_STABILITY_TIME_MS = 10 * 1000;
45093
+ var MIN_STABILITY_TIME_MS2 = 10 * 1000;
44970
45094
  var DEFAULT_STALE_TIMEOUT_MS = 180000;
44971
45095
  var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
44972
45096
 
@@ -45092,7 +45216,10 @@ class BackgroundManager {
45092
45216
  const createResult = await this.client.session.create({
45093
45217
  body: {
45094
45218
  parentID: input.parentSessionID,
45095
- title: `Background: ${input.description}`
45219
+ title: `Background: ${input.description}`,
45220
+ permission: [
45221
+ { permission: "question", action: "deny", pattern: "*" }
45222
+ ]
45096
45223
  },
45097
45224
  query: {
45098
45225
  directory: parentDirectory
@@ -45150,11 +45277,14 @@ class BackgroundManager {
45150
45277
  hasSkillContent: !!input.skillContent,
45151
45278
  promptLength: input.prompt.length
45152
45279
  });
45280
+ const launchModel = input.model ? { providerID: input.model.providerID, modelID: input.model.modelID } : undefined;
45281
+ const launchVariant = input.model?.variant;
45153
45282
  this.client.session.prompt({
45154
45283
  path: { id: sessionID },
45155
45284
  body: {
45156
45285
  agent: input.agent,
45157
- ...input.model ? { model: input.model } : {},
45286
+ ...launchModel ? { model: launchModel } : {},
45287
+ ...launchVariant ? { variant: launchVariant } : {},
45158
45288
  system: input.skillContent,
45159
45289
  tools: {
45160
45290
  ...getAgentToolRestrictions(input.agent),
@@ -45343,11 +45473,14 @@ class BackgroundManager {
45343
45473
  model: existingTask.model,
45344
45474
  promptLength: input.prompt.length
45345
45475
  });
45476
+ const resumeModel = existingTask.model ? { providerID: existingTask.model.providerID, modelID: existingTask.model.modelID } : undefined;
45477
+ const resumeVariant = existingTask.model?.variant;
45346
45478
  this.client.session.prompt({
45347
45479
  path: { id: existingTask.sessionID },
45348
45480
  body: {
45349
45481
  agent: existingTask.agent,
45350
- ...existingTask.model ? { model: existingTask.model } : {},
45482
+ ...resumeModel ? { model: resumeModel } : {},
45483
+ ...resumeVariant ? { variant: resumeVariant } : {},
45351
45484
  tools: {
45352
45485
  ...getAgentToolRestrictions(existingTask.agent),
45353
45486
  task: false,
@@ -45897,7 +46030,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
45897
46030
  if (!startedAt)
45898
46031
  continue;
45899
46032
  const elapsedMs = Date.now() - startedAt.getTime();
45900
- if (elapsedMs >= MIN_STABILITY_TIME_MS) {
46033
+ if (elapsedMs >= MIN_STABILITY_TIME_MS2) {
45901
46034
  if (task.lastMsgCount === currentMsgCount) {
45902
46035
  task.stablePolls = (task.stablePolls ?? 0) + 1;
45903
46036
  if (task.stablePolls >= 3) {
@@ -51136,7 +51269,7 @@ var GitMasterConfigSchema = exports_external.object({
51136
51269
  commit_footer: exports_external.boolean().default(true),
51137
51270
  include_co_authored_by: exports_external.boolean().default(true)
51138
51271
  });
51139
- var BrowserAutomationProviderSchema = exports_external.enum(["playwright", "agent-browser"]);
51272
+ var BrowserAutomationProviderSchema = exports_external.enum(["playwright", "agent-browser", "dev-browser"]);
51140
51273
  var BrowserAutomationConfigSchema = exports_external.object({
51141
51274
  provider: BrowserAutomationProviderSchema.default("playwright")
51142
51275
  });
@@ -51154,6 +51287,20 @@ var TmuxConfigSchema = exports_external.object({
51154
51287
  main_pane_min_width: exports_external.number().min(40).default(120),
51155
51288
  agent_pane_min_width: exports_external.number().min(20).default(40)
51156
51289
  });
51290
+ var SisyphusTasksConfigSchema = exports_external.object({
51291
+ enabled: exports_external.boolean().default(false),
51292
+ storage_path: exports_external.string().default(".sisyphus/tasks"),
51293
+ claude_code_compat: exports_external.boolean().default(false)
51294
+ });
51295
+ var SisyphusSwarmConfigSchema = exports_external.object({
51296
+ enabled: exports_external.boolean().default(false),
51297
+ storage_path: exports_external.string().default(".sisyphus/teams"),
51298
+ ui_mode: exports_external.enum(["toast", "tmux", "both"]).default("toast")
51299
+ });
51300
+ var SisyphusConfigSchema = exports_external.object({
51301
+ tasks: SisyphusTasksConfigSchema.optional(),
51302
+ swarm: SisyphusSwarmConfigSchema.optional()
51303
+ });
51157
51304
  var OhMyOpenCodeConfigSchema = exports_external.object({
51158
51305
  $schema: exports_external.string().optional(),
51159
51306
  disabled_mcps: exports_external.array(AnyMcpNameSchema).optional(),
@@ -51174,7 +51321,8 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
51174
51321
  notification: NotificationConfigSchema.optional(),
51175
51322
  git_master: GitMasterConfigSchema.optional(),
51176
51323
  browser_automation_engine: BrowserAutomationConfigSchema.optional(),
51177
- tmux: TmuxConfigSchema.optional()
51324
+ tmux: TmuxConfigSchema.optional(),
51325
+ sisyphus: SisyphusConfigSchema.optional()
51178
51326
  });
51179
51327
  // src/plugin-config.ts
51180
51328
  init_shared();
@@ -54594,6 +54742,7 @@ var context7 = {
54594
54742
  type: "remote",
54595
54743
  url: "https://mcp.context7.com/mcp",
54596
54744
  enabled: true,
54745
+ headers: process.env.CONTEXT7_API_KEY ? { Authorization: `Bearer ${process.env.CONTEXT7_API_KEY}` } : undefined,
54597
54746
  oauth: false
54598
54747
  };
54599
54748
 
@@ -55475,6 +55624,20 @@ Generate plan to: \`.sisyphus/plans/{name}.md\`
55475
55624
  \`\`\`markdown
55476
55625
  # {Plan Title}
55477
55626
 
55627
+ ## TL;DR
55628
+
55629
+ > **Quick Summary**: [1-2 sentences capturing the core objective and approach]
55630
+ >
55631
+ > **Deliverables**: [Bullet list of concrete outputs]
55632
+ > - [Output 1]
55633
+ > - [Output 2]
55634
+ >
55635
+ > **Estimated Effort**: [Quick | Short | Medium | Large | XL]
55636
+ > **Parallel Execution**: [YES - N waves | NO - sequential]
55637
+ > **Critical Path**: [Task X \u2192 Task Y \u2192 Task Z]
55638
+
55639
+ ---
55640
+
55478
55641
  ## Context
55479
55642
 
55480
55643
  ### Original Request
@@ -55575,29 +55738,55 @@ Each TODO includes detailed verification procedures:
55575
55738
 
55576
55739
  ---
55577
55740
 
55578
- ## Task Flow
55741
+ ## Execution Strategy
55742
+
55743
+ ### Parallel Execution Waves
55744
+
55745
+ > Maximize throughput by grouping independent tasks into parallel waves.
55746
+ > Each wave completes before the next begins.
55579
55747
 
55580
55748
  \`\`\`
55581
- Task 1 \u2192 Task 2 \u2192 Task 3
55582
- \u2198 Task 4 (parallel)
55749
+ Wave 1 (Start Immediately):
55750
+ \u251C\u2500\u2500 Task 1: [no dependencies]
55751
+ \u2514\u2500\u2500 Task 5: [no dependencies]
55752
+
55753
+ Wave 2 (After Wave 1):
55754
+ \u251C\u2500\u2500 Task 2: [depends: 1]
55755
+ \u251C\u2500\u2500 Task 3: [depends: 1]
55756
+ \u2514\u2500\u2500 Task 6: [depends: 5]
55757
+
55758
+ Wave 3 (After Wave 2):
55759
+ \u2514\u2500\u2500 Task 4: [depends: 2, 3]
55760
+
55761
+ Critical Path: Task 1 \u2192 Task 2 \u2192 Task 4
55762
+ Parallel Speedup: ~40% faster than sequential
55583
55763
  \`\`\`
55584
55764
 
55585
- ## Parallelization
55765
+ ### Dependency Matrix
55586
55766
 
55587
- | Group | Tasks | Reason |
55588
- |-------|-------|--------|
55589
- | A | 2, 3 | Independent files |
55767
+ | Task | Depends On | Blocks | Can Parallelize With |
55768
+ |------|------------|--------|---------------------|
55769
+ | 1 | None | 2, 3 | 5 |
55770
+ | 2 | 1 | 4 | 3, 6 |
55771
+ | 3 | 1 | 4 | 2, 6 |
55772
+ | 4 | 2, 3 | None | None (final) |
55773
+ | 5 | None | 6 | 1 |
55774
+ | 6 | 5 | None | 2, 3 |
55590
55775
 
55591
- | Task | Depends On | Reason |
55592
- |------|------------|--------|
55593
- | 4 | 1 | Requires output from 1 |
55776
+ ### Agent Dispatch Summary
55777
+
55778
+ | Wave | Tasks | Recommended Agents |
55779
+ |------|-------|-------------------|
55780
+ | 1 | 1, 5 | delegate_task(category="...", load_skills=[...], run_in_background=true) |
55781
+ | 2 | 2, 3, 6 | dispatch parallel after Wave 1 completes |
55782
+ | 3 | 4 | final integration task |
55594
55783
 
55595
55784
  ---
55596
55785
 
55597
55786
  ## TODOs
55598
55787
 
55599
55788
  > Implementation + Test = ONE Task. Never separate.
55600
- > Specify parallelizability for EVERY task.
55789
+ > EVERY task MUST have: Recommended Agent Profile + Parallelization info.
55601
55790
 
55602
55791
  - [ ] 1. [Task Title]
55603
55792
 
@@ -55608,7 +55797,21 @@ Task 1 \u2192 Task 2 \u2192 Task 3
55608
55797
  **Must NOT do**:
55609
55798
  - [Specific exclusions from guardrails]
55610
55799
 
55611
- **Parallelizable**: YES (with 3, 4) | NO (depends on 0)
55800
+ **Recommended Agent Profile**:
55801
+ > Select category + skills based on task domain. Justify each choice.
55802
+ - **Category**: \`[visual-engineering | ultrabrain | artistry | quick | unspecified-low | unspecified-high | writing]\`
55803
+ - Reason: [Why this category fits the task domain]
55804
+ - **Skills**: [\`skill-1\`, \`skill-2\`]
55805
+ - \`skill-1\`: [Why needed - domain overlap explanation]
55806
+ - \`skill-2\`: [Why needed - domain overlap explanation]
55807
+ - **Skills Evaluated but Omitted**:
55808
+ - \`omitted-skill\`: [Why domain doesn't overlap]
55809
+
55810
+ **Parallelization**:
55811
+ - **Can Run In Parallel**: YES | NO
55812
+ - **Parallel Group**: Wave N (with Tasks X, Y) | Sequential
55813
+ - **Blocks**: [Tasks that depend on this task completing]
55814
+ - **Blocked By**: [Tasks this depends on] | None (can start immediately)
55612
55815
 
55613
55816
  **References** (CRITICAL - Be Exhaustive):
55614
55817
 
@@ -55917,7 +56120,7 @@ function createConfigHandler(deps) {
55917
56120
  name: "prometheus",
55918
56121
  ...resolvedModel ? { model: resolvedModel } : {},
55919
56122
  ...variantToUse ? { variant: variantToUse } : {},
55920
- mode: "primary",
56123
+ mode: "all",
55921
56124
  prompt: PROMETHEUS_SYSTEM_PROMPT,
55922
56125
  permission: PROMETHEUS_PERMISSION,
55923
56126
  description: `${configAgent?.plan?.description ?? "Plan agent"} (Prometheus - OhMyOpenCode)`,
@@ -55945,7 +56148,11 @@ function createConfigHandler(deps) {
55945
56148
  value ? migrateAgentConfig(value) : value
55946
56149
  ])) : {};
55947
56150
  const migratedBuild = configAgent?.build ? migrateAgentConfig(configAgent.build) : {};
55948
- const planDemoteConfig = replacePlan && agentConfig["prometheus"] ? { ...agentConfig["prometheus"], name: "plan", mode: "subagent" } : undefined;
56151
+ const planDemoteConfig = replacePlan && agentConfig["prometheus"] ? {
56152
+ ...agentConfig["prometheus"],
56153
+ name: "plan",
56154
+ mode: "subagent"
56155
+ } : undefined;
55949
56156
  config3.agent = {
55950
56157
  ...agentConfig,
55951
56158
  ...Object.fromEntries(Object.entries(builtinAgents).filter(([k]) => k !== "sisyphus")),
@@ -56005,8 +56212,8 @@ function createConfigHandler(deps) {
56005
56212
  };
56006
56213
  const mcpResult = pluginConfig.claude_code?.mcp ?? true ? await loadMcpConfigs() : { servers: {} };
56007
56214
  config3.mcp = {
56008
- ...config3.mcp,
56009
56215
  ...createBuiltinMcps(pluginConfig.disabled_mcps),
56216
+ ...config3.mcp,
56010
56217
  ...mcpResult.servers,
56011
56218
  ...pluginComponents.mcpServers
56012
56219
  };
@@ -56120,6 +56327,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
56120
56327
  const prometheusMdOnly = isHookEnabled("prometheus-md-only") ? createPrometheusMdOnlyHook(ctx) : null;
56121
56328
  const sisyphusJuniorNotepad = isHookEnabled("sisyphus-junior-notepad") ? createSisyphusJuniorNotepadHook(ctx) : null;
56122
56329
  const questionLabelTruncator = createQuestionLabelTruncatorHook();
56330
+ const subagentQuestionBlocker = createSubagentQuestionBlockerHook();
56123
56331
  const taskResumeInfo = createTaskResumeInfoHook();
56124
56332
  const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig);
56125
56333
  const backgroundManager = new BackgroundManager(ctx, pluginConfig.background_task, {
@@ -56257,6 +56465,16 @@ var OhMyOpenCodePlugin = async (ctx) => {
56257
56465
  await claudeCodeHooks["chat.message"]?.(input, output);
56258
56466
  await autoSlashCommand?.["chat.message"]?.(input, output);
56259
56467
  await startWork?.["chat.message"]?.(input, output);
56468
+ if (!hasConnectedProvidersCache()) {
56469
+ ctx.client.tui.showToast({
56470
+ body: {
56471
+ title: "\u26A0\uFE0F Provider Cache Missing",
56472
+ message: "Model filtering disabled. RESTART OpenCode to enable full functionality.",
56473
+ variant: "warning",
56474
+ duration: 6000
56475
+ }
56476
+ }).catch(() => {});
56477
+ }
56260
56478
  if (ralphLoop) {
56261
56479
  const parts = output.parts;
56262
56480
  const promptText = parts?.filter((p) => p.type === "text" && p.text).map((p) => p.text).join(`
@@ -56366,6 +56584,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
56366
56584
  }
56367
56585
  },
56368
56586
  "tool.execute.before": async (input, output) => {
56587
+ await subagentQuestionBlocker["tool.execute.before"]?.(input, output);
56369
56588
  await questionLabelTruncator["tool.execute.before"]?.(input, output);
56370
56589
  await claudeCodeHooks["tool.execute.before"](input, output);
56371
56590
  await nonInteractiveEnv?.["tool.execute.before"](input, output);