@owloops/browserbird 1.4.2 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +63 -12
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -122,8 +122,8 @@ function unknownSubcommand(subcommand, command, validCommands) {
|
|
|
122
122
|
/** @fileoverview ASCII banner displayed on daemon startup and in help text. */
|
|
123
123
|
const pkg = createRequire(import.meta.url)("../package.json");
|
|
124
124
|
const buildInfo = [];
|
|
125
|
-
buildInfo.push(`commit: ${"
|
|
126
|
-
buildInfo.push(`built: 2026-03-
|
|
125
|
+
buildInfo.push(`commit: ${"084f5f5f8a7edbc9bc8cd7fd189abda396b27f99".substring(0, 7)}`);
|
|
126
|
+
buildInfo.push(`built: 2026-03-11T22:42:24+04:00`);
|
|
127
127
|
const buildString = buildInfo.length > 0 ? ` (${buildInfo.join(", ")})` : "";
|
|
128
128
|
const VERSION = `browserbird ${pkg.version}${buildString}`;
|
|
129
129
|
const BIRD = [
|
|
@@ -2764,6 +2764,18 @@ function sessionTimeoutBlocks(timeoutMs, opts) {
|
|
|
2764
2764
|
});
|
|
2765
2765
|
return blocks;
|
|
2766
2766
|
}
|
|
2767
|
+
function sessionStopBlocks(sessionKey) {
|
|
2768
|
+
return [{
|
|
2769
|
+
type: "actions",
|
|
2770
|
+
elements: [{
|
|
2771
|
+
type: "button",
|
|
2772
|
+
text: plain("Stop Session"),
|
|
2773
|
+
action_id: "session_stop",
|
|
2774
|
+
value: sessionKey,
|
|
2775
|
+
style: "danger"
|
|
2776
|
+
}]
|
|
2777
|
+
}];
|
|
2778
|
+
}
|
|
2767
2779
|
function busyBlocks(activeCount, maxConcurrent) {
|
|
2768
2780
|
return [section("*Too many active sessions*"), context(`${activeCount}/${maxConcurrent} slots in use. Try again shortly.`)];
|
|
2769
2781
|
}
|
|
@@ -3166,11 +3178,36 @@ function createHandler(client, getConfig, signal, getTeamId) {
|
|
|
3166
3178
|
teamId,
|
|
3167
3179
|
userId
|
|
3168
3180
|
});
|
|
3181
|
+
let streamDead = false;
|
|
3169
3182
|
let fullText = "";
|
|
3170
3183
|
let completion;
|
|
3171
3184
|
let hasError = false;
|
|
3172
3185
|
let timedOut = false;
|
|
3173
3186
|
let timedOutMs = 0;
|
|
3187
|
+
function isStreamExpired(err) {
|
|
3188
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3189
|
+
return msg.includes("not_in_streaming_state") || msg.includes("streaming");
|
|
3190
|
+
}
|
|
3191
|
+
async function safeAppend(content) {
|
|
3192
|
+
if (streamDead) return;
|
|
3193
|
+
try {
|
|
3194
|
+
await streamer.append(content);
|
|
3195
|
+
} catch (err) {
|
|
3196
|
+
if (isStreamExpired(err)) {
|
|
3197
|
+
streamDead = true;
|
|
3198
|
+
logger.warn("slack stream expired, falling back to regular messages");
|
|
3199
|
+
} else throw err;
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
async function safeStop(opts) {
|
|
3203
|
+
if (streamDead) return;
|
|
3204
|
+
try {
|
|
3205
|
+
await streamer.stop(opts);
|
|
3206
|
+
} catch (err) {
|
|
3207
|
+
if (isStreamExpired(err)) streamDead = true;
|
|
3208
|
+
else throw err;
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3174
3211
|
for await (const event of events) {
|
|
3175
3212
|
if (signal.aborted) break;
|
|
3176
3213
|
logger.debug(`stream event: ${event.type}`);
|
|
@@ -3181,7 +3218,7 @@ function createHandler(client, getConfig, signal, getTeamId) {
|
|
|
3181
3218
|
case "text_delta": {
|
|
3182
3219
|
const safe = redact(event.delta);
|
|
3183
3220
|
fullText += safe;
|
|
3184
|
-
await
|
|
3221
|
+
await safeAppend({ markdown_text: safe });
|
|
3185
3222
|
break;
|
|
3186
3223
|
}
|
|
3187
3224
|
case "tool_images":
|
|
@@ -3203,7 +3240,7 @@ function createHandler(client, getConfig, signal, getTeamId) {
|
|
|
3203
3240
|
const safeError = redact(event.error);
|
|
3204
3241
|
logger.error(`agent error: ${safeError}`);
|
|
3205
3242
|
insertLog("error", "spawn", safeError, channelId);
|
|
3206
|
-
await
|
|
3243
|
+
await safeAppend({ markdown_text: `\n\nError: ${safeError}` });
|
|
3207
3244
|
break;
|
|
3208
3245
|
}
|
|
3209
3246
|
case "timeout":
|
|
@@ -3214,12 +3251,12 @@ function createHandler(client, getConfig, signal, getTeamId) {
|
|
|
3214
3251
|
}
|
|
3215
3252
|
}
|
|
3216
3253
|
if (timedOut) {
|
|
3217
|
-
await
|
|
3254
|
+
await safeStop({});
|
|
3218
3255
|
const blocks = sessionTimeoutBlocks(timedOutMs, { sessionUid });
|
|
3219
3256
|
await client.postMessage(channelId, threadTs, `Session timed out after ${Math.round(timedOutMs / 6e4)} minutes.`, { blocks });
|
|
3220
3257
|
} else {
|
|
3221
3258
|
const footerBlocks = completion ? completionFooterBlocks(completion, hasError, meta.birdName, userId) : void 0;
|
|
3222
|
-
await
|
|
3259
|
+
await safeStop(footerBlocks ? { blocks: footerBlocks } : {});
|
|
3223
3260
|
}
|
|
3224
3261
|
}
|
|
3225
3262
|
async function uploadImages(images, channelId, threadTs) {
|
|
@@ -3284,6 +3321,7 @@ function createHandler(client, getConfig, signal, getTeamId) {
|
|
|
3284
3321
|
}, signal);
|
|
3285
3322
|
lock.killCurrent = kill;
|
|
3286
3323
|
client.setStatus?.(channelId, threadTs, "is thinking...").catch(() => {});
|
|
3324
|
+
client.postEphemeral(channelId, threadTs, userId, "Session running.", { blocks: sessionStopBlocks(key) }).catch(() => {});
|
|
3287
3325
|
if (isNew) {
|
|
3288
3326
|
const title = prompt.length > 60 ? prompt.slice(0, 57) + "..." : prompt;
|
|
3289
3327
|
client.setTitle?.(channelId, threadTs, title).catch(() => {});
|
|
@@ -3328,6 +3366,13 @@ function createHandler(client, getConfig, signal, getTeamId) {
|
|
|
3328
3366
|
function activeCount() {
|
|
3329
3367
|
return activeSpawns;
|
|
3330
3368
|
}
|
|
3369
|
+
function killSession(key) {
|
|
3370
|
+
const lock = locks.get(key);
|
|
3371
|
+
if (!lock?.killCurrent) return false;
|
|
3372
|
+
lock.killCurrent();
|
|
3373
|
+
lock.queue.length = 0;
|
|
3374
|
+
return true;
|
|
3375
|
+
}
|
|
3331
3376
|
function killAll() {
|
|
3332
3377
|
for (const lock of locks.values()) {
|
|
3333
3378
|
lock.killCurrent?.();
|
|
@@ -3338,7 +3383,8 @@ function createHandler(client, getConfig, signal, getTeamId) {
|
|
|
3338
3383
|
return {
|
|
3339
3384
|
handle,
|
|
3340
3385
|
activeCount,
|
|
3341
|
-
killAll
|
|
3386
|
+
killAll,
|
|
3387
|
+
killSession
|
|
3342
3388
|
};
|
|
3343
3389
|
}
|
|
3344
3390
|
|
|
@@ -3736,14 +3782,19 @@ function createSlackChannel(getConfig, signal) {
|
|
|
3736
3782
|
const user = body["user"]?.["id"];
|
|
3737
3783
|
if (!actionsArr || !channel) return;
|
|
3738
3784
|
for (const action of actionsArr) {
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
const sessionUid =
|
|
3785
|
+
const actionId = action["action_id"];
|
|
3786
|
+
if (actionId === "session_error_overflow" || actionId === "session_retry") {
|
|
3787
|
+
const raw = actionId === "session_error_overflow" ? action["selected_option"]?.["value"] : action["value"];
|
|
3788
|
+
if (!raw?.startsWith("retry:")) continue;
|
|
3789
|
+
const sessionUid = raw.slice(6);
|
|
3744
3790
|
if (!sessionUid) continue;
|
|
3745
3791
|
await handleSessionRetry(sessionUid, channel, user ?? "unknown", getConfig(), handler);
|
|
3746
3792
|
}
|
|
3793
|
+
if (actionId === "session_stop") {
|
|
3794
|
+
const sessionKey = action["value"];
|
|
3795
|
+
if (!sessionKey) continue;
|
|
3796
|
+
if (handler.killSession(sessionKey)) logger.info(`session stopped by ${user}: ${sessionKey}`);
|
|
3797
|
+
}
|
|
3747
3798
|
}
|
|
3748
3799
|
}
|
|
3749
3800
|
});
|