oh-my-opencode 4.3.0 → 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 +59 -32
- package/dist/features/background-agent/parent-wake-dedupe.d.ts +19 -0
- package/dist/features/background-agent/parent-wake-notifier.d.ts +2 -19
- package/dist/hooks/tool-pair-validator/hook.d.ts +6 -1
- package/dist/index.js +504 -229
- package/dist/plugin-handlers/provider-config-handler.d.ts +1 -0
- package/dist/shared/bun-spawn-shim.d.ts +3 -0
- package/dist/shared/migration/model-versions.d.ts +6 -0
- package/dist/shared/process-stream-reader.d.ts +3 -0
- package/dist/tools/glob/cli.d.ts +3 -1
- package/dist/tools/grep/cli.d.ts +4 -2
- package/dist/tools/shared/search-process-output.d.ts +7 -0
- package/package.json +12 -12
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) {
|
|
@@ -9157,8 +9162,14 @@ var init_external_plugin_detector = __esm(() => {
|
|
|
9157
9162
|
});
|
|
9158
9163
|
|
|
9159
9164
|
// src/shared/bun-spawn-shim.ts
|
|
9160
|
-
import {
|
|
9165
|
+
import {
|
|
9166
|
+
spawn as nodeSpawn,
|
|
9167
|
+
spawnSync as nodeSpawnSync
|
|
9168
|
+
} from "child_process";
|
|
9161
9169
|
import { Readable, Writable } from "stream";
|
|
9170
|
+
function getBunRuntime() {
|
|
9171
|
+
return typeof Bun === "undefined" ? undefined : runtime.Bun;
|
|
9172
|
+
}
|
|
9162
9173
|
function emptyReadableStream() {
|
|
9163
9174
|
return new ReadableStream({
|
|
9164
9175
|
start(controller) {
|
|
@@ -9191,6 +9202,38 @@ function resolveStdio(options) {
|
|
|
9191
9202
|
return options.stdio;
|
|
9192
9203
|
return [options.stdin ?? "ignore", options.stdout ?? "pipe", options.stderr ?? "inherit"];
|
|
9193
9204
|
}
|
|
9205
|
+
function createNodeSpawnOptions(options, platform = process.platform) {
|
|
9206
|
+
const nodeOptions = {
|
|
9207
|
+
stdio: resolveStdio(options),
|
|
9208
|
+
shell: false
|
|
9209
|
+
};
|
|
9210
|
+
if (options.cwd !== undefined)
|
|
9211
|
+
nodeOptions.cwd = options.cwd;
|
|
9212
|
+
if (options.env !== undefined)
|
|
9213
|
+
nodeOptions.env = options.env;
|
|
9214
|
+
if (options.detached !== undefined)
|
|
9215
|
+
nodeOptions.detached = options.detached;
|
|
9216
|
+
if (options.signal !== undefined)
|
|
9217
|
+
nodeOptions.signal = options.signal;
|
|
9218
|
+
if (platform === "win32") {
|
|
9219
|
+
nodeOptions.windowsHide = true;
|
|
9220
|
+
}
|
|
9221
|
+
return nodeOptions;
|
|
9222
|
+
}
|
|
9223
|
+
function createNodeSpawnSyncOptions(options, platform = process.platform) {
|
|
9224
|
+
const nodeOptions = {
|
|
9225
|
+
stdio: resolveStdio(options),
|
|
9226
|
+
shell: false
|
|
9227
|
+
};
|
|
9228
|
+
if (options.cwd !== undefined)
|
|
9229
|
+
nodeOptions.cwd = options.cwd;
|
|
9230
|
+
if (options.env !== undefined)
|
|
9231
|
+
nodeOptions.env = options.env;
|
|
9232
|
+
if (platform === "win32") {
|
|
9233
|
+
nodeOptions.windowsHide = true;
|
|
9234
|
+
}
|
|
9235
|
+
return nodeOptions;
|
|
9236
|
+
}
|
|
9194
9237
|
function wrapNodeProcess(proc) {
|
|
9195
9238
|
let exitCode = null;
|
|
9196
9239
|
const exited = new Promise((resolve5, reject) => {
|
|
@@ -9232,42 +9275,45 @@ function wrapNodeProcess(proc) {
|
|
|
9232
9275
|
}
|
|
9233
9276
|
};
|
|
9234
9277
|
}
|
|
9278
|
+
function toSpawnSyncBuffer(output) {
|
|
9279
|
+
if (output === null) {
|
|
9280
|
+
return;
|
|
9281
|
+
}
|
|
9282
|
+
return Buffer.isBuffer(output) ? output : Buffer.from(output, "utf8");
|
|
9283
|
+
}
|
|
9235
9284
|
function spawn2(cmdOrOpts, opts) {
|
|
9236
|
-
if (IS_BUN)
|
|
9237
|
-
return runtime.Bun.spawn(cmdOrOpts, opts);
|
|
9238
9285
|
const { cmd, opts: options } = resolveCommand(cmdOrOpts, opts);
|
|
9286
|
+
const bun = getBunRuntime();
|
|
9287
|
+
if (bun)
|
|
9288
|
+
return bun.spawn(cmd, options);
|
|
9239
9289
|
const [bin, ...args] = cmd;
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
detached: options.detached,
|
|
9245
|
-
signal: options.signal
|
|
9246
|
-
});
|
|
9290
|
+
if (bin === undefined) {
|
|
9291
|
+
throw new Error("Cannot spawn an empty command");
|
|
9292
|
+
}
|
|
9293
|
+
const proc = nodeSpawn(bin, args, createNodeSpawnOptions(options));
|
|
9247
9294
|
return wrapNodeProcess(proc);
|
|
9248
9295
|
}
|
|
9249
9296
|
function spawnSync(cmdOrOpts, opts) {
|
|
9250
|
-
if (IS_BUN)
|
|
9251
|
-
return runtime.Bun.spawnSync(cmdOrOpts, opts);
|
|
9252
9297
|
const { cmd, opts: options } = resolveCommand(cmdOrOpts, opts);
|
|
9298
|
+
const bun = getBunRuntime();
|
|
9299
|
+
if (bun)
|
|
9300
|
+
return bun.spawnSync(cmd, options);
|
|
9253
9301
|
const [bin, ...args] = cmd;
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
});
|
|
9302
|
+
if (bin === undefined) {
|
|
9303
|
+
throw new Error("Cannot spawnSync an empty command");
|
|
9304
|
+
}
|
|
9305
|
+
const result = nodeSpawnSync(bin, args, createNodeSpawnSyncOptions(options));
|
|
9259
9306
|
return {
|
|
9260
9307
|
exitCode: result.status ?? 1,
|
|
9261
|
-
stdout: result.stdout
|
|
9262
|
-
stderr: result.stderr
|
|
9308
|
+
stdout: toSpawnSyncBuffer(result.stdout),
|
|
9309
|
+
stderr: toSpawnSyncBuffer(result.stderr),
|
|
9263
9310
|
success: (result.status ?? 1) === 0,
|
|
9264
9311
|
pid: result.pid ?? -1
|
|
9265
9312
|
};
|
|
9266
9313
|
}
|
|
9267
|
-
var runtime
|
|
9314
|
+
var runtime;
|
|
9268
9315
|
var init_bun_spawn_shim = __esm(() => {
|
|
9269
9316
|
runtime = globalThis;
|
|
9270
|
-
IS_BUN = typeof runtime.Bun !== "undefined";
|
|
9271
9317
|
});
|
|
9272
9318
|
|
|
9273
9319
|
// src/shared/archive-entry-validator.ts
|
|
@@ -9326,6 +9372,52 @@ function validateArchiveEntries(entries, destDir) {
|
|
|
9326
9372
|
}
|
|
9327
9373
|
var init_archive_entry_validator = () => {};
|
|
9328
9374
|
|
|
9375
|
+
// src/shared/process-stream-reader.ts
|
|
9376
|
+
function bufferFromChunk(chunk) {
|
|
9377
|
+
if (Buffer.isBuffer(chunk)) {
|
|
9378
|
+
return chunk;
|
|
9379
|
+
}
|
|
9380
|
+
if (chunk instanceof Uint8Array) {
|
|
9381
|
+
return Buffer.from(chunk);
|
|
9382
|
+
}
|
|
9383
|
+
if (typeof chunk === "string") {
|
|
9384
|
+
return Buffer.from(chunk, "utf8");
|
|
9385
|
+
}
|
|
9386
|
+
throw new TypeError(`Unsupported process stream chunk type: ${typeof chunk}`);
|
|
9387
|
+
}
|
|
9388
|
+
async function readWebStream(stream) {
|
|
9389
|
+
const reader = stream.getReader();
|
|
9390
|
+
const chunks = [];
|
|
9391
|
+
try {
|
|
9392
|
+
while (true) {
|
|
9393
|
+
const result = await reader.read();
|
|
9394
|
+
if (result.done) {
|
|
9395
|
+
return chunks;
|
|
9396
|
+
}
|
|
9397
|
+
chunks.push(Buffer.from(result.value));
|
|
9398
|
+
}
|
|
9399
|
+
} finally {
|
|
9400
|
+
reader.releaseLock();
|
|
9401
|
+
}
|
|
9402
|
+
}
|
|
9403
|
+
async function readNodeStream(stream) {
|
|
9404
|
+
const chunks = [];
|
|
9405
|
+
for await (const chunk of stream) {
|
|
9406
|
+
chunks.push(bufferFromChunk(chunk));
|
|
9407
|
+
}
|
|
9408
|
+
return chunks;
|
|
9409
|
+
}
|
|
9410
|
+
function isWebReadableStream(stream) {
|
|
9411
|
+
return typeof ReadableStream !== "undefined" && stream instanceof ReadableStream;
|
|
9412
|
+
}
|
|
9413
|
+
async function readProcessStream(stream) {
|
|
9414
|
+
if (!stream) {
|
|
9415
|
+
return "";
|
|
9416
|
+
}
|
|
9417
|
+
const chunks = isWebReadableStream(stream) ? await readWebStream(stream) : await readNodeStream(stream);
|
|
9418
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
9419
|
+
}
|
|
9420
|
+
|
|
9329
9421
|
// src/shared/zip-entry-listing/python-zip-entry-listing.ts
|
|
9330
9422
|
function isPythonZipListingAvailable() {
|
|
9331
9423
|
const proc = spawnSync(["python3", "--version"], {
|
|
@@ -9363,8 +9455,8 @@ async function listZipEntriesWithPython(archivePath) {
|
|
|
9363
9455
|
});
|
|
9364
9456
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9365
9457
|
proc.exited,
|
|
9366
|
-
|
|
9367
|
-
|
|
9458
|
+
readProcessStream(proc.stdout),
|
|
9459
|
+
readProcessStream(proc.stderr)
|
|
9368
9460
|
]);
|
|
9369
9461
|
if (exitCode !== 0) {
|
|
9370
9462
|
throw new Error(`zip entry listing failed (exit ${exitCode}): ${stderr}`);
|
|
@@ -9431,8 +9523,8 @@ async function listZipEntriesWithPowerShell(archivePath, escapePowerShellPath, e
|
|
|
9431
9523
|
});
|
|
9432
9524
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9433
9525
|
proc.exited,
|
|
9434
|
-
|
|
9435
|
-
|
|
9526
|
+
readProcessStream(proc.stdout),
|
|
9527
|
+
readProcessStream(proc.stderr)
|
|
9436
9528
|
]);
|
|
9437
9529
|
if (exitCode !== 0) {
|
|
9438
9530
|
throw new Error(`zip entry listing failed (exit ${exitCode}): ${stderr}`);
|
|
@@ -9495,8 +9587,8 @@ async function listZipEntriesWithTar(archivePath) {
|
|
|
9495
9587
|
});
|
|
9496
9588
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9497
9589
|
proc.exited,
|
|
9498
|
-
|
|
9499
|
-
|
|
9590
|
+
readProcessStream(proc.stdout),
|
|
9591
|
+
readProcessStream(proc.stderr)
|
|
9500
9592
|
]);
|
|
9501
9593
|
if (exitCode !== 0) {
|
|
9502
9594
|
throw new Error(`zip entry listing failed (exit ${exitCode}): ${stderr}`);
|
|
@@ -9516,8 +9608,8 @@ async function readZipSymlinkTarget(archivePath, entryPath) {
|
|
|
9516
9608
|
});
|
|
9517
9609
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9518
9610
|
proc.exited,
|
|
9519
|
-
|
|
9520
|
-
|
|
9611
|
+
readProcessStream(proc.stdout),
|
|
9612
|
+
readProcessStream(proc.stderr)
|
|
9521
9613
|
]);
|
|
9522
9614
|
if (exitCode !== 0) {
|
|
9523
9615
|
throw new Error(`zip symlink target read failed (exit ${exitCode}): ${stderr}`);
|
|
@@ -9560,8 +9652,8 @@ async function listZipEntriesWithZipInfo(archivePath) {
|
|
|
9560
9652
|
});
|
|
9561
9653
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9562
9654
|
proc.exited,
|
|
9563
|
-
|
|
9564
|
-
|
|
9655
|
+
readProcessStream(proc.stdout),
|
|
9656
|
+
readProcessStream(proc.stderr)
|
|
9565
9657
|
]);
|
|
9566
9658
|
if (exitCode !== 0) {
|
|
9567
9659
|
throw new Error(`zip entry listing failed (exit ${exitCode}): ${stderr}`);
|
|
@@ -9657,7 +9749,7 @@ async function extractZip(archivePath, destDir) {
|
|
|
9657
9749
|
}
|
|
9658
9750
|
const exitCode = await proc.exited;
|
|
9659
9751
|
if (exitCode !== 0) {
|
|
9660
|
-
const stderr = await
|
|
9752
|
+
const stderr = await readProcessStream(proc.stderr);
|
|
9661
9753
|
throw new Error(`zip extraction failed (exit ${exitCode}): ${stderr}`);
|
|
9662
9754
|
}
|
|
9663
9755
|
}
|
|
@@ -9716,20 +9808,20 @@ function createNodeFile(path6) {
|
|
|
9716
9808
|
};
|
|
9717
9809
|
}
|
|
9718
9810
|
function bunFile(path6) {
|
|
9719
|
-
if (
|
|
9811
|
+
if (IS_BUN)
|
|
9720
9812
|
return runtime2.Bun.file(path6);
|
|
9721
9813
|
return createNodeFile(path6);
|
|
9722
9814
|
}
|
|
9723
9815
|
async function bunWrite(path6, data) {
|
|
9724
|
-
if (
|
|
9816
|
+
if (IS_BUN)
|
|
9725
9817
|
return runtime2.Bun.write(path6, data);
|
|
9726
9818
|
await writeFile(path6, toWritableData(data));
|
|
9727
9819
|
return byteLength(data);
|
|
9728
9820
|
}
|
|
9729
|
-
var runtime2,
|
|
9821
|
+
var runtime2, IS_BUN;
|
|
9730
9822
|
var init_bun_file_shim = __esm(() => {
|
|
9731
9823
|
runtime2 = globalThis;
|
|
9732
|
-
|
|
9824
|
+
IS_BUN = typeof runtime2.Bun !== "undefined";
|
|
9733
9825
|
});
|
|
9734
9826
|
|
|
9735
9827
|
// src/shared/binary-downloader.ts
|
|
@@ -9766,7 +9858,7 @@ async function extractTarGz(archivePath, destDir, options) {
|
|
|
9766
9858
|
});
|
|
9767
9859
|
const exitCode = await proc.exited;
|
|
9768
9860
|
if (exitCode !== 0) {
|
|
9769
|
-
const stderr = await
|
|
9861
|
+
const stderr = await readProcessStream(proc.stderr);
|
|
9770
9862
|
if (isTarTraversalErrorOutput(stderr)) {
|
|
9771
9863
|
throw new Error(`Unsafe archive entry: path contains path traversal (${archivePath})`);
|
|
9772
9864
|
}
|
|
@@ -9816,8 +9908,8 @@ async function listTarEntries(archivePath, cwd) {
|
|
|
9816
9908
|
});
|
|
9817
9909
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9818
9910
|
proc.exited,
|
|
9819
|
-
|
|
9820
|
-
|
|
9911
|
+
readProcessStream(proc.stdout),
|
|
9912
|
+
readProcessStream(proc.stderr)
|
|
9821
9913
|
]);
|
|
9822
9914
|
if (isTarTraversalErrorOutput(stderr)) {
|
|
9823
9915
|
throw new Error(`Unsafe archive entry: path contains path traversal (${archivePath})`);
|
|
@@ -69279,6 +69371,8 @@ async function buildReadyNotificationContent(ctx, input) {
|
|
|
69279
69371
|
|
|
69280
69372
|
// src/hooks/session-notification-sender.ts
|
|
69281
69373
|
init_shared();
|
|
69374
|
+
import { execFile } from "child_process";
|
|
69375
|
+
import { promisify as promisify2 } from "util";
|
|
69282
69376
|
import { platform } from "os";
|
|
69283
69377
|
|
|
69284
69378
|
// src/hooks/session-notification-utils.ts
|
|
@@ -69288,7 +69382,7 @@ init_logger();
|
|
|
69288
69382
|
import { accessSync as accessSync2, constants as constants4 } from "fs";
|
|
69289
69383
|
import { delimiter, join as join29 } from "path";
|
|
69290
69384
|
var runtime3 = globalThis;
|
|
69291
|
-
var
|
|
69385
|
+
var IS_BUN2 = typeof runtime3.Bun !== "undefined";
|
|
69292
69386
|
function isUnsafeCommandName(commandName) {
|
|
69293
69387
|
if (commandName.includes("/") || commandName.includes("\\"))
|
|
69294
69388
|
return true;
|
|
@@ -69323,7 +69417,7 @@ function bunWhich(commandName) {
|
|
|
69323
69417
|
return null;
|
|
69324
69418
|
if (isUnsafeCommandName(commandName))
|
|
69325
69419
|
return null;
|
|
69326
|
-
if (
|
|
69420
|
+
if (IS_BUN2)
|
|
69327
69421
|
return runtime3.Bun?.which(commandName) ?? null;
|
|
69328
69422
|
const pathValue = resolvePathValue();
|
|
69329
69423
|
if (!pathValue)
|
|
@@ -69459,14 +69553,33 @@ function getDefaultSoundPath(platform2) {
|
|
|
69459
69553
|
}
|
|
69460
69554
|
}
|
|
69461
69555
|
var hasLoggedUnavailableShellHelper = false;
|
|
69462
|
-
function
|
|
69463
|
-
if (typeof ctx
|
|
69464
|
-
return
|
|
69556
|
+
function getShellRunner(ctx) {
|
|
69557
|
+
if (typeof ctx.$ === "function")
|
|
69558
|
+
return ctx.$;
|
|
69465
69559
|
if (!hasLoggedUnavailableShellHelper) {
|
|
69466
69560
|
hasLoggedUnavailableShellHelper = true;
|
|
69467
|
-
log("[session-notification] ctx.$ unavailable;
|
|
69561
|
+
log("[session-notification] ctx.$ unavailable; falling back to child_process.execFile");
|
|
69468
69562
|
}
|
|
69469
|
-
return
|
|
69563
|
+
return;
|
|
69564
|
+
}
|
|
69565
|
+
function logCommandFailure(commandName, error) {
|
|
69566
|
+
log("[session-notification] notification command failed", {
|
|
69567
|
+
commandName,
|
|
69568
|
+
error: typeof error === "string" ? error : error.message
|
|
69569
|
+
});
|
|
69570
|
+
}
|
|
69571
|
+
function logOperationFailure(operation, error) {
|
|
69572
|
+
log("[session-notification] notification operation failed", {
|
|
69573
|
+
operation,
|
|
69574
|
+
error: typeof error === "string" ? error : error.message
|
|
69575
|
+
});
|
|
69576
|
+
}
|
|
69577
|
+
async function runQuiet(command) {
|
|
69578
|
+
if (typeof command.quiet === "function") {
|
|
69579
|
+
await command.quiet();
|
|
69580
|
+
return;
|
|
69581
|
+
}
|
|
69582
|
+
await command;
|
|
69470
69583
|
}
|
|
69471
69584
|
async function runQuietNothrow(command) {
|
|
69472
69585
|
const safeCommand = typeof command.nothrow === "function" ? command.nothrow() : command;
|
|
@@ -69476,85 +69589,124 @@ async function runQuietNothrow(command) {
|
|
|
69476
69589
|
}
|
|
69477
69590
|
await safeCommand;
|
|
69478
69591
|
}
|
|
69479
|
-
async function
|
|
69480
|
-
|
|
69592
|
+
async function runExecFile(commandPath, args) {
|
|
69593
|
+
const execFileAsync = promisify2(execFile);
|
|
69594
|
+
await execFileAsync(commandPath, [...args], { windowsHide: true });
|
|
69595
|
+
}
|
|
69596
|
+
async function runNotificationCommand(ctx, commandPath, args, shellCommand, shellFailureMode = "nothrow") {
|
|
69597
|
+
const shell = getShellRunner(ctx);
|
|
69598
|
+
if (shell) {
|
|
69599
|
+
if (shellFailureMode === "throw") {
|
|
69600
|
+
await runQuiet(shellCommand(shell));
|
|
69601
|
+
return;
|
|
69602
|
+
}
|
|
69603
|
+
await runQuietNothrow(shellCommand(shell));
|
|
69481
69604
|
return;
|
|
69482
|
-
|
|
69483
|
-
|
|
69484
|
-
|
|
69485
|
-
|
|
69486
|
-
|
|
69487
|
-
|
|
69488
|
-
|
|
69489
|
-
|
|
69490
|
-
|
|
69491
|
-
|
|
69492
|
-
|
|
69493
|
-
|
|
69494
|
-
|
|
69495
|
-
|
|
69496
|
-
|
|
69497
|
-
|
|
69498
|
-
|
|
69605
|
+
}
|
|
69606
|
+
await runExecFile(commandPath, args);
|
|
69607
|
+
}
|
|
69608
|
+
async function sendSessionNotification(ctx, platform2, title, message) {
|
|
69609
|
+
try {
|
|
69610
|
+
switch (platform2) {
|
|
69611
|
+
case "darwin": {
|
|
69612
|
+
const cmuxPath = await getCmuxPath();
|
|
69613
|
+
if (cmuxPath) {
|
|
69614
|
+
try {
|
|
69615
|
+
await runNotificationCommand(ctx, cmuxPath, ["notify", "--title", title, "--body", message], (shell) => shell`${cmuxPath} notify --title ${title} --body ${message}`, "throw");
|
|
69616
|
+
break;
|
|
69617
|
+
} catch (error) {
|
|
69618
|
+
if (error instanceof Error) {
|
|
69619
|
+
logCommandFailure("cmux", error);
|
|
69620
|
+
} else {
|
|
69621
|
+
logCommandFailure("cmux", String(error));
|
|
69622
|
+
}
|
|
69499
69623
|
}
|
|
69500
|
-
|
|
69501
|
-
|
|
69624
|
+
}
|
|
69625
|
+
const terminalNotifierPath = await getTerminalNotifierPath();
|
|
69626
|
+
if (terminalNotifierPath) {
|
|
69627
|
+
const bundleId = process.env.__CFBundleIdentifier;
|
|
69628
|
+
const args = bundleId ? ["-title", title, "-message", message, "-activate", bundleId] : ["-title", title, "-message", message];
|
|
69629
|
+
try {
|
|
69630
|
+
await runNotificationCommand(ctx, terminalNotifierPath, args, (shell) => bundleId ? shell`${terminalNotifierPath} -title ${title} -message ${message} -activate ${bundleId}` : shell`${terminalNotifierPath} -title ${title} -message ${message}`, "throw");
|
|
69631
|
+
break;
|
|
69632
|
+
} catch (error) {
|
|
69633
|
+
if (error instanceof Error) {
|
|
69634
|
+
logCommandFailure("terminal-notifier", error);
|
|
69635
|
+
} else {
|
|
69636
|
+
logCommandFailure("terminal-notifier", String(error));
|
|
69637
|
+
}
|
|
69638
|
+
}
|
|
69639
|
+
}
|
|
69640
|
+
const osascriptPath = await getOsascriptPath();
|
|
69641
|
+
if (!osascriptPath)
|
|
69642
|
+
return;
|
|
69643
|
+
const escapedTitle = escapeAppleScriptText(title);
|
|
69644
|
+
const escapedMessage = escapeAppleScriptText(message);
|
|
69645
|
+
const appleScript = 'display notification "' + escapedMessage + '" with title "' + escapedTitle + '"';
|
|
69646
|
+
await runNotificationCommand(ctx, osascriptPath, ["-e", appleScript], (shell) => shell`${osascriptPath} -e ${appleScript}`);
|
|
69647
|
+
break;
|
|
69648
|
+
}
|
|
69649
|
+
case "linux": {
|
|
69650
|
+
const notifySendPath = await getNotifySendPath();
|
|
69651
|
+
if (!notifySendPath)
|
|
69652
|
+
return;
|
|
69653
|
+
await runNotificationCommand(ctx, notifySendPath, [title, message], (shell) => shell`${notifySendPath} ${title} ${message} 2>/dev/null`);
|
|
69654
|
+
break;
|
|
69655
|
+
}
|
|
69656
|
+
case "win32": {
|
|
69657
|
+
const powershellPath = await getPowershellPath();
|
|
69658
|
+
if (!powershellPath)
|
|
69659
|
+
return;
|
|
69660
|
+
const toastScript = buildWindowsToastScript(title, message);
|
|
69661
|
+
await runNotificationCommand(ctx, powershellPath, ["-Command", toastScript], (shell) => shell`${powershellPath} -Command ${toastScript}`);
|
|
69662
|
+
break;
|
|
69502
69663
|
}
|
|
69503
|
-
const osascriptPath = await getOsascriptPath();
|
|
69504
|
-
if (!osascriptPath)
|
|
69505
|
-
return;
|
|
69506
|
-
const escapedTitle = escapeAppleScriptText(title);
|
|
69507
|
-
const escapedMessage = escapeAppleScriptText(message);
|
|
69508
|
-
await runQuietNothrow(ctx.$`${osascriptPath} -e ${'display notification "' + escapedMessage + '" with title "' + escapedTitle + '"'}`);
|
|
69509
|
-
break;
|
|
69510
|
-
}
|
|
69511
|
-
case "linux": {
|
|
69512
|
-
const notifySendPath = await getNotifySendPath();
|
|
69513
|
-
if (!notifySendPath)
|
|
69514
|
-
return;
|
|
69515
|
-
await runQuietNothrow(ctx.$`${notifySendPath} ${title} ${message} 2>/dev/null`);
|
|
69516
|
-
break;
|
|
69517
69664
|
}
|
|
69518
|
-
|
|
69519
|
-
|
|
69520
|
-
|
|
69521
|
-
|
|
69522
|
-
|
|
69523
|
-
await runQuietNothrow(ctx.$`${powershellPath} -Command ${toastScript}`);
|
|
69524
|
-
break;
|
|
69665
|
+
} catch (error) {
|
|
69666
|
+
if (error instanceof Error) {
|
|
69667
|
+
logOperationFailure("send", error);
|
|
69668
|
+
} else {
|
|
69669
|
+
logOperationFailure("send", String(error));
|
|
69525
69670
|
}
|
|
69526
69671
|
}
|
|
69527
69672
|
}
|
|
69528
69673
|
async function playSessionNotificationSound(ctx, platform2, soundPath) {
|
|
69529
|
-
|
|
69530
|
-
|
|
69531
|
-
|
|
69532
|
-
|
|
69533
|
-
|
|
69534
|
-
|
|
69535
|
-
|
|
69536
|
-
|
|
69537
|
-
|
|
69538
|
-
|
|
69539
|
-
|
|
69540
|
-
|
|
69541
|
-
|
|
69542
|
-
|
|
69543
|
-
|
|
69544
|
-
|
|
69545
|
-
|
|
69546
|
-
|
|
69674
|
+
try {
|
|
69675
|
+
switch (platform2) {
|
|
69676
|
+
case "darwin": {
|
|
69677
|
+
const afplayPath = await getAfplayPath();
|
|
69678
|
+
if (!afplayPath)
|
|
69679
|
+
return;
|
|
69680
|
+
await runNotificationCommand(ctx, afplayPath, [soundPath], (shell) => shell`${afplayPath} ${soundPath}`);
|
|
69681
|
+
break;
|
|
69682
|
+
}
|
|
69683
|
+
case "linux": {
|
|
69684
|
+
const paplayPath = await getPaplayPath();
|
|
69685
|
+
if (paplayPath) {
|
|
69686
|
+
await runNotificationCommand(ctx, paplayPath, [soundPath], (shell) => shell`${paplayPath} ${soundPath} 2>/dev/null`);
|
|
69687
|
+
} else {
|
|
69688
|
+
const aplayPath = await getAplayPath();
|
|
69689
|
+
if (aplayPath) {
|
|
69690
|
+
await runNotificationCommand(ctx, aplayPath, [soundPath], (shell) => shell`${aplayPath} ${soundPath} 2>/dev/null`);
|
|
69691
|
+
}
|
|
69547
69692
|
}
|
|
69693
|
+
break;
|
|
69694
|
+
}
|
|
69695
|
+
case "win32": {
|
|
69696
|
+
const powershellPath = await getPowershellPath();
|
|
69697
|
+
if (!powershellPath)
|
|
69698
|
+
return;
|
|
69699
|
+
const escaped = escapePowerShellSingleQuotedText(soundPath);
|
|
69700
|
+
const soundScript = "(New-Object Media.SoundPlayer '" + escaped + "').PlaySync()";
|
|
69701
|
+
await runNotificationCommand(ctx, powershellPath, ["-Command", soundScript], (shell) => shell`${powershellPath} -Command ${soundScript}`);
|
|
69702
|
+
break;
|
|
69548
69703
|
}
|
|
69549
|
-
break;
|
|
69550
69704
|
}
|
|
69551
|
-
|
|
69552
|
-
|
|
69553
|
-
|
|
69554
|
-
|
|
69555
|
-
|
|
69556
|
-
await runQuietNothrow(ctx.$`${powershellPath} -Command ${"(New-Object Media.SoundPlayer '" + escaped + "').PlaySync()"}`);
|
|
69557
|
-
break;
|
|
69705
|
+
} catch (error) {
|
|
69706
|
+
if (error instanceof Error) {
|
|
69707
|
+
logOperationFailure("sound", error);
|
|
69708
|
+
} else {
|
|
69709
|
+
logOperationFailure("sound", String(error));
|
|
69558
69710
|
}
|
|
69559
69711
|
}
|
|
69560
69712
|
}
|
|
@@ -71625,6 +71777,27 @@ async function runCommentChecker2(input, cliPath, customPrompt) {
|
|
|
71625
71777
|
// src/hooks/comment-checker/cli-runner.ts
|
|
71626
71778
|
var cliPathPromise = null;
|
|
71627
71779
|
var isRunning = false;
|
|
71780
|
+
var sessionLastWarning = new Map;
|
|
71781
|
+
var DEDUP_WINDOW_MS = 30000;
|
|
71782
|
+
function hasCommentSyntax(text) {
|
|
71783
|
+
if (!text)
|
|
71784
|
+
return false;
|
|
71785
|
+
return /^\s*(\/\/|\/\*|#|--|<!--|:\s*)[\s\S]*$/m.test(text) || /<!--[\s\S]*-->/.test(text);
|
|
71786
|
+
}
|
|
71787
|
+
function hasNewCommentsOnly(oldText, newText) {
|
|
71788
|
+
if (!hasCommentSyntax(newText))
|
|
71789
|
+
return false;
|
|
71790
|
+
if (!hasCommentSyntax(oldText))
|
|
71791
|
+
return true;
|
|
71792
|
+
const oldLines = new Set((oldText ?? "").split(`
|
|
71793
|
+
`).map((l) => l.trim()));
|
|
71794
|
+
const newLines = (newText ?? "").split(`
|
|
71795
|
+
`);
|
|
71796
|
+
return newLines.some((l) => {
|
|
71797
|
+
const trimmed = l.trim();
|
|
71798
|
+
return trimmed && hasCommentSyntax(trimmed) && !oldLines.has(trimmed);
|
|
71799
|
+
});
|
|
71800
|
+
}
|
|
71628
71801
|
async function withCommentCheckerLock(fn, fallback, debugLog3) {
|
|
71629
71802
|
if (isRunning) {
|
|
71630
71803
|
debugLog3("comment-checker already running, skipping");
|
|
@@ -71666,6 +71839,17 @@ async function processWithCli(input, pendingCall, output, cliPath, customPrompt,
|
|
|
71666
71839
|
edits: pendingCall.edits
|
|
71667
71840
|
}
|
|
71668
71841
|
};
|
|
71842
|
+
if (!hasNewCommentsOnly(pendingCall.oldString, pendingCall.newString)) {
|
|
71843
|
+
debugLog3("skipping: no net-new comments in edit (oldString/newString)");
|
|
71844
|
+
return;
|
|
71845
|
+
}
|
|
71846
|
+
const lastWarned = sessionLastWarning.get(pendingCall.sessionID) ?? 0;
|
|
71847
|
+
const now = Date.now();
|
|
71848
|
+
if (now - lastWarned < DEDUP_WINDOW_MS) {
|
|
71849
|
+
debugLog3("dedup: skipping comment warning within dedup window for session", pendingCall.sessionID);
|
|
71850
|
+
return;
|
|
71851
|
+
}
|
|
71852
|
+
sessionLastWarning.set(pendingCall.sessionID, now);
|
|
71669
71853
|
const result = await (deps.runCommentChecker ?? runCommentChecker2)(hookInput, cliPath, customPrompt);
|
|
71670
71854
|
if (result.hasComments && result.message) {
|
|
71671
71855
|
debugLog3("CLI detected comments, appending message");
|
|
@@ -80498,6 +80682,7 @@ function createTeamModeStatusInjector(config, keywordDetectorConfig) {
|
|
|
80498
80682
|
// src/hooks/tool-pair-validator/hook.ts
|
|
80499
80683
|
init_logger();
|
|
80500
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.";
|
|
80501
80686
|
function getToolUseID(part) {
|
|
80502
80687
|
const candidate = part;
|
|
80503
80688
|
if (candidate.type === "tool_use" && typeof candidate.id === "string" && candidate.id.length > 0) {
|
|
@@ -80575,7 +80760,14 @@ function createSyntheticUserMessage(assistantMessage, missingToolUseIDs) {
|
|
|
80575
80760
|
role: "user",
|
|
80576
80761
|
...sessionID ? { sessionID } : {}
|
|
80577
80762
|
},
|
|
80578
|
-
parts:
|
|
80763
|
+
parts: [
|
|
80764
|
+
...missingToolUseIDs.map((toolUseID) => createToolResultPart(toolUseID)),
|
|
80765
|
+
{
|
|
80766
|
+
type: "text",
|
|
80767
|
+
text: TOOL_RESULT_RECOVERY_CONTINUATION,
|
|
80768
|
+
synthetic: true
|
|
80769
|
+
}
|
|
80770
|
+
]
|
|
80579
80771
|
};
|
|
80580
80772
|
}
|
|
80581
80773
|
function getMessageID(message) {
|
|
@@ -91844,6 +92036,10 @@ async function handleAtlasSessionIdle(input) {
|
|
|
91844
92036
|
}
|
|
91845
92037
|
const { boulderState, progress, appendedSession } = activeBoulderSession;
|
|
91846
92038
|
if (progress.isComplete) {
|
|
92039
|
+
if (sessionState.pendingRetryTimer) {
|
|
92040
|
+
clearTimeout(sessionState.pendingRetryTimer);
|
|
92041
|
+
sessionState.pendingRetryTimer = undefined;
|
|
92042
|
+
}
|
|
91847
92043
|
const work = getWorkForSession(ctx.directory, sessionID);
|
|
91848
92044
|
if (work) {
|
|
91849
92045
|
completeBoulder(ctx.directory, work.work_id);
|
|
@@ -91854,6 +92050,10 @@ async function handleAtlasSessionIdle(input) {
|
|
|
91854
92050
|
log(`[${HOOK_NAME7}] Boulder complete`, { sessionID, plan: boulderState.plan_name });
|
|
91855
92051
|
return;
|
|
91856
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
|
+
}
|
|
91857
92057
|
if (sessionState.boulderCompletionNudgedAt?.[work.work_id]) {
|
|
91858
92058
|
log(`[${HOOK_NAME7}] Boulder complete`, { sessionID, plan: boulderState.plan_name });
|
|
91859
92059
|
return;
|
|
@@ -100037,11 +100237,17 @@ var cachedCli = null;
|
|
|
100037
100237
|
var autoInstallAttempted = false;
|
|
100038
100238
|
function findExecutable(name) {
|
|
100039
100239
|
const isWindows2 = process.platform === "win32";
|
|
100040
|
-
const cmd = isWindows2 ? "where" : "which";
|
|
100240
|
+
const cmd = isWindows2 ? "where.exe" : "which";
|
|
100041
100241
|
try {
|
|
100042
|
-
const result = spawnSync2(cmd, [name], {
|
|
100043
|
-
|
|
100044
|
-
|
|
100242
|
+
const result = spawnSync2(cmd, [name], {
|
|
100243
|
+
encoding: "utf-8",
|
|
100244
|
+
timeout: 5000,
|
|
100245
|
+
windowsHide: isWindows2,
|
|
100246
|
+
shell: false
|
|
100247
|
+
});
|
|
100248
|
+
const stdout = result.stdout;
|
|
100249
|
+
if (result.status === 0 && stdout.trim()) {
|
|
100250
|
+
return stdout.trim().split(`
|
|
100045
100251
|
`)[0];
|
|
100046
100252
|
}
|
|
100047
100253
|
} catch {
|
|
@@ -100055,6 +100261,7 @@ function getOpenCodeBundledRg() {
|
|
|
100055
100261
|
const isWindows2 = process.platform === "win32";
|
|
100056
100262
|
const rgName = isWindows2 ? "rg.exe" : "rg";
|
|
100057
100263
|
const candidates = [
|
|
100264
|
+
join81(getOpenCodeCacheDir(), "bin", rgName),
|
|
100058
100265
|
join81(getDataDir(), "opencode", "bin", rgName),
|
|
100059
100266
|
join81(execDir, rgName),
|
|
100060
100267
|
join81(execDir, "bin", rgName),
|
|
@@ -100163,6 +100370,29 @@ class Semaphore {
|
|
|
100163
100370
|
}
|
|
100164
100371
|
var rgSemaphore = new Semaphore(2);
|
|
100165
100372
|
|
|
100373
|
+
// src/tools/shared/search-process-output.ts
|
|
100374
|
+
function getErrorMessage5(error) {
|
|
100375
|
+
return error instanceof Error ? error.message : String(error);
|
|
100376
|
+
}
|
|
100377
|
+
function createProcessTimeout(proc, timeoutMs, timeoutMessage) {
|
|
100378
|
+
return new Promise((_, reject) => {
|
|
100379
|
+
const id = setTimeout(() => {
|
|
100380
|
+
proc.kill();
|
|
100381
|
+
reject(new Error(timeoutMessage));
|
|
100382
|
+
}, timeoutMs);
|
|
100383
|
+
proc.exited.then(() => clearTimeout(id), () => clearTimeout(id));
|
|
100384
|
+
});
|
|
100385
|
+
}
|
|
100386
|
+
async function collectSearchProcessOutput(proc, timeoutMs, timeoutMessage) {
|
|
100387
|
+
const stderrPromise = readProcessStream(proc.stderr).catch(getErrorMessage5);
|
|
100388
|
+
const stdout = await Promise.race([
|
|
100389
|
+
readProcessStream(proc.stdout),
|
|
100390
|
+
createProcessTimeout(proc, timeoutMs, timeoutMessage)
|
|
100391
|
+
]);
|
|
100392
|
+
const [exitCode, stderr] = await Promise.all([proc.exited, stderrPromise]);
|
|
100393
|
+
return { stdout, stderr, exitCode };
|
|
100394
|
+
}
|
|
100395
|
+
|
|
100166
100396
|
// src/tools/grep/cli.ts
|
|
100167
100397
|
function buildRgArgs(options) {
|
|
100168
100398
|
const args = [
|
|
@@ -100286,15 +100516,15 @@ function parseCountOutput(output) {
|
|
|
100286
100516
|
}
|
|
100287
100517
|
return results;
|
|
100288
100518
|
}
|
|
100289
|
-
async function runRg(options, resolvedCli) {
|
|
100519
|
+
async function runRg(options, resolvedCli, processSpawner = spawn2) {
|
|
100290
100520
|
await rgSemaphore.acquire();
|
|
100291
100521
|
try {
|
|
100292
|
-
return await runRgInternal(options, resolvedCli);
|
|
100522
|
+
return await runRgInternal(options, resolvedCli, processSpawner);
|
|
100293
100523
|
} finally {
|
|
100294
100524
|
rgSemaphore.release();
|
|
100295
100525
|
}
|
|
100296
100526
|
}
|
|
100297
|
-
async function runRgInternal(options, resolvedCli) {
|
|
100527
|
+
async function runRgInternal(options, resolvedCli, processSpawner = spawn2) {
|
|
100298
100528
|
const cli = resolvedCli ?? resolveGrepCli();
|
|
100299
100529
|
const args = buildArgs(options, cli.backend);
|
|
100300
100530
|
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS2, DEFAULT_TIMEOUT_MS2);
|
|
@@ -100305,21 +100535,12 @@ async function runRgInternal(options, resolvedCli) {
|
|
|
100305
100535
|
}
|
|
100306
100536
|
const paths = options.paths?.length ? options.paths : ["."];
|
|
100307
100537
|
args.push(...paths);
|
|
100308
|
-
const proc = spawn2([cli.path, ...args], {
|
|
100309
|
-
stdout: "pipe",
|
|
100310
|
-
stderr: "pipe"
|
|
100311
|
-
});
|
|
100312
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
100313
|
-
const id = setTimeout(() => {
|
|
100314
|
-
proc.kill();
|
|
100315
|
-
reject(new Error(`Search timeout after ${timeout}ms`));
|
|
100316
|
-
}, timeout);
|
|
100317
|
-
proc.exited.then(() => clearTimeout(id));
|
|
100318
|
-
});
|
|
100319
100538
|
try {
|
|
100320
|
-
const
|
|
100321
|
-
|
|
100322
|
-
|
|
100539
|
+
const proc = processSpawner([cli.path, ...args], {
|
|
100540
|
+
stdout: "pipe",
|
|
100541
|
+
stderr: "pipe"
|
|
100542
|
+
});
|
|
100543
|
+
const { stdout, stderr, exitCode } = await collectSearchProcessOutput(proc, timeout, `Search timeout after ${timeout}ms`);
|
|
100323
100544
|
const truncated = stdout.length >= DEFAULT_MAX_OUTPUT_BYTES;
|
|
100324
100545
|
const outputToProcess = truncated ? stdout.substring(0, DEFAULT_MAX_OUTPUT_BYTES) : stdout;
|
|
100325
100546
|
if (exitCode > 1 && stderr.trim()) {
|
|
@@ -100350,15 +100571,15 @@ async function runRgInternal(options, resolvedCli) {
|
|
|
100350
100571
|
};
|
|
100351
100572
|
}
|
|
100352
100573
|
}
|
|
100353
|
-
async function runRgCount(options, resolvedCli) {
|
|
100574
|
+
async function runRgCount(options, resolvedCli, processSpawner = spawn2) {
|
|
100354
100575
|
await rgSemaphore.acquire();
|
|
100355
100576
|
try {
|
|
100356
|
-
return await runRgCountInternal(options, resolvedCli);
|
|
100577
|
+
return await runRgCountInternal(options, resolvedCli, processSpawner);
|
|
100357
100578
|
} finally {
|
|
100358
100579
|
rgSemaphore.release();
|
|
100359
100580
|
}
|
|
100360
100581
|
}
|
|
100361
|
-
async function runRgCountInternal(options, resolvedCli) {
|
|
100582
|
+
async function runRgCountInternal(options, resolvedCli, processSpawner = spawn2) {
|
|
100362
100583
|
const cli = resolvedCli ?? resolveGrepCli();
|
|
100363
100584
|
const args = buildArgs({ ...options, context: 0 }, cli.backend);
|
|
100364
100585
|
if (cli.backend === "rg") {
|
|
@@ -100369,19 +100590,15 @@ async function runRgCountInternal(options, resolvedCli) {
|
|
|
100369
100590
|
const paths = options.paths?.length ? options.paths : ["."];
|
|
100370
100591
|
args.push(...paths);
|
|
100371
100592
|
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS2, DEFAULT_TIMEOUT_MS2);
|
|
100372
|
-
const proc = spawn2([cli.path, ...args], {
|
|
100373
|
-
stdout: "pipe",
|
|
100374
|
-
stderr: "pipe"
|
|
100375
|
-
});
|
|
100376
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
100377
|
-
const id = setTimeout(() => {
|
|
100378
|
-
proc.kill();
|
|
100379
|
-
reject(new Error(`Search timeout after ${timeout}ms`));
|
|
100380
|
-
}, timeout);
|
|
100381
|
-
proc.exited.then(() => clearTimeout(id));
|
|
100382
|
-
});
|
|
100383
100593
|
try {
|
|
100384
|
-
const
|
|
100594
|
+
const proc = processSpawner([cli.path, ...args], {
|
|
100595
|
+
stdout: "pipe",
|
|
100596
|
+
stderr: "pipe"
|
|
100597
|
+
});
|
|
100598
|
+
const { stdout, stderr, exitCode } = await collectSearchProcessOutput(proc, timeout, `Search timeout after ${timeout}ms`);
|
|
100599
|
+
if (exitCode > 1 && stderr.trim()) {
|
|
100600
|
+
throw new Error(stderr.trim());
|
|
100601
|
+
}
|
|
100385
100602
|
return parseCountOutput(stdout);
|
|
100386
100603
|
} catch (e) {
|
|
100387
100604
|
throw new Error(`Count search failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -100542,12 +100759,12 @@ function buildPowerShellCommand(options) {
|
|
|
100542
100759
|
const searchPath = paths[0] || ".";
|
|
100543
100760
|
const escapedPath = searchPath.replace(/'/g, "''");
|
|
100544
100761
|
const escapedPattern = options.pattern.replace(/'/g, "''");
|
|
100545
|
-
let psCommand = `Get-ChildItem -
|
|
100762
|
+
let psCommand = `Get-ChildItem -LiteralPath '${escapedPath}' -File -Recurse -Depth ${maxDepth - 1} -Filter '${escapedPattern}'`;
|
|
100546
100763
|
if (options.hidden !== false) {
|
|
100547
100764
|
psCommand += " -Force";
|
|
100548
100765
|
}
|
|
100549
100766
|
psCommand += " -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName";
|
|
100550
|
-
return ["powershell", "-NoProfile", "-Command", psCommand];
|
|
100767
|
+
return ["powershell.exe", "-NoProfile", "-Command", psCommand];
|
|
100551
100768
|
}
|
|
100552
100769
|
async function getFileMtime(filePath) {
|
|
100553
100770
|
try {
|
|
@@ -100557,15 +100774,15 @@ async function getFileMtime(filePath) {
|
|
|
100557
100774
|
return 0;
|
|
100558
100775
|
}
|
|
100559
100776
|
}
|
|
100560
|
-
async function runRgFiles(options, resolvedCli) {
|
|
100777
|
+
async function runRgFiles(options, resolvedCli, processSpawner = spawn2) {
|
|
100561
100778
|
await rgSemaphore.acquire();
|
|
100562
100779
|
try {
|
|
100563
|
-
return await runRgFilesInternal(options, resolvedCli);
|
|
100780
|
+
return await runRgFilesInternal(options, resolvedCli, processSpawner);
|
|
100564
100781
|
} finally {
|
|
100565
100782
|
rgSemaphore.release();
|
|
100566
100783
|
}
|
|
100567
100784
|
}
|
|
100568
|
-
async function runRgFilesInternal(options, resolvedCli) {
|
|
100785
|
+
async function runRgFilesInternal(options, resolvedCli, processSpawner = spawn2) {
|
|
100569
100786
|
const cli = resolvedCli ?? resolveGrepCli();
|
|
100570
100787
|
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3);
|
|
100571
100788
|
const limit = Math.min(options.limit ?? DEFAULT_LIMIT, DEFAULT_LIMIT);
|
|
@@ -100587,22 +100804,13 @@ async function runRgFilesInternal(options, resolvedCli) {
|
|
|
100587
100804
|
cwd = paths[0] || ".";
|
|
100588
100805
|
command = [cli.path, ...args];
|
|
100589
100806
|
}
|
|
100590
|
-
const proc = spawn2(command, {
|
|
100591
|
-
stdout: "pipe",
|
|
100592
|
-
stderr: "pipe",
|
|
100593
|
-
cwd
|
|
100594
|
-
});
|
|
100595
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
100596
|
-
const id = setTimeout(() => {
|
|
100597
|
-
proc.kill();
|
|
100598
|
-
reject(new Error(`Glob search timeout after ${timeout}ms`));
|
|
100599
|
-
}, timeout);
|
|
100600
|
-
proc.exited.then(() => clearTimeout(id));
|
|
100601
|
-
});
|
|
100602
100807
|
try {
|
|
100603
|
-
const
|
|
100604
|
-
|
|
100605
|
-
|
|
100808
|
+
const proc = processSpawner(command, {
|
|
100809
|
+
stdout: "pipe",
|
|
100810
|
+
stderr: "pipe",
|
|
100811
|
+
cwd
|
|
100812
|
+
});
|
|
100813
|
+
const { stdout, stderr, exitCode } = await collectSearchProcessOutput(proc, timeout, `Glob search timeout after ${timeout}ms`);
|
|
100606
100814
|
if (exitCode > 1 && stderr.trim()) {
|
|
100607
100815
|
return {
|
|
100608
100816
|
files: [],
|
|
@@ -102373,7 +102581,7 @@ init_opencode_message_dir();
|
|
|
102373
102581
|
init_logger();
|
|
102374
102582
|
|
|
102375
102583
|
// src/tools/background-task/session-messages.ts
|
|
102376
|
-
function
|
|
102584
|
+
function getErrorMessage6(value) {
|
|
102377
102585
|
if (Array.isArray(value))
|
|
102378
102586
|
return null;
|
|
102379
102587
|
if (value.error === undefined || value.error === null)
|
|
@@ -102556,7 +102764,7 @@ async function formatFullSession(task, client, options) {
|
|
|
102556
102764
|
} catch (error) {
|
|
102557
102765
|
return `Error fetching messages: ${error instanceof Error ? error.message : String(error)}`;
|
|
102558
102766
|
}
|
|
102559
|
-
const errorMessage =
|
|
102767
|
+
const errorMessage = getErrorMessage6(messagesResult);
|
|
102560
102768
|
if (errorMessage) {
|
|
102561
102769
|
return `Error fetching messages: ${errorMessage}`;
|
|
102562
102770
|
}
|
|
@@ -102762,7 +102970,7 @@ async function formatTaskResult(task, client) {
|
|
|
102762
102970
|
} catch (error) {
|
|
102763
102971
|
return `Error fetching messages: ${error instanceof Error ? error.message : String(error)}`;
|
|
102764
102972
|
}
|
|
102765
|
-
const errorMessage =
|
|
102973
|
+
const errorMessage = getErrorMessage6(messagesResult);
|
|
102766
102974
|
if (errorMessage) {
|
|
102767
102975
|
return `Error fetching messages: ${errorMessage}`;
|
|
102768
102976
|
}
|
|
@@ -104640,10 +104848,10 @@ async function waitForLookAtSessionResult(client, sessionID, options) {
|
|
|
104640
104848
|
const isActive = supportedButNeverSeen || statusType !== null && !isTerminal;
|
|
104641
104849
|
const { messages, error: messagesError } = await getSessionMessages(client, sessionID);
|
|
104642
104850
|
const outcome = extractLatestAssistantOutcome(messages);
|
|
104643
|
-
if (outcome.text && !isActive) {
|
|
104851
|
+
if (outcome.text && (!isActive || supportedButNeverSeen)) {
|
|
104644
104852
|
return { messages, outcome, statusType };
|
|
104645
104853
|
}
|
|
104646
|
-
if (outcome.errorName && !isActive) {
|
|
104854
|
+
if (outcome.errorName && (!isActive || supportedButNeverSeen)) {
|
|
104647
104855
|
return { messages, outcome, statusType };
|
|
104648
104856
|
}
|
|
104649
104857
|
if (isActive) {
|
|
@@ -104719,7 +104927,7 @@ Original error: ${createResult.error}`;
|
|
|
104719
104927
|
const sessionID = createResult.data.id;
|
|
104720
104928
|
log(`[look_at] Created session: ${sessionID}`);
|
|
104721
104929
|
log(`[look_at] Sending prompt with ${isBase64Input ? "base64 image" : "file"} to session ${sessionID}`);
|
|
104722
|
-
let
|
|
104930
|
+
let shouldWaitForStatus = true;
|
|
104723
104931
|
try {
|
|
104724
104932
|
await promptSyncWithModelSuggestionRetry(ctx.client, {
|
|
104725
104933
|
path: { id: sessionID },
|
|
@@ -104742,15 +104950,14 @@ Original error: ${createResult.error}`;
|
|
|
104742
104950
|
queueBehavior: "defer"
|
|
104743
104951
|
});
|
|
104744
104952
|
} catch (promptError) {
|
|
104745
|
-
|
|
104746
|
-
|
|
104953
|
+
log("[look_at] Prompt dispatch failed; checking child session evidence:", promptError);
|
|
104954
|
+
shouldWaitForStatus = isAmbiguousPromptDispatchFailure(promptError);
|
|
104747
104955
|
}
|
|
104748
104956
|
let observedMessages;
|
|
104749
104957
|
let observedText;
|
|
104750
|
-
if (typeof ctx.client.session.status === "function") {
|
|
104958
|
+
if (shouldWaitForStatus && typeof ctx.client.session.status === "function") {
|
|
104751
104959
|
const waitResult = await waitForLookAtSessionResult(ctx.client, sessionID, {
|
|
104752
|
-
allowStableIdleWithoutActivity: true
|
|
104753
|
-
allowEmptyStableIdleWithoutActivity: promptFailed
|
|
104960
|
+
allowStableIdleWithoutActivity: true
|
|
104754
104961
|
});
|
|
104755
104962
|
observedText = waitResult.outcome.text ?? undefined;
|
|
104756
104963
|
if (observedText) {
|
|
@@ -106157,6 +106364,9 @@ async function executeBackgroundTask(args, ctx, executorCtx, parentContext, agen
|
|
|
106157
106364
|
|
|
106158
106365
|
Task ID: ${task.id}`;
|
|
106159
106366
|
}
|
|
106367
|
+
if (!sessionId && updatedTask?.sessionId) {
|
|
106368
|
+
sessionId = updatedTask.sessionId;
|
|
106369
|
+
}
|
|
106160
106370
|
if (sessionId) {
|
|
106161
106371
|
registerBackgroundSessionContext({
|
|
106162
106372
|
sessionId,
|
|
@@ -110502,6 +110712,42 @@ function detectRepetitiveToolUse(window) {
|
|
|
110502
110712
|
|
|
110503
110713
|
// src/features/background-agent/parent-wake-notifier.ts
|
|
110504
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
|
|
110505
110751
|
function unrefTimerHandle(handle) {
|
|
110506
110752
|
const maybeUnref = handle.unref;
|
|
110507
110753
|
if (typeof maybeUnref === "function") {
|
|
@@ -110539,7 +110785,7 @@ class ParentWakeNotifier {
|
|
|
110539
110785
|
this.recentParentSessionActivity.set(sessionID, Date.now());
|
|
110540
110786
|
}
|
|
110541
110787
|
queuePendingParentWake(sessionID, notification2, promptContext, shouldReply, delayMs) {
|
|
110542
|
-
const resolvedPromptContext =
|
|
110788
|
+
const resolvedPromptContext = resolveParentWakePromptContext(promptContext);
|
|
110543
110789
|
const pendingWake = this.pendingParentWakes.get(sessionID);
|
|
110544
110790
|
if (pendingWake) {
|
|
110545
110791
|
pendingWake.notifications.push(notification2);
|
|
@@ -110592,6 +110838,12 @@ class ParentWakeNotifier {
|
|
|
110592
110838
|
});
|
|
110593
110839
|
return;
|
|
110594
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
|
+
}
|
|
110595
110847
|
this.pendingParentWakes.delete(sessionID);
|
|
110596
110848
|
const notificationContent = latestWake.notifications.join(`
|
|
110597
110849
|
|
|
@@ -110619,9 +110871,9 @@ class ParentWakeNotifier {
|
|
|
110619
110871
|
});
|
|
110620
110872
|
if (promptResult.status === "failed") {
|
|
110621
110873
|
if (isAmbiguousPostDispatchPromptFailure(promptResult)) {
|
|
110622
|
-
const
|
|
110623
|
-
|
|
110624
|
-
if (await this.hasAcceptedMessageAfterDispatchedParentWake(sessionID,
|
|
110874
|
+
const dispatchedWake2 = cloneParentWake(latestWake);
|
|
110875
|
+
dispatchedWake2.dispatchedAt = dispatchStartedAt;
|
|
110876
|
+
if (await this.hasAcceptedMessageAfterDispatchedParentWake(sessionID, dispatchedWake2)) {
|
|
110625
110877
|
this.trackDispatchedParentWake(sessionID, latestWake, dispatchStartedAt);
|
|
110626
110878
|
log("[background-agent] Treated failed parent wake prompt as accepted after observing session history:", {
|
|
110627
110879
|
sessionID,
|
|
@@ -110633,6 +110885,11 @@ class ParentWakeNotifier {
|
|
|
110633
110885
|
throw promptResult.error;
|
|
110634
110886
|
}
|
|
110635
110887
|
if (promptResult.status === "reserved" && promptResult.reservedBy === "background-agent-parent-wake") {
|
|
110888
|
+
const dispatchedWake2 = this.dispatchedParentWakes.get(sessionID);
|
|
110889
|
+
if (dispatchedWake2 && isRedundantParentWake(latestWake, dispatchedWake2)) {
|
|
110890
|
+
log("[background-agent] Suppressed duplicate parent wake during promptAsync gate hold:", { sessionID });
|
|
110891
|
+
return;
|
|
110892
|
+
}
|
|
110636
110893
|
this.requeueWake(sessionID, latestWake);
|
|
110637
110894
|
this.schedulePendingParentWakeFlush(sessionID, 2000);
|
|
110638
110895
|
log("[background-agent] Requeued parent wake flush reserved by promptAsync gate hold:", { sessionID });
|
|
@@ -110738,28 +110995,9 @@ class ParentWakeNotifier {
|
|
|
110738
110995
|
this.recentParentSessionActivity.delete(sessionID);
|
|
110739
110996
|
return false;
|
|
110740
110997
|
}
|
|
110741
|
-
resolveParentWakePromptContext(promptContext) {
|
|
110742
|
-
const resolvedAgent = resolveRegisteredAgentName(promptContext.agent);
|
|
110743
|
-
return {
|
|
110744
|
-
...promptContext,
|
|
110745
|
-
...resolvedAgent ? { agent: resolvedAgent } : {},
|
|
110746
|
-
...promptContext.model ? { model: { ...promptContext.model } } : {},
|
|
110747
|
-
...promptContext.tools ? { tools: { ...promptContext.tools } } : {}
|
|
110748
|
-
};
|
|
110749
|
-
}
|
|
110750
|
-
cloneParentWake(wake) {
|
|
110751
|
-
const promptContext = this.resolveParentWakePromptContext(wake.promptContext);
|
|
110752
|
-
return {
|
|
110753
|
-
promptContext,
|
|
110754
|
-
notifications: [...wake.notifications],
|
|
110755
|
-
shouldReply: wake.shouldReply,
|
|
110756
|
-
...wake.dispatchedAt !== undefined ? { dispatchedAt: wake.dispatchedAt } : {},
|
|
110757
|
-
...wake.toolCallDeferralStartedAt !== undefined ? { toolCallDeferralStartedAt: wake.toolCallDeferralStartedAt } : {}
|
|
110758
|
-
};
|
|
110759
|
-
}
|
|
110760
110998
|
trackDispatchedParentWake(sessionID, wake, dispatchedAt) {
|
|
110761
110999
|
this.clearDispatchedParentWake(sessionID);
|
|
110762
|
-
const dispatchedWake =
|
|
111000
|
+
const dispatchedWake = cloneParentWake(wake);
|
|
110763
111001
|
dispatchedWake.dispatchedAt = dispatchedAt;
|
|
110764
111002
|
this.dispatchedParentWakes.set(sessionID, dispatchedWake);
|
|
110765
111003
|
const timer = setTimeout(() => {
|
|
@@ -110937,7 +111175,7 @@ class ParentWakeNotifier {
|
|
|
110937
111175
|
pendingWake.toolCallDeferralStartedAt ??= latestWake.toolCallDeferralStartedAt;
|
|
110938
111176
|
return;
|
|
110939
111177
|
}
|
|
110940
|
-
this.pendingParentWakes.set(sessionID,
|
|
111178
|
+
this.pendingParentWakes.set(sessionID, cloneParentWake(latestWake));
|
|
110941
111179
|
}
|
|
110942
111180
|
}
|
|
110943
111181
|
|
|
@@ -110993,9 +111231,13 @@ function describeProcessCleanupError(error) {
|
|
|
110993
111231
|
return { raw: String(error) };
|
|
110994
111232
|
}
|
|
110995
111233
|
function registerErrorEvent(signal) {
|
|
111234
|
+
let logging = false;
|
|
110996
111235
|
const listener = (error) => {
|
|
110997
|
-
|
|
111236
|
+
if (logging)
|
|
111237
|
+
return;
|
|
111238
|
+
logging = true;
|
|
110998
111239
|
log(`[background-agent] ${signal} observed; keeping host alive and skipping cleanup (signal handlers run on real shutdown)`, describeProcessCleanupError(error));
|
|
111240
|
+
logging = false;
|
|
110999
111241
|
};
|
|
111000
111242
|
process.on(signal, listener);
|
|
111001
111243
|
return listener;
|
|
@@ -132156,13 +132398,16 @@ function maybeCreateAtlasConfig(input) {
|
|
|
132156
132398
|
return;
|
|
132157
132399
|
const orchestratorOverride = agentOverrides["atlas"];
|
|
132158
132400
|
const atlasRequirement = AGENT_MODEL_REQUIREMENTS["atlas"];
|
|
132159
|
-
|
|
132401
|
+
let atlasResolution = applyModelResolution({
|
|
132160
132402
|
uiSelectedModel: orchestratorOverride?.model !== undefined ? undefined : uiSelectedModel,
|
|
132161
132403
|
userModel: orchestratorOverride?.model,
|
|
132162
132404
|
requirement: atlasRequirement,
|
|
132163
132405
|
availableModels,
|
|
132164
132406
|
systemDefaultModel
|
|
132165
132407
|
});
|
|
132408
|
+
if (!atlasResolution && orchestratorOverride?.model) {
|
|
132409
|
+
atlasResolution = { model: orchestratorOverride.model, provenance: "override" };
|
|
132410
|
+
}
|
|
132166
132411
|
if (!atlasResolution)
|
|
132167
132412
|
return;
|
|
132168
132413
|
const { model: atlasModel, variant: atlasResolvedVariant } = atlasResolution;
|
|
@@ -135833,6 +136078,14 @@ function supportsImageInput(modelConfig) {
|
|
|
135833
136078
|
}
|
|
135834
136079
|
return modelConfig?.capabilities?.input?.image === true;
|
|
135835
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
|
+
}
|
|
135836
136089
|
function applyProviderConfig(params) {
|
|
135837
136090
|
const providers = params.config.provider;
|
|
135838
136091
|
const modelContextLimitsCache = params.modelCacheState.modelContextLimitsCache;
|
|
@@ -135843,22 +136096,31 @@ function applyProviderConfig(params) {
|
|
|
135843
136096
|
params.modelCacheState.visionCapableModelsCache = visionCapableModelsCache2;
|
|
135844
136097
|
visionCapableModelsCache2.clear();
|
|
135845
136098
|
setVisionCapableModelsCache(visionCapableModelsCache2);
|
|
135846
|
-
if (
|
|
135847
|
-
|
|
135848
|
-
|
|
135849
|
-
|
|
135850
|
-
if (!models)
|
|
135851
|
-
continue;
|
|
135852
|
-
for (const [modelID, modelConfig] of Object.entries(models)) {
|
|
135853
|
-
if (supportsImageInput(modelConfig)) {
|
|
135854
|
-
visionCapableModelsCache2.set(`${providerID}/${modelID}`, { providerID, modelID });
|
|
135855
|
-
}
|
|
135856
|
-
const contextLimit = modelConfig?.limit?.context;
|
|
135857
|
-
if (!contextLimit)
|
|
136099
|
+
if (providers) {
|
|
136100
|
+
for (const [providerID, providerConfig] of Object.entries(providers)) {
|
|
136101
|
+
const models = providerConfig?.models;
|
|
136102
|
+
if (!models)
|
|
135858
136103
|
continue;
|
|
135859
|
-
|
|
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
|
+
}
|
|
135860
136113
|
}
|
|
135861
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
|
+
}
|
|
135862
136124
|
}
|
|
135863
136125
|
|
|
135864
136126
|
// src/plugin-handlers/plugin-components-loader.ts
|
|
@@ -136020,12 +136282,25 @@ function applyToolConfig(params) {
|
|
|
136020
136282
|
}
|
|
136021
136283
|
|
|
136022
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
|
+
}
|
|
136023
136294
|
function createConfigHandler(deps) {
|
|
136024
136295
|
const { ctx, pluginConfig, modelCacheState } = deps;
|
|
136025
136296
|
return async (config) => {
|
|
136026
136297
|
const formatterConfig = config.formatter;
|
|
136027
136298
|
setAdditionalAllowedMcpEnvVars(pluginConfig.mcp_env_allowlist ?? []);
|
|
136028
|
-
applyProviderConfig({
|
|
136299
|
+
applyProviderConfig({
|
|
136300
|
+
config,
|
|
136301
|
+
modelCacheState,
|
|
136302
|
+
trustedVisionCapableModels: collectTrustedVisionCapableModels(pluginConfig)
|
|
136303
|
+
});
|
|
136029
136304
|
clearFormatterCache();
|
|
136030
136305
|
const pluginComponents = await loadPluginComponents({ pluginConfig });
|
|
136031
136306
|
applyHookConfig({ pluginComponents, ctx });
|
|
@@ -139870,7 +140145,7 @@ function createEventHandler2(args) {
|
|
|
139870
140145
|
const recentSyntheticIdles = new Map;
|
|
139871
140146
|
const recentRealIdles = new Map;
|
|
139872
140147
|
const recentAnyIdles = new Map;
|
|
139873
|
-
const
|
|
140148
|
+
const DEDUP_WINDOW_MS2 = 500;
|
|
139874
140149
|
const teamModeConfig = pluginConfig.team_mode?.enabled ? pluginConfig.team_mode : undefined;
|
|
139875
140150
|
const teamLeadOrphanHandler = teamModeConfig ? createTeamLeadOrphanHandler(teamModeConfig, managers.tmuxSessionManager, managers.backgroundManager) : undefined;
|
|
139876
140151
|
const teamMemberErrorHandler = teamModeConfig ? createTeamMemberErrorHandler(teamModeConfig, { client: pluginContext.client }) : undefined;
|
|
@@ -139896,7 +140171,7 @@ function createEventHandler2(args) {
|
|
|
139896
140171
|
};
|
|
139897
140172
|
const shouldDispatchIdleEvent = (sessionID, now) => {
|
|
139898
140173
|
const lastDispatchedAt = recentAnyIdles.get(sessionID);
|
|
139899
|
-
if (lastDispatchedAt !== undefined && now - lastDispatchedAt <
|
|
140174
|
+
if (lastDispatchedAt !== undefined && now - lastDispatchedAt < DEDUP_WINDOW_MS2) {
|
|
139900
140175
|
return false;
|
|
139901
140176
|
}
|
|
139902
140177
|
recentAnyIdles.set(sessionID, now);
|
|
@@ -140059,7 +140334,7 @@ function createEventHandler2(args) {
|
|
|
140059
140334
|
recentRealIdles,
|
|
140060
140335
|
recentAnyIdles,
|
|
140061
140336
|
now: Date.now(),
|
|
140062
|
-
dedupWindowMs:
|
|
140337
|
+
dedupWindowMs: DEDUP_WINDOW_MS2
|
|
140063
140338
|
});
|
|
140064
140339
|
const syntheticIdle = normalizeSessionStatusToIdle(input);
|
|
140065
140340
|
if (input.event.type === "session.idle") {
|
|
@@ -140067,7 +140342,7 @@ function createEventHandler2(args) {
|
|
|
140067
140342
|
if (sessionID) {
|
|
140068
140343
|
const now = Date.now();
|
|
140069
140344
|
const emittedAt = recentSyntheticIdles.get(sessionID);
|
|
140070
|
-
if (emittedAt !== undefined && now - emittedAt <
|
|
140345
|
+
if (emittedAt !== undefined && now - emittedAt < DEDUP_WINDOW_MS2) {
|
|
140071
140346
|
recentSyntheticIdles.delete(sessionID);
|
|
140072
140347
|
const lastAnyIdleAt = recentAnyIdles.get(sessionID);
|
|
140073
140348
|
if (lastAnyIdleAt === emittedAt) {
|
|
@@ -140097,7 +140372,7 @@ function createEventHandler2(args) {
|
|
|
140097
140372
|
const sessionID = syntheticIdle.event.properties?.sessionID;
|
|
140098
140373
|
const now = Date.now();
|
|
140099
140374
|
const emittedAt = recentRealIdles.get(sessionID);
|
|
140100
|
-
if (emittedAt !== undefined && now - emittedAt <
|
|
140375
|
+
if (emittedAt !== undefined && now - emittedAt < DEDUP_WINDOW_MS2) {
|
|
140101
140376
|
recentRealIdles.delete(sessionID);
|
|
140102
140377
|
return;
|
|
140103
140378
|
}
|