botmux 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/backend/sandbox.d.ts.map +1 -1
- package/dist/adapters/backend/sandbox.js +1 -3
- package/dist/adapters/backend/sandbox.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +11 -1
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +143 -86
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/session-marker.d.ts.map +1 -1
- package/dist/core/session-marker.js +5 -2
- package/dist/core/session-marker.js.map +1 -1
- package/dist/core/types.d.ts +48 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +55 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +10 -8
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +90 -9
- package/dist/daemon.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +1 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +1 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +2 -0
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +219 -139
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/child-env.d.ts +8 -0
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +19 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/worker.js +10 -1
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -25,7 +25,7 @@ import { claimPairing } from '../services/pairing-store.js';
|
|
|
25
25
|
import { logger } from '../utils/logger.js';
|
|
26
26
|
import { scheduleTimeZone } from '../utils/timezone.js';
|
|
27
27
|
import { killWorker, suspendWorker, forkWorker, forkAdoptWorker, getCurrentCliVersion, postFreshStreamingCard, postPrivateSnapshotCard, resolvePrivateCardAudience, deliverEphemeralOrReply, deliverWritableTerminalCardTo } from './worker-pool.js';
|
|
28
|
-
import { expandHome, getSessionWorkingDir, getProjectScanDirs, rememberLastCliInput } from './session-manager.js';
|
|
28
|
+
import { expandHome, getSessionWorkingDir, getProjectScanDirs, rememberLastCliInput, buildNewTopicCliInput, ensureSessionWhiteboard, getAvailableBots, } from './session-manager.js';
|
|
29
29
|
import { discoverSlashCommandsForAdapter, listMcpServerNames, supportsFilesystemCommandDiscovery } from './command-discovery.js';
|
|
30
30
|
import { validateWorkingDir } from './working-dir.js';
|
|
31
31
|
import { repinSessionWorkingDir } from './session-cwd.js';
|
|
@@ -52,7 +52,7 @@ import { invalidWorkingDirs } from '../utils/working-dir.js';
|
|
|
52
52
|
import { writeRoleFile, deleteRoleFile, resolveRole, resolveRoleFile, resolveTeamRoleFile, writeTeamRoleFile, deleteTeamRoleFile, MAX_ROLE_BYTES } from './role-resolver.js';
|
|
53
53
|
import { getBotCapability, setBotCapability, clearBotCapability } from '../services/bot-profile-store.js';
|
|
54
54
|
import { deleteRoleProfileEntry, deleteRoleProfileIfEmpty, isValidRoleProfileId, listRoleProfileEntries, listRoleProfiles, MAX_ROLE_PROFILE_ENTRY_BYTES, readRoleProfileEntry, writeRoleProfileEntry, } from '../services/role-profile-store.js';
|
|
55
|
-
import { sessionKey, sessionAnchorId } from './types.js';
|
|
55
|
+
import { sessionKey, sessionAnchorId, markRepoCardConsumed, claimCurrentRepoCard } from './types.js';
|
|
56
56
|
import { t, localeForBot } from '../i18n/index.js';
|
|
57
57
|
import { runSkillsImCommand } from './skills/im-command.js';
|
|
58
58
|
import { fetchDaemonIpc } from './daemon-ipc-auth.js';
|
|
@@ -1381,29 +1381,43 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1381
1381
|
// CLI in whatever workingDir is currently set on the session. Shared by
|
|
1382
1382
|
// `commitRepoSelection` (a repo was named) and the bare-`/repo` launch
|
|
1383
1383
|
// (use the default workingDir) — both only run while `pendingRepo`.
|
|
1384
|
-
const forkPendingCli = async (replyText) => {
|
|
1384
|
+
const forkPendingCli = async (replyText, claimAlready = false) => {
|
|
1385
|
+
if (!claimAlready) {
|
|
1386
|
+
if (ds.pendingRepoCommitInFlight) {
|
|
1387
|
+
await sessionReply(rootId, t('cmd.repo.worktree_in_progress', undefined, loc));
|
|
1388
|
+
return false;
|
|
1389
|
+
}
|
|
1390
|
+
ds.pendingRepoCommitInFlight = true;
|
|
1391
|
+
}
|
|
1385
1392
|
const selfBot = getBot(ds.larkAppId);
|
|
1386
1393
|
const botCfg = selfBot.config;
|
|
1387
1394
|
const commitGenSessionId = ds.session.sessionId;
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1395
|
+
let committed = false;
|
|
1396
|
+
try {
|
|
1397
|
+
// Keep pendingRepo=true while prompt context is awaited. Incoming
|
|
1398
|
+
// messages stay in the same buffer and card selections see the
|
|
1399
|
+
// shared in-flight claim instead of treating this as a repo switch.
|
|
1400
|
+
const initiallyBuffered = (ds.pendingPrompt?.trim().length ?? 0) > 0 ||
|
|
1401
|
+
(ds.pendingAttachments?.length ?? 0) > 0 ||
|
|
1402
|
+
(ds.pendingFollowUps?.length ?? 0) > 0;
|
|
1403
|
+
const availableBots = initiallyBuffered
|
|
1404
|
+
? await getAvailableBots(ds.larkAppId, ds.chatId)
|
|
1405
|
+
: [];
|
|
1406
|
+
const stillActive = activeSessions.get(sessionKey(rootId, larkAppId)) === ds;
|
|
1407
|
+
if (!stillActive || ds.session.sessionId !== commitGenSessionId ||
|
|
1408
|
+
!ds.pendingRepo || (ds.worker && !ds.worker.killed)) {
|
|
1409
|
+
logger.warn(`[${logTag}] Session changed while preparing the pending CLI (${commitGenSessionId} → ${ds.session.sessionId}, active=${stillActive}, pending=${!!ds.pendingRepo}, worker=${!!ds.worker})`);
|
|
1410
|
+
return false;
|
|
1411
|
+
}
|
|
1412
|
+
const pendingPrompt = ds.pendingPrompt ?? '';
|
|
1413
|
+
const pendingRawInput = ds.pendingRawInput;
|
|
1414
|
+
const hasBufferedInput = pendingPrompt.trim().length > 0 ||
|
|
1415
|
+
(ds.pendingAttachments?.length ?? 0) > 0 ||
|
|
1416
|
+
(ds.pendingFollowUps?.length ?? 0) > 0;
|
|
1417
|
+
if (hasBufferedInput)
|
|
1405
1418
|
ensureSessionWhiteboard(ds);
|
|
1406
|
-
|
|
1419
|
+
const wrappedInput = hasBufferedInput
|
|
1420
|
+
? buildNewTopicCliInput(pendingPrompt, ds.session.sessionId, ds.session.cliId ?? botCfg.cliId, ds.session.cliPathOverride ?? botCfg.cliPathOverride, ds.pendingAttachments, ds.pendingMentions, availableBots, ds.pendingFollowUps, { name: selfBot.botName, openId: selfBot.botOpenId }, loc, ds.pendingSender, {
|
|
1407
1421
|
larkAppId,
|
|
1408
1422
|
chatId: ds.chatId,
|
|
1409
1423
|
whiteboardId: ds.session.whiteboardId,
|
|
@@ -1413,64 +1427,82 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1413
1427
|
codexAppMessageContext: ds.pendingCodexAppMessageContext,
|
|
1414
1428
|
codexAppFollowUps: ds.pendingCodexAppFollowUps,
|
|
1415
1429
|
codexAppFollowUpContexts: ds.pendingCodexAppFollowUpContexts,
|
|
1416
|
-
})
|
|
1430
|
+
})
|
|
1431
|
+
: { content: '' };
|
|
1432
|
+
const pendingTurnId = ds.pendingTurnId;
|
|
1433
|
+
const previousPendingFollowUpInput = ds.pendingFollowUpInput;
|
|
1434
|
+
if (pendingRawInput && hasBufferedInput) {
|
|
1417
1435
|
ds.pendingFollowUpInput = {
|
|
1418
1436
|
userPrompt: ds.pendingCodexAppText !== undefined || ds.pendingCodexAppFollowUps
|
|
1419
1437
|
? [ds.pendingCodexAppText ?? '', ...(ds.pendingCodexAppFollowUps ?? [])].filter(Boolean).join('\n\n')
|
|
1420
1438
|
: pendingPrompt || ds.pendingFollowUps?.join('\n\n') || '',
|
|
1421
|
-
cliInput:
|
|
1422
|
-
...(
|
|
1423
|
-
|
|
1439
|
+
cliInput: wrappedInput.content,
|
|
1440
|
+
...(ds.pendingFollowUpTurnId ? { turnId: ds.pendingFollowUpTurnId } : {}),
|
|
1441
|
+
...((ds.session.cliId ?? botCfg.cliId) === 'codex-app' && botCfg.codexAppCleanInput === true && wrappedInput.codexAppInput
|
|
1442
|
+
? { codexAppInput: wrappedInput.codexAppInput }
|
|
1424
1443
|
: {}),
|
|
1425
1444
|
codexAppInputGateFrozen: true,
|
|
1426
1445
|
};
|
|
1427
1446
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1447
|
+
ds.pendingRepo = false;
|
|
1448
|
+
publishAttentionPatch(ds);
|
|
1449
|
+
try {
|
|
1450
|
+
if (pendingRawInput)
|
|
1451
|
+
forkWorker(ds, '', false);
|
|
1452
|
+
else if (pendingTurnId && hasBufferedInput)
|
|
1453
|
+
forkWorker(ds, wrappedInput, { turnId: pendingTurnId });
|
|
1454
|
+
else if (hasBufferedInput)
|
|
1455
|
+
forkWorker(ds, wrappedInput);
|
|
1456
|
+
else
|
|
1457
|
+
forkWorker(ds, '', false);
|
|
1458
|
+
}
|
|
1459
|
+
catch (e) {
|
|
1460
|
+
ds.pendingRepo = true;
|
|
1461
|
+
ds.pendingFollowUpInput = previousPendingFollowUpInput;
|
|
1462
|
+
publishAttentionPatch(ds);
|
|
1463
|
+
throw e;
|
|
1464
|
+
}
|
|
1465
|
+
if (pendingRawInput || hasBufferedInput) {
|
|
1466
|
+
rememberLastCliInput(ds, pendingRawInput ?? pendingPrompt, pendingRawInput ?? wrappedInput);
|
|
1467
|
+
}
|
|
1468
|
+
ds.pendingPrompt = undefined;
|
|
1469
|
+
ds.pendingCodexAppText = undefined;
|
|
1470
|
+
ds.pendingCodexAppApplicationContext = undefined;
|
|
1471
|
+
ds.pendingCodexAppMessageContext = undefined;
|
|
1472
|
+
ds.pendingAttachments = undefined;
|
|
1473
|
+
ds.pendingMentions = undefined;
|
|
1474
|
+
ds.pendingSubstituteTrigger = undefined;
|
|
1475
|
+
ds.pendingSender = undefined;
|
|
1476
|
+
ds.pendingFollowUps = undefined;
|
|
1477
|
+
ds.pendingCodexAppFollowUps = undefined;
|
|
1478
|
+
ds.pendingCodexAppFollowUpContexts = undefined;
|
|
1479
|
+
ds.pendingFollowUpTurnId = undefined;
|
|
1480
|
+
ds.pendingTurnId = undefined;
|
|
1481
|
+
committed = true;
|
|
1482
|
+
// Local one-shot consume BEFORE network awaits. Feishu withdraw is
|
|
1483
|
+
// best-effort; stale card clicks must be rejected via this mark even
|
|
1484
|
+
// if sessionReply throws or deleteMessage lags/fails.
|
|
1485
|
+
const cardToWithdraw = ds.repoCardMessageId;
|
|
1486
|
+
markRepoCardConsumed(ds, cardToWithdraw);
|
|
1487
|
+
ds.repoCardMessageId = undefined;
|
|
1488
|
+
// Hold the claim through confirmation + best-effort card withdraw.
|
|
1489
|
+
try {
|
|
1490
|
+
await sessionReply(rootId, replyText);
|
|
1491
|
+
}
|
|
1492
|
+
catch (e) {
|
|
1493
|
+
logger.warn(`[${logTag}] Confirm reply after pending repo commit failed: ${e instanceof Error ? e.message : e}`);
|
|
1494
|
+
}
|
|
1495
|
+
if (cardToWithdraw) {
|
|
1496
|
+
try {
|
|
1497
|
+
await deleteMessage(ds.larkAppId, cardToWithdraw);
|
|
1498
|
+
}
|
|
1499
|
+
catch { /* best-effort */ }
|
|
1453
1500
|
}
|
|
1454
|
-
rememberLastCliInput(ds, pendingPrompt, prompt);
|
|
1455
|
-
forkWorker(ds, prompt);
|
|
1456
1501
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
// anything (see worker.ts: the init prompt is only queued when truthy).
|
|
1460
|
-
forkWorker(ds, '', false);
|
|
1502
|
+
finally {
|
|
1503
|
+
ds.pendingRepoCommitInFlight = false;
|
|
1461
1504
|
}
|
|
1462
|
-
|
|
1463
|
-
ds.pendingCodexAppText = undefined;
|
|
1464
|
-
ds.pendingCodexAppApplicationContext = undefined;
|
|
1465
|
-
ds.pendingCodexAppMessageContext = undefined;
|
|
1466
|
-
ds.pendingAttachments = undefined;
|
|
1467
|
-
ds.pendingMentions = undefined;
|
|
1468
|
-
ds.pendingSubstituteTrigger = undefined;
|
|
1469
|
-
ds.pendingSender = undefined;
|
|
1470
|
-
ds.pendingFollowUps = undefined;
|
|
1471
|
-
ds.pendingCodexAppFollowUps = undefined;
|
|
1472
|
-
ds.pendingCodexAppFollowUpContexts = undefined;
|
|
1473
|
-
await sessionReply(rootId, replyText);
|
|
1505
|
+
return committed;
|
|
1474
1506
|
};
|
|
1475
1507
|
// Shared commit path for an already-resolved repo: update the session's
|
|
1476
1508
|
// working dir, then either fork into the pending CLI (first spawn) or
|
|
@@ -1478,13 +1510,27 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1478
1510
|
// numeric `/repo <N>` form and the `/repo <path|name>` form.
|
|
1479
1511
|
const commitRepoSelection = async (selectedPath, displayName, how) => {
|
|
1480
1512
|
if (ds.pendingRepo) {
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
ds.
|
|
1486
|
-
|
|
1487
|
-
|
|
1513
|
+
if (ds.pendingRepoCommitInFlight) {
|
|
1514
|
+
await sessionReply(rootId, t('cmd.repo.worktree_in_progress', undefined, loc));
|
|
1515
|
+
return false;
|
|
1516
|
+
}
|
|
1517
|
+
ds.pendingRepoCommitInFlight = true;
|
|
1518
|
+
try {
|
|
1519
|
+
// Claim before changing cwd so a card selection cannot overwrite
|
|
1520
|
+
// this choice while the pending prompt is prepared.
|
|
1521
|
+
ds.workingDir = selectedPath;
|
|
1522
|
+
ds.session.workingDir = selectedPath;
|
|
1523
|
+
ds.session.riffRepoDirs = undefined;
|
|
1524
|
+
sessionStore.updateSession(ds.session);
|
|
1525
|
+
// forkPendingCli owns claim release + confirm reply + card withdraw.
|
|
1526
|
+
if (!await forkPendingCli(t('cmd.repo.selected_in_pending', { name: displayName }, loc), true)) {
|
|
1527
|
+
return false;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
catch (e) {
|
|
1531
|
+
ds.pendingRepoCommitInFlight = false;
|
|
1532
|
+
throw e;
|
|
1533
|
+
}
|
|
1488
1534
|
}
|
|
1489
1535
|
else {
|
|
1490
1536
|
// Safety net: a mid-session `/repo` switch closes the running
|
|
@@ -1498,11 +1544,15 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1498
1544
|
// anchor — expected; `/close` the new one first, or use the
|
|
1499
1545
|
// command.) Mirrors the `/close` case above.
|
|
1500
1546
|
//
|
|
1547
|
+
// Claim any open repo card BEFORE killWorker / await so a concurrent
|
|
1548
|
+
// card click cannot double-switch while this text path runs.
|
|
1549
|
+
//
|
|
1501
1550
|
// The new cwd is NOT written onto the old session here — it would
|
|
1502
1551
|
// pollute the displaced session's stored workingDir (and the closed
|
|
1503
1552
|
// card), so `claude --resume` later would reopen the old context in
|
|
1504
1553
|
// the new repo's cwd. The new repo is pinned onto the fresh session
|
|
1505
1554
|
// below instead.
|
|
1555
|
+
const claimedCard = claimCurrentRepoCard(ds, undefined);
|
|
1506
1556
|
const closedCard = buildClosedSessionCard(ds, loc);
|
|
1507
1557
|
killWorker(ds);
|
|
1508
1558
|
sessionStore.closeSession(ds.session.sessionId);
|
|
@@ -1522,13 +1572,21 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1522
1572
|
sessionStore.updateSession(ds.session);
|
|
1523
1573
|
ds.hasHistory = false;
|
|
1524
1574
|
forkWorker(ds, '', false);
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1575
|
+
try {
|
|
1576
|
+
await sessionReply(rootId, t('cmd.repo.switched_to', { name: displayName }, loc));
|
|
1577
|
+
}
|
|
1578
|
+
catch (e) {
|
|
1579
|
+
logger.warn(`[${logTag}] Confirm reply after mid-session repo switch failed: ${e instanceof Error ? e.message : e}`);
|
|
1580
|
+
}
|
|
1581
|
+
if (claimedCard) {
|
|
1582
|
+
try {
|
|
1583
|
+
await deleteMessage(ds.larkAppId, claimedCard);
|
|
1584
|
+
}
|
|
1585
|
+
catch { /* best-effort */ }
|
|
1586
|
+
}
|
|
1530
1587
|
}
|
|
1531
1588
|
logger.info(`[${logTag}] Repo selected via ${how}: ${selectedPath}`);
|
|
1589
|
+
return true;
|
|
1532
1590
|
};
|
|
1533
1591
|
// `/repo wt <N|name|path> [branch]` → create a worktree off the repo's
|
|
1534
1592
|
// remote default branch and open THAT as the session repo. Without a
|
|
@@ -1563,7 +1621,7 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1563
1621
|
}
|
|
1564
1622
|
repoPath = resolved.path;
|
|
1565
1623
|
}
|
|
1566
|
-
if (ds.worktreeCreating) {
|
|
1624
|
+
if (ds.worktreeCreating || ds.pendingRepoCommitInFlight) {
|
|
1567
1625
|
await sessionReply(rootId, t('cmd.repo.worktree_in_progress', undefined, loc));
|
|
1568
1626
|
break;
|
|
1569
1627
|
}
|
|
@@ -1644,7 +1702,7 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1644
1702
|
// would double-fork. One lock gates both kinds until the commit
|
|
1645
1703
|
// settles. (Bare `/repo` without pending only posts the picker card —
|
|
1646
1704
|
// harmless, so it stays open.)
|
|
1647
|
-
if (ds?.worktreeCreating && (repoArg || ds
|
|
1705
|
+
if ((ds?.worktreeCreating || ds?.pendingRepoCommitInFlight) && (repoArg || ds?.pendingRepo)) {
|
|
1648
1706
|
await sessionReply(rootId, t('cmd.repo.worktree_in_progress', undefined, loc));
|
|
1649
1707
|
break;
|
|
1650
1708
|
}
|
|
@@ -1690,11 +1748,10 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1690
1748
|
break;
|
|
1691
1749
|
}
|
|
1692
1750
|
const cwd = getSessionWorkingDir(ds);
|
|
1751
|
+
// bare /repo is the text twin of skip_repo: launch in the default
|
|
1752
|
+
// cwd without pinning it (forkPendingCli does not write workingDir).
|
|
1753
|
+
// Confirmation + card withdraw run under the claim inside forkPendingCli.
|
|
1693
1754
|
await forkPendingCli(t('cmd.skip.opened', { cwd }, loc));
|
|
1694
|
-
if (ds.repoCardMessageId) {
|
|
1695
|
-
deleteMessage(ds.larkAppId, ds.repoCardMessageId);
|
|
1696
|
-
ds.repoCardMessageId = undefined;
|
|
1697
|
-
}
|
|
1698
1755
|
logger.info(`[${logTag}] Bare /repo while pending → launch in workingDir ${cwd}`);
|
|
1699
1756
|
break;
|
|
1700
1757
|
}
|