oh-my-opencode 3.1.4 → 3.1.6

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
@@ -236,6 +236,9 @@ function createSystemDirective(type) {
236
236
  function isSystemDirective(text) {
237
237
  return text.trimStart().startsWith(SYSTEM_DIRECTIVE_PREFIX);
238
238
  }
239
+ function removeSystemReminders(text) {
240
+ return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, "").trim();
241
+ }
239
242
  var SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: OH-MY-OPENCODE", SystemDirectiveTypes;
240
243
  var init_system_directive = __esm(() => {
241
244
  SystemDirectiveTypes = {
@@ -4550,9 +4553,56 @@ var TAURI_APP_IDENTIFIER = "ai.opencode.desktop", TAURI_APP_IDENTIFIER_DEV = "ai
4550
4553
  var init_opencode_config_dir = () => {};
4551
4554
 
4552
4555
  // src/shared/opencode-version.ts
4553
- var NOT_CACHED;
4556
+ import { execSync } from "child_process";
4557
+ function parseVersion(version) {
4558
+ const cleaned = version.replace(/^v/, "").split("-")[0];
4559
+ return cleaned.split(".").map((n) => parseInt(n, 10) || 0);
4560
+ }
4561
+ function compareVersions(a, b) {
4562
+ const partsA = parseVersion(a);
4563
+ const partsB = parseVersion(b);
4564
+ const maxLen = Math.max(partsA.length, partsB.length);
4565
+ for (let i2 = 0;i2 < maxLen; i2++) {
4566
+ const numA = partsA[i2] ?? 0;
4567
+ const numB = partsB[i2] ?? 0;
4568
+ if (numA < numB)
4569
+ return -1;
4570
+ if (numA > numB)
4571
+ return 1;
4572
+ }
4573
+ return 0;
4574
+ }
4575
+ function isVersionGte(a, b) {
4576
+ return compareVersions(a, b) >= 0;
4577
+ }
4578
+ function getOpenCodeVersion() {
4579
+ if (cachedVersion !== NOT_CACHED) {
4580
+ return cachedVersion;
4581
+ }
4582
+ try {
4583
+ const result = execSync("opencode --version", {
4584
+ encoding: "utf-8",
4585
+ timeout: 5000,
4586
+ stdio: ["pipe", "pipe", "pipe"]
4587
+ }).trim();
4588
+ const versionMatch = result.match(/(\d+\.\d+\.\d+(?:-[\w.]+)?)/);
4589
+ cachedVersion = versionMatch?.[1] ?? null;
4590
+ return cachedVersion;
4591
+ } catch {
4592
+ cachedVersion = null;
4593
+ return null;
4594
+ }
4595
+ }
4596
+ function isOpenCodeVersionAtLeast(version) {
4597
+ const current = getOpenCodeVersion();
4598
+ if (!current)
4599
+ return true;
4600
+ return isVersionGte(current, version);
4601
+ }
4602
+ var OPENCODE_NATIVE_AGENTS_INJECTION_VERSION = "1.1.37", NOT_CACHED, cachedVersion;
4554
4603
  var init_opencode_version = __esm(() => {
4555
4604
  NOT_CACHED = Symbol("NOT_CACHED");
4605
+ cachedVersion = NOT_CACHED;
4556
4606
  });
4557
4607
 
4558
4608
  // src/shared/permission-compat.ts
@@ -4783,6 +4833,131 @@ function equalsIgnoreCase(a, b) {
4783
4833
  return a.toLowerCase() === b.toLowerCase();
4784
4834
  }
4785
4835
 
4836
+ // src/shared/model-requirements.ts
4837
+ var AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS;
4838
+ var init_model_requirements = __esm(() => {
4839
+ AGENT_MODEL_REQUIREMENTS = {
4840
+ sisyphus: {
4841
+ fallbackChain: [
4842
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4843
+ { providers: ["zai-coding-plan"], model: "glm-4.7" },
4844
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "medium" },
4845
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
4846
+ ]
4847
+ },
4848
+ oracle: {
4849
+ fallbackChain: [
4850
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
4851
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4852
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
4853
+ ]
4854
+ },
4855
+ librarian: {
4856
+ fallbackChain: [
4857
+ { providers: ["zai-coding-plan"], model: "glm-4.7" },
4858
+ { providers: ["opencode"], model: "big-pickle" },
4859
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" }
4860
+ ]
4861
+ },
4862
+ explore: {
4863
+ fallbackChain: [
4864
+ { providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" },
4865
+ { providers: ["github-copilot"], model: "gpt-5-mini" },
4866
+ { providers: ["opencode"], model: "gpt-5-nano" }
4867
+ ]
4868
+ },
4869
+ "multimodal-looker": {
4870
+ fallbackChain: [
4871
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
4872
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
4873
+ { providers: ["zai-coding-plan"], model: "glm-4.6v" },
4874
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-haiku-4-5" },
4875
+ { providers: ["opencode"], model: "gpt-5-nano" }
4876
+ ]
4877
+ },
4878
+ prometheus: {
4879
+ fallbackChain: [
4880
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4881
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
4882
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
4883
+ ]
4884
+ },
4885
+ metis: {
4886
+ fallbackChain: [
4887
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4888
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
4889
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" }
4890
+ ]
4891
+ },
4892
+ momus: {
4893
+ fallbackChain: [
4894
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "medium" },
4895
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5" },
4896
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" }
4897
+ ]
4898
+ },
4899
+ atlas: {
4900
+ fallbackChain: [
4901
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
4902
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
4903
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
4904
+ ]
4905
+ }
4906
+ };
4907
+ CATEGORY_MODEL_REQUIREMENTS = {
4908
+ "visual-engineering": {
4909
+ fallbackChain: [
4910
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
4911
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4912
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" }
4913
+ ]
4914
+ },
4915
+ ultrabrain: {
4916
+ fallbackChain: [
4917
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "xhigh" },
4918
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4919
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
4920
+ ]
4921
+ },
4922
+ artistry: {
4923
+ fallbackChain: [
4924
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" },
4925
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4926
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" }
4927
+ ]
4928
+ },
4929
+ quick: {
4930
+ fallbackChain: [
4931
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-haiku-4-5" },
4932
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
4933
+ { providers: ["opencode"], model: "gpt-5-nano" }
4934
+ ]
4935
+ },
4936
+ "unspecified-low": {
4937
+ fallbackChain: [
4938
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
4939
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "medium" },
4940
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" }
4941
+ ]
4942
+ },
4943
+ "unspecified-high": {
4944
+ fallbackChain: [
4945
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4946
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
4947
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
4948
+ ]
4949
+ },
4950
+ writing: {
4951
+ fallbackChain: [
4952
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
4953
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
4954
+ { providers: ["zai-coding-plan"], model: "glm-4.7" },
4955
+ { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" }
4956
+ ]
4957
+ }
4958
+ };
4959
+ });
4960
+
4786
4961
  // src/shared/agent-variant.ts
4787
4962
  function resolveAgentVariant(config, agentName) {
4788
4963
  if (!agentName) {
@@ -4802,13 +4977,39 @@ function resolveAgentVariant(config, agentName) {
4802
4977
  }
4803
4978
  return config.categories?.[categoryName]?.variant;
4804
4979
  }
4980
+ function resolveVariantForModel(config, agentName, currentModel) {
4981
+ const agentRequirement = AGENT_MODEL_REQUIREMENTS[agentName];
4982
+ if (agentRequirement) {
4983
+ return findVariantInChain(agentRequirement.fallbackChain, currentModel.providerID);
4984
+ }
4985
+ const agentOverrides = config.agents;
4986
+ const agentOverride = agentOverrides ? findCaseInsensitive(agentOverrides, agentName) : undefined;
4987
+ const categoryName = agentOverride?.category;
4988
+ if (categoryName) {
4989
+ const categoryRequirement = CATEGORY_MODEL_REQUIREMENTS[categoryName];
4990
+ if (categoryRequirement) {
4991
+ return findVariantInChain(categoryRequirement.fallbackChain, currentModel.providerID);
4992
+ }
4993
+ }
4994
+ return;
4995
+ }
4996
+ function findVariantInChain(fallbackChain, providerID) {
4997
+ for (const entry of fallbackChain) {
4998
+ if (entry.providers.includes(providerID)) {
4999
+ return entry.variant;
5000
+ }
5001
+ }
5002
+ return;
5003
+ }
4805
5004
  function applyAgentVariant(config, agentName, message) {
4806
5005
  const variant = resolveAgentVariant(config, agentName);
4807
5006
  if (variant !== undefined && message.variant === undefined) {
4808
5007
  message.variant = variant;
4809
5008
  }
4810
5009
  }
4811
- var init_agent_variant = () => {};
5010
+ var init_agent_variant = __esm(() => {
5011
+ init_model_requirements();
5012
+ });
4812
5013
 
4813
5014
  // src/shared/session-cursor.ts
4814
5015
  function buildMessageKey(message, index) {
@@ -4942,131 +5143,6 @@ var init_agent_tool_restrictions = __esm(() => {
4942
5143
  };
4943
5144
  });
4944
5145
 
4945
- // src/shared/model-requirements.ts
4946
- var AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS;
4947
- var init_model_requirements = __esm(() => {
4948
- AGENT_MODEL_REQUIREMENTS = {
4949
- sisyphus: {
4950
- fallbackChain: [
4951
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4952
- { providers: ["zai-coding-plan"], model: "glm-4.7" },
4953
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "medium" },
4954
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
4955
- ]
4956
- },
4957
- oracle: {
4958
- fallbackChain: [
4959
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
4960
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4961
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
4962
- ]
4963
- },
4964
- librarian: {
4965
- fallbackChain: [
4966
- { providers: ["zai-coding-plan"], model: "glm-4.7" },
4967
- { providers: ["opencode"], model: "big-pickle" },
4968
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" }
4969
- ]
4970
- },
4971
- explore: {
4972
- fallbackChain: [
4973
- { providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" },
4974
- { providers: ["github-copilot"], model: "gpt-5-mini" },
4975
- { providers: ["opencode"], model: "gpt-5-nano" }
4976
- ]
4977
- },
4978
- "multimodal-looker": {
4979
- fallbackChain: [
4980
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
4981
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
4982
- { providers: ["zai-coding-plan"], model: "glm-4.6v" },
4983
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-haiku-4-5" },
4984
- { providers: ["opencode"], model: "gpt-5-nano" }
4985
- ]
4986
- },
4987
- prometheus: {
4988
- fallbackChain: [
4989
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4990
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
4991
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
4992
- ]
4993
- },
4994
- metis: {
4995
- fallbackChain: [
4996
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
4997
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
4998
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" }
4999
- ]
5000
- },
5001
- momus: {
5002
- fallbackChain: [
5003
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "medium" },
5004
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5" },
5005
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" }
5006
- ]
5007
- },
5008
- atlas: {
5009
- fallbackChain: [
5010
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
5011
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
5012
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
5013
- ]
5014
- }
5015
- };
5016
- CATEGORY_MODEL_REQUIREMENTS = {
5017
- "visual-engineering": {
5018
- fallbackChain: [
5019
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
5020
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
5021
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" }
5022
- ]
5023
- },
5024
- ultrabrain: {
5025
- fallbackChain: [
5026
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "xhigh" },
5027
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
5028
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
5029
- ]
5030
- },
5031
- artistry: {
5032
- fallbackChain: [
5033
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" },
5034
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
5035
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" }
5036
- ]
5037
- },
5038
- quick: {
5039
- fallbackChain: [
5040
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-haiku-4-5" },
5041
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
5042
- { providers: ["opencode"], model: "gpt-5-nano" }
5043
- ]
5044
- },
5045
- "unspecified-low": {
5046
- fallbackChain: [
5047
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
5048
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "medium" },
5049
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" }
5050
- ]
5051
- },
5052
- "unspecified-high": {
5053
- fallbackChain: [
5054
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
5055
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
5056
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
5057
- ]
5058
- },
5059
- writing: {
5060
- fallbackChain: [
5061
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
5062
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
5063
- { providers: ["zai-coding-plan"], model: "glm-4.7" },
5064
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" }
5065
- ]
5066
- }
5067
- };
5068
- });
5069
-
5070
5146
  // src/shared/connected-providers-cache.ts
5071
5147
  import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
5072
5148
  import { join as join12 } from "path";
@@ -5328,26 +5404,7 @@ function resolveModelWithFallback(input) {
5328
5404
  }
5329
5405
  if (fallbackChain && fallbackChain.length > 0) {
5330
5406
  if (availableModels.size === 0) {
5331
- const connectedProviders = readConnectedProvidersCache();
5332
- const connectedSet = connectedProviders ? new Set(connectedProviders) : null;
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
- }
5347
- }
5348
- }
5349
- log("No matching provider in connected cache, falling through to system default");
5350
- }
5407
+ log("No model cache available, skipping fallback chain to use system default");
5351
5408
  }
5352
5409
  for (const entry of fallbackChain) {
5353
5410
  for (const provider of entry.providers) {
@@ -5371,7 +5428,6 @@ function resolveModelWithFallback(input) {
5371
5428
  var init_model_resolver = __esm(() => {
5372
5429
  init_logger();
5373
5430
  init_model_availability();
5374
- init_connected_providers_cache();
5375
5431
  });
5376
5432
 
5377
5433
  // src/shared/session-utils.ts
@@ -7531,9 +7587,9 @@ v${latestVersion} available. Restart OpenCode to apply.` : `OpenCode is now on S
7531
7587
  return;
7532
7588
  hasChecked = true;
7533
7589
  setTimeout(async () => {
7534
- const cachedVersion = getCachedVersion();
7590
+ const cachedVersion2 = getCachedVersion();
7535
7591
  const localDevVersion = getLocalDevVersion(ctx.directory);
7536
- const displayVersion = localDevVersion ?? cachedVersion;
7592
+ const displayVersion = localDevVersion ?? cachedVersion2;
7537
7593
  await showConfigErrorsIfAny(ctx);
7538
7594
  await showModelCacheWarningIfNeeded(ctx);
7539
7595
  await updateAndShowConnectedProvidersCacheStatus(ctx);
@@ -7560,8 +7616,8 @@ async function runBackgroundUpdateCheck(ctx, autoUpdate, getToastMessage) {
7560
7616
  log("[auto-update-checker] Plugin not found in config");
7561
7617
  return;
7562
7618
  }
7563
- const cachedVersion = getCachedVersion();
7564
- const currentVersion = cachedVersion ?? pluginInfo.pinnedVersion;
7619
+ const cachedVersion2 = getCachedVersion();
7620
+ const currentVersion = cachedVersion2 ?? pluginInfo.pinnedVersion;
7565
7621
  if (!currentVersion) {
7566
7622
  log("[auto-update-checker] No version found (cached or pinned)");
7567
7623
  return;
@@ -18505,7 +18561,7 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
18505
18561
  }
18506
18562
  if (result.stdout) {
18507
18563
  try {
18508
- const output = JSON.parse(result.stdout);
18564
+ const output = JSON.parse(result.stdout || "{}");
18509
18565
  let decision;
18510
18566
  let reason;
18511
18567
  let modifiedInput;
@@ -18741,7 +18797,7 @@ ${result.stderr.trim()}`);
18741
18797
  }
18742
18798
  if (result.exitCode === 0 && result.stdout) {
18743
18799
  try {
18744
- const output = JSON.parse(result.stdout);
18800
+ const output = JSON.parse(result.stdout || "{}");
18745
18801
  if (output.decision === "block") {
18746
18802
  return {
18747
18803
  block: true,
@@ -18934,7 +18990,7 @@ async function executeStopHooks(ctx, config, extendedConfig) {
18934
18990
  }
18935
18991
  if (result.stdout) {
18936
18992
  try {
18937
- const output = JSON.parse(result.stdout);
18993
+ const output = JSON.parse(result.stdout || "{}");
18938
18994
  if (output.stop_hook_active !== undefined) {
18939
18995
  stopHookActiveState.set(ctx.sessionId, output.stop_hook_active);
18940
18996
  }
@@ -18991,7 +19047,7 @@ async function executePreCompactHooks(ctx, config, extendedConfig) {
18991
19047
  }
18992
19048
  if (result.stdout) {
18993
19049
  try {
18994
- const output = JSON.parse(result.stdout);
19050
+ const output = JSON.parse(result.stdout || "{}");
18995
19051
  if (output.hookSpecificOutput?.additionalContext) {
18996
19052
  collectedContext.push(...output.hookSpecificOutput.additionalContext);
18997
19053
  } else if (output.context) {
@@ -19194,6 +19250,9 @@ ${result.inputLines ?? ""}`,
19194
19250
  }
19195
19251
  },
19196
19252
  "tool.execute.after": async (input, output) => {
19253
+ if (!output) {
19254
+ return;
19255
+ }
19197
19256
  const claudeConfig = await loadClaudeHooksConfig();
19198
19257
  const extendedConfig = await loadPluginExtendedConfig();
19199
19258
  const cachedInput = getToolInput(input.sessionID, input.tool, input.callID) || {};
@@ -20064,7 +20123,81 @@ You ARE the planner. Your job: create bulletproof work plans.
20064
20123
  - External library APIs and constraints
20065
20124
  - Similar implementations in OSS (via librarian)
20066
20125
 
20067
- **NEVER plan blind. Context first, plan second.**`;
20126
+ **NEVER plan blind. Context first, plan second.**
20127
+
20128
+ ---
20129
+
20130
+ ## MANDATORY OUTPUT: PARALLEL TASK GRAPH + TODO LIST
20131
+
20132
+ **YOUR PRIMARY OUTPUT IS A PARALLEL EXECUTION TASK GRAPH.**
20133
+
20134
+ When you finalize a plan, you MUST structure it for maximum parallel execution:
20135
+
20136
+ ### 1. Parallel Execution Waves (REQUIRED)
20137
+
20138
+ Analyze task dependencies and group independent tasks into parallel waves:
20139
+
20140
+ \`\`\`
20141
+ Wave 1 (Start Immediately - No Dependencies):
20142
+ \u251C\u2500\u2500 Task 1: [description] \u2192 category: X, skills: [a, b]
20143
+ \u2514\u2500\u2500 Task 4: [description] \u2192 category: Y, skills: [c]
20144
+
20145
+ Wave 2 (After Wave 1 Completes):
20146
+ \u251C\u2500\u2500 Task 2: [depends: 1] \u2192 category: X, skills: [a]
20147
+ \u251C\u2500\u2500 Task 3: [depends: 1] \u2192 category: Z, skills: [d]
20148
+ \u2514\u2500\u2500 Task 5: [depends: 4] \u2192 category: Y, skills: [c]
20149
+
20150
+ Wave 3 (After Wave 2 Completes):
20151
+ \u2514\u2500\u2500 Task 6: [depends: 2, 3] \u2192 category: X, skills: [a, b]
20152
+
20153
+ Critical Path: Task 1 \u2192 Task 2 \u2192 Task 6
20154
+ Estimated Parallel Speedup: ~40% faster than sequential
20155
+ \`\`\`
20156
+
20157
+ ### 2. Dependency Matrix (REQUIRED)
20158
+
20159
+ | Task | Depends On | Blocks | Can Parallelize With |
20160
+ |------|------------|--------|---------------------|
20161
+ | 1 | None | 2, 3 | 4 |
20162
+ | 2 | 1 | 6 | 3, 5 |
20163
+ | 3 | 1 | 6 | 2, 5 |
20164
+ | 4 | None | 5 | 1 |
20165
+ | 5 | 4 | None | 2, 3 |
20166
+ | 6 | 2, 3 | None | None (final) |
20167
+
20168
+ ### 3. TODO List Structure (REQUIRED)
20169
+
20170
+ Each TODO item MUST include:
20171
+
20172
+ \`\`\`markdown
20173
+ - [ ] N. [Task Title]
20174
+
20175
+ **What to do**: [Clear steps]
20176
+
20177
+ **Dependencies**: [Task numbers this depends on] | None
20178
+ **Blocks**: [Task numbers that depend on this]
20179
+ **Parallel Group**: Wave N (with Tasks X, Y)
20180
+
20181
+ **Recommended Agent Profile**:
20182
+ - **Category**: \`[visual-engineering | ultrabrain | artistry | quick | unspecified-low | unspecified-high | writing]\`
20183
+ - **Skills**: [\`skill-1\`, \`skill-2\`]
20184
+
20185
+ **Acceptance Criteria**: [Verifiable conditions]
20186
+ \`\`\`
20187
+
20188
+ ### 4. Agent Dispatch Summary (REQUIRED)
20189
+
20190
+ | Wave | Tasks | Dispatch Command |
20191
+ |------|-------|------------------|
20192
+ | 1 | 1, 4 | \`delegate_task(category="...", load_skills=[...], run_in_background=true)\` \xD7 2 |
20193
+ | 2 | 2, 3, 5 | \`delegate_task(...)\` \xD7 3 after Wave 1 completes |
20194
+ | 3 | 6 | \`delegate_task(...)\` final integration |
20195
+
20196
+ **WHY PARALLEL TASK GRAPH IS MANDATORY:**
20197
+ - Orchestrator (Sisyphus) executes tasks in parallel waves
20198
+ - Independent tasks run simultaneously via background agents
20199
+ - Proper dependency tracking prevents race conditions
20200
+ - Category + skills ensure optimal model routing per task`;
20068
20201
  function isPlannerAgent(agentName) {
20069
20202
  if (!agentName)
20070
20203
  return false;
@@ -20169,52 +20302,52 @@ delegate_task(agent="oracle", prompt="Review my approach: [describe plan]")
20169
20302
  YOU MUST LEVERAGE ALL AVAILABLE AGENTS / **CATEGORY + SKILLS** TO THEIR FULLEST POTENTIAL.
20170
20303
  TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
20171
20304
 
20172
- ## MANDATORY: PROMETHEUS AGENT INVOCATION (NON-NEGOTIABLE)
20305
+ ## MANDATORY: PLAN AGENT INVOCATION (NON-NEGOTIABLE)
20173
20306
 
20174
- **YOU MUST ALWAYS INVOKE PROMETHEUS (THE PLANNER) FOR ANY NON-TRIVIAL TASK.**
20307
+ **YOU MUST ALWAYS INVOKE THE PLAN AGENT FOR ANY NON-TRIVIAL TASK.**
20175
20308
 
20176
20309
  | Condition | Action |
20177
20310
  |-----------|--------|
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 |
20311
+ | Task has 2+ steps | MUST call plan agent |
20312
+ | Task scope unclear | MUST call plan agent |
20313
+ | Implementation required | MUST call plan agent |
20314
+ | Architecture decision needed | MUST call plan agent |
20182
20315
 
20183
20316
  \`\`\`
20184
- delegate_task(subagent_type="prometheus", prompt="<gathered context + user request>")
20317
+ delegate_task(subagent_type="plan", prompt="<gathered context + user request>")
20185
20318
  \`\`\`
20186
20319
 
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
20320
+ **WHY PLAN AGENT IS MANDATORY:**
20321
+ - Plan agent analyzes dependencies and parallel execution opportunities
20322
+ - Plan agent outputs a **parallel task graph** with waves and dependencies
20323
+ - Plan agent provides structured TODO list with category + skills per task
20191
20324
  - YOU are an orchestrator, NOT an implementer
20192
20325
 
20193
- ### SESSION CONTINUITY WITH PROMETHEUS (CRITICAL)
20326
+ ### SESSION CONTINUITY WITH PLAN AGENT (CRITICAL)
20194
20327
 
20195
- **Prometheus returns a session_id. USE IT for follow-up interactions.**
20328
+ **Plan agent returns a session_id. USE IT for follow-up interactions.**
20196
20329
 
20197
20330
  | Scenario | Action |
20198
20331
  |----------|--------|
20199
- | Prometheus asks clarifying questions | \`delegate_task(session_id="{returned_session_id}", prompt="<your answer>")\` |
20332
+ | Plan agent asks clarifying questions | \`delegate_task(session_id="{returned_session_id}", prompt="<your answer>")\` |
20200
20333
  | Need to refine the plan | \`delegate_task(session_id="{returned_session_id}", prompt="Please adjust: <feedback>")\` |
20201
20334
  | Plan needs more detail | \`delegate_task(session_id="{returned_session_id}", prompt="Add more detail to Task N")\` |
20202
20335
 
20203
20336
  **WHY SESSION_ID IS CRITICAL:**
20204
- - Prometheus retains FULL conversation context
20337
+ - Plan agent retains FULL conversation context
20205
20338
  - No repeated exploration or context gathering
20206
20339
  - Saves 70%+ tokens on follow-ups
20207
20340
  - Maintains interview continuity until plan is finalized
20208
20341
 
20209
20342
  \`\`\`
20210
20343
  // WRONG: Starting fresh loses all context
20211
- delegate_task(subagent_type="prometheus", prompt="Here's more info...")
20344
+ delegate_task(subagent_type="plan", prompt="Here's more info...")
20212
20345
 
20213
20346
  // CORRECT: Resume preserves everything
20214
20347
  delegate_task(session_id="ses_abc123", prompt="Here's my answer to your question: ...")
20215
20348
  \`\`\`
20216
20349
 
20217
- **FAILURE TO CALL PROMETHEUS = INCOMPLETE WORK.**
20350
+ **FAILURE TO CALL PLAN AGENT = INCOMPLETE WORK.**
20218
20351
 
20219
20352
  ---
20220
20353
 
@@ -20226,7 +20359,7 @@ delegate_task(session_id="ses_abc123", prompt="Here's my answer to your question
20226
20359
  |-----------|--------|-----|
20227
20360
  | Codebase exploration | delegate_task(subagent_type="explore", run_in_background=true) | Parallel, context-efficient |
20228
20361
  | Documentation lookup | delegate_task(subagent_type="librarian", run_in_background=true) | Specialized knowledge |
20229
- | Planning | delegate_task(subagent_type="plan") | Structured work breakdown |
20362
+ | Planning | delegate_task(subagent_type="plan") | Parallel task graph + structured TODO list |
20230
20363
  | Architecture/Debugging | delegate_task(subagent_type="oracle") | High-IQ reasoning |
20231
20364
  | Implementation | delegate_task(category="...", load_skills=[...]) | Domain-optimized models |
20232
20365
 
@@ -20286,20 +20419,20 @@ delegate_task(..., run_in_background=true) // task_id_3
20286
20419
  delegate_task(subagent_type="librarian", run_in_background=true, prompt="...")
20287
20420
  \`\`\`
20288
20421
 
20289
- 2. **INVOKE PROMETHEUS** (MANDATORY for non-trivial tasks):
20422
+ 2. **INVOKE PLAN AGENT** (MANDATORY for non-trivial tasks):
20290
20423
  \`\`\`
20291
- result = delegate_task(subagent_type="prometheus", prompt="<context + request>")
20424
+ result = delegate_task(subagent_type="plan", prompt="<context + request>")
20292
20425
  // STORE the session_id for follow-ups!
20293
- prometheus_session_id = result.session_id
20426
+ plan_session_id = result.session_id
20294
20427
  \`\`\`
20295
20428
 
20296
- 3. **ITERATE WITH PROMETHEUS** (if clarification needed):
20429
+ 3. **ITERATE WITH PLAN AGENT** (if clarification needed):
20297
20430
  \`\`\`
20298
20431
  // Use session_id to continue the conversation
20299
- delegate_task(session_id=prometheus_session_id, prompt="<answer to Prometheus's question>")
20432
+ delegate_task(session_id=plan_session_id, prompt="<answer to plan agent's question>")
20300
20433
  \`\`\`
20301
20434
 
20302
- 4. **EXECUTE VIA DELEGATION** (category + skills from Prometheus's plan):
20435
+ 4. **EXECUTE VIA DELEGATION** (category + skills from plan agent's output):
20303
20436
  \`\`\`
20304
20437
  delegate_task(category="...", load_skills=[...], prompt="<task from plan>")
20305
20438
  \`\`\`
@@ -20378,9 +20511,9 @@ Write these criteria explicitly. Share with user if scope is non-trivial.
20378
20511
  THE USER ASKED FOR X. DELIVER EXACTLY X. NOT A SUBSET. NOT A DEMO. NOT A STARTING POINT.
20379
20512
 
20380
20513
  1. EXPLORES + LIBRARIANS (background)
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)
20514
+ 2. GATHER -> delegate_task(subagent_type="plan", prompt="<context + request>")
20515
+ 3. ITERATE WITH PLAN AGENT (session_id resume) UNTIL PLAN IS FINALIZED
20516
+ 4. WORK BY DELEGATING TO CATEGORY + SKILLS AGENTS (following plan agent's parallel task graph)
20384
20517
 
20385
20518
  NOW.
20386
20519
 
@@ -20453,7 +20586,8 @@ function createKeywordDetectorHook(ctx, collector) {
20453
20586
  return;
20454
20587
  }
20455
20588
  const currentAgent = getSessionAgent(input.sessionID) ?? input.agent;
20456
- let detectedKeywords = detectKeywordsWithType(removeCodeBlocks2(promptText), currentAgent);
20589
+ const cleanText = removeSystemReminders(promptText);
20590
+ let detectedKeywords = detectKeywordsWithType(removeCodeBlocks2(cleanText), currentAgent);
20457
20591
  if (isPlannerAgent(currentAgent)) {
20458
20592
  detectedKeywords = detectedKeywords.filter((k) => k.type !== "ultrawork");
20459
20593
  }
@@ -21577,6 +21711,9 @@ async function loadMcpJsonFromDir(skillDir) {
21577
21711
  function parseAllowedTools(allowedTools) {
21578
21712
  if (!allowedTools)
21579
21713
  return;
21714
+ if (Array.isArray(allowedTools)) {
21715
+ return allowedTools.map((t) => t.trim()).filter(Boolean);
21716
+ }
21580
21717
  return allowedTools.split(/\s+/).filter(Boolean);
21581
21718
  }
21582
21719
  async function loadSkillFromPath(skillPath, resolvedPath, defaultName, scope) {
@@ -21744,6 +21881,14 @@ init_deep_merge();
21744
21881
  import { readFileSync as readFileSync21, existsSync as existsSync31 } from "fs";
21745
21882
  import { dirname as dirname7, resolve as resolve6, isAbsolute as isAbsolute2 } from "path";
21746
21883
  import { homedir as homedir10 } from "os";
21884
+ function parseAllowedToolsFromMetadata(allowedTools) {
21885
+ if (!allowedTools)
21886
+ return;
21887
+ if (Array.isArray(allowedTools)) {
21888
+ return allowedTools.map((t) => t.trim()).filter(Boolean);
21889
+ }
21890
+ return allowedTools.split(/\s+/).filter(Boolean);
21891
+ }
21747
21892
  var SCOPE_PRIORITY = {
21748
21893
  builtin: 1,
21749
21894
  config: 2,
@@ -21835,7 +21980,7 @@ $ARGUMENTS
21835
21980
  subtask: entry.subtask ?? fileMetadata.subtask,
21836
21981
  argumentHint: entry["argument-hint"] || fileMetadata["argument-hint"]
21837
21982
  };
21838
- const allowedTools = entry["allowed-tools"] || (fileMetadata["allowed-tools"] ? fileMetadata["allowed-tools"].split(/\s+/).filter(Boolean) : undefined);
21983
+ const allowedTools = entry["allowed-tools"] || (fileMetadata["allowed-tools"] ? parseAllowedToolsFromMetadata(fileMetadata["allowed-tools"]) : undefined);
21839
21984
  return {
21840
21985
  name,
21841
21986
  path: entry.from ? resolveFilePath2(entry.from, configDir) : undefined,
@@ -24617,7 +24762,7 @@ ${contextInfo}`;
24617
24762
  };
24618
24763
  }
24619
24764
  // src/hooks/atlas/index.ts
24620
- import { execSync } from "child_process";
24765
+ import { execSync as execSync2 } from "child_process";
24621
24766
  init_hook_message_injector();
24622
24767
  init_logger();
24623
24768
  init_system_directive();
@@ -24856,7 +25001,7 @@ function extractSessionIdFromOutput(output) {
24856
25001
  }
24857
25002
  function getGitDiffStats(directory) {
24858
25003
  try {
24859
- const output = execSync("git diff --numstat HEAD", {
25004
+ const output = execSync2("git diff --numstat HEAD", {
24860
25005
  cwd: directory,
24861
25006
  encoding: "utf-8",
24862
25007
  timeout: 5000,
@@ -24864,7 +25009,7 @@ function getGitDiffStats(directory) {
24864
25009
  }).trim();
24865
25010
  if (!output)
24866
25011
  return [];
24867
- const statusOutput = execSync("git status --porcelain", {
25012
+ const statusOutput = execSync2("git status --porcelain", {
24868
25013
  cwd: directory,
24869
25014
  encoding: "utf-8",
24870
25015
  timeout: 5000,
@@ -25161,6 +25306,9 @@ function createAtlasHook(ctx, options) {
25161
25306
  }
25162
25307
  },
25163
25308
  "tool.execute.after": async (input, output) => {
25309
+ if (!output) {
25310
+ return;
25311
+ }
25164
25312
  if (!isCallerOrchestrator(input.sessionID)) {
25165
25313
  return;
25166
25314
  }
@@ -43589,7 +43737,10 @@ async function executeSync(args, toolContext, ctx) {
43589
43737
  const createResult = await ctx.client.session.create({
43590
43738
  body: {
43591
43739
  parentID: toolContext.sessionID,
43592
- title: `${args.description} (@${args.subagent_type} subagent)`
43740
+ title: `${args.description} (@${args.subagent_type} subagent)`,
43741
+ permission: [
43742
+ { permission: "question", action: "deny", pattern: "*" }
43743
+ ]
43593
43744
  },
43594
43745
  query: {
43595
43746
  directory: parentDirectory
@@ -43597,6 +43748,17 @@ async function executeSync(args, toolContext, ctx) {
43597
43748
  });
43598
43749
  if (createResult.error) {
43599
43750
  log(`[call_omo_agent] Session create error:`, createResult.error);
43751
+ const errorStr = String(createResult.error);
43752
+ if (errorStr.toLowerCase().includes("unauthorized")) {
43753
+ return `Error: Failed to create session (Unauthorized). This may be due to:
43754
+ 1. OAuth token restrictions (e.g., Claude Code credentials are restricted to Claude Code only)
43755
+ 2. Provider authentication issues
43756
+ 3. Session permission inheritance problems
43757
+
43758
+ Try using a different provider or API key authentication.
43759
+
43760
+ Original error: ${createResult.error}`;
43761
+ }
43600
43762
  return `Error: Failed to create session: ${createResult.error}`;
43601
43763
  }
43602
43764
  sessionID = createResult.data.id;
@@ -43837,7 +43999,10 @@ If the requested information is not found, clearly state what is missing.`;
43837
43999
  const createResult = await ctx.client.session.create({
43838
44000
  body: {
43839
44001
  parentID: toolContext.sessionID,
43840
- title: `look_at: ${args.goal.substring(0, 50)}`
44002
+ title: `look_at: ${args.goal.substring(0, 50)}`,
44003
+ permission: [
44004
+ { permission: "question", action: "deny", pattern: "*" }
44005
+ ]
43841
44006
  },
43842
44007
  query: {
43843
44008
  directory: parentDirectory
@@ -43845,27 +44010,63 @@ If the requested information is not found, clearly state what is missing.`;
43845
44010
  });
43846
44011
  if (createResult.error) {
43847
44012
  log(`[look_at] Session create error:`, createResult.error);
44013
+ const errorStr = String(createResult.error);
44014
+ if (errorStr.toLowerCase().includes("unauthorized")) {
44015
+ return `Error: Failed to create session (Unauthorized). This may be due to:
44016
+ 1. OAuth token restrictions (e.g., Claude Code credentials are restricted to Claude Code only)
44017
+ 2. Provider authentication issues
44018
+ 3. Session permission inheritance problems
44019
+
44020
+ Try using a different provider or API key authentication.
44021
+
44022
+ Original error: ${createResult.error}`;
44023
+ }
43848
44024
  return `Error: Failed to create session: ${createResult.error}`;
43849
44025
  }
43850
44026
  const sessionID = createResult.data.id;
43851
44027
  log(`[look_at] Created session: ${sessionID}`);
43852
44028
  log(`[look_at] Sending prompt with file passthrough to session ${sessionID}`);
43853
- await ctx.client.session.prompt({
43854
- path: { id: sessionID },
43855
- body: {
43856
- agent: MULTIMODAL_LOOKER_AGENT,
43857
- tools: {
43858
- task: false,
43859
- call_omo_agent: false,
43860
- look_at: false,
43861
- read: false
43862
- },
43863
- parts: [
43864
- { type: "text", text: prompt },
43865
- { type: "file", mime: mimeType, url: pathToFileURL2(args.file_path).href, filename }
43866
- ]
44029
+ try {
44030
+ await ctx.client.session.prompt({
44031
+ path: { id: sessionID },
44032
+ body: {
44033
+ agent: MULTIMODAL_LOOKER_AGENT,
44034
+ tools: {
44035
+ task: false,
44036
+ call_omo_agent: false,
44037
+ look_at: false,
44038
+ read: false
44039
+ },
44040
+ parts: [
44041
+ { type: "text", text: prompt },
44042
+ { type: "file", mime: mimeType, url: pathToFileURL2(args.file_path).href, filename }
44043
+ ]
44044
+ }
44045
+ });
44046
+ } catch (promptError) {
44047
+ const errorMessage = promptError instanceof Error ? promptError.message : String(promptError);
44048
+ log(`[look_at] Prompt error:`, promptError);
44049
+ const isJsonParseError = errorMessage.includes("JSON") && (errorMessage.includes("EOF") || errorMessage.includes("parse"));
44050
+ if (isJsonParseError) {
44051
+ return `Error: Failed to analyze file - received malformed response from multimodal-looker agent.
44052
+
44053
+ This typically occurs when:
44054
+ 1. The multimodal-looker model is not available or not connected
44055
+ 2. The model does not support this file type (${mimeType})
44056
+ 3. The API returned an empty or truncated response
44057
+
44058
+ File: ${args.file_path}
44059
+ MIME type: ${mimeType}
44060
+
44061
+ Try:
44062
+ - Ensure a vision-capable model (e.g., gemini-3-flash, gpt-5.2) is available
44063
+ - Check provider connections in opencode settings
44064
+ - For text files like .md, .txt, use the Read tool instead
44065
+
44066
+ Original error: ${errorMessage}`;
43867
44067
  }
43868
- });
44068
+ return `Error: Failed to send prompt to multimodal-looker agent: ${errorMessage}`;
44069
+ }
43869
44070
  log(`[look_at] Prompt sent, fetching messages...`);
43870
44071
  const messagesResult = await ctx.client.session.messages({
43871
44072
  path: { id: sessionID }
@@ -44483,7 +44684,7 @@ To continue this session: session_id="${args.session_id}"`;
44483
44684
  }
44484
44685
  } else {
44485
44686
  const resolution = resolveModelWithFallback({
44486
- userModel: userCategories?.[args.category]?.model ?? sisyphusJuniorModel,
44687
+ userModel: userCategories?.[args.category]?.model ?? resolved.model ?? sisyphusJuniorModel,
44487
44688
  fallbackChain: requirement.fallbackChain,
44488
44689
  availableModels,
44489
44690
  systemDefaultModel
@@ -44508,7 +44709,7 @@ To continue this session: session_id="${args.session_id}"`;
44508
44709
  }
44509
44710
  modelInfo = { model: actualModel, type: type2, source };
44510
44711
  const parsedModel = parseModelString(actualModel);
44511
- const variantToUse = userCategories?.[args.category]?.variant ?? resolvedVariant;
44712
+ const variantToUse = userCategories?.[args.category]?.variant ?? resolvedVariant ?? resolved.config.variant;
44512
44713
  categoryModel = parsedModel ? variantToUse ? { ...parsedModel, variant: variantToUse } : parsedModel : undefined;
44513
44714
  }
44514
44715
  }
@@ -44681,6 +44882,11 @@ To continue this session: session_id="${sessionID}"`;
44681
44882
  return `Cannot use subagent_type="${SISYPHUS_JUNIOR_AGENT}" directly. Use category parameter instead (e.g., ${categoryExamples}).
44682
44883
 
44683
44884
  Sisyphus-Junior is spawned automatically when you specify a category. Pick the appropriate category for your task domain.`;
44885
+ }
44886
+ if (isPlanAgent(agentName) && isPlanAgent(parentAgent)) {
44887
+ return `You are prometheus. You cannot delegate to prometheus via delegate_task.
44888
+
44889
+ Create the work plan directly - that's your job as the planning agent.`;
44684
44890
  }
44685
44891
  agentToUse = agentName;
44686
44892
  try {
@@ -44809,6 +45015,7 @@ To continue this session: session_id="${task.sessionID}"`;
44809
45015
  }
44810
45016
  });
44811
45017
  try {
45018
+ const allowDelegateTask = isPlanAgent(agentToUse);
44812
45019
  await client2.session.prompt({
44813
45020
  path: { id: sessionID },
44814
45021
  body: {
@@ -44816,7 +45023,7 @@ To continue this session: session_id="${task.sessionID}"`;
44816
45023
  system: systemContent,
44817
45024
  tools: {
44818
45025
  task: false,
44819
- delegate_task: false,
45026
+ delegate_task: allowDelegateTask,
44820
45027
  call_omo_agent: true,
44821
45028
  question: false
44822
45029
  },
@@ -52919,6 +53126,8 @@ call_omo_agent(subagent_type="librarian", prompt="Find OSS implementations of Z.
52919
53126
  - [Risk 2]: [Mitigation]
52920
53127
 
52921
53128
  ## Directives for Prometheus
53129
+
53130
+ ### Core Directives
52922
53131
  - MUST: [Required action]
52923
53132
  - MUST: [Required action]
52924
53133
  - MUST NOT: [Forbidden action]
@@ -52926,6 +53135,29 @@ call_omo_agent(subagent_type="librarian", prompt="Find OSS implementations of Z.
52926
53135
  - PATTERN: Follow \`[file:lines]\`
52927
53136
  - TOOL: Use \`[specific tool]\` for [purpose]
52928
53137
 
53138
+ ### QA/Acceptance Criteria Directives (MANDATORY)
53139
+ > **ZERO USER INTERVENTION PRINCIPLE**: All acceptance criteria MUST be executable by agents.
53140
+
53141
+ - MUST: Write acceptance criteria as executable commands (curl, bun test, playwright actions)
53142
+ - MUST: Include exact expected outputs, not vague descriptions
53143
+ - MUST: Specify verification tool for each deliverable type (playwright for UI, curl for API, etc.)
53144
+ - MUST NOT: Create criteria requiring "user manually tests..."
53145
+ - MUST NOT: Create criteria requiring "user visually confirms..."
53146
+ - MUST NOT: Create criteria requiring "user clicks/interacts..."
53147
+ - MUST NOT: Use placeholders without concrete examples (bad: "[endpoint]", good: "/api/users")
53148
+
53149
+ Example of GOOD acceptance criteria:
53150
+ \`\`\`
53151
+ curl -s http://localhost:3000/api/health | jq '.status'
53152
+ # Assert: Output is "ok"
53153
+ \`\`\`
53154
+
53155
+ Example of BAD acceptance criteria (FORBIDDEN):
53156
+ \`\`\`
53157
+ User opens browser and checks if the page loads correctly.
53158
+ User confirms the button works as expected.
53159
+ \`\`\`
53160
+
52929
53161
  ## Recommended Approach
52930
53162
  [1-2 sentence summary of how to proceed]
52931
53163
  \`\`\`
@@ -52952,12 +53184,16 @@ call_omo_agent(subagent_type="librarian", prompt="Find OSS implementations of Z.
52952
53184
  - Ask generic questions ("What's the scope?")
52953
53185
  - Proceed without addressing ambiguity
52954
53186
  - Make assumptions about user's codebase
53187
+ - Suggest acceptance criteria requiring user intervention ("user manually tests", "user confirms", "user clicks")
53188
+ - Leave QA/acceptance criteria vague or placeholder-heavy
52955
53189
 
52956
53190
  **ALWAYS**:
52957
53191
  - Classify intent FIRST
52958
53192
  - Be specific ("Should this change UserService only, or also AuthService?")
52959
53193
  - Explore before asking (for Build/Research intents)
52960
53194
  - Provide actionable directives for Prometheus
53195
+ - Include QA automation directives in every output
53196
+ - Ensure acceptance criteria are agent-executable (commands, not human actions)
52961
53197
  `;
52962
53198
  var metisRestrictions = createAgentToolRestrictions([
52963
53199
  "write",
@@ -55714,27 +55950,37 @@ Each TODO follows RED-GREEN-REFACTOR:
55714
55950
  - Example: Create \`src/__tests__/example.test.ts\`
55715
55951
  - Verify: \`bun test\` \u2192 1 test passes
55716
55952
 
55717
- ### If Manual QA Only
55953
+ ### If Automated Verification Only (NO User Intervention)
55718
55954
 
55719
- **CRITICAL**: Without automated tests, manual verification MUST be exhaustive.
55955
+ > **CRITICAL PRINCIPLE: ZERO USER INTERVENTION**
55956
+ >
55957
+ > **NEVER** create acceptance criteria that require:
55958
+ > - "User manually tests..." / "\uC0AC\uC6A9\uC790\uAC00 \uC9C1\uC811 \uD14C\uC2A4\uD2B8..."
55959
+ > - "User visually confirms..." / "\uC0AC\uC6A9\uC790\uAC00 \uB208\uC73C\uB85C \uD655\uC778..."
55960
+ > - "User interacts with..." / "\uC0AC\uC6A9\uC790\uAC00 \uC9C1\uC811 \uC870\uC791..."
55961
+ > - "Ask user to verify..." / "\uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778 \uC694\uCCAD..."
55962
+ > - ANY step that requires a human to perform an action
55963
+ >
55964
+ > **ALL verification MUST be automated and executable by the agent.**
55965
+ > If a verification cannot be automated, find an automated alternative or explicitly note it as a known limitation.
55720
55966
 
55721
- Each TODO includes detailed verification procedures:
55967
+ Each TODO includes EXECUTABLE verification procedures that agents can run directly:
55722
55968
 
55723
55969
  **By Deliverable Type:**
55724
55970
 
55725
- | Type | Verification Tool | Procedure |
55726
- |------|------------------|-----------|
55727
- | **Frontend/UI** | Playwright browser | Navigate, interact, screenshot |
55728
- | **TUI/CLI** | interactive_bash (tmux) | Run command, verify output |
55729
- | **API/Backend** | curl / httpie | Send request, verify response |
55730
- | **Library/Module** | Node/Python REPL | Import, call, verify |
55731
- | **Config/Infra** | Shell commands | Apply, verify state |
55971
+ | Type | Verification Tool | Automated Procedure |
55972
+ |------|------------------|---------------------|
55973
+ | **Frontend/UI** | Playwright browser via playwright skill | Agent navigates, clicks, screenshots, asserts DOM state |
55974
+ | **TUI/CLI** | interactive_bash (tmux) | Agent runs command, captures output, validates expected strings |
55975
+ | **API/Backend** | curl / httpie via Bash | Agent sends request, parses response, validates JSON fields |
55976
+ | **Library/Module** | Node/Python REPL via Bash | Agent imports, calls function, compares output |
55977
+ | **Config/Infra** | Shell commands via Bash | Agent applies config, runs state check, validates output |
55732
55978
 
55733
- **Evidence Required:**
55734
- - Commands run with actual output
55735
- - Screenshots for visual changes
55736
- - Response bodies for API changes
55737
- - Terminal output for CLI changes
55979
+ **Evidence Requirements (Agent-Executable):**
55980
+ - Command output captured and compared against expected patterns
55981
+ - Screenshots saved to .sisyphus/evidence/ for visual verification
55982
+ - JSON response fields validated with specific assertions
55983
+ - Exit codes checked (0 = success)
55738
55984
 
55739
55985
  ---
55740
55986
 
@@ -55844,53 +56090,76 @@ Parallel Speedup: ~40% faster than sequential
55844
56090
 
55845
56091
  **Acceptance Criteria**:
55846
56092
 
55847
- > CRITICAL: Acceptance = EXECUTION, not just "it should work".
55848
- > The executor MUST run these commands and verify output.
56093
+ > **CRITICAL: AGENT-EXECUTABLE VERIFICATION ONLY**
56094
+ >
56095
+ > - Acceptance = EXECUTION by the agent, not "user checks if it works"
56096
+ > - Every criterion MUST be verifiable by running a command or using a tool
56097
+ > - NO steps like "user opens browser", "user clicks", "user confirms"
56098
+ > - If you write "[placeholder]" - REPLACE IT with actual values based on task context
55849
56099
 
55850
56100
  **If TDD (tests enabled):**
55851
- - [ ] Test file created: \`[path].test.ts\`
55852
- - [ ] Test covers: [specific scenario]
55853
- - [ ] \`bun test [file]\` \u2192 PASS (N tests, 0 failures)
55854
-
55855
- **Manual Execution Verification (ALWAYS include, even with tests):**
55856
-
55857
- *Choose based on deliverable type:*
55858
-
55859
- **For Frontend/UI changes:**
55860
- - [ ] Using playwright browser automation:
55861
- - Navigate to: \`http://localhost:[port]/[path]\`
55862
- - Action: [click X, fill Y, scroll to Z]
55863
- - Verify: [visual element appears, animation completes, state changes]
55864
- - Screenshot: Save evidence to \`.sisyphus/evidence/[task-id]-[step].png\`
55865
-
55866
- **For TUI/CLI changes:**
55867
- - [ ] Using interactive_bash (tmux session):
55868
- - Command: \`[exact command to run]\`
55869
- - Input sequence: [if interactive, list inputs]
55870
- - Expected output contains: \`[expected string or pattern]\`
55871
- - Exit code: [0 for success, specific code if relevant]
55872
-
55873
- **For API/Backend changes:**
55874
- - [ ] Request: \`curl -X [METHOD] http://localhost:[port]/[endpoint] -H "Content-Type: application/json" -d '[body]'\`
55875
- - [ ] Response status: [200/201/etc]
55876
- - [ ] Response body contains: \`{"key": "expected_value"}\`
55877
-
55878
- **For Library/Module changes:**
55879
- - [ ] REPL verification:
55880
- \`\`\`
55881
- > import { [function] } from '[module]'
55882
- > [function]([args])
55883
- Expected: [output]
55884
- \`\`\`
55885
-
55886
- **For Config/Infra changes:**
55887
- - [ ] Apply: \`[command to apply config]\`
55888
- - [ ] Verify state: \`[command to check state]\` \u2192 \`[expected output]\`
55889
-
55890
- **Evidence Required:**
55891
- - [ ] Command output captured (copy-paste actual terminal output)
55892
- - [ ] Screenshot saved (for visual changes)
55893
- - [ ] Response body logged (for API changes)
56101
+ - [ ] Test file created: src/auth/login.test.ts
56102
+ - [ ] Test covers: successful login returns JWT token
56103
+ - [ ] bun test src/auth/login.test.ts \u2192 PASS (3 tests, 0 failures)
56104
+
56105
+ **Automated Verification (ALWAYS include, choose by deliverable type):**
56106
+
56107
+ **For Frontend/UI changes** (using playwright skill):
56108
+ \\\`\\\`\\\`
56109
+ # Agent executes via playwright browser automation:
56110
+ 1. Navigate to: http://localhost:3000/login
56111
+ 2. Fill: input[name="email"] with "test@example.com"
56112
+ 3. Fill: input[name="password"] with "password123"
56113
+ 4. Click: button[type="submit"]
56114
+ 5. Wait for: selector ".dashboard-welcome" to be visible
56115
+ 6. Assert: text "Welcome back" appears on page
56116
+ 7. Screenshot: .sisyphus/evidence/task-1-login-success.png
56117
+ \\\`\\\`\\\`
56118
+
56119
+ **For TUI/CLI changes** (using interactive_bash):
56120
+ \\\`\\\`\\\`
56121
+ # Agent executes via tmux session:
56122
+ 1. Command: ./my-cli --config test.yaml
56123
+ 2. Wait for: "Configuration loaded" in output
56124
+ 3. Send keys: "q" to quit
56125
+ 4. Assert: Exit code 0
56126
+ 5. Assert: Output contains "Goodbye"
56127
+ \\\`\\\`\\\`
56128
+
56129
+ **For API/Backend changes** (using Bash curl):
56130
+ \\\`\\\`\\\`bash
56131
+ # Agent runs:
56132
+ curl -s -X POST http://localhost:8080/api/users \\
56133
+ -H "Content-Type: application/json" \\
56134
+ -d '{"email":"new@test.com","name":"Test User"}' \\
56135
+ | jq '.id'
56136
+ # Assert: Returns non-empty UUID
56137
+ # Assert: HTTP status 201
56138
+ \\\`\\\`\\\`
56139
+
56140
+ **For Library/Module changes** (using Bash node/bun):
56141
+ \\\`\\\`\\\`bash
56142
+ # Agent runs:
56143
+ bun -e "import { validateEmail } from './src/utils/validate'; console.log(validateEmail('test@example.com'))"
56144
+ # Assert: Output is "true"
56145
+
56146
+ bun -e "import { validateEmail } from './src/utils/validate'; console.log(validateEmail('invalid'))"
56147
+ # Assert: Output is "false"
56148
+ \\\`\\\`\\\`
56149
+
56150
+ **For Config/Infra changes** (using Bash):
56151
+ \\\`\\\`\\\`bash
56152
+ # Agent runs:
56153
+ docker compose up -d
56154
+ # Wait 5s for containers
56155
+ docker compose ps --format json | jq '.[].State'
56156
+ # Assert: All states are "running"
56157
+ \\\`\\\`\\\`
56158
+
56159
+ **Evidence to Capture:**
56160
+ - [ ] Terminal output from verification commands (actual output, not expected)
56161
+ - [ ] Screenshot files in .sisyphus/evidence/ for UI changes
56162
+ - [ ] JSON response bodies for API changes
55894
56163
 
55895
56164
  **Commit**: YES | NO (groups with N)
55896
56165
  - Message: \`type(scope): desc\`
@@ -56292,7 +56561,19 @@ var OhMyOpenCodePlugin = async (ctx) => {
56292
56561
  const toolOutputTruncator = isHookEnabled("tool-output-truncator") ? createToolOutputTruncatorHook(ctx, {
56293
56562
  experimental: pluginConfig.experimental
56294
56563
  }) : null;
56295
- const directoryAgentsInjector = isHookEnabled("directory-agents-injector") ? createDirectoryAgentsInjectorHook(ctx) : null;
56564
+ let directoryAgentsInjector = null;
56565
+ if (isHookEnabled("directory-agents-injector")) {
56566
+ const currentVersion = getOpenCodeVersion();
56567
+ const hasNativeSupport = currentVersion !== null && isOpenCodeVersionAtLeast(OPENCODE_NATIVE_AGENTS_INJECTION_VERSION);
56568
+ if (hasNativeSupport) {
56569
+ log("directory-agents-injector auto-disabled due to native OpenCode support", {
56570
+ currentVersion,
56571
+ nativeVersion: OPENCODE_NATIVE_AGENTS_INJECTION_VERSION
56572
+ });
56573
+ } else {
56574
+ directoryAgentsInjector = createDirectoryAgentsInjectorHook(ctx);
56575
+ }
56576
+ }
56296
56577
  const directoryReadmeInjector = isHookEnabled("directory-readme-injector") ? createDirectoryReadmeInjectorHook(ctx) : null;
56297
56578
  const emptyTaskResponseDetector = isHookEnabled("empty-task-response-detector") ? createEmptyTaskResponseDetectorHook(ctx) : null;
56298
56579
  const thinkMode = isHookEnabled("think-mode") ? createThinkModeHook() : null;
@@ -56453,13 +56734,20 @@ var OhMyOpenCodePlugin = async (ctx) => {
56453
56734
  }
56454
56735
  const message = output.message;
56455
56736
  if (firstMessageVariantGate.shouldOverride(input.sessionID)) {
56456
- const variant = resolveAgentVariant(pluginConfig, input.agent);
56737
+ const variant = input.model && input.agent ? resolveVariantForModel(pluginConfig, input.agent, input.model) : resolveAgentVariant(pluginConfig, input.agent);
56457
56738
  if (variant !== undefined) {
56458
56739
  message.variant = variant;
56459
56740
  }
56460
56741
  firstMessageVariantGate.markApplied(input.sessionID);
56461
56742
  } else {
56462
- applyAgentVariant(pluginConfig, input.agent, message);
56743
+ if (input.model && input.agent && message.variant === undefined) {
56744
+ const variant = resolveVariantForModel(pluginConfig, input.agent, input.model);
56745
+ if (variant !== undefined) {
56746
+ message.variant = variant;
56747
+ }
56748
+ } else {
56749
+ applyAgentVariant(pluginConfig, input.agent, message);
56750
+ }
56463
56751
  }
56464
56752
  await keywordDetector?.["chat.message"]?.(input, output);
56465
56753
  await claudeCodeHooks["chat.message"]?.(input, output);
@@ -56636,6 +56924,9 @@ var OhMyOpenCodePlugin = async (ctx) => {
56636
56924
  }
56637
56925
  },
56638
56926
  "tool.execute.after": async (input, output) => {
56927
+ if (!output) {
56928
+ return;
56929
+ }
56639
56930
  await claudeCodeHooks["tool.execute.after"](input, output);
56640
56931
  await toolOutputTruncator?.["tool.execute.after"](input, output);
56641
56932
  await contextWindowMonitor?.["tool.execute.after"](input, output);