oh-my-opencode 3.7.3 → 3.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.ja.md +8 -8
  2. package/README.ko.md +8 -8
  3. package/README.md +8 -8
  4. package/README.zh-cn.md +8 -8
  5. package/dist/agents/atlas/default.d.ts +1 -1
  6. package/dist/agents/atlas/gpt.d.ts +1 -1
  7. package/dist/agents/metis.d.ts +1 -1
  8. package/dist/agents/prometheus/behavioral-summary.d.ts +1 -1
  9. package/dist/agents/prometheus/identity-constraints.d.ts +1 -1
  10. package/dist/agents/prometheus/interview-mode.d.ts +1 -1
  11. package/dist/agents/prometheus/plan-generation.d.ts +1 -1
  12. package/dist/agents/prometheus/plan-template.d.ts +1 -1
  13. package/dist/agents/prometheus/system-prompt.d.ts +1 -1
  14. package/dist/agents/sisyphus-junior/agent.d.ts +1 -1
  15. package/dist/cli/config-manager/antigravity-provider-configuration.d.ts +2 -2
  16. package/dist/cli/index.js +187 -83
  17. package/dist/cli/model-fallback-types.d.ts +5 -0
  18. package/dist/cli/run/event-state.d.ts +4 -0
  19. package/dist/cli/run/stdin-suppression.d.ts +12 -0
  20. package/dist/config/schema/agent-overrides.d.ts +60 -0
  21. package/dist/config/schema/hooks.d.ts +2 -1
  22. package/dist/config/schema/oh-my-opencode-config.d.ts +58 -1
  23. package/dist/create-hooks.d.ts +2 -1
  24. package/dist/features/background-agent/parent-session-context-resolver.d.ts +1 -0
  25. package/dist/hooks/atlas/recent-model-resolver.d.ts +6 -0
  26. package/dist/hooks/atlas/system-reminder-templates.d.ts +1 -1
  27. package/dist/hooks/index.d.ts +2 -1
  28. package/dist/hooks/{sisyphus-gpt-hephaestus-reminder → no-sisyphus-gpt}/hook.d.ts +6 -1
  29. package/dist/hooks/no-sisyphus-gpt/index.d.ts +1 -0
  30. package/dist/hooks/session-recovery/types.d.ts +1 -0
  31. package/dist/hooks/ultrawork-model-override/hook.d.ts +6 -0
  32. package/dist/hooks/ultrawork-model-override/index.d.ts +1 -0
  33. package/dist/hooks/unstable-agent-babysitter/task-message-analyzer.d.ts +1 -0
  34. package/dist/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.d.ts +2 -0
  35. package/dist/index.js +559 -453
  36. package/dist/plugin/hooks/create-core-hooks.d.ts +2 -1
  37. package/dist/plugin/hooks/create-session-hooks.d.ts +4 -2
  38. package/dist/shared/index.d.ts +1 -0
  39. package/dist/shared/prompt-tools.d.ts +3 -0
  40. package/dist/tools/delegate-task/constants.d.ts +1 -1
  41. package/package.json +8 -8
  42. package/dist/cli/run/opencode-bin-path.d.ts +0 -3
  43. package/dist/hooks/sisyphus-gpt-hephaestus-reminder/index.d.ts +0 -1
package/dist/cli/index.js CHANGED
@@ -6513,6 +6513,7 @@ var init_hook_names = __esm(() => {
6513
6513
  HOOK_NAME_MAP = {
6514
6514
  "anthropic-auto-compact": "anthropic-context-window-limit-recovery",
6515
6515
  "sisyphus-orchestrator": "atlas",
6516
+ "sisyphus-gpt-hephaestus-reminder": "no-sisyphus-gpt",
6516
6517
  "empty-message-sanitizer": null
6517
6518
  };
6518
6519
  });
@@ -6550,7 +6551,8 @@ var MODEL_VERSION_MAP;
6550
6551
  var init_model_versions = __esm(() => {
6551
6552
  MODEL_VERSION_MAP = {
6552
6553
  "openai/gpt-5.2-codex": "openai/gpt-5.3-codex",
6553
- "anthropic/claude-opus-4-5": "anthropic/claude-opus-4-6"
6554
+ "anthropic/claude-opus-4-5": "anthropic/claude-opus-4-6",
6555
+ "anthropic/claude-sonnet-4-5": "anthropic/claude-sonnet-4-6"
6554
6556
  };
6555
6557
  });
6556
6558
 
@@ -6786,7 +6788,7 @@ var init_model_requirements = __esm(() => {
6786
6788
  { providers: ["kimi-for-coding"], model: "k2p5" },
6787
6789
  { providers: ["opencode"], model: "kimi-k2.5-free" },
6788
6790
  { providers: ["zai-coding-plan"], model: "glm-4.7" },
6789
- { providers: ["opencode"], model: "glm-4.7-free" }
6791
+ { providers: ["opencode"], model: "big-pickle" }
6790
6792
  ],
6791
6793
  requiresAnyModel: true
6792
6794
  },
@@ -6806,8 +6808,8 @@ var init_model_requirements = __esm(() => {
6806
6808
  librarian: {
6807
6809
  fallbackChain: [
6808
6810
  { providers: ["zai-coding-plan"], model: "glm-4.7" },
6809
- { providers: ["opencode"], model: "glm-4.7-free" },
6810
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" }
6811
+ { providers: ["opencode"], model: "big-pickle" },
6812
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" }
6811
6813
  ]
6812
6814
  },
6813
6815
  explore: {
@@ -6857,7 +6859,7 @@ var init_model_requirements = __esm(() => {
6857
6859
  fallbackChain: [
6858
6860
  { providers: ["kimi-for-coding"], model: "k2p5" },
6859
6861
  { providers: ["opencode"], model: "kimi-k2.5-free" },
6860
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
6862
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
6861
6863
  { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
6862
6864
  { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
6863
6865
  ]
@@ -6904,7 +6906,7 @@ var init_model_requirements = __esm(() => {
6904
6906
  },
6905
6907
  "unspecified-low": {
6906
6908
  fallbackChain: [
6907
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
6909
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
6908
6910
  { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
6909
6911
  { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" }
6910
6912
  ]
@@ -6920,7 +6922,7 @@ var init_model_requirements = __esm(() => {
6920
6922
  fallbackChain: [
6921
6923
  { providers: ["kimi-for-coding"], model: "k2p5" },
6922
6924
  { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
6923
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" }
6925
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" }
6924
6926
  ]
6925
6927
  }
6926
6928
  };
@@ -7268,6 +7270,17 @@ var init_safe_create_hook = __esm(() => {
7268
7270
  // src/shared/session-directory-resolver.ts
7269
7271
  var init_session_directory_resolver = () => {};
7270
7272
 
7273
+ // src/shared/session-tools-store.ts
7274
+ var store;
7275
+ var init_session_tools_store = __esm(() => {
7276
+ store = new Map;
7277
+ });
7278
+
7279
+ // src/shared/prompt-tools.ts
7280
+ var init_prompt_tools = __esm(() => {
7281
+ init_session_tools_store();
7282
+ });
7283
+
7271
7284
  // src/shared/index.ts
7272
7285
  var init_shared = __esm(() => {
7273
7286
  init_model_resolution_pipeline();
@@ -7311,6 +7324,7 @@ var init_shared = __esm(() => {
7311
7324
  init_opencode_storage_paths();
7312
7325
  init_opencode_message_dir();
7313
7326
  init_session_directory_resolver();
7327
+ init_prompt_tools();
7314
7328
  });
7315
7329
 
7316
7330
  // src/cli/config-manager/config-context.ts
@@ -7581,7 +7595,7 @@ function isProviderAvailable(provider, availability) {
7581
7595
  // src/cli/provider-model-id-transform.ts
7582
7596
  function transformModelForProvider(provider, model) {
7583
7597
  if (provider === "github-copilot") {
7584
- return model.replace("claude-opus-4-6", "claude-opus-4.6").replace("claude-sonnet-4-5", "claude-sonnet-4.5").replace("claude-haiku-4-5", "claude-haiku-4.5").replace("claude-sonnet-4", "claude-sonnet-4").replace("gemini-3-pro", "gemini-3-pro-preview").replace("gemini-3-flash", "gemini-3-flash-preview");
7598
+ return model.replace("claude-opus-4-6", "claude-opus-4.6").replace("claude-sonnet-4-6", "claude-sonnet-4.6").replace("claude-haiku-4-5", "claude-haiku-4.5").replace("claude-sonnet-4", "claude-sonnet-4").replace("gemini-3-pro", "gemini-3-pro-preview").replace("gemini-3-flash", "gemini-3-flash-preview");
7585
7599
  }
7586
7600
  return model;
7587
7601
  }
@@ -7655,6 +7669,14 @@ function generateModelConfig(config) {
7655
7669
  if (req.requiresAnyModel && !isAnyFallbackEntryAvailable(fallbackChain, avail)) {
7656
7670
  continue;
7657
7671
  }
7672
+ if (avail.native.claude && !avail.isMaxPlan) {
7673
+ agents[role] = {
7674
+ model: "anthropic/claude-sonnet-4-6",
7675
+ variant: "max",
7676
+ ultrawork: { model: "anthropic/claude-opus-4-6", variant: "max" }
7677
+ };
7678
+ continue;
7679
+ }
7658
7680
  const resolved2 = resolveModelFromChain(fallbackChain, avail);
7659
7681
  if (resolved2) {
7660
7682
  const variant = resolved2.variant ?? req.variant;
@@ -7698,7 +7720,7 @@ function generateModelConfig(config) {
7698
7720
  categories
7699
7721
  };
7700
7722
  }
7701
- var ZAI_MODEL = "zai-coding-plan/glm-4.7", ULTIMATE_FALLBACK = "opencode/glm-4.7-free", SCHEMA_URL = "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json";
7723
+ var ZAI_MODEL = "zai-coding-plan/glm-4.7", ULTIMATE_FALLBACK = "opencode/big-pickle", SCHEMA_URL = "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json";
7702
7724
  var init_model_fallback = __esm(() => {
7703
7725
  init_model_requirements();
7704
7726
  init_fallback_chain_resolution();
@@ -7983,13 +8005,13 @@ var init_antigravity_provider_configuration = __esm(() => {
7983
8005
  high: { thinkingLevel: "high" }
7984
8006
  }
7985
8007
  },
7986
- "antigravity-claude-sonnet-4-5": {
7987
- name: "Claude Sonnet 4.5 (Antigravity)",
8008
+ "antigravity-claude-sonnet-4-6": {
8009
+ name: "Claude Sonnet 4.6 (Antigravity)",
7988
8010
  limit: { context: 200000, output: 64000 },
7989
8011
  modalities: { input: ["text", "image", "pdf"], output: ["text"] }
7990
8012
  },
7991
- "antigravity-claude-sonnet-4-5-thinking": {
7992
- name: "Claude Sonnet 4.5 Thinking (Antigravity)",
8013
+ "antigravity-claude-sonnet-4-6-thinking": {
8014
+ name: "Claude Sonnet 4.6 Thinking (Antigravity)",
7993
8015
  limit: { context: 200000, output: 64000 },
7994
8016
  modalities: { input: ["text", "image", "pdf"], output: ["text"] },
7995
8017
  variants: {
@@ -9053,7 +9075,7 @@ var {
9053
9075
  // package.json
9054
9076
  var package_default = {
9055
9077
  name: "oh-my-opencode",
9056
- version: "3.7.3",
9078
+ version: "3.7.4",
9057
9079
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
9058
9080
  main: "dist/index.js",
9059
9081
  types: "dist/index.d.ts",
@@ -9127,13 +9149,13 @@ var package_default = {
9127
9149
  typescript: "^5.7.3"
9128
9150
  },
9129
9151
  optionalDependencies: {
9130
- "oh-my-opencode-darwin-arm64": "3.7.3",
9131
- "oh-my-opencode-darwin-x64": "3.7.3",
9132
- "oh-my-opencode-linux-arm64": "3.7.3",
9133
- "oh-my-opencode-linux-arm64-musl": "3.7.3",
9134
- "oh-my-opencode-linux-x64": "3.7.3",
9135
- "oh-my-opencode-linux-x64-musl": "3.7.3",
9136
- "oh-my-opencode-windows-x64": "3.7.3"
9152
+ "oh-my-opencode-darwin-arm64": "3.7.4",
9153
+ "oh-my-opencode-darwin-x64": "3.7.4",
9154
+ "oh-my-opencode-linux-arm64": "3.7.4",
9155
+ "oh-my-opencode-linux-arm64-musl": "3.7.4",
9156
+ "oh-my-opencode-linux-x64": "3.7.4",
9157
+ "oh-my-opencode-linux-x64-musl": "3.7.4",
9158
+ "oh-my-opencode-windows-x64": "3.7.4"
9137
9159
  },
9138
9160
  trustedDependencies: [
9139
9161
  "@ast-grep/cli",
@@ -9366,7 +9388,7 @@ async function runCliInstaller(args, version) {
9366
9388
  console.log();
9367
9389
  }
9368
9390
  if (!config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen) {
9369
- printWarning("No model providers configured. Using opencode/glm-4.7-free as fallback.");
9391
+ printWarning("No model providers configured. Using opencode/big-pickle as fallback.");
9370
9392
  }
9371
9393
  console.log(`${SYMBOLS.star} ${import_picocolors2.default.bold(import_picocolors2.default.green(isUpdate ? "Configuration updated!" : "Installation complete!"))}`);
9372
9394
  console.log(` Run ${import_picocolors2.default.cyan("opencode")} to start!`);
@@ -9992,9 +10014,9 @@ async function promptInstallConfig(detected) {
9992
10014
  const claude = await selectOrCancel({
9993
10015
  message: "Do you have a Claude Pro/Max subscription?",
9994
10016
  options: [
9995
- { value: "no", label: "No", hint: "Will use opencode/glm-4.7-free as fallback" },
10017
+ { value: "no", label: "No", hint: "Will use opencode/big-pickle as fallback" },
9996
10018
  { value: "yes", label: "Yes (standard)", hint: "Claude Opus 4.5 for orchestration" },
9997
- { value: "max20", label: "Yes (max20 mode)", hint: "Full power with Claude Sonnet 4.5 for Librarian" }
10019
+ { value: "max20", label: "Yes (max20 mode)", hint: "Full power with Claude Sonnet 4.6 for Librarian" }
9998
10020
  ],
9999
10021
  initialValue: initial.claude
10000
10022
  });
@@ -10147,7 +10169,7 @@ async function runTuiInstaller(args, version) {
10147
10169
  console.log();
10148
10170
  }
10149
10171
  if (!config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen) {
10150
- M2.warn("No model providers configured. Using opencode/glm-4.7-free as fallback.");
10172
+ M2.warn("No model providers configured. Using opencode/big-pickle as fallback.");
10151
10173
  }
10152
10174
  Me(formatConfigSummary(config), isUpdate ? "Updated Configuration" : "Installation Complete");
10153
10175
  M2.success(import_picocolors4.default.bold(isUpdate ? "Configuration updated!" : "Installation complete!"));
@@ -10212,7 +10234,9 @@ function createEventState() {
10212
10234
  lastThinkingSummary: "",
10213
10235
  textAtLineStart: true,
10214
10236
  thinkingAtLineStart: false,
10215
- currentMessageId: null
10237
+ currentMessageId: null,
10238
+ messageStartedAtById: {},
10239
+ completionMetaPrintedByMessageId: {}
10216
10240
  };
10217
10241
  }
10218
10242
  // src/cli/run/event-formatting.ts
@@ -10572,6 +10596,19 @@ function getPartMessageId(props) {
10572
10596
  function getDeltaMessageId(props) {
10573
10597
  return props?.messageID;
10574
10598
  }
10599
+ function renderCompletionMetaLine(state, messageID) {
10600
+ if (state.completionMetaPrintedByMessageId[messageID])
10601
+ return;
10602
+ const startedAt = state.messageStartedAtById[messageID];
10603
+ const elapsedSec = startedAt ? ((Date.now() - startedAt) / 1000).toFixed(1) : "0.0";
10604
+ const agent = state.currentAgent ?? "assistant";
10605
+ const model = state.currentModel ?? "unknown-model";
10606
+ const variant = state.currentVariant ? ` (${state.currentVariant})` : "";
10607
+ process.stdout.write(import_picocolors7.default.dim(`
10608
+ ${displayChars.treeEnd} ${agent} \xB7 ${model}${variant} \xB7 ${elapsedSec}s
10609
+ `));
10610
+ state.completionMetaPrintedByMessageId[messageID] = true;
10611
+ }
10575
10612
  function handleSessionIdle(ctx, payload, state) {
10576
10613
  if (payload.type !== "session.idle")
10577
10614
  return;
@@ -10646,6 +10683,12 @@ function handleMessagePartUpdated(ctx, payload, state) {
10646
10683
  state.hasReceivedMeaningfulWork = true;
10647
10684
  }
10648
10685
  state.lastPartText = part.text;
10686
+ if (part.time?.end) {
10687
+ const messageID = part.messageID ?? state.currentMessageId;
10688
+ if (messageID) {
10689
+ renderCompletionMetaLine(state, messageID);
10690
+ }
10691
+ }
10649
10692
  }
10650
10693
  if (part.type === "tool") {
10651
10694
  handleToolPart(ctx, part, state);
@@ -10740,6 +10783,10 @@ function handleMessageUpdated(ctx, payload, state) {
10740
10783
  state.textAtLineStart = true;
10741
10784
  state.thinkingAtLineStart = false;
10742
10785
  closeThinkBlockIfNeeded(state);
10786
+ if (messageID) {
10787
+ state.messageStartedAtById[messageID] = Date.now();
10788
+ state.completionMetaPrintedByMessageId[messageID] = false;
10789
+ }
10743
10790
  }
10744
10791
  const agent = props?.info?.agent ?? null;
10745
10792
  const model = props?.info?.modelID ?? null;
@@ -23244,6 +23291,10 @@ var AgentOverrideConfigSchema = exports_external.object({
23244
23291
  type: exports_external.enum(["enabled", "disabled"]),
23245
23292
  budgetTokens: exports_external.number().optional()
23246
23293
  }).optional(),
23294
+ ultrawork: exports_external.object({
23295
+ model: exports_external.string(),
23296
+ variant: exports_external.string().optional()
23297
+ }).optional(),
23247
23298
  reasoningEffort: exports_external.enum(["low", "medium", "high", "xhigh"]).optional(),
23248
23299
  textVerbosity: exports_external.enum(["low", "medium", "high"]).optional(),
23249
23300
  providerOptions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
@@ -23413,6 +23464,7 @@ var HookNameSchema = exports_external.enum([
23413
23464
  "non-interactive-env",
23414
23465
  "interactive-bash-session",
23415
23466
  "thinking-block-validator",
23467
+ "ultrawork-model-override",
23416
23468
  "ralph-loop",
23417
23469
  "category-skill-reminder",
23418
23470
  "compaction-context-injector",
@@ -23424,7 +23476,7 @@ var HookNameSchema = exports_external.enum([
23424
23476
  "delegate-task-retry",
23425
23477
  "prometheus-md-only",
23426
23478
  "sisyphus-junior-notepad",
23427
- "sisyphus-gpt-hephaestus-reminder",
23479
+ "no-sisyphus-gpt",
23428
23480
  "start-work",
23429
23481
  "atlas",
23430
23482
  "unstable-agent-babysitter",
@@ -25192,26 +25244,6 @@ async function withWorkingOpencodePath(startServer, finder = findWorkingOpencode
25192
25244
  }
25193
25245
  }
25194
25246
 
25195
- // src/cli/run/opencode-bin-path.ts
25196
- import { delimiter as delimiter2, dirname as dirname2 } from "path";
25197
- import { createRequire } from "module";
25198
- var resolveFromCurrentModule = createRequire(import.meta.url).resolve;
25199
- function prependResolvedOpencodeBinToPath(env = process.env, resolve2 = resolveFromCurrentModule) {
25200
- let resolvedPath;
25201
- try {
25202
- resolvedPath = resolve2("opencode-ai/bin/opencode");
25203
- } catch {
25204
- return;
25205
- }
25206
- const opencodeBinDir = dirname2(resolvedPath);
25207
- const currentPath = env.PATH ?? "";
25208
- const pathSegments = currentPath ? currentPath.split(delimiter2) : [];
25209
- if (pathSegments.includes(opencodeBinDir)) {
25210
- return;
25211
- }
25212
- env.PATH = currentPath ? `${opencodeBinDir}${delimiter2}${currentPath}` : opencodeBinDir;
25213
- }
25214
-
25215
25247
  // src/cli/run/server-connection.ts
25216
25248
  function isPortStartFailure(error45, port) {
25217
25249
  if (!(error45 instanceof Error)) {
@@ -25219,6 +25251,12 @@ function isPortStartFailure(error45, port) {
25219
25251
  }
25220
25252
  return error45.message.includes(`Failed to start server on port ${port}`);
25221
25253
  }
25254
+ function isPortRangeExhausted(error45) {
25255
+ if (!(error45 instanceof Error)) {
25256
+ return false;
25257
+ }
25258
+ return error45.message.includes("No available port found in range");
25259
+ }
25222
25260
  async function startServer(options) {
25223
25261
  const { signal, port } = options;
25224
25262
  const { client: client3, server: server2 } = await withWorkingOpencodePath(() => createOpencode({ signal, port, hostname: "127.0.0.1" }));
@@ -25226,7 +25264,6 @@ async function startServer(options) {
25226
25264
  return { client: client3, cleanup: () => server2.close() };
25227
25265
  }
25228
25266
  async function createServerConnection(options) {
25229
- prependResolvedOpencodeBinToPath();
25230
25267
  const { port, attach, signal } = options;
25231
25268
  if (attach !== undefined) {
25232
25269
  console.log(import_picocolors9.default.dim("Attaching to existing server at"), import_picocolors9.default.cyan(attach));
@@ -25259,7 +25296,24 @@ async function createServerConnection(options) {
25259
25296
  const client3 = createOpencodeClient({ baseUrl: `http://127.0.0.1:${port}` });
25260
25297
  return { client: client3, cleanup: () => {} };
25261
25298
  }
25262
- const { port: selectedPort, wasAutoSelected } = await getAvailableServerPort(DEFAULT_SERVER_PORT, "127.0.0.1");
25299
+ let selectedPort;
25300
+ let wasAutoSelected;
25301
+ try {
25302
+ const selected = await getAvailableServerPort(DEFAULT_SERVER_PORT, "127.0.0.1");
25303
+ selectedPort = selected.port;
25304
+ wasAutoSelected = selected.wasAutoSelected;
25305
+ } catch (error45) {
25306
+ if (!isPortRangeExhausted(error45)) {
25307
+ throw error45;
25308
+ }
25309
+ const defaultPortIsAvailable = await isPortAvailable(DEFAULT_SERVER_PORT, "127.0.0.1");
25310
+ if (defaultPortIsAvailable) {
25311
+ throw error45;
25312
+ }
25313
+ console.log(import_picocolors9.default.dim("Port range exhausted, attaching to existing server on"), import_picocolors9.default.cyan(DEFAULT_SERVER_PORT.toString()));
25314
+ const client3 = createOpencodeClient({ baseUrl: `http://127.0.0.1:${DEFAULT_SERVER_PORT}` });
25315
+ return { client: client3, cleanup: () => {} };
25316
+ }
25263
25317
  if (wasAutoSelected) {
25264
25318
  console.log(import_picocolors9.default.dim("Auto-selected port"), import_picocolors9.default.cyan(selectedPort.toString()));
25265
25319
  } else {
@@ -25457,7 +25511,7 @@ var NOTEPAD_DIR = "notepads";
25457
25511
  var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
25458
25512
  // src/features/boulder-state/storage.ts
25459
25513
  import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync3, readdirSync } from "fs";
25460
- import { dirname as dirname3, join as join9, basename } from "path";
25514
+ import { dirname as dirname2, join as join9, basename } from "path";
25461
25515
  function getBoulderFilePath(directory) {
25462
25516
  return join9(directory, BOULDER_DIR, BOULDER_FILE);
25463
25517
  }
@@ -25538,7 +25592,7 @@ function getActiveContinuationMarkerReason(marker) {
25538
25592
  // src/hooks/ralph-loop/storage.ts
25539
25593
  init_frontmatter();
25540
25594
  import { existsSync as existsSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync9, unlinkSync, mkdirSync as mkdirSync5 } from "fs";
25541
- import { dirname as dirname4, join as join11 } from "path";
25595
+ import { dirname as dirname3, join as join11 } from "path";
25542
25596
 
25543
25597
  // src/hooks/ralph-loop/constants.ts
25544
25598
  var DEFAULT_STATE_FILE = ".sisyphus/ralph-loop.local.md";
@@ -25623,7 +25677,7 @@ async function checkCompletionConditions(ctx) {
25623
25677
  const continuationState = getContinuationState(ctx.directory, ctx.sessionID);
25624
25678
  if (continuationState.hasActiveHookMarker) {
25625
25679
  const reason = continuationState.activeHookMarkerReason ?? "continuation hook is active";
25626
- console.log(import_picocolors13.default.dim(` Waiting: ${reason}`));
25680
+ logWaiting(ctx, reason);
25627
25681
  return false;
25628
25682
  }
25629
25683
  if (!continuationState.hasTodoHookMarker && !await areAllTodosComplete(ctx)) {
@@ -25632,7 +25686,7 @@ async function checkCompletionConditions(ctx) {
25632
25686
  if (!await areAllChildrenIdle(ctx)) {
25633
25687
  return false;
25634
25688
  }
25635
- if (!areContinuationHooksIdle(continuationState)) {
25689
+ if (!areContinuationHooksIdle(ctx, continuationState)) {
25636
25690
  return false;
25637
25691
  }
25638
25692
  return true;
@@ -25641,13 +25695,13 @@ async function checkCompletionConditions(ctx) {
25641
25695
  return false;
25642
25696
  }
25643
25697
  }
25644
- function areContinuationHooksIdle(continuationState) {
25698
+ function areContinuationHooksIdle(ctx, continuationState) {
25645
25699
  if (continuationState.hasActiveBoulder) {
25646
- console.log(import_picocolors13.default.dim(" Waiting: boulder continuation is active"));
25700
+ logWaiting(ctx, "boulder continuation is active");
25647
25701
  return false;
25648
25702
  }
25649
25703
  if (continuationState.hasActiveRalphLoop) {
25650
- console.log(import_picocolors13.default.dim(" Waiting: ralph-loop continuation is active"));
25704
+ logWaiting(ctx, "ralph-loop continuation is active");
25651
25705
  return false;
25652
25706
  }
25653
25707
  return true;
@@ -25660,7 +25714,7 @@ async function areAllTodosComplete(ctx) {
25660
25714
  const todos = normalizeSDKResponse(todosRes, []);
25661
25715
  const incompleteTodos = todos.filter((t) => t.status !== "completed" && t.status !== "cancelled");
25662
25716
  if (incompleteTodos.length > 0) {
25663
- console.log(import_picocolors13.default.dim(` Waiting: ${incompleteTodos.length} todos remaining`));
25717
+ logWaiting(ctx, `${incompleteTodos.length} todos remaining`);
25664
25718
  return false;
25665
25719
  }
25666
25720
  return true;
@@ -25684,7 +25738,7 @@ async function areAllDescendantsIdle(ctx, sessionID, allStatuses) {
25684
25738
  for (const child of children) {
25685
25739
  const status = allStatuses[child.id];
25686
25740
  if (status && status.type !== "idle") {
25687
- console.log(import_picocolors13.default.dim(` Waiting: session ${child.id.slice(0, 8)}... is ${status.type}`));
25741
+ logWaiting(ctx, `session ${child.id.slice(0, 8)}... is ${status.type}`);
25688
25742
  return false;
25689
25743
  }
25690
25744
  const descendantsIdle = await areAllDescendantsIdle(ctx, child.id, allStatuses);
@@ -25694,6 +25748,12 @@ async function areAllDescendantsIdle(ctx, sessionID, allStatuses) {
25694
25748
  }
25695
25749
  return true;
25696
25750
  }
25751
+ function logWaiting(ctx, message) {
25752
+ if (!ctx.verbose) {
25753
+ return;
25754
+ }
25755
+ console.log(import_picocolors13.default.dim(` Waiting: ${message}`));
25756
+ }
25697
25757
 
25698
25758
  // src/cli/run/poll-for-completion.ts
25699
25759
  init_shared();
@@ -25813,6 +25873,41 @@ async function loadAgentProfileColors(client3) {
25813
25873
  }
25814
25874
  }
25815
25875
 
25876
+ // src/cli/run/stdin-suppression.ts
25877
+ function includesCtrlC(chunk) {
25878
+ const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
25879
+ return text.includes("\x03");
25880
+ }
25881
+ function suppressRunInput(stdin = process.stdin, onInterrupt = () => {
25882
+ process.kill(process.pid, "SIGINT");
25883
+ }) {
25884
+ if (!stdin.isTTY) {
25885
+ return () => {};
25886
+ }
25887
+ const wasRaw = stdin.isRaw === true;
25888
+ const wasPaused = stdin.isPaused?.() ?? false;
25889
+ const canSetRawMode = typeof stdin.setRawMode === "function";
25890
+ const onData = (chunk) => {
25891
+ if (includesCtrlC(chunk)) {
25892
+ onInterrupt();
25893
+ }
25894
+ };
25895
+ if (canSetRawMode) {
25896
+ stdin.setRawMode(true);
25897
+ }
25898
+ stdin.on("data", onData);
25899
+ stdin.resume();
25900
+ return () => {
25901
+ stdin.removeListener("data", onData);
25902
+ if (canSetRawMode) {
25903
+ stdin.setRawMode(wasRaw);
25904
+ }
25905
+ if (wasPaused) {
25906
+ stdin.pause();
25907
+ }
25908
+ };
25909
+ }
25910
+
25816
25911
  // src/cli/run/runner.ts
25817
25912
  var EVENT_PROCESSOR_SHUTDOWN_TIMEOUT_MS = 2000;
25818
25913
  async function waitForEventProcessorShutdown(eventProcessor, timeoutMs = EVENT_PROCESSOR_SHUTDOWN_TIMEOUT_MS) {
@@ -25843,12 +25938,15 @@ async function run(options) {
25843
25938
  const cleanup = () => {
25844
25939
  serverCleanup();
25845
25940
  };
25846
- process.on("SIGINT", () => {
25941
+ const restoreInput = suppressRunInput();
25942
+ const handleSigint = () => {
25847
25943
  console.log(import_picocolors15.default.yellow(`
25848
25944
  Interrupted. Shutting down...`));
25945
+ restoreInput();
25849
25946
  cleanup();
25850
25947
  process.exit(130);
25851
- });
25948
+ };
25949
+ process.on("SIGINT", handleSigint);
25852
25950
  try {
25853
25951
  const sessionID = await resolveSession({
25854
25952
  client: client3,
@@ -25871,6 +25969,9 @@ Interrupted. Shutting down...`));
25871
25969
  path: { id: sessionID },
25872
25970
  body: {
25873
25971
  agent: resolvedAgent,
25972
+ tools: {
25973
+ question: false
25974
+ },
25874
25975
  parts: [{ type: "text", text: message }]
25875
25976
  },
25876
25977
  query: { directory }
@@ -25902,6 +26003,9 @@ Interrupted. Shutting down...`));
25902
26003
  } catch (err) {
25903
26004
  cleanup();
25904
26005
  throw err;
26006
+ } finally {
26007
+ process.removeListener("SIGINT", handleSigint);
26008
+ restoreInput();
25905
26009
  }
25906
26010
  } catch (err) {
25907
26011
  if (jsonManager)
@@ -26780,8 +26884,8 @@ async function checkConfig() {
26780
26884
 
26781
26885
  // src/cli/doctor/checks/dependencies.ts
26782
26886
  import { existsSync as existsSync24 } from "fs";
26783
- import { createRequire as createRequire2 } from "module";
26784
- import { dirname as dirname7, join as join22 } from "path";
26887
+ import { createRequire } from "module";
26888
+ import { dirname as dirname6, join as join22 } from "path";
26785
26889
  async function checkBinaryExists(binary2) {
26786
26890
  try {
26787
26891
  const path9 = Bun.which(binary2);
@@ -26868,9 +26972,9 @@ async function checkAstGrepNapi() {
26868
26972
  function findCommentCheckerPackageBinary() {
26869
26973
  const binaryName = process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
26870
26974
  try {
26871
- const require2 = createRequire2(import.meta.url);
26975
+ const require2 = createRequire(import.meta.url);
26872
26976
  const pkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
26873
- const binaryPath = join22(dirname7(pkgPath), "bin", binaryName);
26977
+ const binaryPath = join22(dirname6(pkgPath), "bin", binaryName);
26874
26978
  if (existsSync24(binaryPath))
26875
26979
  return binaryPath;
26876
26980
  } catch {}
@@ -27488,7 +27592,7 @@ async function doctor(options = { mode: "default" }) {
27488
27592
  // src/features/mcp-oauth/storage.ts
27489
27593
  init_shared();
27490
27594
  import { chmodSync, existsSync as existsSync27, mkdirSync as mkdirSync6, readFileSync as readFileSync28, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "fs";
27491
- import { dirname as dirname8, join as join25 } from "path";
27595
+ import { dirname as dirname7, join as join25 } from "path";
27492
27596
  var STORAGE_FILE_NAME = "mcp-oauth.json";
27493
27597
  function getMcpOauthStoragePath() {
27494
27598
  return join25(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
@@ -27538,14 +27642,14 @@ function readStore() {
27538
27642
  return null;
27539
27643
  }
27540
27644
  }
27541
- function writeStore(store) {
27645
+ function writeStore(store2) {
27542
27646
  const filePath = getMcpOauthStoragePath();
27543
27647
  try {
27544
- const dir = dirname8(filePath);
27648
+ const dir = dirname7(filePath);
27545
27649
  if (!existsSync27(dir)) {
27546
27650
  mkdirSync6(dir, { recursive: true });
27547
27651
  }
27548
- writeFileSync12(filePath, JSON.stringify(store, null, 2), { encoding: "utf-8", mode: 384 });
27652
+ writeFileSync12(filePath, JSON.stringify(store2, null, 2), { encoding: "utf-8", mode: 384 });
27549
27653
  chmodSync(filePath, 384);
27550
27654
  return true;
27551
27655
  } catch {
@@ -27553,28 +27657,28 @@ function writeStore(store) {
27553
27657
  }
27554
27658
  }
27555
27659
  function loadToken(serverHost, resource) {
27556
- const store = readStore();
27557
- if (!store)
27660
+ const store2 = readStore();
27661
+ if (!store2)
27558
27662
  return null;
27559
27663
  const key = buildKey(serverHost, resource);
27560
- return store[key] ?? null;
27664
+ return store2[key] ?? null;
27561
27665
  }
27562
27666
  function saveToken(serverHost, resource, token) {
27563
- const store = readStore() ?? {};
27667
+ const store2 = readStore() ?? {};
27564
27668
  const key = buildKey(serverHost, resource);
27565
- store[key] = token;
27566
- return writeStore(store);
27669
+ store2[key] = token;
27670
+ return writeStore(store2);
27567
27671
  }
27568
27672
  function deleteToken(serverHost, resource) {
27569
- const store = readStore();
27570
- if (!store)
27673
+ const store2 = readStore();
27674
+ if (!store2)
27571
27675
  return true;
27572
27676
  const key = buildKey(serverHost, resource);
27573
- if (!(key in store)) {
27677
+ if (!(key in store2)) {
27574
27678
  return true;
27575
27679
  }
27576
- delete store[key];
27577
- if (Object.keys(store).length === 0) {
27680
+ delete store2[key];
27681
+ if (Object.keys(store2).length === 0) {
27578
27682
  try {
27579
27683
  const filePath = getMcpOauthStoragePath();
27580
27684
  if (existsSync27(filePath)) {
@@ -27585,16 +27689,16 @@ function deleteToken(serverHost, resource) {
27585
27689
  return false;
27586
27690
  }
27587
27691
  }
27588
- return writeStore(store);
27692
+ return writeStore(store2);
27589
27693
  }
27590
27694
  function listTokensByHost(serverHost) {
27591
- const store = readStore();
27592
- if (!store)
27695
+ const store2 = readStore();
27696
+ if (!store2)
27593
27697
  return {};
27594
27698
  const host = normalizeHost(serverHost);
27595
27699
  const prefix = `${host}/`;
27596
27700
  const result = {};
27597
- for (const [key, value] of Object.entries(store)) {
27701
+ for (const [key, value] of Object.entries(store2)) {
27598
27702
  if (key.startsWith(prefix)) {
27599
27703
  result[key] = value;
27600
27704
  }
@@ -10,9 +10,14 @@ export interface ProviderAvailability {
10
10
  kimiForCoding: boolean;
11
11
  isMaxPlan: boolean;
12
12
  }
13
+ export interface UltraworkConfig {
14
+ model: string;
15
+ variant?: string;
16
+ }
13
17
  export interface AgentConfig {
14
18
  model: string;
15
19
  variant?: string;
20
+ ultrawork?: UltraworkConfig;
16
21
  }
17
22
  export interface CategoryConfig {
18
23
  model: string;
@@ -39,5 +39,9 @@ export interface EventState {
39
39
  thinkingAtLineStart: boolean;
40
40
  /** Current assistant message ID — prevents counter resets on repeated message.updated for same message */
41
41
  currentMessageId: string | null;
42
+ /** Assistant message start timestamp by message ID */
43
+ messageStartedAtById: Record<string, number>;
44
+ /** Prevent duplicate completion metadata lines per message */
45
+ completionMetaPrintedByMessageId: Record<string, boolean>;
42
46
  }
43
47
  export declare function createEventState(): EventState;
@@ -0,0 +1,12 @@
1
+ type StdinLike = {
2
+ isTTY?: boolean;
3
+ isRaw?: boolean;
4
+ setRawMode?: (mode: boolean) => void;
5
+ isPaused?: () => boolean;
6
+ resume: () => void;
7
+ pause: () => void;
8
+ on: (event: "data", listener: (chunk: string | Uint8Array) => void) => void;
9
+ removeListener: (event: "data", listener: (chunk: string | Uint8Array) => void) => void;
10
+ };
11
+ export declare function suppressRunInput(stdin?: StdinLike, onInterrupt?: () => void): () => void;
12
+ export {};