oh-my-opencode-slim 0.7.0 → 0.8.1

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 (36) hide show
  1. package/README.md +9 -1
  2. package/dist/agents/index.d.ts +1 -1
  3. package/dist/agents/orchestrator.d.ts +1 -1
  4. package/dist/background/background-manager.d.ts +42 -0
  5. package/dist/background/tmux-session-manager.d.ts +5 -0
  6. package/dist/cli/config-manager.d.ts +3 -0
  7. package/dist/cli/dynamic-model-selection.d.ts +14 -2
  8. package/dist/cli/external-rankings.d.ts +8 -0
  9. package/dist/cli/index.js +1897 -272
  10. package/dist/cli/model-key-normalization.d.ts +1 -0
  11. package/dist/cli/opencode-models.d.ts +4 -0
  12. package/dist/cli/paths.d.ts +2 -0
  13. package/dist/cli/precedence-resolver.d.ts +16 -0
  14. package/dist/cli/providers.d.ts +1 -1
  15. package/dist/cli/scoring-v2/engine.d.ts +4 -0
  16. package/dist/cli/scoring-v2/features.d.ts +3 -0
  17. package/dist/cli/scoring-v2/index.d.ts +4 -0
  18. package/dist/cli/scoring-v2/types.d.ts +17 -0
  19. package/dist/cli/scoring-v2/weights.d.ts +2 -0
  20. package/dist/cli/skills.d.ts +17 -0
  21. package/dist/cli/system.d.ts +2 -0
  22. package/dist/cli/types.d.ts +45 -1
  23. package/dist/config/constants.d.ts +1 -0
  24. package/dist/config/schema.d.ts +94 -2
  25. package/dist/hooks/autopilot/index.d.ts +43 -0
  26. package/dist/hooks/delegate-task-retry/guidance.d.ts +2 -0
  27. package/dist/hooks/delegate-task-retry/hook.d.ts +8 -0
  28. package/dist/hooks/delegate-task-retry/index.d.ts +4 -0
  29. package/dist/hooks/delegate-task-retry/patterns.d.ts +11 -0
  30. package/dist/hooks/index.d.ts +2 -0
  31. package/dist/hooks/json-error-recovery/hook.d.ts +18 -0
  32. package/dist/hooks/json-error-recovery/index.d.ts +1 -0
  33. package/dist/index.js +356 -16
  34. package/dist/utils/env.d.ts +1 -0
  35. package/dist/utils/index.d.ts +1 -0
  36. package/package.json +8 -8
package/dist/index.js CHANGED
@@ -3277,6 +3277,16 @@ var require_main = __commonJS((exports) => {
3277
3277
  exports.createMessageConnection = createMessageConnection;
3278
3278
  });
3279
3279
 
3280
+ // src/cli/custom-skills.ts
3281
+ var CUSTOM_SKILLS = [
3282
+ {
3283
+ name: "cartography",
3284
+ description: "Repository understanding and hierarchical codemap generation",
3285
+ allowedAgents: ["orchestrator", "explorer"],
3286
+ sourcePath: "src/skills/cartography"
3287
+ }
3288
+ ];
3289
+
3280
3290
  // src/cli/skills.ts
3281
3291
  var RECOMMENDED_SKILLS = [
3282
3292
  {
@@ -3298,6 +3308,13 @@ var RECOMMENDED_SKILLS = [
3298
3308
  ]
3299
3309
  }
3300
3310
  ];
3311
+ var PERMISSION_ONLY_SKILLS = [
3312
+ {
3313
+ name: "requesting-code-review",
3314
+ allowedAgents: ["oracle"],
3315
+ description: "Code review template for reviewer subagents in multi-step workflows"
3316
+ }
3317
+ ];
3301
3318
  function getSkillPermissionsForAgent(agentName, skillList) {
3302
3319
  const permissions = {
3303
3320
  "*": agentName === "orchestrator" ? "allow" : "deny"
@@ -3321,6 +3338,18 @@ function getSkillPermissionsForAgent(agentName, skillList) {
3321
3338
  permissions[skill.skillName] = "allow";
3322
3339
  }
3323
3340
  }
3341
+ for (const skill of CUSTOM_SKILLS) {
3342
+ const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
3343
+ if (isAllowed) {
3344
+ permissions[skill.name] = "allow";
3345
+ }
3346
+ }
3347
+ for (const skill of PERMISSION_ONLY_SKILLS) {
3348
+ const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
3349
+ if (isAllowed) {
3350
+ permissions[skill.name] = "allow";
3351
+ }
3352
+ }
3324
3353
  return permissions;
3325
3354
  }
3326
3355
 
@@ -3338,6 +3367,14 @@ var SUBAGENT_NAMES = [
3338
3367
  ];
3339
3368
  var ORCHESTRATOR_NAME = "orchestrator";
3340
3369
  var ALL_AGENT_NAMES = [ORCHESTRATOR_NAME, ...SUBAGENT_NAMES];
3370
+ var SUBAGENT_DELEGATION_RULES = {
3371
+ orchestrator: SUBAGENT_NAMES,
3372
+ fixer: [],
3373
+ designer: [],
3374
+ explorer: [],
3375
+ librarian: [],
3376
+ oracle: []
3377
+ };
3341
3378
  var DEFAULT_MODELS = {
3342
3379
  orchestrator: "kimi-for-coding/k2p5",
3343
3380
  oracle: "openai/gpt-5.2-codex",
@@ -16939,6 +16976,34 @@ function date4(params) {
16939
16976
  // node_modules/zod/v4/classic/external.js
16940
16977
  config(en_default());
16941
16978
  // src/config/schema.ts
16979
+ var ProviderModelIdSchema = exports_external.string().regex(/^[^/\s]+\/[^\s]+$/, "Expected provider/model format (provider/.../model)");
16980
+ var ManualAgentPlanSchema = exports_external.object({
16981
+ primary: ProviderModelIdSchema,
16982
+ fallback1: ProviderModelIdSchema,
16983
+ fallback2: ProviderModelIdSchema,
16984
+ fallback3: ProviderModelIdSchema
16985
+ }).superRefine((value, ctx) => {
16986
+ const unique = new Set([
16987
+ value.primary,
16988
+ value.fallback1,
16989
+ value.fallback2,
16990
+ value.fallback3
16991
+ ]);
16992
+ if (unique.size !== 4) {
16993
+ ctx.addIssue({
16994
+ code: exports_external.ZodIssueCode.custom,
16995
+ message: "primary and fallbacks must be unique per agent"
16996
+ });
16997
+ }
16998
+ });
16999
+ var ManualPlanSchema = exports_external.object({
17000
+ orchestrator: ManualAgentPlanSchema,
17001
+ oracle: ManualAgentPlanSchema,
17002
+ designer: ManualAgentPlanSchema,
17003
+ explorer: ManualAgentPlanSchema,
17004
+ librarian: ManualAgentPlanSchema,
17005
+ fixer: ManualAgentPlanSchema
17006
+ }).strict();
16942
17007
  var AgentModelChainSchema = exports_external.array(exports_external.string()).min(1);
16943
17008
  var FallbackChainsSchema = exports_external.object({
16944
17009
  orchestrator: AgentModelChainSchema.optional(),
@@ -16947,7 +17012,7 @@ var FallbackChainsSchema = exports_external.object({
16947
17012
  explorer: AgentModelChainSchema.optional(),
16948
17013
  librarian: AgentModelChainSchema.optional(),
16949
17014
  fixer: AgentModelChainSchema.optional()
16950
- }).strict();
17015
+ }).catchall(AgentModelChainSchema);
16951
17016
  var AgentOverrideConfigSchema = exports_external.object({
16952
17017
  model: exports_external.string().optional(),
16953
17018
  temperature: exports_external.number().min(0).max(2).optional(),
@@ -16974,11 +17039,14 @@ var BackgroundTaskConfigSchema = exports_external.object({
16974
17039
  });
16975
17040
  var FailoverConfigSchema = exports_external.object({
16976
17041
  enabled: exports_external.boolean().default(true),
16977
- timeoutMs: exports_external.number().min(1000).max(120000).default(15000),
17042
+ timeoutMs: exports_external.number().min(0).default(15000),
16978
17043
  chains: FallbackChainsSchema.default({})
16979
17044
  });
16980
17045
  var PluginConfigSchema = exports_external.object({
16981
17046
  preset: exports_external.string().optional(),
17047
+ scoringEngineVersion: exports_external.enum(["v1", "v2-shadow", "v2"]).optional(),
17048
+ balanceProviderUsage: exports_external.boolean().optional(),
17049
+ manualPlan: ManualPlanSchema.optional(),
16982
17050
  presets: exports_external.record(exports_external.string(), PresetSchema).optional(),
16983
17051
  agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
16984
17052
  disabled_mcps: exports_external.array(exports_external.string()).optional(),
@@ -17241,9 +17309,10 @@ var FIXER_PROMPT = `You are Fixer - a fast, focused implementation specialist.
17241
17309
 
17242
17310
  **Constraints**:
17243
17311
  - NO external research (no websearch, context7, grep_app)
17244
- - NO delegation (no background_task)
17312
+ - NO delegation (no background_task, no spawning subagents)
17245
17313
  - No multi-step research/planning; minimal execution sequence ok
17246
- - If context is insufficient, read the files listed; only ask for missing inputs you cannot retrieve
17314
+ - If context is insufficient: use grep/glob/lsp_diagnostics directly \u2014 do not delegate
17315
+ - Only ask for missing inputs you truly cannot retrieve yourself
17247
17316
 
17248
17317
  **Output Format**:
17249
17318
  <summary>
@@ -17430,7 +17499,7 @@ Each specialist delivers 10x results in their domain:
17430
17499
  - @explorer \u2192 Parallel discovery when you need to find unknowns, not read knowns
17431
17500
  - @librarian \u2192 Complex/evolving APIs where docs prevent errors, not basic usage
17432
17501
  - @oracle \u2192 High-stakes decisions where wrong choice is costly, not routine calls
17433
- - @designer \u2192 User-facing experiences where polish matters, not internal logic
17502
+ - @designer \u2192 User-facing experiences where polish matters, not internal logic
17434
17503
  - @fixer \u2192 Parallel execution of clear specs, not explaining trivial changes
17435
17504
 
17436
17505
  **Delegation efficiency:**
@@ -17465,6 +17534,10 @@ Balance: respect dependencies, avoid parallelizing what must be sequential.
17465
17534
  - Confirm specialists completed successfully
17466
17535
  - Verify solution meets requirements
17467
17536
 
17537
+ ## Agent Role Mapping
17538
+ When a workflow calls for an **implementer** subagent: dispatch \`@fixer\`. Fixer has enforced constraints (no research, no delegation, structured output) that match the implementer role exactly.
17539
+ When a workflow calls for a **reviewer** subagent: dispatch \`@oracle\`. Oracle has the depth for architectural review and access to code review skills.
17540
+
17468
17541
  </Workflow>
17469
17542
 
17470
17543
  <Communication>
@@ -17522,6 +17595,8 @@ ${customAppendPrompt}`;
17522
17595
  function applyOverrides(agent, override) {
17523
17596
  if (override.model)
17524
17597
  agent.config.model = override.model;
17598
+ if (override.variant)
17599
+ agent.config.variant = override.variant;
17525
17600
  if (override.temperature !== undefined)
17526
17601
  agent.config.temperature = override.temperature;
17527
17602
  }
@@ -17829,6 +17904,16 @@ async function closeTmuxPane(paneId) {
17829
17904
  return false;
17830
17905
  }
17831
17906
  try {
17907
+ log("[tmux] closeTmuxPane: sending Ctrl+C for graceful shutdown", {
17908
+ paneId
17909
+ });
17910
+ const ctrlCProc = spawn([tmux, "send-keys", "-t", paneId, "C-c"], {
17911
+ stdout: "pipe",
17912
+ stderr: "pipe"
17913
+ });
17914
+ await ctrlCProc.exited;
17915
+ await new Promise((r) => setTimeout(r, 250));
17916
+ log("[tmux] closeTmuxPane: killing pane", { paneId });
17832
17917
  const proc = spawn([tmux, "kill-pane", "-t", paneId], {
17833
17918
  stdout: "pipe",
17834
17919
  stderr: "pipe"
@@ -17959,6 +18044,7 @@ function generateTaskId() {
17959
18044
  class BackgroundTaskManager {
17960
18045
  tasks = new Map;
17961
18046
  tasksBySessionId = new Map;
18047
+ agentBySessionId = new Map;
17962
18048
  client;
17963
18049
  directory;
17964
18050
  tmuxEnabled;
@@ -17978,6 +18064,20 @@ class BackgroundTaskManager {
17978
18064
  };
17979
18065
  this.maxConcurrentStarts = this.backgroundConfig.maxConcurrentStarts;
17980
18066
  }
18067
+ getSubagentRules(agentName) {
18068
+ return SUBAGENT_DELEGATION_RULES[agentName] ?? ["explorer"];
18069
+ }
18070
+ isAgentAllowed(parentSessionId, requestedAgent) {
18071
+ const parentAgentName = this.agentBySessionId.get(parentSessionId) ?? "orchestrator";
18072
+ const allowedSubagents = this.getSubagentRules(parentAgentName);
18073
+ if (allowedSubagents.length === 0)
18074
+ return false;
18075
+ return allowedSubagents.includes(requestedAgent);
18076
+ }
18077
+ getAllowedSubagents(parentSessionId) {
18078
+ const parentAgentName = this.agentBySessionId.get(parentSessionId) ?? "orchestrator";
18079
+ return this.getSubagentRules(parentAgentName);
18080
+ }
17981
18081
  launch(opts) {
17982
18082
  const task = {
17983
18083
  id: generateTaskId(),
@@ -18028,6 +18128,10 @@ class BackgroundTaskManager {
18028
18128
  return chain;
18029
18129
  }
18030
18130
  async promptWithTimeout(args, timeoutMs) {
18131
+ if (timeoutMs <= 0) {
18132
+ await this.client.session.prompt(args);
18133
+ return;
18134
+ }
18031
18135
  await Promise.race([
18032
18136
  this.client.session.prompt(args),
18033
18137
  new Promise((_, reject) => {
@@ -18037,6 +18141,13 @@ class BackgroundTaskManager {
18037
18141
  })
18038
18142
  ]);
18039
18143
  }
18144
+ calculateToolPermissions(agentName) {
18145
+ const allowedSubagents = this.getSubagentRules(agentName);
18146
+ if (allowedSubagents.length === 0) {
18147
+ return { background_task: false, task: false };
18148
+ }
18149
+ return { background_task: true, task: true };
18150
+ }
18040
18151
  async startTask(task) {
18041
18152
  task.status = "starting";
18042
18153
  this.activeStarts++;
@@ -18057,19 +18168,21 @@ class BackgroundTaskManager {
18057
18168
  }
18058
18169
  task.sessionId = session.data.id;
18059
18170
  this.tasksBySessionId.set(session.data.id, task.id);
18171
+ this.agentBySessionId.set(session.data.id, task.agent);
18060
18172
  task.status = "running";
18061
18173
  if (this.tmuxEnabled) {
18062
18174
  await new Promise((r) => setTimeout(r, 500));
18063
18175
  }
18176
+ const toolPermissions = this.calculateToolPermissions(task.agent);
18064
18177
  const promptQuery = { directory: this.directory };
18065
18178
  const resolvedVariant = resolveAgentVariant(this.config, task.agent);
18066
18179
  const basePromptBody = applyAgentVariant(resolvedVariant, {
18067
18180
  agent: task.agent,
18068
- tools: { background_task: false, task: false },
18181
+ tools: toolPermissions,
18069
18182
  parts: [{ type: "text", text: task.prompt }]
18070
18183
  });
18071
- const timeoutMs = this.config?.fallback?.timeoutMs ?? FALLBACK_FAILOVER_TIMEOUT_MS;
18072
18184
  const fallbackEnabled = this.config?.fallback?.enabled ?? true;
18185
+ const timeoutMs = fallbackEnabled ? this.config?.fallback?.timeoutMs ?? FALLBACK_FAILOVER_TIMEOUT_MS : 0;
18073
18186
  const chain = fallbackEnabled ? this.resolveFallbackChain(task.agent) : [];
18074
18187
  const attemptModels = chain.length > 0 ? chain : [undefined];
18075
18188
  const errors3 = [];
@@ -18133,6 +18246,33 @@ class BackgroundTaskManager {
18133
18246
  await this.extractAndCompleteTask(task);
18134
18247
  }
18135
18248
  }
18249
+ async handleSessionDeleted(event) {
18250
+ if (event.type !== "session.deleted")
18251
+ return;
18252
+ const sessionId = event.properties?.info?.id ?? event.properties?.sessionID;
18253
+ if (!sessionId)
18254
+ return;
18255
+ const taskId = this.tasksBySessionId.get(sessionId);
18256
+ if (!taskId)
18257
+ return;
18258
+ const task = this.tasks.get(taskId);
18259
+ if (!task)
18260
+ return;
18261
+ if (task.status === "running" || task.status === "pending") {
18262
+ log(`[background-manager] Session deleted, cancelling task: ${task.id}`);
18263
+ task.status = "cancelled";
18264
+ task.completedAt = new Date;
18265
+ task.error = "Session deleted";
18266
+ this.tasksBySessionId.delete(sessionId);
18267
+ this.agentBySessionId.delete(sessionId);
18268
+ const resolver = this.completionResolvers.get(taskId);
18269
+ if (resolver) {
18270
+ resolver(task);
18271
+ this.completionResolvers.delete(taskId);
18272
+ }
18273
+ log(`[background-manager] Task cancelled due to session deletion: ${task.id}`);
18274
+ }
18275
+ }
18136
18276
  async extractAndCompleteTask(task) {
18137
18277
  if (!task.sessionId)
18138
18278
  return;
@@ -18175,6 +18315,12 @@ class BackgroundTaskManager {
18175
18315
  }
18176
18316
  if (task.sessionId) {
18177
18317
  this.tasksBySessionId.delete(task.sessionId);
18318
+ this.agentBySessionId.delete(task.sessionId);
18319
+ }
18320
+ if (task.sessionId) {
18321
+ this.client.session.abort({
18322
+ path: { id: task.sessionId }
18323
+ }).catch(() => {});
18178
18324
  }
18179
18325
  if (task.parentSessionId) {
18180
18326
  this.sendCompletionNotification(task).catch((err) => {
@@ -18261,6 +18407,7 @@ class BackgroundTaskManager {
18261
18407
  this.completionResolvers.clear();
18262
18408
  this.tasks.clear();
18263
18409
  this.tasksBySessionId.clear();
18410
+ this.agentBySessionId.clear();
18264
18411
  }
18265
18412
  }
18266
18413
  // src/background/tmux-session-manager.ts
@@ -18342,6 +18489,19 @@ class TmuxSessionManager {
18342
18489
  await this.closeSession(sessionId);
18343
18490
  }
18344
18491
  }
18492
+ async onSessionDeleted(event) {
18493
+ if (!this.enabled)
18494
+ return;
18495
+ if (event.type !== "session.deleted")
18496
+ return;
18497
+ const sessionId = event.properties?.sessionID;
18498
+ if (!sessionId)
18499
+ return;
18500
+ log("[tmux-session-manager] session deleted, closing pane", {
18501
+ sessionId
18502
+ });
18503
+ await this.closeSession(sessionId);
18504
+ }
18345
18505
  startPolling() {
18346
18506
  if (this.pollInterval)
18347
18507
  return;
@@ -18420,6 +18580,8 @@ class TmuxSessionManager {
18420
18580
  // src/hooks/auto-update-checker/cache.ts
18421
18581
  import * as fs3 from "fs";
18422
18582
  import * as path4 from "path";
18583
+ // src/cli/dynamic-model-selection.ts
18584
+ var FREE_BIASED_PROVIDERS = new Set(["opencode"]);
18423
18585
  // src/hooks/auto-update-checker/constants.ts
18424
18586
  import * as os3 from "os";
18425
18587
  import * as path3 from "path";
@@ -18809,10 +18971,169 @@ function showToast(ctx, title, message, variant = "info", duration3 = 3000) {
18809
18971
  body: { title, message, variant, duration: duration3 }
18810
18972
  }).catch(() => {});
18811
18973
  }
18974
+ // src/hooks/delegate-task-retry/patterns.ts
18975
+ var DELEGATE_TASK_ERROR_PATTERNS = [
18976
+ {
18977
+ pattern: "run_in_background",
18978
+ errorType: "missing_run_in_background",
18979
+ fixHint: "Add run_in_background=false (delegation) or run_in_background=true (parallel exploration)."
18980
+ },
18981
+ {
18982
+ pattern: "load_skills",
18983
+ errorType: "missing_load_skills",
18984
+ fixHint: "Add load_skills=[] (empty array when no skill is needed)."
18985
+ },
18986
+ {
18987
+ pattern: "category OR subagent_type",
18988
+ errorType: "mutual_exclusion",
18989
+ fixHint: 'Provide only one: category (e.g., "unspecified-low") OR subagent_type (e.g., "explorer").'
18990
+ },
18991
+ {
18992
+ pattern: "Must provide either category or subagent_type",
18993
+ errorType: "missing_category_or_agent",
18994
+ fixHint: 'Add either category="unspecified-low" or subagent_type="explorer".'
18995
+ },
18996
+ {
18997
+ pattern: "Unknown category",
18998
+ errorType: "unknown_category",
18999
+ fixHint: "Use a valid category listed in the error output."
19000
+ },
19001
+ {
19002
+ pattern: "Unknown agent",
19003
+ errorType: "unknown_agent",
19004
+ fixHint: "Use a valid agent name from the available list."
19005
+ },
19006
+ {
19007
+ pattern: "Skills not found",
19008
+ errorType: "unknown_skills",
19009
+ fixHint: "Use valid skill names listed in the error output."
19010
+ },
19011
+ {
19012
+ pattern: "is not allowed. Allowed agents:",
19013
+ errorType: "background_agent_not_allowed",
19014
+ fixHint: "Use one of the allowed agents shown in the error or delegate from a parent agent that can call this subagent."
19015
+ }
19016
+ ];
19017
+ function detectDelegateTaskError(output) {
19018
+ if (!output || typeof output !== "string")
19019
+ return null;
19020
+ const hasErrorSignal = output.includes("[ERROR]") || output.includes("Invalid arguments") || output.includes("is not allowed. Allowed agents:");
19021
+ if (!hasErrorSignal)
19022
+ return null;
19023
+ for (const pattern of DELEGATE_TASK_ERROR_PATTERNS) {
19024
+ if (output.includes(pattern.pattern)) {
19025
+ return {
19026
+ errorType: pattern.errorType,
19027
+ originalOutput: output
19028
+ };
19029
+ }
19030
+ }
19031
+ return null;
19032
+ }
19033
+ // src/hooks/delegate-task-retry/guidance.ts
19034
+ function extractAvailableList(output) {
19035
+ const match = output.match(/Allowed agents:\s*(.+)$/m);
19036
+ if (match)
19037
+ return match[1].trim();
19038
+ const available = output.match(/Available[^:]*:\s*(.+)$/m);
19039
+ if (available)
19040
+ return available[1].trim();
19041
+ return null;
19042
+ }
19043
+ function buildRetryGuidance(errorInfo) {
19044
+ const pattern = DELEGATE_TASK_ERROR_PATTERNS.find((p) => p.errorType === errorInfo.errorType);
19045
+ if (!pattern) {
19046
+ return `
19047
+ [delegate-task retry] Fix parameters and retry with corrected arguments.`;
19048
+ }
19049
+ const available = extractAvailableList(errorInfo.originalOutput);
19050
+ const lines = [
19051
+ "",
19052
+ "[delegate-task retry suggestion]",
19053
+ `Error type: ${errorInfo.errorType}`,
19054
+ `Fix: ${pattern.fixHint}`
19055
+ ];
19056
+ if (available) {
19057
+ lines.push(`Available: ${available}`);
19058
+ }
19059
+ lines.push("Retry now with corrected parameters. Example:", 'task(description="...", prompt="...", category="unspecified-low", run_in_background=false, load_skills=[])');
19060
+ return lines.join(`
19061
+ `);
19062
+ }
19063
+ // src/hooks/delegate-task-retry/hook.ts
19064
+ function createDelegateTaskRetryHook(_ctx) {
19065
+ return {
19066
+ "tool.execute.after": async (input, output) => {
19067
+ const toolName = input.tool.toLowerCase();
19068
+ const isDelegateTool = toolName === "task" || toolName === "background_task";
19069
+ if (!isDelegateTool)
19070
+ return;
19071
+ if (typeof output.output !== "string")
19072
+ return;
19073
+ const detected = detectDelegateTaskError(output.output);
19074
+ if (!detected)
19075
+ return;
19076
+ output.output += `
19077
+ ${buildRetryGuidance(detected)}`;
19078
+ }
19079
+ };
19080
+ }
19081
+ // src/hooks/json-error-recovery/hook.ts
19082
+ var JSON_ERROR_TOOL_EXCLUDE_LIST = [
19083
+ "bash",
19084
+ "read",
19085
+ "glob",
19086
+ "grep",
19087
+ "webfetch",
19088
+ "grep_app_searchgithub",
19089
+ "websearch_web_search_exa"
19090
+ ];
19091
+ var JSON_ERROR_PATTERNS = [
19092
+ /json parse error/i,
19093
+ /failed to parse json/i,
19094
+ /invalid json/i,
19095
+ /malformed json/i,
19096
+ /unexpected end of json input/i,
19097
+ /syntaxerror:\s*unexpected token.*json/i,
19098
+ /json[^\n]*expected '\}'/i,
19099
+ /json[^\n]*unexpected eof/i
19100
+ ];
19101
+ var JSON_ERROR_REMINDER_MARKER = "[JSON PARSE ERROR - IMMEDIATE ACTION REQUIRED]";
19102
+ var JSON_ERROR_EXCLUDED_TOOLS = new Set(JSON_ERROR_TOOL_EXCLUDE_LIST);
19103
+ var JSON_ERROR_REMINDER = `
19104
+ [JSON PARSE ERROR - IMMEDIATE ACTION REQUIRED]
19105
+
19106
+ You sent invalid JSON arguments. The system could not parse your tool call.
19107
+ STOP and do this NOW:
19108
+
19109
+ 1. LOOK at the error message above to see what was expected vs what you sent.
19110
+ 2. CORRECT your JSON syntax (missing braces, unescaped quotes, trailing commas, etc).
19111
+ 3. RETRY the tool call with valid JSON.
19112
+
19113
+ DO NOT repeat the exact same invalid call.
19114
+ `;
19115
+ function createJsonErrorRecoveryHook(_ctx) {
19116
+ return {
19117
+ "tool.execute.after": async (input, output) => {
19118
+ if (JSON_ERROR_EXCLUDED_TOOLS.has(input.tool.toLowerCase()))
19119
+ return;
19120
+ if (typeof output.output !== "string")
19121
+ return;
19122
+ if (output.output.includes(JSON_ERROR_REMINDER_MARKER))
19123
+ return;
19124
+ const outputText = output.output;
19125
+ const hasJsonError = JSON_ERROR_PATTERNS.some((pattern) => pattern.test(outputText));
19126
+ if (hasJsonError) {
19127
+ output.output += `
19128
+ ${JSON_ERROR_REMINDER}`;
19129
+ }
19130
+ }
19131
+ };
19132
+ }
18812
19133
  // src/hooks/phase-reminder/index.ts
18813
- var PHASE_REMINDER = `<reminder>\u26A0\uFE0F MANDATORY: Understand\u2192DELEGATE(! based on each agent rules)\u2192Split-and-Parallelize(?)\u2192Plan\u2192Execute\u2192Verify
18814
- Available Specialist: @oracle @librarian @explorer @designer @fixer
18815
- </reminder>`;
19134
+ var PHASE_REMINDER = `<reminder>Recall Workflow Rules:
19135
+ Understand \u2192 find the best path (delegate based on rules and parallelize independent work) \u2192 execute \u2192 verify.
19136
+ If delegating, launch the specialist in the same turn you mention it.</reminder>`;
18816
19137
  function createPhaseReminderHook() {
18817
19138
  return {
18818
19139
  "experimental.chat.messages.transform": async (_input, output) => {
@@ -18852,7 +19173,7 @@ ${originalText}`;
18852
19173
  var NUDGE = `
18853
19174
 
18854
19175
  ---
18855
- Reminder to follow the workflow instructions, consider delegation to specialist(s)`;
19176
+ Workflow Reminder: delegate based on rules; If mentioning a specialist, launch it in this same turn.`;
18856
19177
  function createPostReadNudgeHook() {
18857
19178
  return {
18858
19179
  "tool.execute.after": async (input, output) => {
@@ -31789,11 +32110,16 @@ Key behaviors:
31789
32110
  const agent = String(args.agent);
31790
32111
  const prompt = String(args.prompt);
31791
32112
  const description = String(args.description);
32113
+ const parentSessionId = toolContext.sessionID;
32114
+ if (!manager.isAgentAllowed(parentSessionId, agent)) {
32115
+ const allowed = manager.getAllowedSubagents(parentSessionId);
32116
+ return `Agent '${agent}' is not allowed. Allowed agents: ${allowed.join(", ")}`;
32117
+ }
31792
32118
  const task = manager.launch({
31793
32119
  agent,
31794
32120
  prompt,
31795
32121
  description,
31796
- parentSessionId: toolContext.sessionID
32122
+ parentSessionId
31797
32123
  });
31798
32124
  return `Background task launched.
31799
32125
 
@@ -32164,11 +32490,14 @@ ${summary}`);
32164
32490
 
32165
32491
  // src/tools/grep/tools.ts
32166
32492
  var grep = tool({
32167
- description: "Fast content search tool with safety limits (60s timeout, 10MB output). " + "Searches file contents using regular expressions. " + 'Supports full regex syntax (eg. "log.*Error", "function\\s+\\w+", etc.). ' + 'Filter files by pattern with the include parameter (eg. "*.js", "*.{ts,tsx}"). ' + "Returns file paths with matches sorted by modification time.",
32493
+ description: "Fast content search tool with safety limits (60s timeout, 10MB output). " + "Searches file contents using regular expressions. " + 'Supports full regex syntax (eg. "log.*Error", "function\\s+\\w+", etc.). ' + 'Filter files by pattern with the include parameter (e.g. "*.js", "*.{ts,tsx}"). ' + "Returns file paths with matches sorted by modification time.",
32168
32494
  args: {
32169
32495
  pattern: tool.schema.string().describe("The regex pattern to search for in file contents"),
32170
32496
  include: tool.schema.string().optional().describe('File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")'),
32171
- path: tool.schema.string().optional().describe("The directory to search in. Defaults to the current working directory.")
32497
+ path: tool.schema.string().optional().describe("The directory to search in. Defaults to the current working directory."),
32498
+ caseSensitive: tool.schema.boolean().optional().default(false).describe("Perform case-sensitive search (default: false)"),
32499
+ wholeWord: tool.schema.boolean().optional().default(false).describe("Match whole words only (default: false)"),
32500
+ fixedStrings: tool.schema.boolean().optional().default(false).describe("Treat pattern as literal string (default: false)")
32172
32501
  },
32173
32502
  execute: async (args) => {
32174
32503
  try {
@@ -32178,7 +32507,10 @@ var grep = tool({
32178
32507
  pattern: args.pattern,
32179
32508
  paths: paths2,
32180
32509
  globs,
32181
- context: 0
32510
+ context: 0,
32511
+ caseSensitive: args.caseSensitive ?? false,
32512
+ wholeWord: args.wholeWord ?? false,
32513
+ fixedStrings: args.fixedStrings ?? false
32182
32514
  });
32183
32515
  return formatGrepResult(result);
32184
32516
  } catch (e) {
@@ -33107,6 +33439,8 @@ var OhMyOpenCodeLite = async (ctx) => {
33107
33439
  });
33108
33440
  const phaseReminderHook = createPhaseReminderHook();
33109
33441
  const postReadNudgeHook = createPostReadNudgeHook();
33442
+ const delegateTaskRetryHook = createDelegateTaskRetryHook(ctx);
33443
+ const jsonErrorRecoveryHook = createJsonErrorRecoveryHook(ctx);
33110
33444
  return {
33111
33445
  name: "oh-my-opencode-slim",
33112
33446
  agent: agents,
@@ -33162,9 +33496,15 @@ var OhMyOpenCodeLite = async (ctx) => {
33162
33496
  await tmuxSessionManager.onSessionCreated(input.event);
33163
33497
  await backgroundManager.handleSessionStatus(input.event);
33164
33498
  await tmuxSessionManager.onSessionStatus(input.event);
33499
+ await backgroundManager.handleSessionDeleted(input.event);
33500
+ await tmuxSessionManager.onSessionDeleted(input.event);
33165
33501
  },
33166
33502
  "experimental.chat.messages.transform": phaseReminderHook["experimental.chat.messages.transform"],
33167
- "tool.execute.after": postReadNudgeHook["tool.execute.after"]
33503
+ "tool.execute.after": async (input, output) => {
33504
+ await delegateTaskRetryHook["tool.execute.after"](input, output);
33505
+ await jsonErrorRecoveryHook["tool.execute.after"](input, output);
33506
+ await postReadNudgeHook["tool.execute.after"](input, output);
33507
+ }
33168
33508
  };
33169
33509
  };
33170
33510
  var src_default = OhMyOpenCodeLite;
@@ -0,0 +1 @@
1
+ export declare function getEnv(name: string): string | undefined;
@@ -1,4 +1,5 @@
1
1
  export * from './agent-variant';
2
+ export * from './env';
2
3
  export { log } from './logger';
3
4
  export * from './polling';
4
5
  export * from './tmux';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode-slim",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "Lightweight agent orchestration plugin for OpenCode - a slimmed-down fork of oh-my-opencode",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -50,17 +50,17 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@ast-grep/cli": "^0.40.0",
53
- "@modelcontextprotocol/sdk": "^1.25.1",
54
- "@opencode-ai/plugin": "^1.1.19",
55
- "@opencode-ai/sdk": "^1.1.19",
53
+ "@modelcontextprotocol/sdk": "^1.26.0",
54
+ "@opencode-ai/plugin": "^1.2.6",
55
+ "@opencode-ai/sdk": "^1.2.6",
56
56
  "vscode-jsonrpc": "^8.2.0",
57
57
  "vscode-languageserver-protocol": "^3.17.5",
58
- "zod": "^4.1.8"
58
+ "zod": "^4.3.6"
59
59
  },
60
60
  "devDependencies": {
61
- "@biomejs/biome": "2.3.11",
62
- "bun-types": "latest",
63
- "typescript": "^5.7.3"
61
+ "@biomejs/biome": "2.4.2",
62
+ "bun-types": "1.3.9",
63
+ "typescript": "^5.9.3"
64
64
  },
65
65
  "trustedDependencies": [
66
66
  "@ast-grep/cli"