oh-my-opencode 3.5.2 → 3.5.3
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 +26 -9
- package/dist/cli/run/poll-for-completion.d.ts +1 -0
- package/dist/features/background-agent/background-event-handler.d.ts +1 -0
- package/dist/features/background-agent/manager.d.ts +2 -0
- package/dist/features/background-agent/session-idle-event-handler.d.ts +10 -0
- package/dist/features/background-agent/session-task-cleanup.d.ts +10 -0
- package/dist/features/background-agent/stale-task-pruner.d.ts +7 -1
- package/dist/hooks/prometheus-md-only/agent-matcher.d.ts +1 -0
- package/dist/index.js +208 -35
- package/package.json +8 -8
package/dist/cli/index.js
CHANGED
|
@@ -7052,6 +7052,12 @@ var init_tmux = __esm(() => {
|
|
|
7052
7052
|
var init_model_suggestion_retry = __esm(() => {
|
|
7053
7053
|
init_logger();
|
|
7054
7054
|
});
|
|
7055
|
+
|
|
7056
|
+
// src/shared/opencode-server-auth.ts
|
|
7057
|
+
var init_opencode_server_auth = __esm(() => {
|
|
7058
|
+
init_logger();
|
|
7059
|
+
});
|
|
7060
|
+
|
|
7055
7061
|
// src/shared/port-utils.ts
|
|
7056
7062
|
async function isPortAvailable(port, hostname = "127.0.0.1") {
|
|
7057
7063
|
try {
|
|
@@ -7128,6 +7134,7 @@ var init_shared = __esm(() => {
|
|
|
7128
7134
|
init_session_utils();
|
|
7129
7135
|
init_tmux();
|
|
7130
7136
|
init_model_suggestion_retry();
|
|
7137
|
+
init_opencode_server_auth();
|
|
7131
7138
|
init_port_utils();
|
|
7132
7139
|
init_git_worktree();
|
|
7133
7140
|
init_safe_create_hook();
|
|
@@ -8870,7 +8877,7 @@ var {
|
|
|
8870
8877
|
// package.json
|
|
8871
8878
|
var package_default = {
|
|
8872
8879
|
name: "oh-my-opencode",
|
|
8873
|
-
version: "3.5.
|
|
8880
|
+
version: "3.5.3",
|
|
8874
8881
|
description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
8875
8882
|
main: "dist/index.js",
|
|
8876
8883
|
types: "dist/index.d.ts",
|
|
@@ -8944,13 +8951,13 @@ var package_default = {
|
|
|
8944
8951
|
typescript: "^5.7.3"
|
|
8945
8952
|
},
|
|
8946
8953
|
optionalDependencies: {
|
|
8947
|
-
"oh-my-opencode-darwin-arm64": "3.5.
|
|
8948
|
-
"oh-my-opencode-darwin-x64": "3.5.
|
|
8949
|
-
"oh-my-opencode-linux-arm64": "3.5.
|
|
8950
|
-
"oh-my-opencode-linux-arm64-musl": "3.5.
|
|
8951
|
-
"oh-my-opencode-linux-x64": "3.5.
|
|
8952
|
-
"oh-my-opencode-linux-x64-musl": "3.5.
|
|
8953
|
-
"oh-my-opencode-windows-x64": "3.5.
|
|
8954
|
+
"oh-my-opencode-darwin-arm64": "3.5.3",
|
|
8955
|
+
"oh-my-opencode-darwin-x64": "3.5.3",
|
|
8956
|
+
"oh-my-opencode-linux-arm64": "3.5.3",
|
|
8957
|
+
"oh-my-opencode-linux-arm64-musl": "3.5.3",
|
|
8958
|
+
"oh-my-opencode-linux-x64": "3.5.3",
|
|
8959
|
+
"oh-my-opencode-linux-x64-musl": "3.5.3",
|
|
8960
|
+
"oh-my-opencode-windows-x64": "3.5.3"
|
|
8954
8961
|
},
|
|
8955
8962
|
trustedDependencies: [
|
|
8956
8963
|
"@ast-grep/cli",
|
|
@@ -24752,11 +24759,14 @@ async function areAllDescendantsIdle(ctx, sessionID, allStatuses) {
|
|
|
24752
24759
|
var DEFAULT_POLL_INTERVAL_MS = 500;
|
|
24753
24760
|
var DEFAULT_REQUIRED_CONSECUTIVE = 3;
|
|
24754
24761
|
var ERROR_GRACE_CYCLES = 3;
|
|
24762
|
+
var MIN_STABILIZATION_MS = 1e4;
|
|
24755
24763
|
async function pollForCompletion(ctx, eventState, abortController, options = {}) {
|
|
24756
24764
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
24757
24765
|
const requiredConsecutive = options.requiredConsecutive ?? DEFAULT_REQUIRED_CONSECUTIVE;
|
|
24766
|
+
const minStabilizationMs = options.minStabilizationMs ?? MIN_STABILIZATION_MS;
|
|
24758
24767
|
let consecutiveCompleteChecks = 0;
|
|
24759
24768
|
let errorCycleCount = 0;
|
|
24769
|
+
let firstWorkTimestamp = null;
|
|
24760
24770
|
while (!abortController.signal.aborted) {
|
|
24761
24771
|
await new Promise((resolve2) => setTimeout(resolve2, pollIntervalMs));
|
|
24762
24772
|
if (eventState.mainSessionError) {
|
|
@@ -24784,6 +24794,13 @@ Session ended with error: ${eventState.lastError}`));
|
|
|
24784
24794
|
consecutiveCompleteChecks = 0;
|
|
24785
24795
|
continue;
|
|
24786
24796
|
}
|
|
24797
|
+
if (firstWorkTimestamp === null) {
|
|
24798
|
+
firstWorkTimestamp = Date.now();
|
|
24799
|
+
}
|
|
24800
|
+
if (Date.now() - firstWorkTimestamp < minStabilizationMs) {
|
|
24801
|
+
consecutiveCompleteChecks = 0;
|
|
24802
|
+
continue;
|
|
24803
|
+
}
|
|
24787
24804
|
const shouldExit = await checkCompletionConditions(ctx);
|
|
24788
24805
|
if (shouldExit) {
|
|
24789
24806
|
consecutiveCompleteChecks++;
|
|
@@ -24848,7 +24865,7 @@ Interrupted. Shutting down...`));
|
|
|
24848
24865
|
});
|
|
24849
24866
|
console.log(import_picocolors14.default.dim(`Session: ${sessionID}`));
|
|
24850
24867
|
const ctx = { client: client3, sessionID, directory, abortController };
|
|
24851
|
-
const events = await client3.event.subscribe();
|
|
24868
|
+
const events = await client3.event.subscribe({ query: { directory } });
|
|
24852
24869
|
const eventState = createEventState();
|
|
24853
24870
|
const eventProcessor = processEvents(ctx, events.stream, eventState).catch(() => {});
|
|
24854
24871
|
console.log(import_picocolors14.default.dim(`
|
|
@@ -3,5 +3,6 @@ import type { EventState } from "./events";
|
|
|
3
3
|
export interface PollOptions {
|
|
4
4
|
pollIntervalMs?: number;
|
|
5
5
|
requiredConsecutive?: number;
|
|
6
|
+
minStabilizationMs?: number;
|
|
6
7
|
}
|
|
7
8
|
export declare function pollForCompletion(ctx: RunContext, eventState: EventState, abortController: AbortController, options?: PollOptions): Promise<number>;
|
|
@@ -7,6 +7,7 @@ export declare function handleBackgroundEvent(args: {
|
|
|
7
7
|
event: Event;
|
|
8
8
|
findBySession: (sessionID: string) => BackgroundTask | undefined;
|
|
9
9
|
getAllDescendantTasks: (sessionID: string) => BackgroundTask[];
|
|
10
|
+
releaseConcurrencyKey?: (key: string) => void;
|
|
10
11
|
cancelTask: (taskId: string, options: {
|
|
11
12
|
source: string;
|
|
12
13
|
reason: string;
|
|
@@ -114,6 +114,8 @@ export declare class BackgroundManager {
|
|
|
114
114
|
private formatDuration;
|
|
115
115
|
private isAbortedSessionError;
|
|
116
116
|
private getErrorText;
|
|
117
|
+
private isRecord;
|
|
118
|
+
private getSessionErrorMessage;
|
|
117
119
|
private hasRunningTasks;
|
|
118
120
|
private pruneStaleTasksAndNotifications;
|
|
119
121
|
private checkAndInterruptStaleTasks;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BackgroundTask } from "./types";
|
|
2
|
+
export declare function handleSessionIdleBackgroundEvent(args: {
|
|
3
|
+
properties: Record<string, unknown>;
|
|
4
|
+
findBySession: (sessionID: string) => BackgroundTask | undefined;
|
|
5
|
+
idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>>;
|
|
6
|
+
validateSessionHasOutput: (sessionID: string) => Promise<boolean>;
|
|
7
|
+
checkSessionTodos: (sessionID: string) => Promise<boolean>;
|
|
8
|
+
tryCompleteTask: (task: BackgroundTask, source: string) => Promise<boolean>;
|
|
9
|
+
emitIdleEvent: (sessionID: string) => void;
|
|
10
|
+
}): void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BackgroundTask } from "./types";
|
|
2
|
+
export declare function cleanupTaskAfterSessionEnds(args: {
|
|
3
|
+
task: BackgroundTask;
|
|
4
|
+
tasks: Map<string, BackgroundTask>;
|
|
5
|
+
idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>>;
|
|
6
|
+
completionTimers: Map<string, ReturnType<typeof setTimeout>>;
|
|
7
|
+
cleanupPendingByParent: (task: BackgroundTask) => void;
|
|
8
|
+
clearNotificationsForTask: (taskId: string) => void;
|
|
9
|
+
releaseConcurrencyKey?: (key: string) => void;
|
|
10
|
+
}): void;
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import type { BackgroundTask } from "./types";
|
|
1
|
+
import type { BackgroundTask, LaunchInput } from "./types";
|
|
2
2
|
import type { ConcurrencyManager } from "./concurrency";
|
|
3
|
+
type QueueItem = {
|
|
4
|
+
task: BackgroundTask;
|
|
5
|
+
input: LaunchInput;
|
|
6
|
+
};
|
|
3
7
|
export declare function pruneStaleState(args: {
|
|
4
8
|
tasks: Map<string, BackgroundTask>;
|
|
5
9
|
notifications: Map<string, BackgroundTask[]>;
|
|
10
|
+
queuesByKey: Map<string, QueueItem[]>;
|
|
6
11
|
concurrencyManager: ConcurrencyManager;
|
|
7
12
|
cleanupPendingByParent: (task: BackgroundTask) => void;
|
|
8
13
|
clearNotificationsForTask: (taskId: string) => void;
|
|
9
14
|
}): void;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isPrometheusAgent(agentName: string | undefined): boolean;
|
package/dist/index.js
CHANGED
|
@@ -34314,6 +34314,7 @@ async function promptSyncWithModelSuggestionRetry(client, args) {
|
|
|
34314
34314
|
}
|
|
34315
34315
|
}
|
|
34316
34316
|
// src/shared/opencode-server-auth.ts
|
|
34317
|
+
init_logger();
|
|
34317
34318
|
function getServerBasicAuthHeader() {
|
|
34318
34319
|
const password = process.env.OPENCODE_SERVER_PASSWORD;
|
|
34319
34320
|
if (!password) {
|
|
@@ -34323,27 +34324,128 @@ function getServerBasicAuthHeader() {
|
|
|
34323
34324
|
const token = Buffer.from(`${username}:${password}`, "utf8").toString("base64");
|
|
34324
34325
|
return `Basic ${token}`;
|
|
34325
34326
|
}
|
|
34327
|
+
function isRecord(value) {
|
|
34328
|
+
return typeof value === "object" && value !== null;
|
|
34329
|
+
}
|
|
34330
|
+
function isRequestFetch(value) {
|
|
34331
|
+
return typeof value === "function";
|
|
34332
|
+
}
|
|
34333
|
+
function wrapRequestFetch(baseFetch, auth) {
|
|
34334
|
+
return async (request) => {
|
|
34335
|
+
const headers = new Headers(request.headers);
|
|
34336
|
+
headers.set("Authorization", auth);
|
|
34337
|
+
return baseFetch(new Request(request, { headers }));
|
|
34338
|
+
};
|
|
34339
|
+
}
|
|
34340
|
+
function getInternalClient(client) {
|
|
34341
|
+
if (!isRecord(client)) {
|
|
34342
|
+
return null;
|
|
34343
|
+
}
|
|
34344
|
+
const internal = client["_client"];
|
|
34345
|
+
return isRecord(internal) ? internal : null;
|
|
34346
|
+
}
|
|
34347
|
+
function tryInjectViaSetConfigHeaders(internal, auth) {
|
|
34348
|
+
const setConfig = internal["setConfig"];
|
|
34349
|
+
if (typeof setConfig !== "function") {
|
|
34350
|
+
return false;
|
|
34351
|
+
}
|
|
34352
|
+
setConfig({
|
|
34353
|
+
headers: {
|
|
34354
|
+
Authorization: auth
|
|
34355
|
+
}
|
|
34356
|
+
});
|
|
34357
|
+
return true;
|
|
34358
|
+
}
|
|
34359
|
+
function tryInjectViaInterceptors(internal, auth) {
|
|
34360
|
+
const interceptors = internal["interceptors"];
|
|
34361
|
+
if (!isRecord(interceptors)) {
|
|
34362
|
+
return false;
|
|
34363
|
+
}
|
|
34364
|
+
const requestInterceptors = interceptors["request"];
|
|
34365
|
+
if (!isRecord(requestInterceptors)) {
|
|
34366
|
+
return false;
|
|
34367
|
+
}
|
|
34368
|
+
const use = requestInterceptors["use"];
|
|
34369
|
+
if (typeof use !== "function") {
|
|
34370
|
+
return false;
|
|
34371
|
+
}
|
|
34372
|
+
use((request) => {
|
|
34373
|
+
if (!request.headers.get("Authorization")) {
|
|
34374
|
+
request.headers.set("Authorization", auth);
|
|
34375
|
+
}
|
|
34376
|
+
return request;
|
|
34377
|
+
});
|
|
34378
|
+
return true;
|
|
34379
|
+
}
|
|
34380
|
+
function tryInjectViaFetchWrapper(internal, auth) {
|
|
34381
|
+
const getConfig = internal["getConfig"];
|
|
34382
|
+
const setConfig = internal["setConfig"];
|
|
34383
|
+
if (typeof getConfig !== "function" || typeof setConfig !== "function") {
|
|
34384
|
+
return false;
|
|
34385
|
+
}
|
|
34386
|
+
const config2 = getConfig();
|
|
34387
|
+
if (!isRecord(config2)) {
|
|
34388
|
+
return false;
|
|
34389
|
+
}
|
|
34390
|
+
const fetchValue = config2["fetch"];
|
|
34391
|
+
if (!isRequestFetch(fetchValue)) {
|
|
34392
|
+
return false;
|
|
34393
|
+
}
|
|
34394
|
+
setConfig({
|
|
34395
|
+
fetch: wrapRequestFetch(fetchValue, auth)
|
|
34396
|
+
});
|
|
34397
|
+
return true;
|
|
34398
|
+
}
|
|
34399
|
+
function tryInjectViaMutableInternalConfig(internal, auth) {
|
|
34400
|
+
const configValue = internal["_config"];
|
|
34401
|
+
if (!isRecord(configValue)) {
|
|
34402
|
+
return false;
|
|
34403
|
+
}
|
|
34404
|
+
const fetchValue = configValue["fetch"];
|
|
34405
|
+
if (!isRequestFetch(fetchValue)) {
|
|
34406
|
+
return false;
|
|
34407
|
+
}
|
|
34408
|
+
configValue["fetch"] = wrapRequestFetch(fetchValue, auth);
|
|
34409
|
+
return true;
|
|
34410
|
+
}
|
|
34411
|
+
function tryInjectViaTopLevelFetch(client, auth) {
|
|
34412
|
+
if (!isRecord(client)) {
|
|
34413
|
+
return false;
|
|
34414
|
+
}
|
|
34415
|
+
const fetchValue = client["fetch"];
|
|
34416
|
+
if (!isRequestFetch(fetchValue)) {
|
|
34417
|
+
return false;
|
|
34418
|
+
}
|
|
34419
|
+
client["fetch"] = wrapRequestFetch(fetchValue, auth);
|
|
34420
|
+
return true;
|
|
34421
|
+
}
|
|
34326
34422
|
function injectServerAuthIntoClient(client) {
|
|
34327
34423
|
const auth = getServerBasicAuthHeader();
|
|
34328
34424
|
if (!auth) {
|
|
34329
34425
|
return;
|
|
34330
34426
|
}
|
|
34331
34427
|
try {
|
|
34332
|
-
|
|
34333
|
-
|
|
34428
|
+
const internal = getInternalClient(client);
|
|
34429
|
+
if (internal) {
|
|
34430
|
+
const injectedHeaders = tryInjectViaSetConfigHeaders(internal, auth);
|
|
34431
|
+
const injectedInterceptors = tryInjectViaInterceptors(internal, auth);
|
|
34432
|
+
const injectedFetch = tryInjectViaFetchWrapper(internal, auth);
|
|
34433
|
+
const injectedMutable = tryInjectViaMutableInternalConfig(internal, auth);
|
|
34434
|
+
const injected2 = injectedHeaders || injectedInterceptors || injectedFetch || injectedMutable;
|
|
34435
|
+
if (!injected2) {
|
|
34436
|
+
log("[opencode-server-auth] OPENCODE_SERVER_PASSWORD is set but SDK client structure is incompatible", {
|
|
34437
|
+
keys: Object.keys(internal)
|
|
34438
|
+
});
|
|
34439
|
+
}
|
|
34440
|
+
return;
|
|
34334
34441
|
}
|
|
34335
|
-
const
|
|
34336
|
-
if (
|
|
34337
|
-
|
|
34442
|
+
const injected = tryInjectViaTopLevelFetch(client, auth);
|
|
34443
|
+
if (!injected) {
|
|
34444
|
+
log("[opencode-server-auth] OPENCODE_SERVER_PASSWORD is set but no compatible SDK client found");
|
|
34338
34445
|
}
|
|
34339
|
-
internal.setConfig({
|
|
34340
|
-
headers: {
|
|
34341
|
-
Authorization: auth
|
|
34342
|
-
}
|
|
34343
|
-
});
|
|
34344
34446
|
} catch (error45) {
|
|
34345
34447
|
const message = error45 instanceof Error ? error45.message : String(error45);
|
|
34346
|
-
|
|
34448
|
+
log("[opencode-server-auth] Failed to inject server auth", { message });
|
|
34347
34449
|
}
|
|
34348
34450
|
}
|
|
34349
34451
|
// src/shared/git-worktree/parse-status-porcelain.ts
|
|
@@ -42711,7 +42813,7 @@ async function executeSlashCommand(parsed, options) {
|
|
|
42711
42813
|
if (!command) {
|
|
42712
42814
|
return {
|
|
42713
42815
|
success: false,
|
|
42714
|
-
error: `Command "/${parsed.command}" not found. Use the slashcommand tool to list available commands.`
|
|
42816
|
+
error: parsed.command.includes(":") ? `Marketplace plugin commands like "/${parsed.command}" are not supported. Use .claude/commands/ for custom commands.` : `Command "/${parsed.command}" not found. Use the slashcommand tool to list available commands.`
|
|
42715
42817
|
};
|
|
42716
42818
|
}
|
|
42717
42819
|
try {
|
|
@@ -42984,6 +43086,11 @@ function getAgentFromSession(sessionID, directory) {
|
|
|
42984
43086
|
return getAgentFromMessageFiles(sessionID);
|
|
42985
43087
|
}
|
|
42986
43088
|
|
|
43089
|
+
// src/hooks/prometheus-md-only/agent-matcher.ts
|
|
43090
|
+
function isPrometheusAgent(agentName) {
|
|
43091
|
+
return agentName?.toLowerCase().includes(PROMETHEUS_AGENT) ?? false;
|
|
43092
|
+
}
|
|
43093
|
+
|
|
42987
43094
|
// src/hooks/prometheus-md-only/path-policy.ts
|
|
42988
43095
|
import { relative as relative3, resolve as resolve7, isAbsolute as isAbsolute3 } from "path";
|
|
42989
43096
|
function isAllowedFile(filePath, workspaceRoot) {
|
|
@@ -43008,7 +43115,7 @@ function createPrometheusMdOnlyHook(ctx) {
|
|
|
43008
43115
|
return {
|
|
43009
43116
|
"tool.execute.before": async (input, output) => {
|
|
43010
43117
|
const agentName = getAgentFromSession(input.sessionID, ctx.directory);
|
|
43011
|
-
if (agentName
|
|
43118
|
+
if (!isPrometheusAgent(agentName)) {
|
|
43012
43119
|
return;
|
|
43013
43120
|
}
|
|
43014
43121
|
const toolName = input.tool;
|
|
@@ -44410,16 +44517,16 @@ var THINKING_SUMMARY_MAX_CHARS = 500;
|
|
|
44410
44517
|
function hasData(value) {
|
|
44411
44518
|
return typeof value === "object" && value !== null && "data" in value;
|
|
44412
44519
|
}
|
|
44413
|
-
function
|
|
44520
|
+
function isRecord2(value) {
|
|
44414
44521
|
return typeof value === "object" && value !== null;
|
|
44415
44522
|
}
|
|
44416
44523
|
function getMessageInfo(value) {
|
|
44417
|
-
if (!
|
|
44524
|
+
if (!isRecord2(value))
|
|
44418
44525
|
return;
|
|
44419
|
-
if (!
|
|
44526
|
+
if (!isRecord2(value.info))
|
|
44420
44527
|
return;
|
|
44421
44528
|
const info = value.info;
|
|
44422
|
-
const modelValue =
|
|
44529
|
+
const modelValue = isRecord2(info.model) ? info.model : undefined;
|
|
44423
44530
|
const model = modelValue && typeof modelValue.providerID === "string" && typeof modelValue.modelID === "string" ? { providerID: modelValue.providerID, modelID: modelValue.modelID } : undefined;
|
|
44424
44531
|
return {
|
|
44425
44532
|
role: typeof info.role === "string" ? info.role : undefined,
|
|
@@ -44430,11 +44537,11 @@ function getMessageInfo(value) {
|
|
|
44430
44537
|
};
|
|
44431
44538
|
}
|
|
44432
44539
|
function getMessageParts(value) {
|
|
44433
|
-
if (!
|
|
44540
|
+
if (!isRecord2(value))
|
|
44434
44541
|
return [];
|
|
44435
44542
|
if (!Array.isArray(value.parts))
|
|
44436
44543
|
return [];
|
|
44437
|
-
return value.parts.filter(
|
|
44544
|
+
return value.parts.filter(isRecord2).map((part) => ({
|
|
44438
44545
|
type: typeof part.type === "string" ? part.type : undefined,
|
|
44439
44546
|
text: typeof part.text === "string" ? part.text : undefined,
|
|
44440
44547
|
thinking: typeof part.thinking === "string" ? part.thinking : undefined
|
|
@@ -47834,7 +47941,9 @@ Provide a command or skill name to execute.`;
|
|
|
47834
47941
|
|
|
47835
47942
|
${formatCommandList(allItems)}`;
|
|
47836
47943
|
}
|
|
47837
|
-
return `
|
|
47944
|
+
return commandName.includes(":") ? `Marketplace plugin commands like "/${commandName}" are not supported. Use .claude/commands/ for custom commands.
|
|
47945
|
+
|
|
47946
|
+
${formatCommandList(allItems)}` : `Command or skill "/${commandName}" not found.
|
|
47838
47947
|
|
|
47839
47948
|
${formatCommandList(allItems)}
|
|
47840
47949
|
|
|
@@ -50213,7 +50322,8 @@ Use \`background_output\` with task_id="${task.id}" to check progress.
|
|
|
50213
50322
|
|
|
50214
50323
|
<task_metadata>
|
|
50215
50324
|
session_id: ${task.sessionID}
|
|
50216
|
-
|
|
50325
|
+
${task.agent ? `subagent: ${task.agent}
|
|
50326
|
+
` : ""}</task_metadata>`;
|
|
50217
50327
|
} catch (error45) {
|
|
50218
50328
|
return formatDetailedError(error45, {
|
|
50219
50329
|
operation: "Continue background task",
|
|
@@ -50666,7 +50776,8 @@ ${result.textContent || "(No text output)"}
|
|
|
50666
50776
|
|
|
50667
50777
|
<task_metadata>
|
|
50668
50778
|
session_id: ${args.session_id}
|
|
50669
|
-
|
|
50779
|
+
${resumeAgent ? `subagent: ${resumeAgent}
|
|
50780
|
+
` : ""}</task_metadata>`;
|
|
50670
50781
|
} finally {
|
|
50671
50782
|
if (toastManager) {
|
|
50672
50783
|
toastManager.removeTask(taskId);
|
|
@@ -52764,7 +52875,7 @@ class BackgroundManager {
|
|
|
52764
52875
|
while (queue && queue.length > 0) {
|
|
52765
52876
|
const item = queue[0];
|
|
52766
52877
|
await this.concurrencyManager.acquire(key);
|
|
52767
|
-
if (item.task.status === "cancelled") {
|
|
52878
|
+
if (item.task.status === "cancelled" || item.task.status === "error") {
|
|
52768
52879
|
this.concurrencyManager.release(key);
|
|
52769
52880
|
queue.shift();
|
|
52770
52881
|
continue;
|
|
@@ -53188,6 +53299,38 @@ class BackgroundManager {
|
|
|
53188
53299
|
log("[background-agent] Error in session.idle handler:", err);
|
|
53189
53300
|
});
|
|
53190
53301
|
}
|
|
53302
|
+
if (event.type === "session.error") {
|
|
53303
|
+
const sessionID = typeof props?.sessionID === "string" ? props.sessionID : undefined;
|
|
53304
|
+
if (!sessionID)
|
|
53305
|
+
return;
|
|
53306
|
+
const task = this.findBySession(sessionID);
|
|
53307
|
+
if (!task || task.status !== "running")
|
|
53308
|
+
return;
|
|
53309
|
+
const errorMessage = props ? this.getSessionErrorMessage(props) : undefined;
|
|
53310
|
+
task.status = "error";
|
|
53311
|
+
task.error = errorMessage ?? "Session error";
|
|
53312
|
+
task.completedAt = new Date;
|
|
53313
|
+
if (task.concurrencyKey) {
|
|
53314
|
+
this.concurrencyManager.release(task.concurrencyKey);
|
|
53315
|
+
task.concurrencyKey = undefined;
|
|
53316
|
+
}
|
|
53317
|
+
const completionTimer = this.completionTimers.get(task.id);
|
|
53318
|
+
if (completionTimer) {
|
|
53319
|
+
clearTimeout(completionTimer);
|
|
53320
|
+
this.completionTimers.delete(task.id);
|
|
53321
|
+
}
|
|
53322
|
+
const idleTimer = this.idleDeferralTimers.get(task.id);
|
|
53323
|
+
if (idleTimer) {
|
|
53324
|
+
clearTimeout(idleTimer);
|
|
53325
|
+
this.idleDeferralTimers.delete(task.id);
|
|
53326
|
+
}
|
|
53327
|
+
this.cleanupPendingByParent(task);
|
|
53328
|
+
this.tasks.delete(task.id);
|
|
53329
|
+
this.clearNotificationsForTask(task.id);
|
|
53330
|
+
if (task.sessionID) {
|
|
53331
|
+
subagentSessions.delete(task.sessionID);
|
|
53332
|
+
}
|
|
53333
|
+
}
|
|
53191
53334
|
if (event.type === "session.deleted") {
|
|
53192
53335
|
const info = props?.info;
|
|
53193
53336
|
if (!info || typeof info.id !== "string")
|
|
@@ -53610,6 +53753,22 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
53610
53753
|
}
|
|
53611
53754
|
return "";
|
|
53612
53755
|
}
|
|
53756
|
+
isRecord(value) {
|
|
53757
|
+
return typeof value === "object" && value !== null;
|
|
53758
|
+
}
|
|
53759
|
+
getSessionErrorMessage(properties) {
|
|
53760
|
+
const errorRaw = properties["error"];
|
|
53761
|
+
if (!this.isRecord(errorRaw))
|
|
53762
|
+
return;
|
|
53763
|
+
const dataRaw = errorRaw["data"];
|
|
53764
|
+
if (this.isRecord(dataRaw)) {
|
|
53765
|
+
const message2 = dataRaw["message"];
|
|
53766
|
+
if (typeof message2 === "string")
|
|
53767
|
+
return message2;
|
|
53768
|
+
}
|
|
53769
|
+
const message = errorRaw["message"];
|
|
53770
|
+
return typeof message === "string" ? message : undefined;
|
|
53771
|
+
}
|
|
53613
53772
|
hasRunningTasks() {
|
|
53614
53773
|
for (const task of this.tasks.values()) {
|
|
53615
53774
|
if (task.status === "running")
|
|
@@ -53620,6 +53779,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
53620
53779
|
pruneStaleTasksAndNotifications() {
|
|
53621
53780
|
const now = Date.now();
|
|
53622
53781
|
for (const [taskId, task] of this.tasks.entries()) {
|
|
53782
|
+
const wasPending = task.status === "pending";
|
|
53623
53783
|
const timestamp2 = task.status === "pending" ? task.queuedAt?.getTime() : task.startedAt?.getTime();
|
|
53624
53784
|
if (!timestamp2) {
|
|
53625
53785
|
continue;
|
|
@@ -53636,6 +53796,19 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
53636
53796
|
task.concurrencyKey = undefined;
|
|
53637
53797
|
}
|
|
53638
53798
|
this.cleanupPendingByParent(task);
|
|
53799
|
+
if (wasPending) {
|
|
53800
|
+
const key = task.model ? `${task.model.providerID}/${task.model.modelID}` : task.agent;
|
|
53801
|
+
const queue = this.queuesByKey.get(key);
|
|
53802
|
+
if (queue) {
|
|
53803
|
+
const index = queue.findIndex((item) => item.task.id === taskId);
|
|
53804
|
+
if (index !== -1) {
|
|
53805
|
+
queue.splice(index, 1);
|
|
53806
|
+
if (queue.length === 0) {
|
|
53807
|
+
this.queuesByKey.delete(key);
|
|
53808
|
+
}
|
|
53809
|
+
}
|
|
53810
|
+
}
|
|
53811
|
+
}
|
|
53639
53812
|
this.clearNotificationsForTask(taskId);
|
|
53640
53813
|
this.tasks.delete(taskId);
|
|
53641
53814
|
if (task.sessionID) {
|
|
@@ -58082,7 +58255,7 @@ async function getOrRegisterClient(options) {
|
|
|
58082
58255
|
}
|
|
58083
58256
|
}
|
|
58084
58257
|
function parseRegistrationResponse(data) {
|
|
58085
|
-
if (!
|
|
58258
|
+
if (!isRecord3(data))
|
|
58086
58259
|
return null;
|
|
58087
58260
|
const clientId = data.client_id;
|
|
58088
58261
|
if (typeof clientId !== "string" || clientId.length === 0)
|
|
@@ -58093,7 +58266,7 @@ function parseRegistrationResponse(data) {
|
|
|
58093
58266
|
}
|
|
58094
58267
|
return { clientId };
|
|
58095
58268
|
}
|
|
58096
|
-
function
|
|
58269
|
+
function isRecord3(value) {
|
|
58097
58270
|
return typeof value === "object" && value !== null;
|
|
58098
58271
|
}
|
|
58099
58272
|
|
|
@@ -63598,7 +63771,7 @@ function maybeCreateAtlasConfig(input) {
|
|
|
63598
63771
|
function sanitizeMarkdownTableCell(value) {
|
|
63599
63772
|
return value.replace(/\r?\n/g, " ").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
63600
63773
|
}
|
|
63601
|
-
function
|
|
63774
|
+
function isRecord4(value) {
|
|
63602
63775
|
return typeof value === "object" && value !== null;
|
|
63603
63776
|
}
|
|
63604
63777
|
function parseRegisteredAgentSummaries(input) {
|
|
@@ -63606,7 +63779,7 @@ function parseRegisteredAgentSummaries(input) {
|
|
|
63606
63779
|
return [];
|
|
63607
63780
|
const result = [];
|
|
63608
63781
|
for (const item of input) {
|
|
63609
|
-
if (!
|
|
63782
|
+
if (!isRecord4(item))
|
|
63610
63783
|
continue;
|
|
63611
63784
|
const name = typeof item.name === "string" ? item.name : undefined;
|
|
63612
63785
|
if (!name)
|
|
@@ -66894,11 +67067,11 @@ async function createTools(args) {
|
|
|
66894
67067
|
}
|
|
66895
67068
|
|
|
66896
67069
|
// src/plugin/chat-params.ts
|
|
66897
|
-
function
|
|
67070
|
+
function isRecord5(value) {
|
|
66898
67071
|
return typeof value === "object" && value !== null;
|
|
66899
67072
|
}
|
|
66900
67073
|
function buildChatParamsInput(raw) {
|
|
66901
|
-
if (!
|
|
67074
|
+
if (!isRecord5(raw))
|
|
66902
67075
|
return null;
|
|
66903
67076
|
const sessionID = raw.sessionID;
|
|
66904
67077
|
const agent = raw.agent;
|
|
@@ -66909,11 +67082,11 @@ function buildChatParamsInput(raw) {
|
|
|
66909
67082
|
return null;
|
|
66910
67083
|
if (typeof agent !== "string")
|
|
66911
67084
|
return null;
|
|
66912
|
-
if (!
|
|
67085
|
+
if (!isRecord5(model))
|
|
66913
67086
|
return null;
|
|
66914
|
-
if (!
|
|
67087
|
+
if (!isRecord5(provider))
|
|
66915
67088
|
return null;
|
|
66916
|
-
if (!
|
|
67089
|
+
if (!isRecord5(message))
|
|
66917
67090
|
return null;
|
|
66918
67091
|
const providerID = model.providerID;
|
|
66919
67092
|
const modelID = model.modelID;
|
|
@@ -66934,12 +67107,12 @@ function buildChatParamsInput(raw) {
|
|
|
66934
67107
|
};
|
|
66935
67108
|
}
|
|
66936
67109
|
function isChatParamsOutput(raw) {
|
|
66937
|
-
if (!
|
|
67110
|
+
if (!isRecord5(raw))
|
|
66938
67111
|
return false;
|
|
66939
|
-
if (!
|
|
67112
|
+
if (!isRecord5(raw.options)) {
|
|
66940
67113
|
raw.options = {};
|
|
66941
67114
|
}
|
|
66942
|
-
return
|
|
67115
|
+
return isRecord5(raw.options);
|
|
66943
67116
|
}
|
|
66944
67117
|
function createChatParamsHandler(args) {
|
|
66945
67118
|
return async (input, output) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-opencode",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.3",
|
|
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",
|
|
@@ -74,13 +74,13 @@
|
|
|
74
74
|
"typescript": "^5.7.3"
|
|
75
75
|
},
|
|
76
76
|
"optionalDependencies": {
|
|
77
|
-
"oh-my-opencode-darwin-arm64": "3.5.
|
|
78
|
-
"oh-my-opencode-darwin-x64": "3.5.
|
|
79
|
-
"oh-my-opencode-linux-arm64": "3.5.
|
|
80
|
-
"oh-my-opencode-linux-arm64-musl": "3.5.
|
|
81
|
-
"oh-my-opencode-linux-x64": "3.5.
|
|
82
|
-
"oh-my-opencode-linux-x64-musl": "3.5.
|
|
83
|
-
"oh-my-opencode-windows-x64": "3.5.
|
|
77
|
+
"oh-my-opencode-darwin-arm64": "3.5.3",
|
|
78
|
+
"oh-my-opencode-darwin-x64": "3.5.3",
|
|
79
|
+
"oh-my-opencode-linux-arm64": "3.5.3",
|
|
80
|
+
"oh-my-opencode-linux-arm64-musl": "3.5.3",
|
|
81
|
+
"oh-my-opencode-linux-x64": "3.5.3",
|
|
82
|
+
"oh-my-opencode-linux-x64-musl": "3.5.3",
|
|
83
|
+
"oh-my-opencode-windows-x64": "3.5.3"
|
|
84
84
|
},
|
|
85
85
|
"trustedDependencies": [
|
|
86
86
|
"@ast-grep/cli",
|