oh-my-opencode 4.3.1 → 4.4.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.
- package/dist/cli/index.js +22 -15
- package/dist/features/background-agent/parent-wake-dedupe.d.ts +19 -0
- package/dist/features/background-agent/parent-wake-notifier.d.ts +2 -20
- package/dist/hooks/tool-pair-validator/hook.d.ts +6 -1
- package/dist/index.js +132 -55
- package/dist/plugin-handlers/provider-config-handler.d.ts +1 -0
- package/dist/shared/migration/model-versions.d.ts +6 -0
- package/package.json +12 -12
package/dist/cli/index.js
CHANGED
|
@@ -7463,7 +7463,6 @@ var init_model_versions = __esm(() => {
|
|
|
7463
7463
|
"anthropic/claude-opus-4-5": "anthropic/claude-opus-4-7",
|
|
7464
7464
|
"anthropic/claude-opus-4-6": "anthropic/claude-opus-4-7",
|
|
7465
7465
|
"anthropic/claude-sonnet-4-5": "anthropic/claude-sonnet-4-6",
|
|
7466
|
-
"openai/gpt-5.3-codex": "openai/gpt-5.4",
|
|
7467
7466
|
"openai/gpt-5.4": "openai/gpt-5.5"
|
|
7468
7467
|
};
|
|
7469
7468
|
});
|
|
@@ -7719,6 +7718,12 @@ function migrateConfigFile(configPath, rawConfig) {
|
|
|
7719
7718
|
delete copy.omo_agent;
|
|
7720
7719
|
needsWrite = true;
|
|
7721
7720
|
}
|
|
7721
|
+
if (copy.lsp !== undefined) {
|
|
7722
|
+
const droppedServers = copy.lsp && typeof copy.lsp === "object" ? Object.keys(copy.lsp) : [];
|
|
7723
|
+
log("Removed obsolete 'lsp' config key from oh-my-opencode config. Custom LSP servers are now configured in .opencode/lsp.json at the project root (consumed by the 'lsp' MCP server). Move any server definitions there to restore them.", { configPath, droppedServers });
|
|
7724
|
+
delete copy.lsp;
|
|
7725
|
+
needsWrite = true;
|
|
7726
|
+
}
|
|
7722
7727
|
if (copy.experimental && typeof copy.experimental === "object") {
|
|
7723
7728
|
const experimental = copy.experimental;
|
|
7724
7729
|
if ("hashline_edit" in experimental) {
|
|
@@ -55440,7 +55445,7 @@ var {
|
|
|
55440
55445
|
// package.json
|
|
55441
55446
|
var package_default = {
|
|
55442
55447
|
name: "oh-my-opencode",
|
|
55443
|
-
version: "4.
|
|
55448
|
+
version: "4.4.0",
|
|
55444
55449
|
description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
55445
55450
|
main: "./dist/index.js",
|
|
55446
55451
|
types: "dist/index.d.ts",
|
|
@@ -55550,17 +55555,17 @@ var package_default = {
|
|
|
55550
55555
|
zod: "^4.4.3"
|
|
55551
55556
|
},
|
|
55552
55557
|
optionalDependencies: {
|
|
55553
|
-
"oh-my-opencode-darwin-arm64": "4.
|
|
55554
|
-
"oh-my-opencode-darwin-x64": "4.
|
|
55555
|
-
"oh-my-opencode-darwin-x64-baseline": "4.
|
|
55556
|
-
"oh-my-opencode-linux-arm64": "4.
|
|
55557
|
-
"oh-my-opencode-linux-arm64-musl": "4.
|
|
55558
|
-
"oh-my-opencode-linux-x64": "4.
|
|
55559
|
-
"oh-my-opencode-linux-x64-baseline": "4.
|
|
55560
|
-
"oh-my-opencode-linux-x64-musl": "4.
|
|
55561
|
-
"oh-my-opencode-linux-x64-musl-baseline": "4.
|
|
55562
|
-
"oh-my-opencode-windows-x64": "4.
|
|
55563
|
-
"oh-my-opencode-windows-x64-baseline": "4.
|
|
55558
|
+
"oh-my-opencode-darwin-arm64": "4.4.0",
|
|
55559
|
+
"oh-my-opencode-darwin-x64": "4.4.0",
|
|
55560
|
+
"oh-my-opencode-darwin-x64-baseline": "4.4.0",
|
|
55561
|
+
"oh-my-opencode-linux-arm64": "4.4.0",
|
|
55562
|
+
"oh-my-opencode-linux-arm64-musl": "4.4.0",
|
|
55563
|
+
"oh-my-opencode-linux-x64": "4.4.0",
|
|
55564
|
+
"oh-my-opencode-linux-x64-baseline": "4.4.0",
|
|
55565
|
+
"oh-my-opencode-linux-x64-musl": "4.4.0",
|
|
55566
|
+
"oh-my-opencode-linux-x64-musl-baseline": "4.4.0",
|
|
55567
|
+
"oh-my-opencode-windows-x64": "4.4.0",
|
|
55568
|
+
"oh-my-opencode-windows-x64-baseline": "4.4.0"
|
|
55564
55569
|
},
|
|
55565
55570
|
overrides: {
|
|
55566
55571
|
hono: "^4.12.18",
|
|
@@ -57649,8 +57654,10 @@ var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
|
57649
57654
|
function renderAgentHeader(agent, model, variant, agentColorsByName) {
|
|
57650
57655
|
if (!agent && !model)
|
|
57651
57656
|
return;
|
|
57652
|
-
const
|
|
57653
|
-
const
|
|
57657
|
+
const normalizedAgent = agent?.normalize("NFC") ?? null;
|
|
57658
|
+
const normalizedModel = model?.normalize("NFC") ?? null;
|
|
57659
|
+
const agentLabel = agent ? import_picocolors6.default.bold(colorizeWithProfileColor(normalizedAgent ?? agent, agentColorsByName[agent])) : "";
|
|
57660
|
+
const modelBase = normalizedModel ?? "";
|
|
57654
57661
|
const variantSuffix = variant ? ` (${variant})` : "";
|
|
57655
57662
|
const modelLabel = model ? import_picocolors6.default.dim(`${modelBase}${variantSuffix}`) : "";
|
|
57656
57663
|
process.stdout.write(`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type ParentWakePromptContext = {
|
|
2
|
+
agent?: string;
|
|
3
|
+
model?: {
|
|
4
|
+
providerID: string;
|
|
5
|
+
modelID: string;
|
|
6
|
+
};
|
|
7
|
+
variant?: string;
|
|
8
|
+
tools?: Record<string, boolean>;
|
|
9
|
+
};
|
|
10
|
+
export type PendingParentWake = {
|
|
11
|
+
promptContext: ParentWakePromptContext;
|
|
12
|
+
notifications: string[];
|
|
13
|
+
shouldReply: boolean;
|
|
14
|
+
dispatchedAt?: number;
|
|
15
|
+
toolCallDeferralStartedAt?: number;
|
|
16
|
+
};
|
|
17
|
+
export declare function resolveParentWakePromptContext(promptContext: ParentWakePromptContext): ParentWakePromptContext;
|
|
18
|
+
export declare function cloneParentWake(wake: PendingParentWake): PendingParentWake;
|
|
19
|
+
export declare function isRedundantParentWake(latestWake: PendingParentWake, dispatchedWake: PendingParentWake): boolean;
|
|
@@ -1,21 +1,7 @@
|
|
|
1
1
|
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
import { type ParentWakePromptContext, type PendingParentWake } from "./parent-wake-dedupe";
|
|
2
3
|
type OpencodeClient = PluginInput["client"];
|
|
3
|
-
export type ParentWakePromptContext
|
|
4
|
-
agent?: string;
|
|
5
|
-
model?: {
|
|
6
|
-
providerID: string;
|
|
7
|
-
modelID: string;
|
|
8
|
-
};
|
|
9
|
-
variant?: string;
|
|
10
|
-
tools?: Record<string, boolean>;
|
|
11
|
-
};
|
|
12
|
-
export type PendingParentWake = {
|
|
13
|
-
promptContext: ParentWakePromptContext;
|
|
14
|
-
notifications: string[];
|
|
15
|
-
shouldReply: boolean;
|
|
16
|
-
dispatchedAt?: number;
|
|
17
|
-
toolCallDeferralStartedAt?: number;
|
|
18
|
-
};
|
|
4
|
+
export type { ParentWakePromptContext, PendingParentWake } from "./parent-wake-dedupe";
|
|
19
5
|
type ParentWakeNotifierDeps = {
|
|
20
6
|
client: OpencodeClient;
|
|
21
7
|
directory: string;
|
|
@@ -59,10 +45,7 @@ export declare class ParentWakeNotifier {
|
|
|
59
45
|
shutdown(): void;
|
|
60
46
|
private isSessionActive;
|
|
61
47
|
private hasRecentParentSessionActivity;
|
|
62
|
-
private resolveParentWakePromptContext;
|
|
63
|
-
private cloneParentWake;
|
|
64
48
|
private trackDispatchedParentWake;
|
|
65
|
-
private isSameParentWake;
|
|
66
49
|
private loadParentWakeSessionMessages;
|
|
67
50
|
private getParentWakeMessageRole;
|
|
68
51
|
private getParentWakeMessageFinish;
|
|
@@ -76,4 +59,3 @@ export declare class ParentWakeNotifier {
|
|
|
76
59
|
private hasAcceptedMessageAfterDispatchedParentWake;
|
|
77
60
|
private requeueWake;
|
|
78
61
|
}
|
|
79
|
-
export {};
|
|
@@ -15,7 +15,12 @@ type ToolResultPart = {
|
|
|
15
15
|
}>;
|
|
16
16
|
[key: string]: unknown;
|
|
17
17
|
};
|
|
18
|
-
type
|
|
18
|
+
type TextPart = {
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
synthetic: true;
|
|
22
|
+
};
|
|
23
|
+
type TransformPart = Part | ToolUsePart | ToolResultPart | TextPart;
|
|
19
24
|
type TransformMessageInfo = Message | {
|
|
20
25
|
role: "user";
|
|
21
26
|
sessionID?: string;
|
package/dist/index.js
CHANGED
|
@@ -7285,7 +7285,6 @@ var init_model_versions = __esm(() => {
|
|
|
7285
7285
|
"anthropic/claude-opus-4-5": "anthropic/claude-opus-4-7",
|
|
7286
7286
|
"anthropic/claude-opus-4-6": "anthropic/claude-opus-4-7",
|
|
7287
7287
|
"anthropic/claude-sonnet-4-5": "anthropic/claude-sonnet-4-6",
|
|
7288
|
-
"openai/gpt-5.3-codex": "openai/gpt-5.4",
|
|
7289
7288
|
"openai/gpt-5.4": "openai/gpt-5.5"
|
|
7290
7289
|
};
|
|
7291
7290
|
});
|
|
@@ -8570,6 +8569,12 @@ function migrateConfigFile(configPath, rawConfig) {
|
|
|
8570
8569
|
delete copy.omo_agent;
|
|
8571
8570
|
needsWrite = true;
|
|
8572
8571
|
}
|
|
8572
|
+
if (copy.lsp !== undefined) {
|
|
8573
|
+
const droppedServers = copy.lsp && typeof copy.lsp === "object" ? Object.keys(copy.lsp) : [];
|
|
8574
|
+
log("Removed obsolete 'lsp' config key from oh-my-opencode config. Custom LSP servers are now configured in .opencode/lsp.json at the project root (consumed by the 'lsp' MCP server). Move any server definitions there to restore them.", { configPath, droppedServers });
|
|
8575
|
+
delete copy.lsp;
|
|
8576
|
+
needsWrite = true;
|
|
8577
|
+
}
|
|
8573
8578
|
if (copy.experimental && typeof copy.experimental === "object") {
|
|
8574
8579
|
const experimental = copy.experimental;
|
|
8575
8580
|
if ("hashline_edit" in experimental) {
|
|
@@ -80677,6 +80682,7 @@ function createTeamModeStatusInjector(config, keywordDetectorConfig) {
|
|
|
80677
80682
|
// src/hooks/tool-pair-validator/hook.ts
|
|
80678
80683
|
init_logger();
|
|
80679
80684
|
var TOOL_RESULT_PLACEHOLDER = "Tool output unavailable (context compacted)";
|
|
80685
|
+
var TOOL_RESULT_RECOVERY_CONTINUATION = "Recovered missing tool results. Continue from the repaired tool output.";
|
|
80680
80686
|
function getToolUseID(part) {
|
|
80681
80687
|
const candidate = part;
|
|
80682
80688
|
if (candidate.type === "tool_use" && typeof candidate.id === "string" && candidate.id.length > 0) {
|
|
@@ -80754,7 +80760,14 @@ function createSyntheticUserMessage(assistantMessage, missingToolUseIDs) {
|
|
|
80754
80760
|
role: "user",
|
|
80755
80761
|
...sessionID ? { sessionID } : {}
|
|
80756
80762
|
},
|
|
80757
|
-
parts:
|
|
80763
|
+
parts: [
|
|
80764
|
+
...missingToolUseIDs.map((toolUseID) => createToolResultPart(toolUseID)),
|
|
80765
|
+
{
|
|
80766
|
+
type: "text",
|
|
80767
|
+
text: TOOL_RESULT_RECOVERY_CONTINUATION,
|
|
80768
|
+
synthetic: true
|
|
80769
|
+
}
|
|
80770
|
+
]
|
|
80758
80771
|
};
|
|
80759
80772
|
}
|
|
80760
80773
|
function getMessageID(message) {
|
|
@@ -92023,6 +92036,10 @@ async function handleAtlasSessionIdle(input) {
|
|
|
92023
92036
|
}
|
|
92024
92037
|
const { boulderState, progress, appendedSession } = activeBoulderSession;
|
|
92025
92038
|
if (progress.isComplete) {
|
|
92039
|
+
if (sessionState.pendingRetryTimer) {
|
|
92040
|
+
clearTimeout(sessionState.pendingRetryTimer);
|
|
92041
|
+
sessionState.pendingRetryTimer = undefined;
|
|
92042
|
+
}
|
|
92026
92043
|
const work = getWorkForSession(ctx.directory, sessionID);
|
|
92027
92044
|
if (work) {
|
|
92028
92045
|
completeBoulder(ctx.directory, work.work_id);
|
|
@@ -92033,6 +92050,10 @@ async function handleAtlasSessionIdle(input) {
|
|
|
92033
92050
|
log(`[${HOOK_NAME7}] Boulder complete`, { sessionID, plan: boulderState.plan_name });
|
|
92034
92051
|
return;
|
|
92035
92052
|
}
|
|
92053
|
+
if (options?.isContinuationStopped?.(sessionID)) {
|
|
92054
|
+
log(`[${HOOK_NAME7}] Boulder completion nudge skipped because continuation stopped`, { sessionID, plan: boulderState.plan_name });
|
|
92055
|
+
return;
|
|
92056
|
+
}
|
|
92036
92057
|
if (sessionState.boulderCompletionNudgedAt?.[work.work_id]) {
|
|
92037
92058
|
log(`[${HOOK_NAME7}] Boulder complete`, { sessionID, plan: boulderState.plan_name });
|
|
92038
92059
|
return;
|
|
@@ -100240,6 +100261,7 @@ function getOpenCodeBundledRg() {
|
|
|
100240
100261
|
const isWindows2 = process.platform === "win32";
|
|
100241
100262
|
const rgName = isWindows2 ? "rg.exe" : "rg";
|
|
100242
100263
|
const candidates = [
|
|
100264
|
+
join81(getOpenCodeCacheDir(), "bin", rgName),
|
|
100243
100265
|
join81(getDataDir(), "opencode", "bin", rgName),
|
|
100244
100266
|
join81(execDir, rgName),
|
|
100245
100267
|
join81(execDir, "bin", rgName),
|
|
@@ -104826,10 +104848,10 @@ async function waitForLookAtSessionResult(client, sessionID, options) {
|
|
|
104826
104848
|
const isActive = supportedButNeverSeen || statusType !== null && !isTerminal;
|
|
104827
104849
|
const { messages, error: messagesError } = await getSessionMessages(client, sessionID);
|
|
104828
104850
|
const outcome = extractLatestAssistantOutcome(messages);
|
|
104829
|
-
if (outcome.text && !isActive) {
|
|
104851
|
+
if (outcome.text && (!isActive || supportedButNeverSeen)) {
|
|
104830
104852
|
return { messages, outcome, statusType };
|
|
104831
104853
|
}
|
|
104832
|
-
if (outcome.errorName && !isActive) {
|
|
104854
|
+
if (outcome.errorName && (!isActive || supportedButNeverSeen)) {
|
|
104833
104855
|
return { messages, outcome, statusType };
|
|
104834
104856
|
}
|
|
104835
104857
|
if (isActive) {
|
|
@@ -104905,7 +104927,7 @@ Original error: ${createResult.error}`;
|
|
|
104905
104927
|
const sessionID = createResult.data.id;
|
|
104906
104928
|
log(`[look_at] Created session: ${sessionID}`);
|
|
104907
104929
|
log(`[look_at] Sending prompt with ${isBase64Input ? "base64 image" : "file"} to session ${sessionID}`);
|
|
104908
|
-
let
|
|
104930
|
+
let shouldWaitForStatus = true;
|
|
104909
104931
|
try {
|
|
104910
104932
|
await promptSyncWithModelSuggestionRetry(ctx.client, {
|
|
104911
104933
|
path: { id: sessionID },
|
|
@@ -104928,15 +104950,14 @@ Original error: ${createResult.error}`;
|
|
|
104928
104950
|
queueBehavior: "defer"
|
|
104929
104951
|
});
|
|
104930
104952
|
} catch (promptError) {
|
|
104931
|
-
|
|
104932
|
-
|
|
104953
|
+
log("[look_at] Prompt dispatch failed; checking child session evidence:", promptError);
|
|
104954
|
+
shouldWaitForStatus = isAmbiguousPromptDispatchFailure(promptError);
|
|
104933
104955
|
}
|
|
104934
104956
|
let observedMessages;
|
|
104935
104957
|
let observedText;
|
|
104936
|
-
if (typeof ctx.client.session.status === "function") {
|
|
104958
|
+
if (shouldWaitForStatus && typeof ctx.client.session.status === "function") {
|
|
104937
104959
|
const waitResult = await waitForLookAtSessionResult(ctx.client, sessionID, {
|
|
104938
|
-
allowStableIdleWithoutActivity: true
|
|
104939
|
-
allowEmptyStableIdleWithoutActivity: promptFailed
|
|
104960
|
+
allowStableIdleWithoutActivity: true
|
|
104940
104961
|
});
|
|
104941
104962
|
observedText = waitResult.outcome.text ?? undefined;
|
|
104942
104963
|
if (observedText) {
|
|
@@ -106343,6 +106364,9 @@ async function executeBackgroundTask(args, ctx, executorCtx, parentContext, agen
|
|
|
106343
106364
|
|
|
106344
106365
|
Task ID: ${task.id}`;
|
|
106345
106366
|
}
|
|
106367
|
+
if (!sessionId && updatedTask?.sessionId) {
|
|
106368
|
+
sessionId = updatedTask.sessionId;
|
|
106369
|
+
}
|
|
106346
106370
|
if (sessionId) {
|
|
106347
106371
|
registerBackgroundSessionContext({
|
|
106348
106372
|
sessionId,
|
|
@@ -110688,6 +110712,42 @@ function detectRepetitiveToolUse(window) {
|
|
|
110688
110712
|
|
|
110689
110713
|
// src/features/background-agent/parent-wake-notifier.ts
|
|
110690
110714
|
init_shared();
|
|
110715
|
+
|
|
110716
|
+
// src/features/background-agent/parent-wake-dedupe.ts
|
|
110717
|
+
function resolveParentWakePromptContext(promptContext) {
|
|
110718
|
+
const resolvedAgent = resolveRegisteredAgentName(promptContext.agent);
|
|
110719
|
+
return {
|
|
110720
|
+
...promptContext,
|
|
110721
|
+
...resolvedAgent ? { agent: resolvedAgent } : {},
|
|
110722
|
+
...promptContext.model ? { model: { ...promptContext.model } } : {},
|
|
110723
|
+
...promptContext.tools ? { tools: { ...promptContext.tools } } : {}
|
|
110724
|
+
};
|
|
110725
|
+
}
|
|
110726
|
+
function cloneParentWake(wake) {
|
|
110727
|
+
const promptContext = resolveParentWakePromptContext(wake.promptContext);
|
|
110728
|
+
return {
|
|
110729
|
+
promptContext,
|
|
110730
|
+
notifications: [...wake.notifications],
|
|
110731
|
+
shouldReply: wake.shouldReply,
|
|
110732
|
+
...wake.dispatchedAt !== undefined ? { dispatchedAt: wake.dispatchedAt } : {},
|
|
110733
|
+
...wake.toolCallDeferralStartedAt !== undefined ? { toolCallDeferralStartedAt: wake.toolCallDeferralStartedAt } : {}
|
|
110734
|
+
};
|
|
110735
|
+
}
|
|
110736
|
+
function isRedundantParentWake(latestWake, dispatchedWake) {
|
|
110737
|
+
return parentWakePromptContextMatches(latestWake, dispatchedWake) && parentWakeReplyModeIsCovered(latestWake, dispatchedWake) && parentWakeNotificationsAreCovered(latestWake, dispatchedWake);
|
|
110738
|
+
}
|
|
110739
|
+
function parentWakePromptContextMatches(left, right) {
|
|
110740
|
+
return JSON.stringify(left.promptContext) === JSON.stringify(right.promptContext);
|
|
110741
|
+
}
|
|
110742
|
+
function parentWakeReplyModeIsCovered(latestWake, dispatchedWake) {
|
|
110743
|
+
return !latestWake.shouldReply || dispatchedWake.shouldReply;
|
|
110744
|
+
}
|
|
110745
|
+
function parentWakeNotificationsAreCovered(latestWake, dispatchedWake) {
|
|
110746
|
+
const dispatchedNotifications = new Set(dispatchedWake.notifications);
|
|
110747
|
+
return latestWake.notifications.every((notification2) => dispatchedNotifications.has(notification2));
|
|
110748
|
+
}
|
|
110749
|
+
|
|
110750
|
+
// src/features/background-agent/parent-wake-notifier.ts
|
|
110691
110751
|
function unrefTimerHandle(handle) {
|
|
110692
110752
|
const maybeUnref = handle.unref;
|
|
110693
110753
|
if (typeof maybeUnref === "function") {
|
|
@@ -110725,7 +110785,7 @@ class ParentWakeNotifier {
|
|
|
110725
110785
|
this.recentParentSessionActivity.set(sessionID, Date.now());
|
|
110726
110786
|
}
|
|
110727
110787
|
queuePendingParentWake(sessionID, notification2, promptContext, shouldReply, delayMs) {
|
|
110728
|
-
const resolvedPromptContext =
|
|
110788
|
+
const resolvedPromptContext = resolveParentWakePromptContext(promptContext);
|
|
110729
110789
|
const pendingWake = this.pendingParentWakes.get(sessionID);
|
|
110730
110790
|
if (pendingWake) {
|
|
110731
110791
|
pendingWake.notifications.push(notification2);
|
|
@@ -110778,6 +110838,12 @@ class ParentWakeNotifier {
|
|
|
110778
110838
|
});
|
|
110779
110839
|
return;
|
|
110780
110840
|
}
|
|
110841
|
+
const dispatchedWake = this.dispatchedParentWakes.get(sessionID);
|
|
110842
|
+
if (dispatchedWake && isRedundantParentWake(latestWake, dispatchedWake)) {
|
|
110843
|
+
this.pendingParentWakes.delete(sessionID);
|
|
110844
|
+
log("[background-agent] Suppressed duplicate parent wake already dispatched:", { sessionID });
|
|
110845
|
+
return;
|
|
110846
|
+
}
|
|
110781
110847
|
this.pendingParentWakes.delete(sessionID);
|
|
110782
110848
|
const notificationContent = latestWake.notifications.join(`
|
|
110783
110849
|
|
|
@@ -110805,9 +110871,9 @@ class ParentWakeNotifier {
|
|
|
110805
110871
|
});
|
|
110806
110872
|
if (promptResult.status === "failed") {
|
|
110807
110873
|
if (isAmbiguousPostDispatchPromptFailure(promptResult)) {
|
|
110808
|
-
const
|
|
110809
|
-
|
|
110810
|
-
if (await this.hasAcceptedMessageAfterDispatchedParentWake(sessionID,
|
|
110874
|
+
const dispatchedWake2 = cloneParentWake(latestWake);
|
|
110875
|
+
dispatchedWake2.dispatchedAt = dispatchStartedAt;
|
|
110876
|
+
if (await this.hasAcceptedMessageAfterDispatchedParentWake(sessionID, dispatchedWake2)) {
|
|
110811
110877
|
this.trackDispatchedParentWake(sessionID, latestWake, dispatchStartedAt);
|
|
110812
110878
|
log("[background-agent] Treated failed parent wake prompt as accepted after observing session history:", {
|
|
110813
110879
|
sessionID,
|
|
@@ -110819,8 +110885,8 @@ class ParentWakeNotifier {
|
|
|
110819
110885
|
throw promptResult.error;
|
|
110820
110886
|
}
|
|
110821
110887
|
if (promptResult.status === "reserved" && promptResult.reservedBy === "background-agent-parent-wake") {
|
|
110822
|
-
const
|
|
110823
|
-
if (
|
|
110888
|
+
const dispatchedWake2 = this.dispatchedParentWakes.get(sessionID);
|
|
110889
|
+
if (dispatchedWake2 && isRedundantParentWake(latestWake, dispatchedWake2)) {
|
|
110824
110890
|
log("[background-agent] Suppressed duplicate parent wake during promptAsync gate hold:", { sessionID });
|
|
110825
110891
|
return;
|
|
110826
110892
|
}
|
|
@@ -110929,28 +110995,9 @@ class ParentWakeNotifier {
|
|
|
110929
110995
|
this.recentParentSessionActivity.delete(sessionID);
|
|
110930
110996
|
return false;
|
|
110931
110997
|
}
|
|
110932
|
-
resolveParentWakePromptContext(promptContext) {
|
|
110933
|
-
const resolvedAgent = resolveRegisteredAgentName(promptContext.agent);
|
|
110934
|
-
return {
|
|
110935
|
-
...promptContext,
|
|
110936
|
-
...resolvedAgent ? { agent: resolvedAgent } : {},
|
|
110937
|
-
...promptContext.model ? { model: { ...promptContext.model } } : {},
|
|
110938
|
-
...promptContext.tools ? { tools: { ...promptContext.tools } } : {}
|
|
110939
|
-
};
|
|
110940
|
-
}
|
|
110941
|
-
cloneParentWake(wake) {
|
|
110942
|
-
const promptContext = this.resolveParentWakePromptContext(wake.promptContext);
|
|
110943
|
-
return {
|
|
110944
|
-
promptContext,
|
|
110945
|
-
notifications: [...wake.notifications],
|
|
110946
|
-
shouldReply: wake.shouldReply,
|
|
110947
|
-
...wake.dispatchedAt !== undefined ? { dispatchedAt: wake.dispatchedAt } : {},
|
|
110948
|
-
...wake.toolCallDeferralStartedAt !== undefined ? { toolCallDeferralStartedAt: wake.toolCallDeferralStartedAt } : {}
|
|
110949
|
-
};
|
|
110950
|
-
}
|
|
110951
110998
|
trackDispatchedParentWake(sessionID, wake, dispatchedAt) {
|
|
110952
110999
|
this.clearDispatchedParentWake(sessionID);
|
|
110953
|
-
const dispatchedWake =
|
|
111000
|
+
const dispatchedWake = cloneParentWake(wake);
|
|
110954
111001
|
dispatchedWake.dispatchedAt = dispatchedAt;
|
|
110955
111002
|
this.dispatchedParentWakes.set(sessionID, dispatchedWake);
|
|
110956
111003
|
const timer = setTimeout(() => {
|
|
@@ -110960,9 +111007,6 @@ class ParentWakeNotifier {
|
|
|
110960
111007
|
unrefTimerHandle(timer);
|
|
110961
111008
|
this.dispatchedParentWakeTimers.set(sessionID, timer);
|
|
110962
111009
|
}
|
|
110963
|
-
isSameParentWake(left, right) {
|
|
110964
|
-
return left.shouldReply === right.shouldReply && JSON.stringify(left.notifications) === JSON.stringify(right.notifications) && JSON.stringify(left.promptContext) === JSON.stringify(right.promptContext);
|
|
110965
|
-
}
|
|
110966
111010
|
async loadParentWakeSessionMessages(sessionID) {
|
|
110967
111011
|
try {
|
|
110968
111012
|
const messagesResp = await messagesInDirectory(this.deps.client, {
|
|
@@ -111131,7 +111175,7 @@ class ParentWakeNotifier {
|
|
|
111131
111175
|
pendingWake.toolCallDeferralStartedAt ??= latestWake.toolCallDeferralStartedAt;
|
|
111132
111176
|
return;
|
|
111133
111177
|
}
|
|
111134
|
-
this.pendingParentWakes.set(sessionID,
|
|
111178
|
+
this.pendingParentWakes.set(sessionID, cloneParentWake(latestWake));
|
|
111135
111179
|
}
|
|
111136
111180
|
}
|
|
111137
111181
|
|
|
@@ -132354,13 +132398,16 @@ function maybeCreateAtlasConfig(input) {
|
|
|
132354
132398
|
return;
|
|
132355
132399
|
const orchestratorOverride = agentOverrides["atlas"];
|
|
132356
132400
|
const atlasRequirement = AGENT_MODEL_REQUIREMENTS["atlas"];
|
|
132357
|
-
|
|
132401
|
+
let atlasResolution = applyModelResolution({
|
|
132358
132402
|
uiSelectedModel: orchestratorOverride?.model !== undefined ? undefined : uiSelectedModel,
|
|
132359
132403
|
userModel: orchestratorOverride?.model,
|
|
132360
132404
|
requirement: atlasRequirement,
|
|
132361
132405
|
availableModels,
|
|
132362
132406
|
systemDefaultModel
|
|
132363
132407
|
});
|
|
132408
|
+
if (!atlasResolution && orchestratorOverride?.model) {
|
|
132409
|
+
atlasResolution = { model: orchestratorOverride.model, provenance: "override" };
|
|
132410
|
+
}
|
|
132364
132411
|
if (!atlasResolution)
|
|
132365
132412
|
return;
|
|
132366
132413
|
const { model: atlasModel, variant: atlasResolvedVariant } = atlasResolution;
|
|
@@ -136031,6 +136078,14 @@ function supportsImageInput(modelConfig) {
|
|
|
136031
136078
|
}
|
|
136032
136079
|
return modelConfig?.capabilities?.input?.image === true;
|
|
136033
136080
|
}
|
|
136081
|
+
function parseTrustedModel(modelString) {
|
|
136082
|
+
const [providerID, ...modelIDParts] = modelString.split("/");
|
|
136083
|
+
const modelID = modelIDParts.join("/");
|
|
136084
|
+
if (!providerID || modelID.length === 0) {
|
|
136085
|
+
return;
|
|
136086
|
+
}
|
|
136087
|
+
return { providerID, modelID };
|
|
136088
|
+
}
|
|
136034
136089
|
function applyProviderConfig(params) {
|
|
136035
136090
|
const providers = params.config.provider;
|
|
136036
136091
|
const modelContextLimitsCache = params.modelCacheState.modelContextLimitsCache;
|
|
@@ -136041,22 +136096,31 @@ function applyProviderConfig(params) {
|
|
|
136041
136096
|
params.modelCacheState.visionCapableModelsCache = visionCapableModelsCache2;
|
|
136042
136097
|
visionCapableModelsCache2.clear();
|
|
136043
136098
|
setVisionCapableModelsCache(visionCapableModelsCache2);
|
|
136044
|
-
if (
|
|
136045
|
-
|
|
136046
|
-
|
|
136047
|
-
|
|
136048
|
-
if (!models)
|
|
136049
|
-
continue;
|
|
136050
|
-
for (const [modelID, modelConfig] of Object.entries(models)) {
|
|
136051
|
-
if (supportsImageInput(modelConfig)) {
|
|
136052
|
-
visionCapableModelsCache2.set(`${providerID}/${modelID}`, { providerID, modelID });
|
|
136053
|
-
}
|
|
136054
|
-
const contextLimit = modelConfig?.limit?.context;
|
|
136055
|
-
if (!contextLimit)
|
|
136099
|
+
if (providers) {
|
|
136100
|
+
for (const [providerID, providerConfig] of Object.entries(providers)) {
|
|
136101
|
+
const models = providerConfig?.models;
|
|
136102
|
+
if (!models)
|
|
136056
136103
|
continue;
|
|
136057
|
-
|
|
136104
|
+
for (const [modelID, modelConfig] of Object.entries(models)) {
|
|
136105
|
+
if (supportsImageInput(modelConfig)) {
|
|
136106
|
+
visionCapableModelsCache2.set(`${providerID}/${modelID}`, { providerID, modelID });
|
|
136107
|
+
}
|
|
136108
|
+
const contextLimit = modelConfig?.limit?.context;
|
|
136109
|
+
if (!contextLimit)
|
|
136110
|
+
continue;
|
|
136111
|
+
modelContextLimitsCache.set(`${providerID}/${modelID}`, contextLimit);
|
|
136112
|
+
}
|
|
136058
136113
|
}
|
|
136059
136114
|
}
|
|
136115
|
+
for (const trustedModelString of params.trustedVisionCapableModels ?? []) {
|
|
136116
|
+
const trustedModel = parseTrustedModel(trustedModelString);
|
|
136117
|
+
if (!trustedModel)
|
|
136118
|
+
continue;
|
|
136119
|
+
const key = `${trustedModel.providerID}/${trustedModel.modelID}`;
|
|
136120
|
+
if (visionCapableModelsCache2.has(key))
|
|
136121
|
+
continue;
|
|
136122
|
+
visionCapableModelsCache2.set(key, trustedModel);
|
|
136123
|
+
}
|
|
136060
136124
|
}
|
|
136061
136125
|
|
|
136062
136126
|
// src/plugin-handlers/plugin-components-loader.ts
|
|
@@ -136218,12 +136282,25 @@ function applyToolConfig(params) {
|
|
|
136218
136282
|
}
|
|
136219
136283
|
|
|
136220
136284
|
// src/plugin-handlers/config-handler.ts
|
|
136285
|
+
function collectTrustedVisionCapableModels(pluginConfig) {
|
|
136286
|
+
const trusted = [];
|
|
136287
|
+
const multimodalLookerOverride = pluginConfig.agents?.["multimodal-looker"];
|
|
136288
|
+
const configuredModel = multimodalLookerOverride?.model;
|
|
136289
|
+
if (typeof configuredModel === "string" && configuredModel.includes("/")) {
|
|
136290
|
+
trusted.push(configuredModel);
|
|
136291
|
+
}
|
|
136292
|
+
return trusted;
|
|
136293
|
+
}
|
|
136221
136294
|
function createConfigHandler(deps) {
|
|
136222
136295
|
const { ctx, pluginConfig, modelCacheState } = deps;
|
|
136223
136296
|
return async (config) => {
|
|
136224
136297
|
const formatterConfig = config.formatter;
|
|
136225
136298
|
setAdditionalAllowedMcpEnvVars(pluginConfig.mcp_env_allowlist ?? []);
|
|
136226
|
-
applyProviderConfig({
|
|
136299
|
+
applyProviderConfig({
|
|
136300
|
+
config,
|
|
136301
|
+
modelCacheState,
|
|
136302
|
+
trustedVisionCapableModels: collectTrustedVisionCapableModels(pluginConfig)
|
|
136303
|
+
});
|
|
136227
136304
|
clearFormatterCache();
|
|
136228
136305
|
const pluginComponents = await loadPluginComponents({ pluginConfig });
|
|
136229
136306
|
applyHookConfig({ pluginComponents, ctx });
|
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
* bumps to newer model versions.
|
|
5
5
|
*
|
|
6
6
|
* Keys are full "provider/model" strings. Only openai and anthropic entries needed.
|
|
7
|
+
*
|
|
8
|
+
* Only include genuinely retired/superseded models here. Do NOT add mappings
|
|
9
|
+
* for current, user-selectable variants — `gpt-5.3-codex` is the canonical
|
|
10
|
+
* codex powerhouse referenced in docs/guide/agent-model-matching.md and is
|
|
11
|
+
* NOT a deprecated alias for `gpt-5.4`. Auto-rewriting an explicit user
|
|
12
|
+
* choice silently broke configurations (#3777).
|
|
7
13
|
*/
|
|
8
14
|
export declare const MODEL_VERSION_MAP: Record<string, string>;
|
|
9
15
|
export declare function migrateModelVersions(configs: Record<string, unknown>, appliedMigrations?: Set<string>): {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-opencode",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -110,17 +110,17 @@
|
|
|
110
110
|
"zod": "^4.4.3"
|
|
111
111
|
},
|
|
112
112
|
"optionalDependencies": {
|
|
113
|
-
"oh-my-opencode-darwin-arm64": "4.
|
|
114
|
-
"oh-my-opencode-darwin-x64": "4.
|
|
115
|
-
"oh-my-opencode-darwin-x64-baseline": "4.
|
|
116
|
-
"oh-my-opencode-linux-arm64": "4.
|
|
117
|
-
"oh-my-opencode-linux-arm64-musl": "4.
|
|
118
|
-
"oh-my-opencode-linux-x64": "4.
|
|
119
|
-
"oh-my-opencode-linux-x64-baseline": "4.
|
|
120
|
-
"oh-my-opencode-linux-x64-musl": "4.
|
|
121
|
-
"oh-my-opencode-linux-x64-musl-baseline": "4.
|
|
122
|
-
"oh-my-opencode-windows-x64": "4.
|
|
123
|
-
"oh-my-opencode-windows-x64-baseline": "4.
|
|
113
|
+
"oh-my-opencode-darwin-arm64": "4.4.0",
|
|
114
|
+
"oh-my-opencode-darwin-x64": "4.4.0",
|
|
115
|
+
"oh-my-opencode-darwin-x64-baseline": "4.4.0",
|
|
116
|
+
"oh-my-opencode-linux-arm64": "4.4.0",
|
|
117
|
+
"oh-my-opencode-linux-arm64-musl": "4.4.0",
|
|
118
|
+
"oh-my-opencode-linux-x64": "4.4.0",
|
|
119
|
+
"oh-my-opencode-linux-x64-baseline": "4.4.0",
|
|
120
|
+
"oh-my-opencode-linux-x64-musl": "4.4.0",
|
|
121
|
+
"oh-my-opencode-linux-x64-musl-baseline": "4.4.0",
|
|
122
|
+
"oh-my-opencode-windows-x64": "4.4.0",
|
|
123
|
+
"oh-my-opencode-windows-x64-baseline": "4.4.0"
|
|
124
124
|
},
|
|
125
125
|
"overrides": {
|
|
126
126
|
"hono": "^4.12.18",
|