botmux 2.110.0 → 2.111.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/adapters/backend/tmux-backend.d.ts +25 -5
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +59 -93
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +5 -0
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/zellij-backend.d.ts +30 -0
- package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zellij-backend.js +80 -13
- package/dist/adapters/backend/zellij-backend.js.map +1 -1
- package/dist/adapters/backend/zellij-socket-probe.d.ts +18 -0
- package/dist/adapters/backend/zellij-socket-probe.d.ts.map +1 -0
- package/dist/adapters/backend/zellij-socket-probe.js +146 -0
- package/dist/adapters/backend/zellij-socket-probe.js.map +1 -0
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +5 -0
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/codex-app-turn.d.ts +43 -0
- package/dist/adapters/cli/codex-app-turn.d.ts.map +1 -0
- package/dist/adapters/cli/codex-app-turn.js +110 -0
- package/dist/adapters/cli/codex-app-turn.js.map +1 -0
- package/dist/adapters/cli/codex-app.d.ts.map +1 -1
- package/dist/adapters/cli/codex-app.js +6 -0
- package/dist/adapters/cli/codex-app.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +1 -0
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/runner-input.d.ts +3 -2
- package/dist/adapters/cli/runner-input.d.ts.map +1 -1
- package/dist/adapters/cli/runner-input.js +7 -4
- package/dist/adapters/cli/runner-input.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +16 -0
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +5 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +1 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/codex-app-runner.js +75 -12
- package/dist/codex-app-runner.js.map +1 -1
- package/dist/core/command-handler.d.ts +11 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +101 -7
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +69 -11
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/doc-comment-prompt.d.ts +57 -0
- package/dist/core/doc-comment-prompt.d.ts.map +1 -1
- package/dist/core/doc-comment-prompt.js +147 -2
- package/dist/core/doc-comment-prompt.js.map +1 -1
- package/dist/core/inflight-input-tracker.d.ts +2 -0
- package/dist/core/inflight-input-tracker.d.ts.map +1 -1
- package/dist/core/inflight-input-tracker.js.map +1 -1
- package/dist/core/passthrough-commands.d.ts.map +1 -1
- package/dist/core/passthrough-commands.js +1 -1
- package/dist/core/passthrough-commands.js.map +1 -1
- package/dist/core/persistent-backend.d.ts +4 -0
- package/dist/core/persistent-backend.d.ts.map +1 -1
- package/dist/core/persistent-backend.js +6 -0
- package/dist/core/persistent-backend.js.map +1 -1
- package/dist/core/plugins/cli-generation.d.ts +3 -0
- package/dist/core/plugins/cli-generation.d.ts.map +1 -1
- package/dist/core/plugins/cli-generation.js +1 -0
- package/dist/core/plugins/cli-generation.js.map +1 -1
- package/dist/core/session-board.d.ts +2 -1
- package/dist/core/session-board.d.ts.map +1 -1
- package/dist/core/session-board.js +3 -2
- package/dist/core/session-board.js.map +1 -1
- package/dist/core/session-create.d.ts +38 -0
- package/dist/core/session-create.d.ts.map +1 -1
- package/dist/core/session-create.js +43 -9
- package/dist/core/session-create.js.map +1 -1
- package/dist/core/session-manager.d.ts +65 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +285 -68
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-rename.d.ts +25 -0
- package/dist/core/session-rename.d.ts.map +1 -0
- package/dist/core/session-rename.js +43 -0
- package/dist/core/session-rename.js.map +1 -0
- package/dist/core/session-title.d.ts +11 -0
- package/dist/core/session-title.d.ts.map +1 -0
- package/dist/core/session-title.js +17 -0
- package/dist/core/session-title.js.map +1 -0
- package/dist/core/trigger-session.d.ts +11 -0
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +61 -20
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +16 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +6 -2
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +48 -15
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/core/zellij-adopt-discovery.d.ts +17 -0
- package/dist/core/zellij-adopt-discovery.d.ts.map +1 -1
- package/dist/core/zellij-adopt-discovery.js +24 -4
- package/dist/core/zellij-adopt-discovery.js.map +1 -1
- package/dist/core/zellij-session-discovery.d.ts +3 -0
- package/dist/core/zellij-session-discovery.d.ts.map +1 -1
- package/dist/core/zellij-session-discovery.js +1 -0
- package/dist/core/zellij-session-discovery.js.map +1 -1
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +181 -81
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +1 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +1 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/federated-group-core.d.ts +29 -8
- package/dist/dashboard/federated-group-core.d.ts.map +1 -1
- package/dist/dashboard/federated-group-core.js +68 -6
- package/dist/dashboard/federated-group-core.js.map +1 -1
- package/dist/dashboard/federation-api.d.ts +10 -9
- package/dist/dashboard/federation-api.d.ts.map +1 -1
- package/dist/dashboard/federation-api.js +82 -3
- package/dist/dashboard/federation-api.js.map +1 -1
- package/dist/dashboard/federation-spoke-api.d.ts +8 -9
- package/dist/dashboard/federation-spoke-api.d.ts.map +1 -1
- package/dist/dashboard/federation-spoke-api.js +7 -2
- package/dist/dashboard/federation-spoke-api.js.map +1 -1
- package/dist/dashboard/team-group.d.ts +21 -0
- package/dist/dashboard/team-group.d.ts.map +1 -1
- package/dist/dashboard/team-group.js +16 -0
- package/dist/dashboard/team-group.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +10 -0
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +33 -1
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +1 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +8 -0
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +5 -1
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/ui.d.ts.map +1 -1
- package/dist/dashboard/web/ui.js +2 -4
- package/dist/dashboard/web/ui.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-7T7DPOV6.js +4 -0
- package/dist/dashboard-web/chunks/{chunk-SHZ5PUYF.js → chunk-IGXYYRYJ.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-GF7DU2B5.js → chunk-ISOD7VDX.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-TORH53FI.js → chunk-OEJIT66L.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-7UY333A5.js → chunk-PNK3HQBI.js} +2 -2
- package/dist/dashboard-web/chunks/{chunk-IAES47WV.js → chunk-YQFXGRSD.js} +1 -1
- package/dist/dashboard-web/chunks/{connectors-page-N6GKR6JL.js → connectors-page-UQ4QBE45.js} +1 -1
- package/dist/dashboard-web/chunks/{groups-page-IK3GXSQU.js → groups-page-5AJPPJH2.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-T2PDFPGO.js → insights-page-R3DFECX4.js} +1 -1
- package/dist/dashboard-web/chunks/{legacy-workflow-page-YQPLKH74.js → legacy-workflow-page-QMC7OZXH.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-7EL3LVJS.js → monitor-room-NLMKJLDY.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-BCADF56L.js → monitoring-page-EHSOX4GV.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-KP7BDLAE.js → office-page-OXJKFJ4R.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-6GWDHDTI.js → overview-page-GJHIVXJF.js} +1 -1
- package/dist/dashboard-web/chunks/{roles-page-2OFR2IBG.js → roles-page-6ZODQISW.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-TAWMTSNL.js → schedules-page-DAGMHVCH.js} +1 -1
- package/dist/dashboard-web/chunks/{sessions-page-42QZHPPP.js → sessions-page-NNZIDHZM.js} +1 -1
- package/dist/dashboard-web/chunks/{settings-page-OVKXUXSV.js → settings-page-LJIXX5LQ.js} +1 -1
- package/dist/dashboard-web/chunks/{skills-page-Y2EC3NBI.js → skills-page-J7EVUHXP.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-QYMJUH5F.js → team-federation-page-YM5JBIII.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-FZJCW5QG.js → v3-page-YLYVHGKD.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-FBHBBCAL.js → whiteboards-page-75N2U6W4.js} +1 -1
- package/dist/dashboard.js +46 -7
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +8 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +8 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +96 -32
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +16 -5
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/services/bot-config-store.d.ts.map +1 -1
- package/dist/services/bot-config-store.js +1 -0
- package/dist/services/bot-config-store.js.map +1 -1
- package/dist/services/card-prefs-store.d.ts +4 -0
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +8 -0
- package/dist/services/card-prefs-store.js.map +1 -1
- package/dist/services/default-worktree.d.ts.map +1 -1
- package/dist/services/default-worktree.js +4 -1
- package/dist/services/default-worktree.js.map +1 -1
- package/dist/services/group-creator.d.ts +19 -0
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +62 -28
- package/dist/services/group-creator.js.map +1 -1
- package/dist/services/groups-store.d.ts +7 -5
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +10 -8
- package/dist/services/groups-store.js.map +1 -1
- package/dist/setup/ensure-tmux.d.ts +17 -0
- package/dist/setup/ensure-tmux.d.ts.map +1 -1
- package/dist/setup/ensure-tmux.js +72 -1
- package/dist/setup/ensure-tmux.js.map +1 -1
- package/dist/types.d.ts +56 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/child-env.d.ts +27 -0
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +59 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/utils/codex-app-context.d.ts +9 -0
- package/dist/utils/codex-app-context.d.ts.map +1 -0
- package/dist/utils/codex-app-context.js +47 -0
- package/dist/utils/codex-app-context.js.map +1 -0
- package/dist/utils/pending-input-queue.d.ts +2 -0
- package/dist/utils/pending-input-queue.d.ts.map +1 -1
- package/dist/utils/pending-input-queue.js +6 -0
- package/dist/utils/pending-input-queue.js.map +1 -1
- package/dist/worker.js +197 -38
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-QXZ2VFJJ.js +0 -4
|
@@ -11,7 +11,7 @@ import * as sessionStore from '../services/session-store.js';
|
|
|
11
11
|
import * as messageQueue from '../services/message-queue.js';
|
|
12
12
|
import { downloadMessageResource, listChatBotMembers, UserTokenMissingError } from '../im/lark/client.js';
|
|
13
13
|
import { logger } from '../utils/logger.js';
|
|
14
|
-
import { forkWorker, forkAdoptWorker, killStalePids, getCurrentCliVersion, restoreUsageLimitRuntimeState, setActiveSessionSafe, isRelayableRealSession, closeSession, getActiveSessionsRegistry, suspendWorker } from './worker-pool.js';
|
|
14
|
+
import { forkWorker, sendWorkerInput, forkAdoptWorker, killStalePids, getCurrentCliVersion, restoreUsageLimitRuntimeState, setActiveSessionSafe, isRelayableRealSession, closeSession, getActiveSessionsRegistry, suspendWorker } from './worker-pool.js';
|
|
15
15
|
import { createCliAdapterSync } from '../adapters/cli/registry.js';
|
|
16
16
|
import { buildBotmuxShellHints } from '../adapters/cli/shared-hints.js';
|
|
17
17
|
import { assertSafeAppId } from '../adapters/cli/read-isolation.js';
|
|
@@ -21,8 +21,9 @@ import { adoptTargetLabel, validateAdoptTargetState } from './session-discovery.
|
|
|
21
21
|
import { getBot, getAllBots, getOwnerOpenId, findOncallChat, effectiveDefaultWorkingDir } from '../bot-registry.js';
|
|
22
22
|
import { dashboardEventBus } from './dashboard-events.js';
|
|
23
23
|
import { composeRowFromActive } from './dashboard-rows.js';
|
|
24
|
-
import { composeSpawnUserContent, deriveSessionTitleFromContent, } from './session-create.js';
|
|
24
|
+
import { composeSpawnCodexAppContext, composeSpawnUserContent, deriveSessionTitleFromContent, } from './session-create.js';
|
|
25
25
|
import { validateZellijAdoptTarget } from './zellij-adopt-discovery.js';
|
|
26
|
+
import { addCodexAppContext } from '../utils/codex-app-context.js';
|
|
26
27
|
import { sessionKey, sessionAnchorId } from './types.js';
|
|
27
28
|
import { announceSessionRow, markSessionActivity, announcePendingRepoSession } from './session-activity.js';
|
|
28
29
|
import { scanMultipleProjects } from '../services/project-scanner.js';
|
|
@@ -352,30 +353,89 @@ export function renderBufferedSenderBlock(sender, cliId, locale) {
|
|
|
352
353
|
const note = renderCursorSenderNote(cliId, true, locale);
|
|
353
354
|
return note ? `${tag}\n${note}` : tag;
|
|
354
355
|
}
|
|
356
|
+
function substituteInstruction(disclosure) {
|
|
357
|
+
return disclosure === 'none'
|
|
358
|
+
? 'This turn was triggered by a configured substitute target mention. Answer on behalf of that target when appropriate.'
|
|
359
|
+
: 'This turn was triggered by a configured substitute target mention. Answer on behalf of that target and clearly disclose that you are answering for them.';
|
|
360
|
+
}
|
|
361
|
+
function renderSubstituteIdentity(tag, identity) {
|
|
362
|
+
if (!identity)
|
|
363
|
+
return '';
|
|
364
|
+
const attrs = [];
|
|
365
|
+
if (identity.name)
|
|
366
|
+
attrs.push(`name="${xmlEscape(identity.name)}"`);
|
|
367
|
+
if (identity.openId)
|
|
368
|
+
attrs.push(`open_id="${xmlEscape(identity.openId)}"`);
|
|
369
|
+
if (identity.userId)
|
|
370
|
+
attrs.push(`user_id="${xmlEscape(identity.userId)}"`);
|
|
371
|
+
if (identity.unionId)
|
|
372
|
+
attrs.push(`union_id="${xmlEscape(identity.unionId)}"`);
|
|
373
|
+
return attrs.length > 0 ? `<${tag} ${attrs.join(' ')} />` : `<${tag} />`;
|
|
374
|
+
}
|
|
375
|
+
/** Preserve the pre-clean-input legacy schema exactly: one effective target,
|
|
376
|
+
* with configured fields taking precedence and event fields only filling
|
|
377
|
+
* missing values. The structured Codex App sidecar keeps both sources below. */
|
|
378
|
+
function renderLegacySubstituteTarget(trigger) {
|
|
379
|
+
const observed = trigger.observedMention;
|
|
380
|
+
const target = {
|
|
381
|
+
name: trigger.target.name ?? observed?.name,
|
|
382
|
+
openId: trigger.target.openId ?? observed?.openId,
|
|
383
|
+
userId: trigger.target.userId ?? observed?.userId,
|
|
384
|
+
unionId: trigger.target.unionId ?? observed?.unionId,
|
|
385
|
+
};
|
|
386
|
+
const attrs = [];
|
|
387
|
+
if (target.name)
|
|
388
|
+
attrs.push(`name="${xmlEscape(target.name)}"`);
|
|
389
|
+
if (target.openId)
|
|
390
|
+
attrs.push(`open_id="${xmlEscape(target.openId)}"`);
|
|
391
|
+
if (target.userId)
|
|
392
|
+
attrs.push(`user_id="${xmlEscape(target.userId)}"`);
|
|
393
|
+
if (target.unionId)
|
|
394
|
+
attrs.push(`union_id="${xmlEscape(target.unionId)}"`);
|
|
395
|
+
return `<target ${attrs.join(' ')} />`;
|
|
396
|
+
}
|
|
397
|
+
/** Legacy prompt envelope. This whole string remains user-role input for the
|
|
398
|
+
* terminal CLIs; Codex App uses the two trust-separated renderers below. */
|
|
355
399
|
function renderSubstituteTrigger(trigger) {
|
|
356
400
|
if (!trigger)
|
|
357
401
|
return '';
|
|
358
|
-
const attrs = [];
|
|
359
|
-
if (trigger.target.name)
|
|
360
|
-
attrs.push(`name="${xmlEscape(trigger.target.name)}"`);
|
|
361
|
-
if (trigger.target.openId)
|
|
362
|
-
attrs.push(`open_id="${xmlEscape(trigger.target.openId)}"`);
|
|
363
|
-
if (trigger.target.userId)
|
|
364
|
-
attrs.push(`user_id="${xmlEscape(trigger.target.userId)}"`);
|
|
365
|
-
if (trigger.target.unionId)
|
|
366
|
-
attrs.push(`union_id="${xmlEscape(trigger.target.unionId)}"`);
|
|
367
402
|
const disclosure = trigger.disclosure ?? 'prefix';
|
|
368
|
-
const instruction = disclosure === 'none'
|
|
369
|
-
? 'This turn was triggered by a configured substitute target mention. Answer on behalf of that target when appropriate.'
|
|
370
|
-
: 'This turn was triggered by a configured substitute target mention. Answer on behalf of that target and clearly disclose that you are answering for them.';
|
|
371
403
|
return [
|
|
372
404
|
'<substitute_trigger>',
|
|
373
|
-
`
|
|
405
|
+
` ${renderLegacySubstituteTarget(trigger)}`,
|
|
374
406
|
` <disclosure>${xmlEscape(disclosure)}</disclosure>`,
|
|
375
|
-
` <instruction>${xmlEscape(
|
|
407
|
+
` <instruction>${xmlEscape(substituteInstruction(disclosure))}</instruction>`,
|
|
376
408
|
'</substitute_trigger>',
|
|
377
409
|
].join('\n');
|
|
378
410
|
}
|
|
411
|
+
/** Botmux-owned policy only. No configured profile or event field may enter
|
|
412
|
+
* this block because Codex App promotes it to developer-role context. */
|
|
413
|
+
function renderSubstitutePolicy(trigger) {
|
|
414
|
+
if (!trigger)
|
|
415
|
+
return '';
|
|
416
|
+
const disclosure = trigger.disclosure ?? 'prefix';
|
|
417
|
+
return [
|
|
418
|
+
'<substitute_policy>',
|
|
419
|
+
' <match>configured_target_mention</match>',
|
|
420
|
+
` <disclosure>${disclosure}</disclosure>`,
|
|
421
|
+
` <instruction>${substituteInstruction(disclosure)}</instruction>`,
|
|
422
|
+
'</substitute_policy>',
|
|
423
|
+
].join('\n');
|
|
424
|
+
}
|
|
425
|
+
/** All identity metadata is untrusted, regardless of whether it came from a
|
|
426
|
+
* saved Lark profile or the current event. Keep the two sources distinct so a
|
|
427
|
+
* matching user_id cannot make conflicting observed IDs look canonical. */
|
|
428
|
+
function renderSubstituteTarget(trigger) {
|
|
429
|
+
if (!trigger)
|
|
430
|
+
return '';
|
|
431
|
+
const observedMention = renderSubstituteIdentity('observed_mention', trigger.observedMention);
|
|
432
|
+
return [
|
|
433
|
+
'<substitute_target>',
|
|
434
|
+
` ${renderSubstituteIdentity('configured_target', trigger.target)}`,
|
|
435
|
+
...(observedMention ? [` ${observedMention}`] : []),
|
|
436
|
+
'</substitute_target>',
|
|
437
|
+
].join('\n');
|
|
438
|
+
}
|
|
379
439
|
export function formatAttachmentsHint(attachments, locale) {
|
|
380
440
|
if (!attachments || attachments.length === 0)
|
|
381
441
|
return '';
|
|
@@ -471,6 +531,52 @@ function hermesFollowupReminder(locale) {
|
|
|
471
531
|
* topic that never collaborates.
|
|
472
532
|
*/
|
|
473
533
|
const AVAILABLE_BOTS_INLINE_MAX = 3;
|
|
534
|
+
function renderMentionBlock(mentions) {
|
|
535
|
+
if (!mentions || mentions.length === 0)
|
|
536
|
+
return '';
|
|
537
|
+
const items = mentions.map(m => {
|
|
538
|
+
const oid = m.openId ? ` open_id="${xmlEscape(m.openId)}"` : '';
|
|
539
|
+
return ` <mention name="${xmlEscape(m.name)}"${oid} />`;
|
|
540
|
+
});
|
|
541
|
+
return `<mentions>\n${items.join('\n')}\n</mentions>`;
|
|
542
|
+
}
|
|
543
|
+
function renderAvailableBotsBlock(availableBots, mentions, locale) {
|
|
544
|
+
if (!availableBots || availableBots.length === 0)
|
|
545
|
+
return '';
|
|
546
|
+
const mentionedOpenIds = new Set(mentions?.map(m => m.openId).filter(Boolean));
|
|
547
|
+
const unmentionedBots = availableBots.filter(b => !mentionedOpenIds.has(b.openId));
|
|
548
|
+
if (unmentionedBots.length === 0)
|
|
549
|
+
return '';
|
|
550
|
+
if (unmentionedBots.length <= AVAILABLE_BOTS_INLINE_MAX) {
|
|
551
|
+
const items = unmentionedBots.map(b => ` <bot name="${xmlEscape(b.displayName)}" open_id="${xmlEscape(b.openId)}" />`);
|
|
552
|
+
return `<available_bots hint="${xmlEscape(t('ai.available_bots.hint', undefined, locale))}">\n${items.join('\n')}\n</available_bots>`;
|
|
553
|
+
}
|
|
554
|
+
const sep = (locale ?? getDefaultLocale()) === 'en' ? ', ' : '、';
|
|
555
|
+
const names = unmentionedBots.map(b => b.displayName).join(sep);
|
|
556
|
+
const line = t('ai.available_bots.collapsed_line', { count: unmentionedBots.length, names }, locale);
|
|
557
|
+
return `<available_bots hint="${xmlEscape(t('ai.available_bots.hint_collapsed', undefined, locale))}" count="${unmentionedBots.length}">\n${xmlEscape(line)}\n</available_bots>`;
|
|
558
|
+
}
|
|
559
|
+
function buildCodexAppTurnInput(opts) {
|
|
560
|
+
const additionalContext = {};
|
|
561
|
+
addCodexAppContext(additionalContext, 'botmux_role', opts.roleBlock ?? '', 'application');
|
|
562
|
+
addCodexAppContext(additionalContext, 'botmux_whiteboard', opts.whiteboardBlock ?? '', 'application');
|
|
563
|
+
addCodexAppContext(additionalContext, 'botmux_sender', opts.senderBlock ?? '', 'untrusted');
|
|
564
|
+
addCodexAppContext(additionalContext, 'botmux_substitute_policy', opts.substitutePolicyBlock ?? '', 'application');
|
|
565
|
+
addCodexAppContext(additionalContext, 'botmux_substitute_target', opts.substituteTargetBlock ?? '', 'untrusted');
|
|
566
|
+
addCodexAppContext(additionalContext, 'botmux_attachments', opts.attachmentBlock ?? '', 'untrusted');
|
|
567
|
+
addCodexAppContext(additionalContext, 'botmux_mentions', opts.mentionBlock ?? '', 'untrusted');
|
|
568
|
+
addCodexAppContext(additionalContext, 'botmux_available_bots', opts.availableBotsBlock ?? '', 'untrusted');
|
|
569
|
+
addCodexAppContext(additionalContext, 'botmux_application_context', opts.applicationContextBlock ?? '', 'application');
|
|
570
|
+
addCodexAppContext(additionalContext, 'botmux_message_context', opts.messageContextBlock ?? '', 'untrusted');
|
|
571
|
+
addCodexAppContext(additionalContext, 'botmux_buffered_followups', opts.bufferedFollowUpsBlock ?? '', 'untrusted');
|
|
572
|
+
return {
|
|
573
|
+
text: opts.text,
|
|
574
|
+
...(Object.keys(additionalContext).length > 0 ? { additionalContext } : {}),
|
|
575
|
+
...(opts.attachments?.some(a => a.type === 'image')
|
|
576
|
+
? { localImages: opts.attachments.filter(a => a.type === 'image').map(a => ({ path: a.path, detail: 'original' })) }
|
|
577
|
+
: {}),
|
|
578
|
+
};
|
|
579
|
+
}
|
|
474
580
|
export function buildNewTopicPrompt(userMessage, sessionId, cliId, cliPathOverride, attachments, mentions, availableBots, followUps, botIdentity, locale, sender, opts) {
|
|
475
581
|
const adapter = createCliAdapterSync(cliId, cliPathOverride);
|
|
476
582
|
// Non-Claude CLIs receive the botmux routing hints inline via the prompt
|
|
@@ -514,41 +620,8 @@ export function buildNewTopicPrompt(userMessage, sessionId, cliId, cliPathOverri
|
|
|
514
620
|
}
|
|
515
621
|
const roleBlock = renderRoleContextBlock(opts?.larkAppId, opts?.chatId);
|
|
516
622
|
const whiteboardBlock = renderWhiteboardBlock({ whiteboardId: opts?.whiteboardId });
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
const items = mentions.map(m => {
|
|
520
|
-
const oid = m.openId ? ` open_id="${xmlEscape(m.openId)}"` : '';
|
|
521
|
-
return ` <mention name="${xmlEscape(m.name)}"${oid} />`;
|
|
522
|
-
});
|
|
523
|
-
mentionBlock = `<mentions>\n${items.join('\n')}\n</mentions>`;
|
|
524
|
-
}
|
|
525
|
-
let botBlock = '';
|
|
526
|
-
if (availableBots && availableBots.length > 0) {
|
|
527
|
-
const mentionedOpenIds = new Set(mentions?.map(m => m.openId).filter(Boolean));
|
|
528
|
-
const unmentionedBots = availableBots.filter(b => !mentionedOpenIds.has(b.openId));
|
|
529
|
-
if (unmentionedBots.length > 0) {
|
|
530
|
-
// ≤ threshold peers: inline the full roster with open_ids so any
|
|
531
|
-
// cross-bot message (a question, collaboration, or full handoff) needs
|
|
532
|
-
// zero round-trip. Above it: collapse to a one-line pointer — names only
|
|
533
|
-
// for awareness, open_ids deferred to an on-demand `botmux bots list` —
|
|
534
|
-
// so a many-bot group doesn't pay a long open_id list on every solo
|
|
535
|
-
// topic. Either way this block is emitted once, on the first message.
|
|
536
|
-
if (unmentionedBots.length <= AVAILABLE_BOTS_INLINE_MAX) {
|
|
537
|
-
const items = unmentionedBots.map(b => ` <bot name="${xmlEscape(b.displayName)}" open_id="${xmlEscape(b.openId)}" />`);
|
|
538
|
-
botBlock = `<available_bots hint="${xmlEscape(t('ai.available_bots.hint', undefined, locale))}">\n${items.join('\n')}\n</available_bots>`;
|
|
539
|
-
}
|
|
540
|
-
else {
|
|
541
|
-
// Resolve the locale the same way t() does (explicit arg → process
|
|
542
|
-
// default) so the separator matches the rendered sentence's language —
|
|
543
|
-
// otherwise an undefined `locale` under an 'en' default would produce an
|
|
544
|
-
// English sentence joined with the Chinese enumeration comma.
|
|
545
|
-
const sep = (locale ?? getDefaultLocale()) === 'en' ? ', ' : '、';
|
|
546
|
-
const names = unmentionedBots.map(b => b.displayName).join(sep);
|
|
547
|
-
const line = t('ai.available_bots.collapsed_line', { count: unmentionedBots.length, names }, locale);
|
|
548
|
-
botBlock = `<available_bots hint="${xmlEscape(t('ai.available_bots.hint_collapsed', undefined, locale))}" count="${unmentionedBots.length}">\n${xmlEscape(line)}\n</available_bots>`;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
623
|
+
const mentionBlock = renderMentionBlock(mentions);
|
|
624
|
+
const botBlock = renderAvailableBotsBlock(availableBots, mentions, locale);
|
|
552
625
|
// Messages the user sent while the repo-selection card was still pending are
|
|
553
626
|
// buffered as followUps. Fold them into the single <user_message> body
|
|
554
627
|
// (blank-line separated) rather than emitting a separate <follow_up_message>
|
|
@@ -603,6 +676,44 @@ export function buildNewTopicPrompt(userMessage, sessionId, cliId, cliPathOverri
|
|
|
603
676
|
// resolves delivery — keeping a single injection site avoids double-rendering.
|
|
604
677
|
return parts.join('\n\n');
|
|
605
678
|
}
|
|
679
|
+
/** Build the legacy opening prompt plus a Codex App structured sidecar. The
|
|
680
|
+
* sibling string API above stays unchanged for every existing caller. Pending-
|
|
681
|
+
* repo follow-ups currently arrive as already-enriched strings (and may contain
|
|
682
|
+
* sender tags), so that rare merged path deliberately falls back to legacy
|
|
683
|
+
* rather than guessing which bytes are user text. */
|
|
684
|
+
export function buildNewTopicCliInput(userMessage, sessionId, cliId, cliPathOverride, attachments, mentions, availableBots, followUps, botIdentity, locale, sender, opts) {
|
|
685
|
+
const content = buildNewTopicPrompt(userMessage, sessionId, cliId, cliPathOverride, attachments, mentions, availableBots, followUps, botIdentity, locale, sender, opts);
|
|
686
|
+
// Legacy pending buffers contain enriched strings. Only materialize those as
|
|
687
|
+
// clean input when the caller also preserved their matching raw texts.
|
|
688
|
+
if (cliId !== 'codex-app' || (followUps && followUps.length > 0 && !opts?.codexAppFollowUps))
|
|
689
|
+
return { content };
|
|
690
|
+
const roleBlock = renderRoleContextBlock(opts?.larkAppId, opts?.chatId);
|
|
691
|
+
const whiteboardBlock = renderWhiteboardBlock({ whiteboardId: opts?.whiteboardId });
|
|
692
|
+
const senderBlock = renderSenderTag(sender);
|
|
693
|
+
const substitutePolicyBlock = renderSubstitutePolicy(opts?.substituteTrigger);
|
|
694
|
+
const substituteTargetBlock = renderSubstituteTarget(opts?.substituteTrigger);
|
|
695
|
+
const attachmentBlock = formatAttachmentsHint(attachments, locale);
|
|
696
|
+
const mentionBlock = renderMentionBlock(mentions);
|
|
697
|
+
const availableBotsBlock = renderAvailableBotsBlock(availableBots, mentions, locale);
|
|
698
|
+
return {
|
|
699
|
+
content,
|
|
700
|
+
codexAppInput: buildCodexAppTurnInput({
|
|
701
|
+
text: [opts?.codexAppText ?? userMessage, ...(opts?.codexAppFollowUps ?? [])].join('\n\n'),
|
|
702
|
+
roleBlock,
|
|
703
|
+
whiteboardBlock,
|
|
704
|
+
senderBlock,
|
|
705
|
+
substitutePolicyBlock,
|
|
706
|
+
substituteTargetBlock,
|
|
707
|
+
attachmentBlock,
|
|
708
|
+
mentionBlock,
|
|
709
|
+
availableBotsBlock,
|
|
710
|
+
applicationContextBlock: opts?.codexAppApplicationContext,
|
|
711
|
+
messageContextBlock: opts?.codexAppMessageContext,
|
|
712
|
+
bufferedFollowUpsBlock: opts?.codexAppFollowUpContexts?.filter(Boolean).join('\n\n'),
|
|
713
|
+
attachments,
|
|
714
|
+
}),
|
|
715
|
+
};
|
|
716
|
+
}
|
|
606
717
|
/**
|
|
607
718
|
* Build the content for a follow-up message (thread reply to an active session).
|
|
608
719
|
* Mirrors buildNewTopicPrompt structure but for subsequent messages.
|
|
@@ -647,15 +758,40 @@ export function buildFollowUpContent(content, sessionId, opts) {
|
|
|
647
758
|
: '';
|
|
648
759
|
if (attachHint)
|
|
649
760
|
parts.push(attachHint);
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
return ` <mention name="${xmlEscape(m.name)}"${oid} />`;
|
|
654
|
-
});
|
|
655
|
-
parts.push(`<mentions>\n${items.join('\n')}\n</mentions>`);
|
|
656
|
-
}
|
|
761
|
+
const mentionBlock = renderMentionBlock(opts?.mentions);
|
|
762
|
+
if (mentionBlock)
|
|
763
|
+
parts.push(mentionBlock);
|
|
657
764
|
return parts.join('\n\n');
|
|
658
765
|
}
|
|
766
|
+
/** Follow-up counterpart of buildNewTopicCliInput. */
|
|
767
|
+
export function buildFollowUpCliInput(content, sessionId, opts) {
|
|
768
|
+
const legacyContent = buildFollowUpContent(content, sessionId, opts);
|
|
769
|
+
if (opts?.cliId !== 'codex-app' || opts.isAdoptMode)
|
|
770
|
+
return { content: legacyContent };
|
|
771
|
+
const roleBlock = renderRoleContextBlock(opts.larkAppId, opts.chatId, { followUp: true });
|
|
772
|
+
const whiteboardBlock = renderWhiteboardBlock({ whiteboardId: opts.whiteboardId });
|
|
773
|
+
const senderBlock = renderSenderTag(opts.sender);
|
|
774
|
+
const substitutePolicyBlock = renderSubstitutePolicy(opts.substituteTrigger);
|
|
775
|
+
const substituteTargetBlock = renderSubstituteTarget(opts.substituteTrigger);
|
|
776
|
+
const attachmentBlock = formatAttachmentsHint(opts.attachments, opts.locale);
|
|
777
|
+
const mentionBlock = renderMentionBlock(opts.mentions);
|
|
778
|
+
return {
|
|
779
|
+
content: legacyContent,
|
|
780
|
+
codexAppInput: buildCodexAppTurnInput({
|
|
781
|
+
text: opts.codexAppText ?? content,
|
|
782
|
+
roleBlock,
|
|
783
|
+
whiteboardBlock,
|
|
784
|
+
senderBlock,
|
|
785
|
+
substitutePolicyBlock,
|
|
786
|
+
substituteTargetBlock,
|
|
787
|
+
attachmentBlock,
|
|
788
|
+
mentionBlock,
|
|
789
|
+
applicationContextBlock: opts.codexAppApplicationContext,
|
|
790
|
+
messageContextBlock: opts.codexAppMessageContext,
|
|
791
|
+
attachments: opts.attachments,
|
|
792
|
+
}),
|
|
793
|
+
};
|
|
794
|
+
}
|
|
659
795
|
/**
|
|
660
796
|
* Build raw input content for adopt-bridge mode.
|
|
661
797
|
*
|
|
@@ -770,6 +906,37 @@ export function buildReforkPrompt(ds, content, opts) {
|
|
|
770
906
|
whiteboardId: ds.session.whiteboardId,
|
|
771
907
|
});
|
|
772
908
|
}
|
|
909
|
+
/** Structured refork variant. Adopted external CLIs intentionally remain on
|
|
910
|
+
* their existing raw bridge path and never receive a Codex App sidecar. */
|
|
911
|
+
export function buildReforkCliInput(ds, content, opts) {
|
|
912
|
+
const locale = opts?.locale ?? localeForBot(ds.larkAppId);
|
|
913
|
+
if (ds.adoptedFrom) {
|
|
914
|
+
return {
|
|
915
|
+
content: buildBridgeInputContent(content, {
|
|
916
|
+
attachments: opts?.attachments,
|
|
917
|
+
mentions: opts?.mentions,
|
|
918
|
+
selfMention: opts?.selfMention,
|
|
919
|
+
locale,
|
|
920
|
+
}),
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
return buildFollowUpCliInput(content, ds.session.sessionId, {
|
|
924
|
+
attachments: opts?.attachments,
|
|
925
|
+
mentions: opts?.mentions,
|
|
926
|
+
isAdoptMode: false,
|
|
927
|
+
cliId: opts?.cliId,
|
|
928
|
+
cliPathOverride: opts?.cliPathOverride,
|
|
929
|
+
locale,
|
|
930
|
+
sender: opts?.sender,
|
|
931
|
+
larkAppId: ds.larkAppId,
|
|
932
|
+
chatId: ds.session.chatId,
|
|
933
|
+
whiteboardId: ds.session.whiteboardId,
|
|
934
|
+
substituteTrigger: opts?.substituteTrigger,
|
|
935
|
+
codexAppText: opts?.codexAppText,
|
|
936
|
+
codexAppApplicationContext: opts?.codexAppApplicationContext,
|
|
937
|
+
codexAppMessageContext: opts?.codexAppMessageContext,
|
|
938
|
+
});
|
|
939
|
+
}
|
|
773
940
|
/**
|
|
774
941
|
* Copy current streaming-card fields from `ds` into the persisted Session and save.
|
|
775
942
|
* Lets the existing card be PATCHed on next screen_update after a daemon restart,
|
|
@@ -787,6 +954,7 @@ export function persistStreamCardState(ds) {
|
|
|
787
954
|
sameUsageLimit(s.usageLimit, ds.usageLimit) &&
|
|
788
955
|
s.lastUserPrompt === ds.lastUserPrompt &&
|
|
789
956
|
s.lastCliInput === ds.lastCliInput &&
|
|
957
|
+
JSON.stringify(s.lastCodexAppInput ?? null) === JSON.stringify(ds.lastCodexAppInput ?? null) &&
|
|
790
958
|
JSON.stringify(s.replyThreadAliases ?? {}) === JSON.stringify(ds.replyThreadAliases ?? {}) &&
|
|
791
959
|
JSON.stringify(s.currentReplyTarget ?? null) === JSON.stringify(ds.currentReplyTarget ?? null))
|
|
792
960
|
return;
|
|
@@ -798,20 +966,38 @@ export function persistStreamCardState(ds) {
|
|
|
798
966
|
s.usageLimit = ds.usageLimit;
|
|
799
967
|
s.lastUserPrompt = ds.lastUserPrompt;
|
|
800
968
|
s.lastCliInput = ds.lastCliInput;
|
|
969
|
+
if (ds.lastCodexAppInput)
|
|
970
|
+
s.lastCodexAppInput = ds.lastCodexAppInput;
|
|
971
|
+
else
|
|
972
|
+
delete s.lastCodexAppInput;
|
|
801
973
|
s.replyThreadAliases = ds.replyThreadAliases;
|
|
802
974
|
s.currentReplyTarget = ds.currentReplyTarget;
|
|
803
975
|
// Clear legacy field so it doesn't drift
|
|
804
976
|
s.streamExpanded = undefined;
|
|
805
977
|
sessionStore.updateSession(s);
|
|
806
978
|
}
|
|
807
|
-
export function rememberLastCliInput(ds, userPrompt, cliInput) {
|
|
979
|
+
export function rememberLastCliInput(ds, userPrompt, cliInput, opts) {
|
|
808
980
|
// A real CLI input means the post-restart silence is over — let the normal
|
|
809
981
|
// card flow resume for this and subsequent turns.
|
|
810
982
|
ds.suppressRecoveryCard = undefined;
|
|
811
983
|
ds.lastUserPrompt = userPrompt;
|
|
812
|
-
|
|
984
|
+
const normalized = typeof cliInput === 'string' ? { content: cliInput } : cliInput;
|
|
985
|
+
ds.lastCliInput = normalized.content;
|
|
986
|
+
const botCfg = getBot(ds.larkAppId).config;
|
|
987
|
+
const effectiveCliId = ds.session.cliId ?? botCfg.cliId;
|
|
988
|
+
const keepCodexAppInput = opts?.codexAppInputAccepted ?? (effectiveCliId === 'codex-app' &&
|
|
989
|
+
botCfg.codexAppCleanInput === true &&
|
|
990
|
+
!ds.adoptedFrom);
|
|
991
|
+
if (keepCodexAppInput && normalized.codexAppInput)
|
|
992
|
+
ds.lastCodexAppInput = normalized.codexAppInput;
|
|
993
|
+
else
|
|
994
|
+
delete ds.lastCodexAppInput;
|
|
813
995
|
ds.session.lastUserPrompt = userPrompt;
|
|
814
|
-
ds.session.lastCliInput =
|
|
996
|
+
ds.session.lastCliInput = normalized.content;
|
|
997
|
+
if (keepCodexAppInput && normalized.codexAppInput)
|
|
998
|
+
ds.session.lastCodexAppInput = normalized.codexAppInput;
|
|
999
|
+
else
|
|
1000
|
+
delete ds.session.lastCodexAppInput;
|
|
815
1001
|
ds.session.replyThreadAliases = ds.replyThreadAliases;
|
|
816
1002
|
ds.session.currentReplyTarget = ds.currentReplyTarget;
|
|
817
1003
|
sessionStore.updateSession(ds.session);
|
|
@@ -903,6 +1089,7 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
903
1089
|
lastMessageAt: sessionLastMessageAtMs(session),
|
|
904
1090
|
hasHistory: false,
|
|
905
1091
|
workingDir: adopted.cwd,
|
|
1092
|
+
ownerOpenId: session.ownerOpenId,
|
|
906
1093
|
adoptedFrom: adopted,
|
|
907
1094
|
streamCardId: session.streamCardId,
|
|
908
1095
|
streamCardNonce: session.streamCardNonce,
|
|
@@ -914,6 +1101,7 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
914
1101
|
usageLimit: session.usageLimit,
|
|
915
1102
|
lastUserPrompt: session.lastUserPrompt,
|
|
916
1103
|
lastCliInput: session.lastCliInput,
|
|
1104
|
+
lastCodexAppInput: session.lastCodexAppInput,
|
|
917
1105
|
replyThreadAliases: session.replyThreadAliases,
|
|
918
1106
|
currentReplyTarget: session.currentReplyTarget,
|
|
919
1107
|
// Restart stays silent for adopt sessions too: forkAdoptWorker shares
|
|
@@ -963,6 +1151,8 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
963
1151
|
workingDir: session.workingDir,
|
|
964
1152
|
ownerOpenId: session.ownerOpenId,
|
|
965
1153
|
pendingPrompt: session.queuedPrompt,
|
|
1154
|
+
pendingCodexAppText: session.queuedCodexAppText,
|
|
1155
|
+
pendingCodexAppMessageContext: session.queuedCodexAppMessageContext,
|
|
966
1156
|
currentTurnTitle: session.currentTurnTitle ?? session.title,
|
|
967
1157
|
};
|
|
968
1158
|
const anchor = sessionAnchorId(ds);
|
|
@@ -989,6 +1179,7 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
989
1179
|
lastMessageAt: sessionLastMessageAtMs(session),
|
|
990
1180
|
hasHistory: true, // restored sessions have prior CLI history
|
|
991
1181
|
workingDir: session.workingDir,
|
|
1182
|
+
ownerOpenId: session.ownerOpenId,
|
|
992
1183
|
// Restore persisted streaming-card state — next screen_update will PATCH
|
|
993
1184
|
// the existing card instead of POSTing a fresh one. If the card was
|
|
994
1185
|
// withdrawn while we were down, the PATCH fails with MessageWithdrawnError
|
|
@@ -1002,6 +1193,7 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1002
1193
|
usageLimit: session.usageLimit,
|
|
1003
1194
|
lastUserPrompt: session.lastUserPrompt,
|
|
1004
1195
|
lastCliInput: session.lastCliInput,
|
|
1196
|
+
lastCodexAppInput: session.lastCodexAppInput,
|
|
1005
1197
|
replyThreadAliases: session.replyThreadAliases,
|
|
1006
1198
|
currentReplyTarget: session.currentReplyTarget,
|
|
1007
1199
|
// Restart stays silent in the group: the recovery re-fork won't post or
|
|
@@ -1276,6 +1468,7 @@ export async function resumeSession(sessionId, activeSessions) {
|
|
|
1276
1468
|
usageLimit: session.usageLimit,
|
|
1277
1469
|
lastUserPrompt: session.lastUserPrompt,
|
|
1278
1470
|
lastCliInput: session.lastCliInput,
|
|
1471
|
+
lastCodexAppInput: session.lastCodexAppInput,
|
|
1279
1472
|
replyThreadAliases: session.replyThreadAliases,
|
|
1280
1473
|
currentReplyTarget: session.currentReplyTarget,
|
|
1281
1474
|
};
|
|
@@ -1403,17 +1596,17 @@ export async function executeScheduledTask(task, activeSessions, refreshCliVersi
|
|
|
1403
1596
|
markSessionActivity(existing);
|
|
1404
1597
|
try {
|
|
1405
1598
|
ensureSessionWhiteboard(existing);
|
|
1406
|
-
const
|
|
1599
|
+
const input = buildFollowUpCliInput(task.prompt, existing.session.sessionId, {
|
|
1407
1600
|
isAdoptMode: false,
|
|
1408
|
-
cliId: bot.config.cliId,
|
|
1409
|
-
cliPathOverride: bot.config.cliPathOverride,
|
|
1601
|
+
cliId: existing.session.cliId ?? bot.config.cliId,
|
|
1602
|
+
cliPathOverride: existing.session.cliPathOverride ?? bot.config.cliPathOverride,
|
|
1410
1603
|
locale: localeForBot(larkAppId),
|
|
1411
1604
|
larkAppId,
|
|
1412
1605
|
chatId: task.chatId,
|
|
1413
1606
|
whiteboardId: existing.session.whiteboardId,
|
|
1414
1607
|
});
|
|
1415
|
-
rememberLastCliInput(existing, task.prompt,
|
|
1416
|
-
existing
|
|
1608
|
+
rememberLastCliInput(existing, task.prompt, input);
|
|
1609
|
+
sendWorkerInput(existing, input);
|
|
1417
1610
|
logger.info(`[scheduler] Task "${task.name}" injected into live session ${existing.session.sessionId}`);
|
|
1418
1611
|
return;
|
|
1419
1612
|
}
|
|
@@ -1452,7 +1645,7 @@ export async function executeScheduledTask(task, activeSessions, refreshCliVersi
|
|
|
1452
1645
|
workingDir: task.workingDir,
|
|
1453
1646
|
};
|
|
1454
1647
|
ensureSessionWhiteboard(ds);
|
|
1455
|
-
const prompt =
|
|
1648
|
+
const prompt = buildNewTopicCliInput(task.prompt, session.sessionId, bot.config.cliId, bot.config.cliPathOverride, undefined, undefined, undefined, undefined, { name: bot.botName, openId: bot.botOpenId }, localeForBot(larkAppId), undefined, { larkAppId, chatId: task.chatId, whiteboardId: ds.session.whiteboardId });
|
|
1456
1649
|
activeSessions.set(sessionKey(anchor, larkAppId), ds);
|
|
1457
1650
|
rememberLastCliInput(ds, task.prompt, prompt);
|
|
1458
1651
|
forkWorker(ds, prompt);
|
|
@@ -1516,7 +1709,14 @@ async function forkOrShowRepoCard(ds, userContent) {
|
|
|
1516
1709
|
return;
|
|
1517
1710
|
}
|
|
1518
1711
|
}
|
|
1519
|
-
const buildPrompt = () =>
|
|
1712
|
+
const buildPrompt = () => buildNewTopicCliInput(userContent, ds.session.sessionId, bot.config.cliId, bot.config.cliPathOverride, undefined, undefined, undefined, undefined, { name: bot.botName, openId: bot.botOpenId }, locale, undefined, {
|
|
1713
|
+
larkAppId,
|
|
1714
|
+
chatId: ds.chatId,
|
|
1715
|
+
whiteboardId: ds.session.whiteboardId,
|
|
1716
|
+
codexAppText: ds.pendingCodexAppText,
|
|
1717
|
+
codexAppApplicationContext: ds.pendingCodexAppApplicationContext,
|
|
1718
|
+
codexAppMessageContext: ds.pendingCodexAppMessageContext,
|
|
1719
|
+
});
|
|
1520
1720
|
if (!ds.workingDir) {
|
|
1521
1721
|
// 没钉目录 → 复用 /repo 选择卡片让用户在群里选仓库。
|
|
1522
1722
|
const scanDirs = getProjectScanDirs(ds).filter(d => existsSync(d));
|
|
@@ -1548,6 +1748,9 @@ async function forkOrShowRepoCard(ds, userContent) {
|
|
|
1548
1748
|
const prompt = buildPrompt();
|
|
1549
1749
|
rememberLastCliInput(ds, userContent, prompt);
|
|
1550
1750
|
forkWorker(ds, prompt);
|
|
1751
|
+
ds.pendingCodexAppText = undefined;
|
|
1752
|
+
ds.pendingCodexAppApplicationContext = undefined;
|
|
1753
|
+
ds.pendingCodexAppMessageContext = undefined;
|
|
1551
1754
|
}
|
|
1552
1755
|
/** 在新建的飞书群里为某个 bot 拉起一条 chat-scope 会话(dashboard「创建会话」用)。
|
|
1553
1756
|
* column='in_progress' → 立即 forkWorker 把内容当首轮发给 CLI;
|
|
@@ -1591,6 +1794,7 @@ export async function spawnDashboardSession(activeSessions, refreshCliVersion, a
|
|
|
1591
1794
|
// 这份已包装内容,激活时直接喂给 buildNewTopicPrompt,保证待办池里起来的 lead
|
|
1592
1795
|
// 也带编排上下文(coworkers 只有此刻可靠,激活时已无从重算)。
|
|
1593
1796
|
const userContent = composeSpawnUserContent({ content, role, coworkers: args.coworkers, locale });
|
|
1797
|
+
const codexAppMessageContext = composeSpawnCodexAppContext({ role, coworkers: args.coworkers, locale });
|
|
1594
1798
|
const resolvedTitle = args.title || deriveSessionTitleFromContent(content);
|
|
1595
1799
|
const session = sessionStore.createSession(chatId, bannerMessageId ?? chatId, resolvedTitle, 'group');
|
|
1596
1800
|
const now = Date.now();
|
|
@@ -1604,6 +1808,8 @@ export async function spawnDashboardSession(activeSessions, refreshCliVersion, a
|
|
|
1604
1808
|
if (column === 'backlog') {
|
|
1605
1809
|
session.queued = true;
|
|
1606
1810
|
session.queuedPrompt = userContent;
|
|
1811
|
+
session.queuedCodexAppText = content;
|
|
1812
|
+
session.queuedCodexAppMessageContext = codexAppMessageContext;
|
|
1607
1813
|
session.kanbanColumn = 'backlog';
|
|
1608
1814
|
}
|
|
1609
1815
|
// 钉 workingDir:oncall 绑定(弹框填了目录)/ bot 默认。都没有 → undefined,激活/开跑时
|
|
@@ -1629,6 +1835,8 @@ export async function spawnDashboardSession(activeSessions, refreshCliVersion, a
|
|
|
1629
1835
|
workingDir,
|
|
1630
1836
|
ownerOpenId: session.ownerOpenId,
|
|
1631
1837
|
currentTurnTitle: resolvedTitle,
|
|
1838
|
+
pendingCodexAppText: content,
|
|
1839
|
+
pendingCodexAppMessageContext: codexAppMessageContext,
|
|
1632
1840
|
};
|
|
1633
1841
|
activeSessions.set(sessionKey(anchor, larkAppId), ds);
|
|
1634
1842
|
if (column === 'backlog') {
|
|
@@ -1655,12 +1863,21 @@ export async function activateQueuedSession(ds) {
|
|
|
1655
1863
|
// 不该发生(queued 一定 worker:null),但保险:清标记即可。
|
|
1656
1864
|
ds.session.queued = false;
|
|
1657
1865
|
ds.session.queuedPrompt = undefined;
|
|
1866
|
+
ds.session.queuedCodexAppText = undefined;
|
|
1867
|
+
ds.session.queuedCodexAppMessageContext = undefined;
|
|
1658
1868
|
sessionStore.updateSession(ds.session);
|
|
1659
1869
|
return { ok: true };
|
|
1660
1870
|
}
|
|
1661
1871
|
const content = ds.session.queuedPrompt ?? ds.pendingPrompt ?? '';
|
|
1872
|
+
// A parked dashboard task may have crossed a daemon restart. Restore the
|
|
1873
|
+
// persisted clean-input sidecar before clearing the durable backlog fields;
|
|
1874
|
+
// forkOrShowRepoCard will carry it through either immediate fork or /repo.
|
|
1875
|
+
ds.pendingCodexAppText ??= ds.session.queuedCodexAppText;
|
|
1876
|
+
ds.pendingCodexAppMessageContext ??= ds.session.queuedCodexAppMessageContext;
|
|
1662
1877
|
ds.session.queued = false;
|
|
1663
1878
|
ds.session.queuedPrompt = undefined;
|
|
1879
|
+
ds.session.queuedCodexAppText = undefined;
|
|
1880
|
+
ds.session.queuedCodexAppMessageContext = undefined;
|
|
1664
1881
|
ds.pendingPrompt = undefined;
|
|
1665
1882
|
// 激活即视为开始:从待办池挪到进行中,让卡片归位。
|
|
1666
1883
|
if (ds.session.kanbanColumn === 'backlog')
|