oh-my-opencode 4.11.1 → 4.12.0

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.
Files changed (149) hide show
  1. package/.agents/skills/codex-qa/scripts/lib/app-server-client.mjs +132 -82
  2. package/.agents/skills/codex-qa/scripts/lib/app-server-client.test.js +48 -0
  3. package/.agents/skills/opencode-qa/scripts/serve-wake-split-probe.sh +361 -84
  4. package/.agents/skills/work-with-pr/SKILL.md +19 -5
  5. package/.opencode/skills/work-with-pr/SKILL.md +19 -5
  6. package/dist/agents/types.d.ts +7 -6
  7. package/dist/cli/index.js +438 -306
  8. package/dist/cli-node/index.js +438 -306
  9. package/dist/features/background-agent/parent-wake-flush-runner.d.ts +1 -0
  10. package/dist/features/background-agent/parent-wake-history-state.d.ts +1 -0
  11. package/dist/features/background-agent/parent-wake-session-history.d.ts +1 -47
  12. package/dist/features/background-agent/parent-wake-session-message.d.ts +47 -0
  13. package/dist/index.js +245 -69
  14. package/dist/plugin/tool-execute-before.d.ts +2 -0
  15. package/dist/plugin-handlers/plugin-components-loader.d.ts +1 -0
  16. package/dist/skills/ulw-plan/SKILL.md +2 -1
  17. package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
  18. package/dist/skills/ulw-plan/references/intent-clear.md +2 -2
  19. package/dist/tools/delegate-task/sync-session-poller.d.ts +1 -2
  20. package/dist/tools/delegate-task/sync-session-turns.d.ts +3 -0
  21. package/dist/tui.js +16 -7
  22. package/package.json +13 -13
  23. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +22 -2
  24. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +25 -9
  25. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  26. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +10 -3
  28. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +9 -2
  29. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  30. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +7 -2
  31. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +2 -1
  32. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +9 -9
  33. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +94 -0
  34. package/packages/omo-codex/plugin/components/codegraph/test/serve-node-support.test.ts +33 -0
  35. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  36. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/comment-checker/test/package-smoke.test.ts +2 -71
  38. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  39. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  40. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  43. package/packages/omo-codex/plugin/components/lsp/package.json +2 -2
  44. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +26 -104
  45. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  46. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +12 -92
  48. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +157 -234
  49. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  50. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -2
  51. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +227 -15
  52. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +62 -2
  53. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +34 -3
  54. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +139 -0
  55. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +111 -0
  56. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +250 -0
  57. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +220 -0
  58. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  59. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  60. package/packages/omo-codex/plugin/components/test-support/package-smoke-fixture.ts +158 -0
  61. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  62. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  63. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -1
  64. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/agents/openai.yaml +1 -1
  65. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
  66. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +2 -2
  67. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +76 -49
  68. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
  69. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  70. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/agents/openai.yaml +1 -1
  71. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +1 -1
  72. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +17 -0
  73. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +17 -0
  74. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +17 -0
  75. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +17 -0
  76. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +17 -0
  77. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +17 -0
  78. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +17 -0
  79. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +17 -0
  80. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +17 -0
  81. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +17 -0
  82. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +16 -0
  83. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +16 -0
  84. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +16 -0
  85. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +16 -0
  86. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +16 -0
  87. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +17 -0
  88. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +16 -0
  89. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +16 -0
  90. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +16 -0
  91. package/packages/omo-codex/plugin/package-lock.json +12 -22
  92. package/packages/omo-codex/plugin/package.json +1 -1
  93. package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +14 -11
  94. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +15 -4
  95. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +37 -0
  96. package/packages/omo-codex/plugin/skills/ast-grep/agents/openai.yaml +2 -0
  97. package/packages/omo-codex/plugin/skills/comment-checker/agents/openai.yaml +2 -0
  98. package/packages/omo-codex/plugin/skills/debugging/agents/openai.yaml +2 -0
  99. package/packages/omo-codex/plugin/skills/frontend/agents/openai.yaml +2 -0
  100. package/packages/omo-codex/plugin/skills/git-master/agents/openai.yaml +1 -1
  101. package/packages/omo-codex/plugin/skills/init-deep/agents/openai.yaml +2 -0
  102. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  103. package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +1 -1
  104. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
  105. package/packages/omo-codex/plugin/skills/lsp/agents/openai.yaml +2 -0
  106. package/packages/omo-codex/plugin/skills/lsp-setup/agents/openai.yaml +2 -0
  107. package/packages/omo-codex/plugin/skills/programming/agents/openai.yaml +2 -0
  108. package/packages/omo-codex/plugin/skills/refactor/agents/openai.yaml +2 -0
  109. package/packages/omo-codex/plugin/skills/remove-ai-slops/agents/openai.yaml +2 -0
  110. package/packages/omo-codex/plugin/skills/review-work/agents/openai.yaml +2 -0
  111. package/packages/omo-codex/plugin/skills/rules/agents/openai.yaml +2 -0
  112. package/packages/omo-codex/plugin/skills/start-work/agents/openai.yaml +2 -0
  113. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +139 -0
  114. package/packages/omo-codex/plugin/skills/teammode/agents/openai.yaml +2 -0
  115. package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +111 -0
  116. package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +250 -0
  117. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +220 -0
  118. package/packages/omo-codex/plugin/skills/ultraresearch/agents/openai.yaml +2 -0
  119. package/packages/omo-codex/plugin/skills/ulw-loop/agents/openai.yaml +1 -1
  120. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -1
  121. package/packages/omo-codex/plugin/skills/ulw-plan/agents/openai.yaml +1 -1
  122. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
  123. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +2 -2
  124. package/packages/omo-codex/plugin/skills/visual-qa/agents/openai.yaml +2 -0
  125. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +34 -33
  126. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +5 -2
  127. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +13 -0
  128. package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +1 -1
  129. package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +14 -6
  130. package/packages/omo-codex/plugin/test/bootstrap-ps-guard.test.mjs +7 -3
  131. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +3 -3
  132. package/packages/omo-codex/plugin/test/display-metadata.test.mjs +83 -0
  133. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +30 -19
  134. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +3 -3
  135. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
  136. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -0
  137. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +9 -4
  138. package/packages/omo-codex/plugin/test/teammode-safety-fixture.mjs +88 -0
  139. package/packages/omo-codex/plugin/test/teammode-safety.test.mjs +240 -0
  140. package/packages/omo-codex/scripts/install-dist/install-local.mjs +385 -206
  141. package/packages/omo-codex/scripts/install-hook-targets.test.mjs +93 -0
  142. package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +2 -2
  143. package/packages/omo-codex/scripts/install-local-bun-global-update.test.mjs +63 -0
  144. package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +109 -1
  145. package/packages/omo-codex/scripts/install-local.mjs +1 -0
  146. package/packages/shared-skills/skills/ulw-plan/SKILL.md +2 -1
  147. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
  148. package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +2 -2
  149. package/packages/omo-codex/plugin/hooks/hooks.json +0 -214
@@ -21,6 +21,7 @@ export declare class ParentWakeFlushRunner {
21
21
  private hasRecentParentSessionActivity;
22
22
  private isUserMessageInProgress;
23
23
  private shouldDeferParentWakeForSessionHistory;
24
+ private confirmParentWakeStillSafeForReply;
24
25
  private requeueWake;
25
26
  }
26
27
  export {};
@@ -2,4 +2,5 @@ import type { PendingParentWake } from "./parent-wake-dedupe";
2
2
  export declare function latestAssistantTurnIsCompletedEmptyNoProgress(messages: readonly unknown[]): boolean;
3
3
  export declare function latestAssistantTurnHasToolBlock(messages: readonly unknown[]): boolean;
4
4
  export declare function latestAssistantTurnHasFreshToolActivity(messages: readonly unknown[], now: number, maxAgeMs: number): boolean;
5
+ export declare function latestAssistantTurnHasStaleUnknownSubstantiveOutput(messages: readonly unknown[], now: number, maxAgeMs: number): boolean;
5
6
  export declare function createEmptyAssistantTurnRetryDedupeKey(wake: PendingParentWake): string;
@@ -1,51 +1,5 @@
1
1
  import type { PendingParentWake } from "./parent-wake-dedupe";
2
- export type ParentWakeSessionMessage = {
3
- readonly info?: {
4
- readonly role?: string;
5
- readonly finish?: string;
6
- readonly error?: unknown;
7
- readonly time?: {
8
- readonly created?: unknown;
9
- readonly updated?: unknown;
10
- readonly completed?: unknown;
11
- readonly start?: unknown;
12
- readonly end?: unknown;
13
- };
14
- };
15
- readonly role?: string;
16
- readonly finish?: string;
17
- readonly error?: unknown;
18
- readonly time?: {
19
- readonly created?: unknown;
20
- readonly updated?: unknown;
21
- readonly completed?: unknown;
22
- readonly start?: unknown;
23
- readonly end?: unknown;
24
- };
25
- readonly parts?: readonly {
26
- readonly type?: string;
27
- readonly text?: string;
28
- readonly synthetic?: boolean;
29
- readonly content?: unknown;
30
- readonly time?: {
31
- readonly created?: unknown;
32
- readonly updated?: unknown;
33
- readonly completed?: unknown;
34
- readonly start?: unknown;
35
- readonly end?: unknown;
36
- };
37
- readonly state?: {
38
- readonly status?: unknown;
39
- readonly time?: {
40
- readonly created?: unknown;
41
- readonly updated?: unknown;
42
- readonly completed?: unknown;
43
- readonly start?: unknown;
44
- readonly end?: unknown;
45
- };
46
- };
47
- }[];
48
- };
2
+ import type { ParentWakeSessionMessage } from "./parent-wake-session-message";
49
3
  export type ToolWaitDeferralDecision = {
50
4
  readonly defer: boolean;
51
5
  readonly skipPromptGateToolStateCheck: boolean;
@@ -0,0 +1,47 @@
1
+ export type ParentWakeSessionMessage = {
2
+ readonly info?: {
3
+ readonly role?: string;
4
+ readonly finish?: string;
5
+ readonly error?: unknown;
6
+ readonly time?: {
7
+ readonly created?: unknown;
8
+ readonly updated?: unknown;
9
+ readonly completed?: unknown;
10
+ readonly start?: unknown;
11
+ readonly end?: unknown;
12
+ };
13
+ };
14
+ readonly role?: string;
15
+ readonly finish?: string;
16
+ readonly error?: unknown;
17
+ readonly time?: {
18
+ readonly created?: unknown;
19
+ readonly updated?: unknown;
20
+ readonly completed?: unknown;
21
+ readonly start?: unknown;
22
+ readonly end?: unknown;
23
+ };
24
+ readonly parts?: readonly {
25
+ readonly type?: string;
26
+ readonly text?: string;
27
+ readonly synthetic?: boolean;
28
+ readonly content?: unknown;
29
+ readonly time?: {
30
+ readonly created?: unknown;
31
+ readonly updated?: unknown;
32
+ readonly completed?: unknown;
33
+ readonly start?: unknown;
34
+ readonly end?: unknown;
35
+ };
36
+ readonly state?: {
37
+ readonly status?: unknown;
38
+ readonly time?: {
39
+ readonly created?: unknown;
40
+ readonly updated?: unknown;
41
+ readonly completed?: unknown;
42
+ readonly start?: unknown;
43
+ readonly end?: unknown;
44
+ };
45
+ };
46
+ }[];
47
+ };
package/dist/index.js CHANGED
@@ -20989,6 +20989,11 @@ function isClaudeOpus47OrLaterModel(model) {
20989
20989
  return false;
20990
20990
  return major > 4 || major === 4 && minor >= 7;
20991
20991
  }
20992
+ var CLAUDE_FABLE_OR_MYTHOS_RE = /claude-(?:fable|mythos)-(?:\d+|preview)/;
20993
+ function isClaudeFableOrMythosModel(model) {
20994
+ const modelName = extractModelName(model).toLowerCase().replaceAll(".", "-");
20995
+ return CLAUDE_FABLE_OR_MYTHOS_RE.test(modelName);
20996
+ }
20992
20997
  function isKimiK2Model(model) {
20993
20998
  const modelName = extractModelName(model).toLowerCase();
20994
20999
  if (modelName.includes("kimi"))
@@ -23386,7 +23391,7 @@ var KIMI_CATEGORIES = [
23386
23391
  // packages/omo-opencode/src/agents/types.ts
23387
23392
  var CLAUDE_THINKING_BUDGET_TOKENS = 32000;
23388
23393
  function buildClaudeThinkingConfig(model) {
23389
- if (isClaudeOpus47OrLaterModel(model)) {
23394
+ if (isClaudeOpus47OrLaterModel(model) || isClaudeFableOrMythosModel(model)) {
23390
23395
  return {};
23391
23396
  }
23392
23397
  return { thinking: { type: "enabled", budgetTokens: CLAUDE_THINKING_BUDGET_TOKENS } };
@@ -69992,7 +69997,7 @@ async function probe(registration) {
69992
69997
  registration.available = false;
69993
69998
  return false;
69994
69999
  }
69995
- const probeUrl = new URL("/session", registration.serverUrl);
70000
+ const probeUrl = new URL("/global/health", registration.serverUrl);
69996
70001
  const authHeader = getServerBasicAuthHeader();
69997
70002
  const headers = authHeader ? { Authorization: authHeader } : {};
69998
70003
  try {
@@ -70026,12 +70031,17 @@ async function probe(registration) {
70026
70031
  return false;
70027
70032
  }
70028
70033
  }
70029
- function hasFreshProbe(registration) {
70030
- return registration.available !== undefined && Date.now() - registration.probeTimestamp < PROBE_TTL_MS;
70034
+ function getFreshProbeAvailability(registration) {
70035
+ const available = registration.available;
70036
+ if (available === undefined || Date.now() - registration.probeTimestamp >= PROBE_TTL_MS) {
70037
+ return;
70038
+ }
70039
+ return available;
70031
70040
  }
70032
70041
  async function resolveAvailability(registration) {
70033
- if (hasFreshProbe(registration)) {
70034
- return registration.available;
70042
+ const freshAvailability = getFreshProbeAvailability(registration);
70043
+ if (freshAvailability !== undefined) {
70044
+ return freshAvailability;
70035
70045
  }
70036
70046
  if (!registration.inFlightProbe) {
70037
70047
  registration.inFlightProbe = probe(registration).finally(() => {
@@ -70066,10 +70076,11 @@ function tryResolveDispatchClientSync2(client3, sessionID) {
70066
70076
  if (!registration.serverUrl) {
70067
70077
  return { client: client3, route: "in-process", reason: "unavailable" };
70068
70078
  }
70069
- if (!hasFreshProbe(registration)) {
70079
+ const freshAvailability = getFreshProbeAvailability(registration);
70080
+ if (freshAvailability === undefined) {
70070
70081
  return;
70071
70082
  }
70072
- if (!registration.available) {
70083
+ if (!freshAvailability) {
70073
70084
  return { client: client3, route: "in-process", reason: "unavailable" };
70074
70085
  }
70075
70086
  const resolvedLiveClient = getOrBuildLiveClient(registration);
@@ -70084,6 +70095,9 @@ async function resolveDispatchClient2(client3, sessionID) {
70084
70095
  return syncResult;
70085
70096
  }
70086
70097
  const registration = registrations.get(client3);
70098
+ if (!registration) {
70099
+ return { client: client3, route: "in-process", reason: "identity" };
70100
+ }
70087
70101
  const isAvailable = await resolveAvailability(registration);
70088
70102
  if (!isAvailable) {
70089
70103
  return { client: client3, route: "in-process", reason: "unavailable" };
@@ -80669,7 +80683,7 @@ function getCachedVersion(options = {}) {
80669
80683
  // package.json
80670
80684
  var package_default = {
80671
80685
  name: "oh-my-opencode",
80672
- version: "4.11.1",
80686
+ version: "4.12.0",
80673
80687
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
80674
80688
  main: "./dist/index.js",
80675
80689
  types: "dist/index.d.ts",
@@ -80839,18 +80853,18 @@ var package_default = {
80839
80853
  typescript: "^6.0.3"
80840
80854
  },
80841
80855
  optionalDependencies: {
80842
- "oh-my-opencode-darwin-arm64": "4.11.1",
80843
- "oh-my-opencode-darwin-x64": "4.11.1",
80844
- "oh-my-opencode-darwin-x64-baseline": "4.11.1",
80845
- "oh-my-opencode-linux-arm64": "4.11.1",
80846
- "oh-my-opencode-linux-arm64-musl": "4.11.1",
80847
- "oh-my-opencode-linux-x64": "4.11.1",
80848
- "oh-my-opencode-linux-x64-baseline": "4.11.1",
80849
- "oh-my-opencode-linux-x64-musl": "4.11.1",
80850
- "oh-my-opencode-linux-x64-musl-baseline": "4.11.1",
80851
- "oh-my-opencode-windows-arm64": "4.11.1",
80852
- "oh-my-opencode-windows-x64": "4.11.1",
80853
- "oh-my-opencode-windows-x64-baseline": "4.11.1"
80856
+ "oh-my-opencode-darwin-arm64": "4.12.0",
80857
+ "oh-my-opencode-darwin-x64": "4.12.0",
80858
+ "oh-my-opencode-darwin-x64-baseline": "4.12.0",
80859
+ "oh-my-opencode-linux-arm64": "4.12.0",
80860
+ "oh-my-opencode-linux-arm64-musl": "4.12.0",
80861
+ "oh-my-opencode-linux-x64": "4.12.0",
80862
+ "oh-my-opencode-linux-x64-baseline": "4.12.0",
80863
+ "oh-my-opencode-linux-x64-musl": "4.12.0",
80864
+ "oh-my-opencode-linux-x64-musl-baseline": "4.12.0",
80865
+ "oh-my-opencode-windows-arm64": "4.12.0",
80866
+ "oh-my-opencode-windows-x64": "4.12.0",
80867
+ "oh-my-opencode-windows-x64-baseline": "4.12.0"
80854
80868
  },
80855
80869
  overrides: {
80856
80870
  hono: "^4.12.18",
@@ -113764,10 +113778,60 @@ function getTimingConfig() {
113764
113778
  };
113765
113779
  }
113766
113780
 
113767
- // packages/omo-opencode/src/tools/delegate-task/sync-session-poller.ts
113768
- init_logger2();
113781
+ // packages/omo-opencode/src/tools/delegate-task/sync-session-turns.ts
113769
113782
  var NON_TERMINAL_FINISH_REASONS = new Set(["tool-calls", "unknown"]);
113770
113783
  var PENDING_TOOL_PART_TYPES = new Set(["tool", "tool_use", "tool-call"]);
113784
+ var ALL_BACKGROUND_TASKS_COMPLETE_MARKER = "[ALL BACKGROUND TASKS COMPLETE]";
113785
+ function getTextParts2(message) {
113786
+ return (message.parts ?? []).filter((part) => part.type === "text").map((part) => part.text ?? "").join(`
113787
+ `);
113788
+ }
113789
+ function isInternalAllCompleteWake(message) {
113790
+ return isTerminalNoReplyUserMessage(message) && getTextParts2(message).includes(ALL_BACKGROUND_TASKS_COMPLETE_MARKER);
113791
+ }
113792
+ function getLastSessionTurns(messages) {
113793
+ let lastRelevantUser;
113794
+ let lastAssistant;
113795
+ for (let i = messages.length - 1;i >= 0; i--) {
113796
+ const msg = messages[i];
113797
+ if (msg === undefined)
113798
+ continue;
113799
+ if (!lastAssistant && msg.info?.role === "assistant")
113800
+ lastAssistant = msg;
113801
+ if (!lastRelevantUser && msg.info?.role === "user" && !isInternalAllCompleteWake(msg)) {
113802
+ lastRelevantUser = msg;
113803
+ }
113804
+ if (lastRelevantUser && lastAssistant)
113805
+ break;
113806
+ }
113807
+ return { lastAssistant, lastRelevantUser };
113808
+ }
113809
+ function isSessionComplete(messages) {
113810
+ const { lastAssistant, lastRelevantUser } = getLastSessionTurns(messages);
113811
+ if (!lastAssistant?.info?.finish)
113812
+ return false;
113813
+ if (NON_TERMINAL_FINISH_REASONS.has(lastAssistant.info.finish))
113814
+ return false;
113815
+ if (lastAssistant.parts?.some((part) => part.type && PENDING_TOOL_PART_TYPES.has(part.type)))
113816
+ return false;
113817
+ if (!lastRelevantUser?.info?.id || !lastAssistant?.info?.id)
113818
+ return false;
113819
+ return lastRelevantUser.info.id < lastAssistant.info.id;
113820
+ }
113821
+ function getTerminalSessionError(messages) {
113822
+ const { lastAssistant, lastRelevantUser } = getLastSessionTurns(messages);
113823
+ if (lastRelevantUser?.info?.id && lastAssistant?.info?.id && lastAssistant.info.id <= lastRelevantUser.info.id) {
113824
+ return null;
113825
+ }
113826
+ if (!lastAssistant?.info || !("error" in lastAssistant.info)) {
113827
+ return null;
113828
+ }
113829
+ const errorMessage = extractErrorMessage(lastAssistant.info.error);
113830
+ return errorMessage && errorMessage.length > 0 ? errorMessage : "Session error";
113831
+ }
113832
+
113833
+ // packages/omo-opencode/src/tools/delegate-task/sync-session-poller.ts
113834
+ init_logger2();
113771
113835
  var ACTIVE_SESSION_STATUSES2 = new Set(["busy", "retry", "running"]);
113772
113836
  var CHILD_WAKE_GRACE_MS = 5000;
113773
113837
  function wait(milliseconds) {
@@ -113794,40 +113858,6 @@ async function fetchSessionMessages(client3, sessionID) {
113794
113858
  const rawData = messagesResult?.data ?? messagesResult;
113795
113859
  return Array.isArray(rawData) ? rawData : [];
113796
113860
  }
113797
- function getTerminalSessionError(messages) {
113798
- const lastAssistant = [...messages].reverse().find((msg) => msg.info?.role === "assistant");
113799
- const lastUser = [...messages].reverse().find((msg) => msg.info?.role === "user");
113800
- if (lastUser?.info?.id && lastAssistant?.info?.id && lastAssistant.info.id <= lastUser.info.id) {
113801
- return null;
113802
- }
113803
- if (!lastAssistant?.info || !("error" in lastAssistant.info)) {
113804
- return null;
113805
- }
113806
- const errorMessage = extractErrorMessage(lastAssistant.info.error);
113807
- return errorMessage && errorMessage.length > 0 ? errorMessage : "Session error";
113808
- }
113809
- function isSessionComplete(messages) {
113810
- let lastUser;
113811
- let lastAssistant;
113812
- for (let i = messages.length - 1;i >= 0; i--) {
113813
- const msg = messages[i];
113814
- if (!lastAssistant && msg.info?.role === "assistant")
113815
- lastAssistant = msg;
113816
- if (!lastUser && msg.info?.role === "user")
113817
- lastUser = msg;
113818
- if (lastUser && lastAssistant)
113819
- break;
113820
- }
113821
- if (!lastAssistant?.info?.finish)
113822
- return false;
113823
- if (NON_TERMINAL_FINISH_REASONS.has(lastAssistant.info.finish))
113824
- return false;
113825
- if (lastAssistant.parts?.some((part) => part.type && PENDING_TOOL_PART_TYPES.has(part.type)))
113826
- return false;
113827
- if (!lastUser?.info?.id || !lastAssistant?.info?.id)
113828
- return false;
113829
- return lastUser.info.id < lastAssistant.info.id;
113830
- }
113831
113861
  var DEFAULT_MAX_ASSISTANT_TURNS = 300;
113832
113862
  async function pollSyncSession(ctx, client3, input, timeoutMs) {
113833
113863
  const syncTiming = getTimingConfig();
@@ -113870,11 +113900,12 @@ async function pollSyncSession(ctx, client3, input, timeoutMs) {
113870
113900
  finalMessages = await fetchSessionMessages(client3, input.sessionID);
113871
113901
  break;
113872
113902
  } catch (error) {
113903
+ const errorMessage = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
113873
113904
  log2("[task] Final messages fetch failed after abort, retrying", {
113874
113905
  sessionID: input.sessionID,
113875
113906
  attempt,
113876
113907
  maxAttempts: abortFetchAttempts,
113877
- error: String(error)
113908
+ error: errorMessage
113878
113909
  });
113879
113910
  if (attempt < abortFetchAttempts) {
113880
113911
  await wait(syncTiming.POLL_INTERVAL_MS);
@@ -113924,7 +113955,8 @@ Session ID: ${input.sessionID}`;
113924
113955
  try {
113925
113956
  messages = await fetchSessionMessages(client3, input.sessionID);
113926
113957
  } catch (error) {
113927
- log2("[task] Poll messages fetch failed, retrying", { sessionID: input.sessionID, error: String(error) });
113958
+ const errorMessage = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
113959
+ log2("[task] Poll messages fetch failed, retrying", { sessionID: input.sessionID, error: errorMessage });
113928
113960
  continue;
113929
113961
  }
113930
113962
  if (input.anchorMessageCount !== undefined && messages.length <= input.anchorMessageCount) {
@@ -119356,9 +119388,38 @@ function timestampFromUnknown(value) {
119356
119388
  }
119357
119389
  return;
119358
119390
  }
119391
+ function latestTimestamp(...values) {
119392
+ let latest;
119393
+ for (const value of values) {
119394
+ const timestamp = timestampFromUnknown(value);
119395
+ if (timestamp === undefined) {
119396
+ continue;
119397
+ }
119398
+ if (latest === undefined || timestamp > latest) {
119399
+ latest = timestamp;
119400
+ }
119401
+ }
119402
+ return latest;
119403
+ }
119404
+ function latestTimeActivity(time) {
119405
+ if (!time) {
119406
+ return;
119407
+ }
119408
+ return latestTimestamp(time.created, time.updated, time.completed, time.start, time.end);
119409
+ }
119359
119410
  function getParentWakeMessageCreatedAt(message) {
119360
119411
  return timestampFromUnknown(message.info?.time?.created ?? message.time?.created);
119361
119412
  }
119413
+ function getParentWakeMessageActivityAt(message) {
119414
+ let latest = latestTimestamp(latestTimeActivity(message.info?.time), latestTimeActivity(message.time));
119415
+ for (const part of message.parts ?? []) {
119416
+ const partActivity = latestTimestamp(latestTimeActivity(part.time), latestTimeActivity(part.state?.time));
119417
+ if (partActivity !== undefined && (latest === undefined || partActivity > latest)) {
119418
+ latest = partActivity;
119419
+ }
119420
+ }
119421
+ return latest;
119422
+ }
119362
119423
 
119363
119424
  // packages/omo-opencode/src/features/background-agent/parent-wake-history-state.ts
119364
119425
  function latestAssistantTurnIsCompletedEmptyNoProgress(messages) {
@@ -119408,6 +119469,20 @@ function latestAssistantTurnHasFreshToolActivity(messages, now, maxAgeMs) {
119408
119469
  }
119409
119470
  return false;
119410
119471
  }
119472
+ function latestAssistantTurnHasStaleUnknownSubstantiveOutput(messages, now, maxAgeMs) {
119473
+ for (let index = messages.length - 1;index >= 0; index--) {
119474
+ const message = messages[index];
119475
+ const role = messageRole(message);
119476
+ if (role === "assistant") {
119477
+ const finish = messageFinish(message);
119478
+ return (finish === undefined || finish === "unknown") && !messageCompleted(message) && messageHasSubstantiveAssistantOutput(message) && !messageHasFreshActivity(message, now, maxAgeMs);
119479
+ }
119480
+ if (role === "user" && !messageIsSyntheticOrInternalUser(message)) {
119481
+ return false;
119482
+ }
119483
+ }
119484
+ return false;
119485
+ }
119411
119486
  function createEmptyAssistantTurnRetryDedupeKey(wake) {
119412
119487
  return [
119413
119488
  "background-agent-parent-wake-empty-retry",
@@ -119422,6 +119497,13 @@ function partHasFreshToolActivity(part, now, maxAgeMs) {
119422
119497
  }
119423
119498
  return timeHasFreshActivity(part.time, now, maxAgeMs) || timeHasFreshActivity(isRecord(part.state) ? part.state.time : undefined, now, maxAgeMs);
119424
119499
  }
119500
+ function messageHasFreshActivity(message, now, maxAgeMs) {
119501
+ if (!isRecord(message)) {
119502
+ return false;
119503
+ }
119504
+ const activityAt = getParentWakeMessageActivityAt(message);
119505
+ return activityAt !== undefined && now - activityAt <= maxAgeMs;
119506
+ }
119425
119507
  function timeHasFreshActivity(time, now, maxAgeMs) {
119426
119508
  if (!isRecord(time)) {
119427
119509
  return false;
@@ -119612,6 +119694,22 @@ class ParentWakeFlushRunner {
119612
119694
  });
119613
119695
  return;
119614
119696
  }
119697
+ const finalToolWaitDecision = await this.confirmParentWakeStillSafeForReply(sessionID, latestWake, toolWaitDecision);
119698
+ if (finalToolWaitDecision.defer) {
119699
+ if (this.deferReplyWakeWhileUnsafe(sessionID, latestWake)) {
119700
+ return;
119701
+ }
119702
+ await this.sendParentWakePrompt(sessionID, latestWake, {
119703
+ emptyAssistantTurnRetry,
119704
+ toolWaitDecision: { ...finalToolWaitDecision, skipPromptGateToolStateCheck: true },
119705
+ forceNoReply: true,
119706
+ retainPendingWake: latestWake.shouldReply
119707
+ });
119708
+ log2("[background-agent] Recorded admit-only parent wake because parent session history became unsafe:", {
119709
+ sessionID
119710
+ });
119711
+ return;
119712
+ }
119615
119713
  const dispatchedWake = this.deps.dispatchedTracker.getWake(sessionID);
119616
119714
  if (dispatchedWake && isRedundantParentWake(latestWake, dispatchedWake)) {
119617
119715
  this.deps.pendingQueue.deleteWake(sessionID);
@@ -119620,7 +119718,7 @@ class ParentWakeFlushRunner {
119620
119718
  }
119621
119719
  await this.sendParentWakePrompt(sessionID, latestWake, {
119622
119720
  emptyAssistantTurnRetry,
119623
- toolWaitDecision
119721
+ toolWaitDecision: finalToolWaitDecision
119624
119722
  });
119625
119723
  }
119626
119724
  schedulePendingParentWakeFlush(sessionID, delayMs) {
@@ -119691,6 +119789,12 @@ class ParentWakeFlushRunner {
119691
119789
  async shouldDeferParentWakeForSessionHistory(sessionID, wake) {
119692
119790
  return this.deps.sessionInspector.shouldDeferForHistory(sessionID, wake);
119693
119791
  }
119792
+ async confirmParentWakeStillSafeForReply(sessionID, wake, decision) {
119793
+ if (!decision.skipPromptGateToolStateCheck) {
119794
+ return decision;
119795
+ }
119796
+ return this.deps.sessionInspector.shouldDeferForHistory(sessionID, wake);
119797
+ }
119694
119798
  requeueWake(sessionID, latestWake) {
119695
119799
  this.deps.pendingQueue.requeueWake(sessionID, latestWake);
119696
119800
  }
@@ -119858,6 +119962,12 @@ function getParentWakeSessionHistoryDeferralDecision(input) {
119858
119962
  log2("[background-agent] Holding parent wake during stale tool-call deferral:", { sessionID: input.sessionID });
119859
119963
  return { defer: true, skipPromptGateToolStateCheck: true };
119860
119964
  }
119965
+ if (now - input.wake.toolCallDeferralStartedAt >= input.toolCallDeferMaxMs && latestAssistantTurnHasStaleUnknownSubstantiveOutput(messages, now, input.toolCallDeferMaxMs)) {
119966
+ log2("[background-agent] Retrying parent wake after stale unknown-finish assistant output:", {
119967
+ sessionID: input.sessionID
119968
+ });
119969
+ return { defer: false, skipPromptGateToolStateCheck: true };
119970
+ }
119861
119971
  log2("[background-agent] Deferred parent wake because latest assistant turn blocks internal prompts:", {
119862
119972
  sessionID: input.sessionID
119863
119973
  });
@@ -144898,7 +145008,7 @@ async function loadPluginComponents(params) {
144898
145008
  const errorMessage = error instanceof Error ? error.message : String(error);
144899
145009
  log2("[config-handler] Plugin loading failed", { error: errorMessage });
144900
145010
  addConfigLoadError({ path: "plugin-loading", error: errorMessage });
144901
- return EMPTY_PLUGIN_COMPONENTS;
145011
+ return { ...EMPTY_PLUGIN_COMPONENTS, retryableLoadFailure: true };
144902
145012
  }
144903
145013
  }
144904
145014
 
@@ -145037,8 +145147,38 @@ function collectTrustedVisionCapableModels(pluginConfig) {
145037
145147
  }
145038
145148
  return trusted;
145039
145149
  }
145150
+ function cloneConfigValue(value) {
145151
+ if (Array.isArray(value)) {
145152
+ return value.map(cloneConfigValue);
145153
+ }
145154
+ if (typeof value === "object" && value !== null) {
145155
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, cloneConfigValue(entry)]));
145156
+ }
145157
+ return value;
145158
+ }
145159
+ function cloneAgentConfig(agents) {
145160
+ return cloneConfigValue(agents);
145161
+ }
145162
+ function createAgentConfigCacheKey(config) {
145163
+ return JSON.stringify({
145164
+ agent: config.agent,
145165
+ default_agent: config.default_agent,
145166
+ model: config.model,
145167
+ skills: config.skills
145168
+ });
145169
+ }
145170
+ function replayAgentConfigSideEffects(params) {
145171
+ if (params.configuredDefaultAgent && typeof params.defaultAgent === "string") {
145172
+ setDefaultAgentForSort(params.defaultAgent);
145173
+ }
145174
+ clearRegisteredAgentNames();
145175
+ for (const name of Object.keys(params.agentResult)) {
145176
+ registerAgentName(name);
145177
+ }
145178
+ }
145040
145179
  function createConfigHandler(deps) {
145041
145180
  const { ctx, pluginConfig, modelCacheState, runtimeSkillSourceUrl } = deps;
145181
+ let agentConfigSnapshot;
145042
145182
  return async (config) => {
145043
145183
  const formatterConfig = config.formatter;
145044
145184
  setAdditionalAllowedMcpEnvVars(pluginConfig.mcp_env_allowlist ?? []);
@@ -145049,13 +145189,36 @@ function createConfigHandler(deps) {
145049
145189
  });
145050
145190
  clearFormatterCache();
145051
145191
  const pluginComponents = await loadPluginComponents({ pluginConfig });
145192
+ const pluginComponentsLoadFailed = pluginComponents.retryableLoadFailure === true;
145052
145193
  applyHookConfig({ pluginComponents });
145053
- const agentResult = await applyAgentConfig({
145054
- config,
145055
- pluginConfig,
145056
- ctx,
145057
- pluginComponents
145058
- });
145194
+ const agentCacheKey = createAgentConfigCacheKey(config);
145195
+ let agentResult;
145196
+ if (!pluginComponentsLoadFailed && agentConfigSnapshot?.cacheKey === agentCacheKey) {
145197
+ config.agent = cloneAgentConfig(agentConfigSnapshot.agents);
145198
+ if (agentConfigSnapshot.defaultAgent !== undefined) {
145199
+ config.default_agent = agentConfigSnapshot.defaultAgent;
145200
+ }
145201
+ agentResult = config.agent;
145202
+ replayAgentConfigSideEffects({
145203
+ agentResult,
145204
+ configuredDefaultAgent: agentConfigSnapshot.configuredDefaultAgent,
145205
+ defaultAgent: config.default_agent
145206
+ });
145207
+ } else {
145208
+ const configuredDefaultAgent = getConfiguredDefaultAgent(config);
145209
+ agentResult = await applyAgentConfig({
145210
+ config,
145211
+ pluginConfig,
145212
+ ctx,
145213
+ pluginComponents
145214
+ });
145215
+ agentConfigSnapshot = pluginComponentsLoadFailed ? undefined : {
145216
+ cacheKey: agentCacheKey,
145217
+ configuredDefaultAgent,
145218
+ defaultAgent: config.default_agent,
145219
+ agents: cloneAgentConfig(agentResult)
145220
+ };
145221
+ }
145059
145222
  applyToolConfig({ config, pluginConfig, agentResult });
145060
145223
  await applyMcpConfig({ config, pluginConfig, ctx, pluginComponents });
145061
145224
  await applyCommandConfig({ config, pluginConfig, ctx, pluginComponents });
@@ -149975,6 +150138,15 @@ async function resolveSessionAgent(client3, sessionId) {
149975
150138
  // packages/omo-opencode/src/plugin/tool-execute-before.ts
149976
150139
  init_constants3();
149977
150140
  init_storage();
150141
+ var BACKGROUND_WAIT_BLOCK_MESSAGE = [
150142
+ "Background task wait is already managed by the plugin.",
150143
+ "End this response now and wait for the <system-reminder> completion notification.",
150144
+ "After that reminder arrives, call background_output with the task_id from the launch result."
150145
+ ].join(" ");
150146
+ function isPureSleepCommand(command) {
150147
+ const commandLines = command.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
150148
+ return commandLines.length > 0 && commandLines.every((line) => /^sleep\s+\d+(?:\.\d+)?[smhd]?\s*$/i.test(line));
150149
+ }
149978
150150
  function getLoopCommandArguments(args, command) {
149979
150151
  const rawUserMessage = typeof args.user_message === "string" ? args.user_message.trim() : "";
149980
150152
  if (rawUserMessage) {
@@ -149984,7 +150156,7 @@ function getLoopCommandArguments(args, command) {
149984
150156
  return rawName.replace(new RegExp(`^/?(${command})\\s*`, "i"), "");
149985
150157
  }
149986
150158
  function createToolExecuteBeforeHandler3(args) {
149987
- const { ctx, hooks } = args;
150159
+ const { ctx, hooks, backgroundManager } = args;
149988
150160
  function buildUltraworkOracleVerificationPrompt(prompt, originalTask, verificationAttemptId) {
149989
150161
  const verificationPrompt = [
149990
150162
  "You are verifying the active ULTRAWORK loop result for this session.",
@@ -150025,6 +150197,9 @@ function createToolExecuteBeforeHandler3(args) {
150025
150197
  callID: input.callID
150026
150198
  });
150027
150199
  }
150200
+ if (isPureSleepCommand(output.args.command) && (backgroundManager?.hasActiveChildTasks(input.sessionID) === true || backgroundManager?.hasPendingParentWake(input.sessionID) === true)) {
150201
+ throw new Error(BACKGROUND_WAIT_BLOCK_MESSAGE);
150202
+ }
150028
150203
  }
150029
150204
  await hooks.writeExistingFileGuard?.["tool.execute.before"]?.(input, output);
150030
150205
  await hooks.notepadWriteGuard?.["tool.execute.before"]?.(input, output);
@@ -150192,7 +150367,8 @@ function createPluginInterface(args) {
150192
150367
  }),
150193
150368
  "tool.execute.before": createToolExecuteBeforeHandler3({
150194
150369
  ctx,
150195
- hooks
150370
+ hooks,
150371
+ backgroundManager: managers.backgroundManager
150196
150372
  }),
150197
150373
  "tool.execute.after": createToolExecuteAfterHandler3({
150198
150374
  ctx,
@@ -1,8 +1,10 @@
1
1
  import type { PluginContext } from "./types";
2
2
  import type { CreatedHooks } from "../create-hooks";
3
+ import type { BackgroundManager } from "../features/background-agent";
3
4
  export declare function createToolExecuteBeforeHandler(args: {
4
5
  ctx: PluginContext;
5
6
  hooks: CreatedHooks;
7
+ backgroundManager?: Pick<BackgroundManager, "hasActiveChildTasks" | "hasPendingParentWake">;
6
8
  }): (input: {
7
9
  tool: string;
8
10
  sessionID: string;
@@ -15,6 +15,7 @@ export type PluginComponents = {
15
15
  installPath: string;
16
16
  error: string;
17
17
  }>;
18
+ retryableLoadFailure?: true;
18
19
  };
19
20
  export declare function loadPluginComponents(params: {
20
21
  pluginConfig: OhMyOpenCodeConfig;
@@ -17,6 +17,7 @@ Outcome-first: explore a lot, ask few sharp questions - or none, when the intent
17
17
 
18
18
  After grounding, make ONE judgment and load ONE intent reference (you ALSO read `references/full-workflow.md` for the shared mechanics - see below). The test keys on whether the desired **OUTCOME** is clear, NOT on request length.
19
19
 
20
+ - **OVERRIDE - explicit ask wins:** if the user explicitly asks to be questioned or interviewed ("ask me", "interview me", "why aren't you asking me" - in any language), route **CLEAR**, run the interview, and turn the adopt-default filter OFF: the user has claimed the forks, so every surviving one is ASKED, not defaulted. This beats the OUTCOME test below, even on a fuzzy brief.
20
21
  - **CLEAR** - the user knows the outcome; the only open items are preferences/tradeoffs the repo cannot answer (genuine owner-decisions). Read **`references/intent-clear.md`**: ask the surviving forks with WHY, run the normal approval gate, high-accuracy review is OPTIONAL (offered as one question).
21
22
  - **UNCLEAR** - the outcome itself is fuzzy (a vague brief, a bootstrap, `$start-work` with no selectable plan, a goal the user cannot yet articulate). Asking would offload your own job onto the user. Read **`references/intent-unclear.md`**: research maximally, adopt and ANNOUNCE best-practice defaults, do NOT ask the user extra questions, and run high-accuracy review AUTOMATICALLY (unless Classify sized the work Trivial).
22
23
  - **ON THE FENCE** - when CLEAR vs UNCLEAR is genuinely ambiguous, treat it as CLEAR and ask exactly ONE question. A user wrongly silenced is worse than one extra question. The dominant failure to guard against is mis-routing a CLEAR request to UNCLEAR, which silently applies defaults and overrides forks the user wanted to own.
@@ -42,7 +43,7 @@ Run it ONCE at plan generation. A plain re-run on an existing plan is a safe no-
42
43
  - **Decision-complete is the north star.** The executor has NO interview context - spell out exact paths, "every X in Y", and an explicit Must-NOT-Have. Leave the implementer ZERO judgment calls.
43
44
  - **Explore before asking.** Discoverable facts (repo/system/docs truth) -> research and cite, never ask. Preferences/tradeoffs -> the only things you bring to the user. When unsure which, treat it as a user-decision.
44
45
  - **CodeGraph first when present.** Use `codegraph_explore` for repo how/where/what/flow questions before wider reads; if codegraph_* tools are absent, inactive/uninitialized, or cold-start unavailable, continue with Read/Grep/Glob/LSP and the ast-grep skill.
45
- - **Two filters** on every candidate question, in order: (1) Could collected evidence answer it? -> explore instead. (2) Could the user's stated intent plus a defensible default answer it? -> adopt the default, record it, do not ask. Only a real fork survives.
46
+ - **Two filters** on every candidate question, in order: (1) Could collected evidence answer it? -> explore instead. (2) Could the user's stated intent plus a defensible default answer it? -> adopt the default, record it, do not ask - UNLESS it is an owner-decision, which always survives as a question even when a default exists: anything irreversible / destructive / safety-critical, or a cross-cutting product choice the user lives with (public config surface, distribution / packaging, external dependency or pinned SHA, data / schema shape). Default the reversible internals; surface the owner-decisions.
46
47
  - **Explore to sufficiency, then STOP.** One research wave per open question; stop when the clearance check is answerable; never re-explore to double-check.
47
48
  - **Parallel-dispatch** independent research in ONE turn and keep working while it runs. Subagent outputs are CLAIMS until you independently verify them.
48
49
  - **Approval is not execution.** Approval authorizes writing the plan ONLY, never implementation. ONE request -> ONE plan, however large.