@usulpro/codex-bee 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2929,18 +2929,19 @@ function getDisplayedRunState(snapshot, now = Date.now()) {
2929
2929
  }
2930
2930
  return "idle";
2931
2931
  }
2932
- function formatSessionInfo(snapshot, now = Date.now()) {
2932
+ function formatSessionInfo(snapshot, mode, now = Date.now()) {
2933
+ if (mode === "off") {
2934
+ return "Bee is in Off mode. Codex-bee stays passive and only records session stats and turn history.";
2935
+ }
2933
2936
  const parts = [];
2934
2937
  if (snapshot.session.currentTurn !== null && snapshot.session.totalTurns !== null) {
2935
2938
  parts.push(`${snapshot.session.currentTurn}/${snapshot.session.totalTurns} turns`);
2936
- } else if (snapshot.session.currentTurn !== null) {
2937
- parts.push(`${snapshot.session.currentTurn} turns`);
2938
2939
  }
2939
2940
  const timeLeftLabel = formatTimeLeft(snapshot.session.stopAt, now);
2940
2941
  if (timeLeftLabel) {
2941
2942
  parts.push(timeLeftLabel);
2942
2943
  }
2943
- const hookStatusLabel = formatHookStatus(snapshot);
2944
+ const hookStatusLabel = snapshot.session.stopScript ? formatHookStatus(snapshot) : null;
2944
2945
  if (hookStatusLabel) {
2945
2946
  parts.push(hookStatusLabel);
2946
2947
  }
@@ -3317,41 +3318,6 @@ function buildFigletLines(title, maxWidth) {
3317
3318
  }
3318
3319
  return [title];
3319
3320
  }
3320
- function buildAnnouncementPanelLines(state, width, palette) {
3321
- if (!state.runtime.announcement || width < 8) {
3322
- return [];
3323
- }
3324
- const innerWidth = Math.max(width - 4, 4);
3325
- const contentLines = wrapText(state.runtime.announcement.message, innerWidth);
3326
- const borderColor = palette.announcementBorder ?? palette.accent ?? palette.primary;
3327
- return [
3328
- createTextLine("announcement-top", `\u256D${"\u2500".repeat(Math.max(width - 2, 0))}\u256E`, {
3329
- backgroundColor: palette.announcementBackground,
3330
- color: borderColor
3331
- }),
3332
- ...contentLines.map((line, index) => createLine(`announcement-${index}`, [
3333
- {
3334
- backgroundColor: palette.announcementBackground,
3335
- color: borderColor,
3336
- text: "\u2502 "
3337
- },
3338
- {
3339
- backgroundColor: palette.announcementBackground,
3340
- color: palette.announcementText ?? palette.bodyText,
3341
- text: padDisplayWidth(line, innerWidth)
3342
- },
3343
- {
3344
- backgroundColor: palette.announcementBackground,
3345
- color: borderColor,
3346
- text: " \u2502"
3347
- }
3348
- ], palette.announcementBackground)),
3349
- createTextLine("announcement-bottom", `\u2570${"\u2500".repeat(Math.max(width - 2, 0))}\u256F`, {
3350
- backgroundColor: palette.announcementBackground,
3351
- color: borderColor
3352
- })
3353
- ];
3354
- }
3355
3321
  function buildHeaderColumnLines(state, title, width, palette) {
3356
3322
  const titleLines = buildFigletLines(title.toUpperCase(), Math.max(width, 16));
3357
3323
  return [
@@ -3359,8 +3325,7 @@ function buildHeaderColumnLines(state, title, width, palette) {
3359
3325
  createTextLine("versions", `codex-bee v${state.runtime.beeVersion} \u2022 codex v${state.runtime.codexVersion}`, {
3360
3326
  color: palette.muted,
3361
3327
  dimColor: !palette.muted
3362
- }),
3363
- ...buildAnnouncementPanelLines(state, width, palette)
3328
+ })
3364
3329
  ];
3365
3330
  }
3366
3331
  function buildHeaderLines(state, title, width, palette) {
@@ -3382,7 +3347,7 @@ function getLogColor(kind, palette) {
3382
3347
  return palette.muted;
3383
3348
  }
3384
3349
  function buildDividerLabel(turn, totalTurns, durationMs, width) {
3385
- const parts = [totalTurns === null ? `${turn} turns` : `${turn}/${totalTurns} turns`];
3350
+ const parts = [totalTurns === null ? `turn ${turn}` : `turn ${turn}/${totalTurns}`];
3386
3351
  if (durationMs !== null) {
3387
3352
  parts.push(formatCompactDuration(durationMs));
3388
3353
  }
@@ -3503,17 +3468,17 @@ function buildLogLines(state, width, palette) {
3503
3468
  state.runtime.logs.forEach((group, groupIndex) => {
3504
3469
  if (groupIndex > 0) {
3505
3470
  logLines.push(createSpacerLine(`divider-gap-${group.id}`, palette.logBackground));
3506
- logLines.push(createTextLine(`divider-${group.id}`, buildDividerLabel(
3507
- group.turn,
3508
- group.totalTurns,
3509
- group.durationMs,
3510
- width
3511
- ), {
3512
- backgroundColor: palette.logBackground,
3513
- color: palette.muted,
3514
- dimColor: !palette.muted
3515
- }));
3516
3471
  }
3472
+ logLines.push(createTextLine(`divider-${group.id}`, buildDividerLabel(
3473
+ group.turn,
3474
+ group.totalTurns,
3475
+ group.durationMs,
3476
+ width
3477
+ ), {
3478
+ backgroundColor: palette.logBackground,
3479
+ color: palette.muted,
3480
+ dimColor: !palette.muted
3481
+ }));
3517
3482
  group.entries.forEach((entry) => {
3518
3483
  const previewLines = wrapAndLimit(normalizeLogEntryText(entry), contentWidth, 3);
3519
3484
  previewLines.forEach((line, lineIndex) => {
@@ -3574,20 +3539,6 @@ function buildStatusTokens(state, palette, animationTick, now, width) {
3574
3539
  text: connectionSpec.compactLabel
3575
3540
  }
3576
3541
  ];
3577
- const versionToken = [
3578
- {
3579
- color: palette.muted,
3580
- dimColor: !palette.muted,
3581
- text: `codex v${state.runtime.codexVersion}`
3582
- }
3583
- ];
3584
- const shortVersionToken = [
3585
- {
3586
- color: palette.muted,
3587
- dimColor: !palette.muted,
3588
- text: `v${state.runtime.codexVersion}`
3589
- }
3590
- ];
3591
3542
  const runStateToken = [
3592
3543
  {
3593
3544
  color: displayedRunState === "running" ? palette.primary : displayedRunState === "quiet" ? palette.warning : palette.muted,
@@ -3623,9 +3574,8 @@ function buildStatusTokens(state, palette, animationTick, now, width) {
3623
3574
  }
3624
3575
  ];
3625
3576
  const variants = [
3626
- [loaderToken, connectionToken, versionToken, runStateToken, hotkeyToken, commandsToken],
3627
- [loaderToken, connectionToken, shortVersionToken, runStateToken, hotkeyToken, commandsToken],
3628
- [loaderToken, connectionToken, shortVersionToken, runStateToken, hotkeyToken, shortCommandsToken],
3577
+ [loaderToken, connectionToken, runStateToken, hotkeyToken, commandsToken],
3578
+ [loaderToken, compactConnectionToken, runStateToken, hotkeyToken, commandsToken],
3629
3579
  [loaderToken, compactConnectionToken, runStateToken, shortHotkeyToken, shortCommandsToken]
3630
3580
  ];
3631
3581
  for (const variant of variants) {
@@ -3721,30 +3671,42 @@ function buildDropdownLines(state, width, palette) {
3721
3671
  ], backgroundColor);
3722
3672
  });
3723
3673
  }
3724
- function buildNoticeLine(state, palette) {
3725
- if (!state.notice) {
3726
- return null;
3674
+ function buildPromptPreviewLines(state, width) {
3675
+ if (state.activeMode === "off") {
3676
+ const lengths = [
3677
+ Math.max(Math.floor(width * 0.68), 18),
3678
+ Math.max(Math.floor(width * 0.52), 14),
3679
+ Math.max(Math.floor(width * 0.6), 16)
3680
+ ];
3681
+ return lengths.map((length, index) => ({
3682
+ colorKind: "muted",
3683
+ text: "\u2591".repeat(Math.max(Math.min(length - index, width), 8))
3684
+ }));
3727
3685
  }
3728
- return createTextLine(`notice-${state.notice.id}`, `\u26A0 ${state.notice.message}`, {
3729
- backgroundColor: palette.statusBottomBackground,
3730
- color: state.notice.kind === "error" ? palette.error : state.notice.kind === "warning" ? palette.warning : palette.primary
3731
- });
3686
+ return formatPromptPreview(getActivePrompt(state), 5).flatMap((line) => wrapAndLimit(line, width, 5)).slice(0, 5).map((line) => ({
3687
+ colorKind: "body",
3688
+ text: line
3689
+ }));
3732
3690
  }
3733
3691
  function buildPromptFooterLines(state, width, palette, cursorVisible, animationTick, now) {
3734
3692
  const lines = [];
3735
3693
  const modeColor = getModeColor(state.activeMode, palette);
3736
3694
  const promptPreviewWidth = Math.max(width - 2, 16);
3737
- const promptLines = formatPromptPreview(getActivePrompt(state), 5).flatMap((line) => wrapAndLimit(line, promptPreviewWidth, 5)).slice(0, 5);
3695
+ const promptLines = buildPromptPreviewLines(state, promptPreviewWidth);
3738
3696
  const inputFieldWidth = Math.max(width - 2, 12);
3739
3697
  const inputContentWidth = Math.max(inputFieldWidth - 1, 8);
3740
3698
  const composerLines = buildComposerVisualLines(state.composerText, inputContentWidth);
3741
3699
  const cursorLineIndex = getComposerCursorLineIndex(composerLines, state.composerCursorOffset);
3742
3700
  const placeholder = getVisibleSlashPlaceholder(state);
3743
- lines.push(createTextLine("session-info", formatSessionInfo(state.runtime, now) || "Session metadata will appear after the first turn.", {
3744
- backgroundColor: palette.statusTopBackground,
3745
- color: palette.bodyText
3746
- }));
3747
- lines.push(createSpacerLine("session-gap", palette.footerBase));
3701
+ const sessionInfoText = formatSessionInfo(state.runtime, state.activeMode, now);
3702
+ if (sessionInfoText) {
3703
+ lines.push(createTextLine("session-info", sessionInfoText, {
3704
+ backgroundColor: palette.statusTopBackground,
3705
+ color: state.activeMode === "off" ? palette.muted : palette.bodyText,
3706
+ dimColor: state.activeMode === "off" && !palette.muted
3707
+ }));
3708
+ lines.push(createSpacerLine("session-gap", palette.footerBase));
3709
+ }
3748
3710
  lines.push(createTextLine("prompt-top-border", "\u2500".repeat(width), {
3749
3711
  backgroundColor: palette.promptBackground,
3750
3712
  color: palette.promptBorder ?? palette.muted
@@ -3763,8 +3725,9 @@ function buildPromptFooterLines(state, width, palette, cursorVisible, animationT
3763
3725
  },
3764
3726
  {
3765
3727
  backgroundColor: palette.promptBackground,
3766
- color: palette.bodyText,
3767
- text: padDisplayWidth(line, width - 2)
3728
+ color: line.colorKind === "muted" ? palette.muted : palette.bodyText,
3729
+ dimColor: line.colorKind === "muted",
3730
+ text: padDisplayWidth(line.text, width - 2)
3768
3731
  }
3769
3732
  ], palette.promptBackground));
3770
3733
  });
@@ -3848,8 +3811,7 @@ function buildPromptFooterLines(state, width, palette, cursorVisible, animationT
3848
3811
  function buildBeeScreenLines(state, title, columns, cursorVisible, palette, animationTick, now) {
3849
3812
  const bodyLines = buildBodyLines(state, title, columns, palette);
3850
3813
  const footerLines = buildPromptFooterLines(state, columns, palette, cursorVisible, animationTick, now);
3851
- const noticeLine = buildNoticeLine(state, palette);
3852
- return noticeLine ? [...bodyLines, createSpacerLine("body-footer-gap"), ...footerLines, createSpacerLine("notice-gap", palette.footerBase), noticeLine] : [...bodyLines, createSpacerLine("body-footer-gap"), ...footerLines];
3814
+ return [...bodyLines, createSpacerLine("body-footer-gap"), ...footerLines];
3853
3815
  }
3854
3816
  function buildCodexScreenLines(state, columns, palette) {
3855
3817
  const bodyLines = state.runtime.codexBufferLines.map((line, index) => createTextLine(`codex-${index}`, line, {
@@ -3871,8 +3833,7 @@ function buildCodexScreenLines(state, columns, palette) {
3871
3833
  dimColor: !palette.muted
3872
3834
  })
3873
3835
  ];
3874
- const noticeLine = buildNoticeLine(state, palette);
3875
- return noticeLine ? [...bodyLines, createSpacerLine("codex-gap"), ...footerLines, createSpacerLine("codex-notice-gap", palette.footerBase), noticeLine] : [...bodyLines, createSpacerLine("codex-gap"), ...footerLines];
3836
+ return [...bodyLines, createSpacerLine("codex-gap"), ...footerLines];
3876
3837
  }
3877
3838
  function buildNarrowScreenLines(columns, palette) {
3878
3839
  return [
@@ -4458,6 +4419,7 @@ var LiveUiRuntimeHost = class {
4458
4419
  #listeners = /* @__PURE__ */ new Set();
4459
4420
  #recentEvents = [];
4460
4421
  #entryCounter = 0;
4422
+ #handledStopFingerprints = /* @__PURE__ */ new Set();
4461
4423
  #lastOutputActivityNotificationAt = 0;
4462
4424
  #pendingPrompts = [];
4463
4425
  #snapshot;
@@ -4508,6 +4470,8 @@ var LiveUiRuntimeHost = class {
4508
4470
  const eventName = capture.payload?.hook_event_name ?? null;
4509
4471
  if (eventName === "SessionStart") {
4510
4472
  const source = capture.payload?.source;
4473
+ this.#handledStopFingerprints.clear();
4474
+ this.#pendingPrompts = [];
4511
4475
  this.#snapshot = {
4512
4476
  ...this.#snapshot,
4513
4477
  connectionState: "connected"
@@ -4557,6 +4521,8 @@ var LiveUiRuntimeHost = class {
4557
4521
  async executeCommand(command) {
4558
4522
  switch (command.id) {
4559
4523
  case "clear-log":
4524
+ this.#handledStopFingerprints.clear();
4525
+ this.#pendingPrompts = [];
4560
4526
  this.#snapshot = {
4561
4527
  ...this.#snapshot,
4562
4528
  logs: [],
@@ -4601,13 +4567,13 @@ var LiveUiRuntimeHost = class {
4601
4567
  case "steps-count": {
4602
4568
  const currentContinuation = this.#continuation.getSnapshot();
4603
4569
  const updatedSnapshot = this.#continuation.updateInteractiveDraft({
4604
- maxContinues: command.steps,
4570
+ maxContinues: currentContinuation.injectionsUsed + command.steps,
4605
4571
  maxDurationMs: currentContinuation.maxDurationMs,
4606
4572
  prompt: currentContinuation.promptText
4607
4573
  });
4608
4574
  this.#syncContinuationState(updatedSnapshot);
4609
- this.#pushEventLine(`Updated the max turns count to ${command.steps}.`);
4610
- return this.#createUpdate(`Updated the live max turns count to ${command.steps}.`);
4575
+ this.#pushEventLine(`Updated the remaining max turns count to ${command.steps}.`);
4576
+ return this.#createUpdate(`Updated the live remaining max turns count to ${command.steps}.`);
4611
4577
  }
4612
4578
  case "stop-script":
4613
4579
  this.#verificationCommand = command.command;
@@ -4692,6 +4658,12 @@ var LiveUiRuntimeHost = class {
4692
4658
  }
4693
4659
  recordStopCaptureHandled(event) {
4694
4660
  const completedAt = Date.now();
4661
+ const terminalStatusMessage = buildTerminalStatusMessage(event);
4662
+ const stopFingerprint = this.#buildStopFingerprint(event, terminalStatusMessage);
4663
+ if (this.#handledStopFingerprints.has(stopFingerprint)) {
4664
+ return;
4665
+ }
4666
+ this.#handledStopFingerprints.add(stopFingerprint);
4695
4667
  const pendingPrompt = this.#pendingPrompts.shift() ?? null;
4696
4668
  const transcriptUserMessage = readUserMessageFromCapture(event.capture);
4697
4669
  const existingGroupIndex = event.capture.payload?.turn_id ? this.#snapshot.logs.findIndex((group) => group.id === event.capture.payload?.turn_id) : -1;
@@ -4714,7 +4686,6 @@ var LiveUiRuntimeHost = class {
4714
4686
  );
4715
4687
  }
4716
4688
  entries.push(this.#nextLogEntry("hook", hookSummary.text, completedAt));
4717
- const terminalStatusMessage = buildTerminalStatusMessage(event);
4718
4689
  if (terminalStatusMessage) {
4719
4690
  entries.push(this.#nextLogEntry("status", terminalStatusMessage, completedAt));
4720
4691
  }
@@ -4724,24 +4695,26 @@ var LiveUiRuntimeHost = class {
4724
4695
  durationMs: turnStartedAt !== null ? Math.max(completedAt - turnStartedAt, 0) : existingGroup?.durationMs ?? null,
4725
4696
  entries,
4726
4697
  id: groupId,
4727
- totalTurns: toUiTotalTurns(continuationSnapshot.maxContinues),
4698
+ totalTurns: null,
4728
4699
  turn: turnNumber
4729
4700
  };
4730
4701
  const nextLogs = existingGroupIndex >= 0 ? this.#snapshot.logs.map((group, index) => index === existingGroupIndex ? nextGroup : group) : [...this.#snapshot.logs, nextGroup];
4702
+ const nextTotalTurns = this.#getUiTotalTurns(continuationSnapshot, nextLogs);
4703
+ const finalizedLogs = this.#applyTotalTurns(nextLogs, nextTotalTurns);
4731
4704
  this.#snapshot = {
4732
4705
  ...this.#snapshot,
4733
4706
  connectionState: "connected",
4734
4707
  announcement: this.#buildAnnouncement(event, terminalStatusMessage),
4735
- logs: nextLogs,
4708
+ logs: finalizedLogs,
4736
4709
  runState: "idle",
4737
4710
  session: {
4738
4711
  ...this.#snapshot.session,
4739
- currentTurn: nextLogs.length,
4712
+ currentTurn: finalizedLogs.length,
4740
4713
  hookStatus: hookSummary.hookStatus,
4741
4714
  lastCodexOutputAt: completedAt,
4742
4715
  stopAt: getStopAt(continuationSnapshot),
4743
4716
  stopScript: this.#verificationCommand,
4744
- totalTurns: toUiTotalTurns(continuationSnapshot.maxContinues)
4717
+ totalTurns: nextTotalTurns
4745
4718
  }
4746
4719
  };
4747
4720
  this.#pushEventLine(
@@ -4783,8 +4756,8 @@ var LiveUiRuntimeHost = class {
4783
4756
  if (!promptText || targetGroup.entries.some((entry) => entry.kind === "user")) {
4784
4757
  return;
4785
4758
  }
4786
- const nextLogs = [...this.#snapshot.logs];
4787
- nextLogs[targetIndex] = {
4759
+ const nextLogs2 = [...this.#snapshot.logs];
4760
+ nextLogs2[targetIndex] = {
4788
4761
  ...targetGroup,
4789
4762
  entries: [
4790
4763
  this.#nextLogEntry("user", promptText, startedAt),
@@ -4793,7 +4766,10 @@ var LiveUiRuntimeHost = class {
4793
4766
  };
4794
4767
  this.#snapshot = {
4795
4768
  ...this.#snapshot,
4796
- logs: nextLogs
4769
+ logs: this.#applyTotalTurns(
4770
+ nextLogs2,
4771
+ this.#getUiTotalTurns(this.#continuation.getSnapshot(), nextLogs2)
4772
+ )
4797
4773
  };
4798
4774
  return;
4799
4775
  }
@@ -4807,12 +4783,14 @@ var LiveUiRuntimeHost = class {
4807
4783
  this.#nextLogEntry("user", promptText, startedAt)
4808
4784
  ],
4809
4785
  id: turnId,
4810
- totalTurns: toUiTotalTurns(this.#continuation.getSnapshot().maxContinues),
4786
+ totalTurns: null,
4811
4787
  turn: turnNumber
4812
4788
  };
4789
+ const nextLogs = [...this.#snapshot.logs, nextGroup];
4790
+ const nextTotalTurns = this.#getUiTotalTurns(this.#continuation.getSnapshot(), nextLogs);
4813
4791
  this.#snapshot = {
4814
4792
  ...this.#snapshot,
4815
- logs: [...this.#snapshot.logs, nextGroup]
4793
+ logs: this.#applyTotalTurns(nextLogs, nextTotalTurns)
4816
4794
  };
4817
4795
  }
4818
4796
  #buildAnnouncement(event, terminalStatusMessage) {
@@ -4872,10 +4850,13 @@ var LiveUiRuntimeHost = class {
4872
4850
  }
4873
4851
  #syncContinuationState(snapshot) {
4874
4852
  const connectionState = this.#snapshot.connectionState;
4875
- const currentTurn = this.#snapshot.logs.length;
4853
+ const nextTotalTurns = this.#getUiTotalTurns(snapshot, this.#snapshot.logs);
4854
+ const nextLogs = this.#applyTotalTurns(this.#snapshot.logs, nextTotalTurns);
4855
+ const currentTurn = nextLogs.length;
4876
4856
  this.#snapshot = {
4877
4857
  ...this.#snapshot,
4878
4858
  connectionState,
4859
+ logs: nextLogs,
4879
4860
  prompts: {
4880
4861
  ...this.#snapshot.prompts,
4881
4862
  static: snapshot.promptText
@@ -4886,10 +4867,37 @@ var LiveUiRuntimeHost = class {
4886
4867
  currentTurn,
4887
4868
  stopAt: getStopAt(snapshot),
4888
4869
  stopScript: this.#verificationCommand,
4889
- totalTurns: toUiTotalTurns(snapshot.maxContinues)
4870
+ totalTurns: nextTotalTurns
4890
4871
  }
4891
4872
  };
4892
4873
  }
4874
+ #buildStopFingerprint(event, terminalStatusMessage) {
4875
+ const turnId = event.capture.payload?.turn_id?.trim();
4876
+ if (turnId) {
4877
+ return `turn:${turnId}`;
4878
+ }
4879
+ return [
4880
+ event.capture.payload?.session_id ?? "session",
4881
+ event.capture.payload?.transcript_path ?? "",
4882
+ event.capture.payload?.last_assistant_message ?? "",
4883
+ terminalStatusMessage ?? ""
4884
+ ].join("::");
4885
+ }
4886
+ #getCompletedTurnCount(logs = this.#snapshot.logs) {
4887
+ return logs.filter((group) => group.entries.some((entry) => entry.kind !== "user")).length;
4888
+ }
4889
+ #getUiTotalTurns(snapshot, logs = this.#snapshot.logs) {
4890
+ if (!Number.isFinite(snapshot.maxContinues)) {
4891
+ return null;
4892
+ }
4893
+ return Math.max(this.#getCompletedTurnCount(logs) - snapshot.injectionsUsed + snapshot.maxContinues, 0);
4894
+ }
4895
+ #applyTotalTurns(logs, totalTurns) {
4896
+ return logs.map((group) => ({
4897
+ ...group,
4898
+ totalTurns
4899
+ }));
4900
+ }
4893
4901
  };
4894
4902
 
4895
4903
  // src/cli.ts