@usulpro/codex-bee 0.1.1 → 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/README.md +22 -109
- package/dist/cli.js +230 -439
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -537,7 +537,6 @@ function renderContinuationPrompt(promptTemplate, capture) {
|
|
|
537
537
|
}
|
|
538
538
|
|
|
539
539
|
// src/auto-continue.ts
|
|
540
|
-
var DURATION_INPUT_PATTERN = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/i;
|
|
541
540
|
var PROMPT_PREVIEW_LENGTH = 96;
|
|
542
541
|
var SINGLE_LINE_WHITESPACE_PATTERN2 = /\s+/g;
|
|
543
542
|
var DEFAULT_CONTINUATION_PROMPT = "continue";
|
|
@@ -561,38 +560,6 @@ function describeContinuationSource(source) {
|
|
|
561
560
|
function toErrorMessage2(error) {
|
|
562
561
|
return error instanceof Error ? error.message : String(error);
|
|
563
562
|
}
|
|
564
|
-
function parseDurationInput(value, options) {
|
|
565
|
-
const trimmedValue = value.trim();
|
|
566
|
-
if (!trimmedValue || /^off$/i.test(trimmedValue)) {
|
|
567
|
-
if (options?.allowEmpty ?? true) {
|
|
568
|
-
return null;
|
|
569
|
-
}
|
|
570
|
-
throw new Error("Duration is required.");
|
|
571
|
-
}
|
|
572
|
-
const match = trimmedValue.match(DURATION_INPUT_PATTERN);
|
|
573
|
-
if (!match) {
|
|
574
|
-
throw new Error(`Unsupported duration value: ${value}`);
|
|
575
|
-
}
|
|
576
|
-
const amount = Number.parseFloat(match[1]);
|
|
577
|
-
const unit = (match[2] ?? "m").toLowerCase();
|
|
578
|
-
if (!Number.isFinite(amount) || amount <= 0) {
|
|
579
|
-
throw new Error(`Unsupported duration value: ${value}`);
|
|
580
|
-
}
|
|
581
|
-
const multiplier = {
|
|
582
|
-
h: 60 * 60 * 1e3,
|
|
583
|
-
m: 60 * 1e3,
|
|
584
|
-
ms: 1,
|
|
585
|
-
s: 1e3
|
|
586
|
-
}[unit];
|
|
587
|
-
if (multiplier === void 0) {
|
|
588
|
-
throw new Error(`Unsupported duration value: ${value}`);
|
|
589
|
-
}
|
|
590
|
-
const durationMs = Math.round(amount * multiplier);
|
|
591
|
-
if (durationMs <= 0) {
|
|
592
|
-
throw new Error(`Unsupported duration value: ${value}`);
|
|
593
|
-
}
|
|
594
|
-
return durationMs;
|
|
595
|
-
}
|
|
596
563
|
function formatDurationMs(value) {
|
|
597
564
|
if (value === null) {
|
|
598
565
|
return "off";
|
|
@@ -815,47 +782,17 @@ bee
|
|
|
815
782
|
Autonomous wrapper around Codex CLI.
|
|
816
783
|
|
|
817
784
|
Usage:
|
|
818
|
-
bee [options] <command> [commandArgs]
|
|
819
|
-
bee [options] -- <command> [commandArgs]
|
|
785
|
+
bee [bee options] <command> [commandArgs]
|
|
786
|
+
bee [bee options] -- <command> [commandArgs]
|
|
820
787
|
|
|
821
|
-
|
|
822
|
-
-h, --help
|
|
823
|
-
--version
|
|
824
|
-
--continue-once <prompt> Inject one follow-up prompt after the first Stop event
|
|
825
|
-
--continue-once-file <path> Load the one-shot follow-up prompt from a file
|
|
826
|
-
--continue-once-agent-file <path>
|
|
827
|
-
Load a one-shot bee-agent config from a file
|
|
828
|
-
--continue-loop <prompt> Inject the same follow-up prompt after multiple Stop events
|
|
829
|
-
--continue-loop-file <path> Load the loop follow-up prompt from a file
|
|
830
|
-
--continue-loop-agent-file <path>
|
|
831
|
-
Load a loop bee-agent config from a file
|
|
832
|
-
--max-continues <count> Guardrail for loop injections (default: 3)
|
|
833
|
-
--max-duration <duration> Session guardrail, for example 90m or 1.5h
|
|
834
|
-
--verify-command <command> Run a verification shell command after each Stop; continue only while it fails
|
|
835
|
-
--inject-delay-ms <milliseconds> Optional debug delay before prompt injection
|
|
788
|
+
Bee options:
|
|
789
|
+
-h, --help Show help
|
|
790
|
+
--version Show version
|
|
836
791
|
|
|
837
|
-
|
|
838
|
-
|
|
792
|
+
All remaining arguments are passed through to the wrapped command unchanged.
|
|
793
|
+
Continuation prompts, guardrails, and verification settings are configured from the in-session UI.
|
|
839
794
|
`;
|
|
840
|
-
|
|
841
|
-
if (currentSource) {
|
|
842
|
-
throw new Error(`Use only one ${modeLabel} continuation source.`);
|
|
843
|
-
}
|
|
844
|
-
return nextSource;
|
|
845
|
-
}
|
|
846
|
-
var WRAPPER_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set([
|
|
847
|
-
"--continue-loop",
|
|
848
|
-
"--continue-loop-agent-file",
|
|
849
|
-
"--continue-loop-file",
|
|
850
|
-
"--continue-once",
|
|
851
|
-
"--continue-once-agent-file",
|
|
852
|
-
"--continue-once-file",
|
|
853
|
-
"--inject-delay-ms",
|
|
854
|
-
"--max-continues",
|
|
855
|
-
"--max-duration",
|
|
856
|
-
"--verify-command"
|
|
857
|
-
]);
|
|
858
|
-
var WRAPPER_FLAGS_WITHOUT_VALUE = /* @__PURE__ */ new Set([
|
|
795
|
+
var WRAPPER_FLAGS = /* @__PURE__ */ new Set([
|
|
859
796
|
"-h",
|
|
860
797
|
"--help",
|
|
861
798
|
"--version"
|
|
@@ -876,31 +813,18 @@ function parseCliArguments(rawArgs) {
|
|
|
876
813
|
);
|
|
877
814
|
}
|
|
878
815
|
const wrapperArgs = [];
|
|
879
|
-
let index = 0;
|
|
880
|
-
while (index < rawArgs.length) {
|
|
816
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
881
817
|
const argument = rawArgs[index];
|
|
882
|
-
if (argument
|
|
883
|
-
return {
|
|
884
|
-
command: argument,
|
|
885
|
-
commandArgs: rawArgs.slice(index + 1),
|
|
886
|
-
wrapperArgs
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
if (WRAPPER_FLAGS_WITHOUT_VALUE.has(argument)) {
|
|
818
|
+
if (WRAPPER_FLAGS.has(argument)) {
|
|
890
819
|
wrapperArgs.push(argument);
|
|
891
|
-
index += 1;
|
|
892
820
|
continue;
|
|
893
821
|
}
|
|
894
|
-
if (
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
wrapperArgs
|
|
899
|
-
|
|
900
|
-
} else {
|
|
901
|
-
index += 1;
|
|
902
|
-
}
|
|
903
|
-
continue;
|
|
822
|
+
if (argument.startsWith("-")) {
|
|
823
|
+
return {
|
|
824
|
+
command: null,
|
|
825
|
+
commandArgs: [],
|
|
826
|
+
wrapperArgs: rawArgs
|
|
827
|
+
};
|
|
904
828
|
}
|
|
905
829
|
return {
|
|
906
830
|
command: argument,
|
|
@@ -916,16 +840,9 @@ function parseCliArguments(rawArgs) {
|
|
|
916
840
|
}
|
|
917
841
|
function parseWrapperOptions(rawArgs) {
|
|
918
842
|
const { command, commandArgs, wrapperArgs } = parseCliArguments(rawArgs);
|
|
919
|
-
let continueLoopSource = null;
|
|
920
|
-
let continueOnceSource = null;
|
|
921
|
-
let injectDelayMs = 0;
|
|
922
|
-
let maxContinues = 3;
|
|
923
|
-
let maxDurationMs = null;
|
|
924
843
|
let showHelp = false;
|
|
925
844
|
let showVersion = false;
|
|
926
|
-
|
|
927
|
-
for (let index = 0; index < wrapperArgs.length; index += 1) {
|
|
928
|
-
const argument = wrapperArgs[index];
|
|
845
|
+
for (const argument of wrapperArgs) {
|
|
929
846
|
if (argument === "-h" || argument === "--help") {
|
|
930
847
|
showHelp = true;
|
|
931
848
|
continue;
|
|
@@ -934,177 +851,18 @@ function parseWrapperOptions(rawArgs) {
|
|
|
934
851
|
showVersion = true;
|
|
935
852
|
continue;
|
|
936
853
|
}
|
|
937
|
-
if (argument
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
throw new Error("Missing value for --continue-once.");
|
|
941
|
-
}
|
|
942
|
-
continueOnceSource = setContinuationSource(
|
|
943
|
-
continueOnceSource,
|
|
944
|
-
{
|
|
945
|
-
kind: "inline",
|
|
946
|
-
template: prompt
|
|
947
|
-
},
|
|
948
|
-
"one-shot"
|
|
949
|
-
);
|
|
950
|
-
index += 1;
|
|
951
|
-
continue;
|
|
952
|
-
}
|
|
953
|
-
if (argument === "--continue-once-file") {
|
|
954
|
-
const path6 = wrapperArgs[index + 1];
|
|
955
|
-
if (!path6) {
|
|
956
|
-
throw new Error("Missing value for --continue-once-file.");
|
|
957
|
-
}
|
|
958
|
-
continueOnceSource = setContinuationSource(
|
|
959
|
-
continueOnceSource,
|
|
960
|
-
{
|
|
961
|
-
kind: "file",
|
|
962
|
-
path: path6
|
|
963
|
-
},
|
|
964
|
-
"one-shot"
|
|
965
|
-
);
|
|
966
|
-
index += 1;
|
|
967
|
-
continue;
|
|
968
|
-
}
|
|
969
|
-
if (argument === "--continue-once-agent-file") {
|
|
970
|
-
const path6 = wrapperArgs[index + 1];
|
|
971
|
-
if (!path6) {
|
|
972
|
-
throw new Error("Missing value for --continue-once-agent-file.");
|
|
973
|
-
}
|
|
974
|
-
continueOnceSource = setContinuationSource(
|
|
975
|
-
continueOnceSource,
|
|
976
|
-
{
|
|
977
|
-
kind: "bee-agent",
|
|
978
|
-
path: path6
|
|
979
|
-
},
|
|
980
|
-
"one-shot"
|
|
981
|
-
);
|
|
982
|
-
index += 1;
|
|
983
|
-
continue;
|
|
984
|
-
}
|
|
985
|
-
if (argument === "--continue-loop") {
|
|
986
|
-
const prompt = wrapperArgs[index + 1];
|
|
987
|
-
if (!prompt) {
|
|
988
|
-
throw new Error("Missing value for --continue-loop.");
|
|
989
|
-
}
|
|
990
|
-
continueLoopSource = setContinuationSource(
|
|
991
|
-
continueLoopSource,
|
|
992
|
-
{
|
|
993
|
-
kind: "inline",
|
|
994
|
-
template: prompt
|
|
995
|
-
},
|
|
996
|
-
"loop"
|
|
997
|
-
);
|
|
998
|
-
index += 1;
|
|
999
|
-
continue;
|
|
1000
|
-
}
|
|
1001
|
-
if (argument === "--continue-loop-file") {
|
|
1002
|
-
const path6 = wrapperArgs[index + 1];
|
|
1003
|
-
if (!path6) {
|
|
1004
|
-
throw new Error("Missing value for --continue-loop-file.");
|
|
1005
|
-
}
|
|
1006
|
-
continueLoopSource = setContinuationSource(
|
|
1007
|
-
continueLoopSource,
|
|
1008
|
-
{
|
|
1009
|
-
kind: "file",
|
|
1010
|
-
path: path6
|
|
1011
|
-
},
|
|
1012
|
-
"loop"
|
|
1013
|
-
);
|
|
1014
|
-
index += 1;
|
|
1015
|
-
continue;
|
|
1016
|
-
}
|
|
1017
|
-
if (argument === "--continue-loop-agent-file") {
|
|
1018
|
-
const path6 = wrapperArgs[index + 1];
|
|
1019
|
-
if (!path6) {
|
|
1020
|
-
throw new Error("Missing value for --continue-loop-agent-file.");
|
|
1021
|
-
}
|
|
1022
|
-
continueLoopSource = setContinuationSource(
|
|
1023
|
-
continueLoopSource,
|
|
1024
|
-
{
|
|
1025
|
-
kind: "bee-agent",
|
|
1026
|
-
path: path6
|
|
1027
|
-
},
|
|
1028
|
-
"loop"
|
|
854
|
+
if (argument.startsWith("-")) {
|
|
855
|
+
throw new Error(
|
|
856
|
+
`Unknown bee option: ${argument}. Continuation, guardrails, and verification are configured from the UI after launch.`
|
|
1029
857
|
);
|
|
1030
|
-
index += 1;
|
|
1031
|
-
continue;
|
|
1032
|
-
}
|
|
1033
|
-
if (argument === "--max-continues") {
|
|
1034
|
-
const value = wrapperArgs[index + 1];
|
|
1035
|
-
if (!value) {
|
|
1036
|
-
throw new Error("Missing value for --max-continues.");
|
|
1037
|
-
}
|
|
1038
|
-
const parsed = Number.parseInt(value, 10);
|
|
1039
|
-
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
1040
|
-
throw new Error(`Invalid --max-continues value: ${value}`);
|
|
1041
|
-
}
|
|
1042
|
-
maxContinues = parsed;
|
|
1043
|
-
index += 1;
|
|
1044
|
-
continue;
|
|
1045
|
-
}
|
|
1046
|
-
if (argument === "--inject-delay-ms") {
|
|
1047
|
-
const value = wrapperArgs[index + 1];
|
|
1048
|
-
if (!value) {
|
|
1049
|
-
throw new Error("Missing value for --inject-delay-ms.");
|
|
1050
|
-
}
|
|
1051
|
-
const parsed = Number.parseInt(value, 10);
|
|
1052
|
-
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
1053
|
-
throw new Error(`Invalid --inject-delay-ms value: ${value}`);
|
|
1054
|
-
}
|
|
1055
|
-
injectDelayMs = parsed;
|
|
1056
|
-
index += 1;
|
|
1057
|
-
continue;
|
|
1058
|
-
}
|
|
1059
|
-
if (argument === "--max-duration") {
|
|
1060
|
-
const value = wrapperArgs[index + 1];
|
|
1061
|
-
if (!value) {
|
|
1062
|
-
throw new Error("Missing value for --max-duration.");
|
|
1063
|
-
}
|
|
1064
|
-
maxDurationMs = parseDurationInput(value, {
|
|
1065
|
-
allowEmpty: false
|
|
1066
|
-
});
|
|
1067
|
-
index += 1;
|
|
1068
|
-
continue;
|
|
1069
|
-
}
|
|
1070
|
-
if (argument === "--verify-command") {
|
|
1071
|
-
const value = wrapperArgs[index + 1];
|
|
1072
|
-
if (!value) {
|
|
1073
|
-
throw new Error("Missing value for --verify-command.");
|
|
1074
|
-
}
|
|
1075
|
-
verificationCommand = value;
|
|
1076
|
-
index += 1;
|
|
1077
|
-
continue;
|
|
1078
858
|
}
|
|
1079
|
-
throw new Error(`
|
|
1080
|
-
}
|
|
1081
|
-
if (continueLoopSource && continueOnceSource) {
|
|
1082
|
-
throw new Error("Use either --continue-once or --continue-loop, not both.");
|
|
1083
|
-
}
|
|
1084
|
-
if (!continueLoopSource && maxContinues !== 3) {
|
|
1085
|
-
throw new Error("--max-continues requires --continue-loop.");
|
|
1086
|
-
}
|
|
1087
|
-
if (!continueLoopSource && !continueOnceSource && maxDurationMs !== null) {
|
|
1088
|
-
throw new Error("--max-duration requires --continue-once or --continue-loop.");
|
|
1089
|
-
}
|
|
1090
|
-
if (!continueLoopSource && !continueOnceSource && verificationCommand !== null) {
|
|
1091
|
-
throw new Error("--verify-command requires --continue-once or --continue-loop.");
|
|
1092
|
-
}
|
|
1093
|
-
const hasWrapperBehavior = continueLoopSource !== null || continueOnceSource !== null || injectDelayMs !== 0 || maxContinues !== 3 || maxDurationMs !== null || verificationCommand !== null;
|
|
1094
|
-
if (!showHelp && !showVersion && command === null && hasWrapperBehavior) {
|
|
1095
|
-
throw new Error("Missing command to wrap. Use `bee <command> [args]`.");
|
|
859
|
+
throw new Error(`Unexpected bee argument before the wrapped command: ${argument}`);
|
|
1096
860
|
}
|
|
1097
861
|
return {
|
|
1098
862
|
command,
|
|
1099
863
|
commandArgs,
|
|
1100
|
-
continueLoopSource,
|
|
1101
|
-
continueOnceSource,
|
|
1102
|
-
injectDelayMs,
|
|
1103
|
-
maxContinues,
|
|
1104
|
-
maxDurationMs,
|
|
1105
864
|
showHelp,
|
|
1106
|
-
showVersion
|
|
1107
|
-
verificationCommand
|
|
865
|
+
showVersion
|
|
1108
866
|
};
|
|
1109
867
|
}
|
|
1110
868
|
|
|
@@ -2133,6 +1891,11 @@ var PtyProxy = class {
|
|
|
2133
1891
|
this.#child = child;
|
|
2134
1892
|
child.stdout.on("data", (chunk) => {
|
|
2135
1893
|
const normalizedChunk = sanitizeTerminalOutput(chunk);
|
|
1894
|
+
this.#options.onOutputActivity?.({
|
|
1895
|
+
bytes: normalizedChunk.length,
|
|
1896
|
+
source: "stdout",
|
|
1897
|
+
timestamp: Date.now()
|
|
1898
|
+
});
|
|
2136
1899
|
if (this.#outputMuted) {
|
|
2137
1900
|
this.#bufferedStdoutChunks.push(Buffer.from(normalizedChunk));
|
|
2138
1901
|
return;
|
|
@@ -2141,6 +1904,11 @@ var PtyProxy = class {
|
|
|
2141
1904
|
});
|
|
2142
1905
|
child.stderr.on("data", (chunk) => {
|
|
2143
1906
|
const normalizedChunk = sanitizeTerminalOutput(chunk);
|
|
1907
|
+
this.#options.onOutputActivity?.({
|
|
1908
|
+
bytes: normalizedChunk.length,
|
|
1909
|
+
source: "stderr",
|
|
1910
|
+
timestamp: Date.now()
|
|
1911
|
+
});
|
|
2144
1912
|
if (this.#outputMuted) {
|
|
2145
1913
|
this.#bufferedStderrChunks.push(Buffer.from(normalizedChunk));
|
|
2146
1914
|
return;
|
|
@@ -2585,6 +2353,7 @@ function createDefaultRuntimeSnapshot() {
|
|
|
2585
2353
|
code: 0,
|
|
2586
2354
|
ok: true
|
|
2587
2355
|
},
|
|
2356
|
+
lastCodexOutputAt: null,
|
|
2588
2357
|
stopAt: null,
|
|
2589
2358
|
stopScript: "pnpm test",
|
|
2590
2359
|
totalTurns: null
|
|
@@ -3097,6 +2866,7 @@ import { Box as Box2, useApp, useInput, useStdin, useStdout } from "ink";
|
|
|
3097
2866
|
import { useEffect, useState } from "react";
|
|
3098
2867
|
|
|
3099
2868
|
// src/ui/core/view-model.ts
|
|
2869
|
+
var CODEX_ACTIVITY_WINDOW_MS = 1800;
|
|
3100
2870
|
var PROMPT_PREVIEW_LINE_LIMIT = 5;
|
|
3101
2871
|
var CODE_BLOCK_PATTERN = /```[\s\S]*?```/g;
|
|
3102
2872
|
function toSingleLine3(value) {
|
|
@@ -3141,18 +2911,37 @@ function formatHookStatus(snapshot) {
|
|
|
3141
2911
|
}
|
|
3142
2912
|
return `hook: ${snapshot.session.hookStatus.ok ? "\u2713" : "\u2717"} ${snapshot.session.hookStatus.code}`;
|
|
3143
2913
|
}
|
|
3144
|
-
function
|
|
2914
|
+
function hasRecentCodexActivity(snapshot, now = Date.now()) {
|
|
2915
|
+
if (snapshot.connectionState === "disconnected") {
|
|
2916
|
+
return false;
|
|
2917
|
+
}
|
|
2918
|
+
if (snapshot.session.lastCodexOutputAt === null) {
|
|
2919
|
+
return false;
|
|
2920
|
+
}
|
|
2921
|
+
return now - snapshot.session.lastCodexOutputAt <= CODEX_ACTIVITY_WINDOW_MS;
|
|
2922
|
+
}
|
|
2923
|
+
function getDisplayedRunState(snapshot, now = Date.now()) {
|
|
2924
|
+
if (hasRecentCodexActivity(snapshot, now)) {
|
|
2925
|
+
return "running";
|
|
2926
|
+
}
|
|
2927
|
+
if (snapshot.runState === "running") {
|
|
2928
|
+
return "quiet";
|
|
2929
|
+
}
|
|
2930
|
+
return "idle";
|
|
2931
|
+
}
|
|
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
|
+
}
|
|
3145
2936
|
const parts = [];
|
|
3146
2937
|
if (snapshot.session.currentTurn !== null && snapshot.session.totalTurns !== null) {
|
|
3147
2938
|
parts.push(`${snapshot.session.currentTurn}/${snapshot.session.totalTurns} turns`);
|
|
3148
|
-
} else if (snapshot.session.currentTurn !== null) {
|
|
3149
|
-
parts.push(`${snapshot.session.currentTurn} turns`);
|
|
3150
2939
|
}
|
|
3151
2940
|
const timeLeftLabel = formatTimeLeft(snapshot.session.stopAt, now);
|
|
3152
2941
|
if (timeLeftLabel) {
|
|
3153
2942
|
parts.push(timeLeftLabel);
|
|
3154
2943
|
}
|
|
3155
|
-
const hookStatusLabel = formatHookStatus(snapshot);
|
|
2944
|
+
const hookStatusLabel = snapshot.session.stopScript ? formatHookStatus(snapshot) : null;
|
|
3156
2945
|
if (hookStatusLabel) {
|
|
3157
2946
|
parts.push(hookStatusLabel);
|
|
3158
2947
|
}
|
|
@@ -3390,13 +3179,15 @@ function renderLine(line, width) {
|
|
|
3390
3179
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
3391
3180
|
var MAX_CONTENT_WIDTH = 200;
|
|
3392
3181
|
var MIN_CONTENT_WIDTH = 80;
|
|
3182
|
+
var LOADER_TICK_MS = 160;
|
|
3393
3183
|
var PROMPT_BAR = "\u2503";
|
|
3394
3184
|
var TIMESTAMP_WIDTH = 8;
|
|
3395
|
-
var
|
|
3396
|
-
|
|
3397
|
-
["
|
|
3398
|
-
["
|
|
3399
|
-
["
|
|
3185
|
+
var LOADER_GLYPHS = ["\u25A0", "\u25A0", "\u25A0", "\u25A0"];
|
|
3186
|
+
var LOADER_COLOR_FRAMES = [
|
|
3187
|
+
["#4F3500", "#8A5B00", "#D89200", "#FFD35A"],
|
|
3188
|
+
["#8A5B00", "#D89200", "#FFD35A", "#D89200"],
|
|
3189
|
+
["#D89200", "#FFD35A", "#D89200", "#8A5B00"],
|
|
3190
|
+
["#FFD35A", "#D89200", "#8A5B00", "#4F3500"]
|
|
3400
3191
|
];
|
|
3401
3192
|
function createPalette(stylesEnabled) {
|
|
3402
3193
|
if (!stylesEnabled) {
|
|
@@ -3527,41 +3318,6 @@ function buildFigletLines(title, maxWidth) {
|
|
|
3527
3318
|
}
|
|
3528
3319
|
return [title];
|
|
3529
3320
|
}
|
|
3530
|
-
function buildAnnouncementPanelLines(state, width, palette) {
|
|
3531
|
-
if (!state.runtime.announcement || width < 8) {
|
|
3532
|
-
return [];
|
|
3533
|
-
}
|
|
3534
|
-
const innerWidth = Math.max(width - 4, 4);
|
|
3535
|
-
const contentLines = wrapText(state.runtime.announcement.message, innerWidth);
|
|
3536
|
-
const borderColor = palette.announcementBorder ?? palette.accent ?? palette.primary;
|
|
3537
|
-
return [
|
|
3538
|
-
createTextLine("announcement-top", `\u256D${"\u2500".repeat(Math.max(width - 2, 0))}\u256E`, {
|
|
3539
|
-
backgroundColor: palette.announcementBackground,
|
|
3540
|
-
color: borderColor
|
|
3541
|
-
}),
|
|
3542
|
-
...contentLines.map((line, index) => createLine(`announcement-${index}`, [
|
|
3543
|
-
{
|
|
3544
|
-
backgroundColor: palette.announcementBackground,
|
|
3545
|
-
color: borderColor,
|
|
3546
|
-
text: "\u2502 "
|
|
3547
|
-
},
|
|
3548
|
-
{
|
|
3549
|
-
backgroundColor: palette.announcementBackground,
|
|
3550
|
-
color: palette.announcementText ?? palette.bodyText,
|
|
3551
|
-
text: padDisplayWidth(line, innerWidth)
|
|
3552
|
-
},
|
|
3553
|
-
{
|
|
3554
|
-
backgroundColor: palette.announcementBackground,
|
|
3555
|
-
color: borderColor,
|
|
3556
|
-
text: " \u2502"
|
|
3557
|
-
}
|
|
3558
|
-
], palette.announcementBackground)),
|
|
3559
|
-
createTextLine("announcement-bottom", `\u2570${"\u2500".repeat(Math.max(width - 2, 0))}\u256F`, {
|
|
3560
|
-
backgroundColor: palette.announcementBackground,
|
|
3561
|
-
color: borderColor
|
|
3562
|
-
})
|
|
3563
|
-
];
|
|
3564
|
-
}
|
|
3565
3321
|
function buildHeaderColumnLines(state, title, width, palette) {
|
|
3566
3322
|
const titleLines = buildFigletLines(title.toUpperCase(), Math.max(width, 16));
|
|
3567
3323
|
return [
|
|
@@ -3569,8 +3325,7 @@ function buildHeaderColumnLines(state, title, width, palette) {
|
|
|
3569
3325
|
createTextLine("versions", `codex-bee v${state.runtime.beeVersion} \u2022 codex v${state.runtime.codexVersion}`, {
|
|
3570
3326
|
color: palette.muted,
|
|
3571
3327
|
dimColor: !palette.muted
|
|
3572
|
-
})
|
|
3573
|
-
...buildAnnouncementPanelLines(state, width, palette)
|
|
3328
|
+
})
|
|
3574
3329
|
];
|
|
3575
3330
|
}
|
|
3576
3331
|
function buildHeaderLines(state, title, width, palette) {
|
|
@@ -3592,7 +3347,7 @@ function getLogColor(kind, palette) {
|
|
|
3592
3347
|
return palette.muted;
|
|
3593
3348
|
}
|
|
3594
3349
|
function buildDividerLabel(turn, totalTurns, durationMs, width) {
|
|
3595
|
-
const parts = [totalTurns === null ?
|
|
3350
|
+
const parts = [totalTurns === null ? `turn ${turn}` : `turn ${turn}/${totalTurns}`];
|
|
3596
3351
|
if (durationMs !== null) {
|
|
3597
3352
|
parts.push(formatCompactDuration(durationMs));
|
|
3598
3353
|
}
|
|
@@ -3713,17 +3468,17 @@ function buildLogLines(state, width, palette) {
|
|
|
3713
3468
|
state.runtime.logs.forEach((group, groupIndex) => {
|
|
3714
3469
|
if (groupIndex > 0) {
|
|
3715
3470
|
logLines.push(createSpacerLine(`divider-gap-${group.id}`, palette.logBackground));
|
|
3716
|
-
logLines.push(createTextLine(`divider-${group.id}`, buildDividerLabel(
|
|
3717
|
-
group.turn,
|
|
3718
|
-
group.totalTurns,
|
|
3719
|
-
group.durationMs,
|
|
3720
|
-
width
|
|
3721
|
-
), {
|
|
3722
|
-
backgroundColor: palette.logBackground,
|
|
3723
|
-
color: palette.muted,
|
|
3724
|
-
dimColor: !palette.muted
|
|
3725
|
-
}));
|
|
3726
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
|
+
}));
|
|
3727
3482
|
group.entries.forEach((entry) => {
|
|
3728
3483
|
const previewLines = wrapAndLimit(normalizeLogEntryText(entry), contentWidth, 3);
|
|
3729
3484
|
previewLines.forEach((line, lineIndex) => {
|
|
@@ -3757,22 +3512,20 @@ function buildBodyLines(state, title, width, palette) {
|
|
|
3757
3512
|
...buildLogLines(state, width, palette)
|
|
3758
3513
|
];
|
|
3759
3514
|
}
|
|
3760
|
-
function buildLoaderSegments(palette, animationTick) {
|
|
3761
|
-
const
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3515
|
+
function buildLoaderSegments(palette, animationTick, isActive) {
|
|
3516
|
+
const frame = LOADER_COLOR_FRAMES[animationTick % LOADER_COLOR_FRAMES.length] ?? LOADER_COLOR_FRAMES[0];
|
|
3517
|
+
return LOADER_GLYPHS.map((glyph, index) => ({
|
|
3518
|
+
color: isActive ? frame[index] ?? palette.primary : ["#A87400", "#BB8500", "#CF9600", "#E0A700"][index] ?? palette.primary,
|
|
3519
|
+
dimColor: !isActive,
|
|
3765
3520
|
text: glyph
|
|
3766
3521
|
}));
|
|
3767
3522
|
}
|
|
3768
3523
|
function measureSegmentsWidth(segments) {
|
|
3769
3524
|
return segments.reduce((total, segment) => total + measureText(segment.text), 0);
|
|
3770
3525
|
}
|
|
3771
|
-
function buildStatusTokens(state, palette, animationTick, width) {
|
|
3772
|
-
const
|
|
3773
|
-
|
|
3774
|
-
text: glyph
|
|
3775
|
-
}));
|
|
3526
|
+
function buildStatusTokens(state, palette, animationTick, now, width) {
|
|
3527
|
+
const displayedRunState = getDisplayedRunState(state.runtime, now);
|
|
3528
|
+
const loaderToken = buildLoaderSegments(palette, animationTick, displayedRunState === "running");
|
|
3776
3529
|
const connectionSpec = getConnectionTokenSpec(state.runtime.connectionState, palette);
|
|
3777
3530
|
const connectionToken = [
|
|
3778
3531
|
{
|
|
@@ -3786,24 +3539,10 @@ function buildStatusTokens(state, palette, animationTick, width) {
|
|
|
3786
3539
|
text: connectionSpec.compactLabel
|
|
3787
3540
|
}
|
|
3788
3541
|
];
|
|
3789
|
-
const versionToken = [
|
|
3790
|
-
{
|
|
3791
|
-
color: palette.muted,
|
|
3792
|
-
dimColor: !palette.muted,
|
|
3793
|
-
text: `codex v${state.runtime.codexVersion}`
|
|
3794
|
-
}
|
|
3795
|
-
];
|
|
3796
|
-
const shortVersionToken = [
|
|
3797
|
-
{
|
|
3798
|
-
color: palette.muted,
|
|
3799
|
-
dimColor: !palette.muted,
|
|
3800
|
-
text: `v${state.runtime.codexVersion}`
|
|
3801
|
-
}
|
|
3802
|
-
];
|
|
3803
3542
|
const runStateToken = [
|
|
3804
3543
|
{
|
|
3805
|
-
color:
|
|
3806
|
-
text:
|
|
3544
|
+
color: displayedRunState === "running" ? palette.primary : displayedRunState === "quiet" ? palette.warning : palette.muted,
|
|
3545
|
+
text: displayedRunState === "running" ? "Running" : displayedRunState === "quiet" ? "Quiet" : "Idle"
|
|
3807
3546
|
}
|
|
3808
3547
|
];
|
|
3809
3548
|
const hotkeyToken = [
|
|
@@ -3835,9 +3574,8 @@ function buildStatusTokens(state, palette, animationTick, width) {
|
|
|
3835
3574
|
}
|
|
3836
3575
|
];
|
|
3837
3576
|
const variants = [
|
|
3838
|
-
[loaderToken, connectionToken,
|
|
3839
|
-
[loaderToken,
|
|
3840
|
-
[loaderToken, connectionToken, shortVersionToken, runStateToken, hotkeyToken, shortCommandsToken],
|
|
3577
|
+
[loaderToken, connectionToken, runStateToken, hotkeyToken, commandsToken],
|
|
3578
|
+
[loaderToken, compactConnectionToken, runStateToken, hotkeyToken, commandsToken],
|
|
3841
3579
|
[loaderToken, compactConnectionToken, runStateToken, shortHotkeyToken, shortCommandsToken]
|
|
3842
3580
|
];
|
|
3843
3581
|
for (const variant of variants) {
|
|
@@ -3851,9 +3589,9 @@ function buildStatusTokens(state, palette, animationTick, width) {
|
|
|
3851
3589
|
}
|
|
3852
3590
|
return variants[variants.length - 1] ?? [];
|
|
3853
3591
|
}
|
|
3854
|
-
function buildStatusBarLine(state, palette, animationTick, width) {
|
|
3592
|
+
function buildStatusBarLine(state, palette, animationTick, now, width) {
|
|
3855
3593
|
const backgroundColor = palette.statusBottomBackground;
|
|
3856
|
-
const tokens = buildStatusTokens(state, palette, animationTick, width);
|
|
3594
|
+
const tokens = buildStatusTokens(state, palette, animationTick, now, width);
|
|
3857
3595
|
const segments = tokens.flatMap((token, index) => index === 0 ? token : [
|
|
3858
3596
|
{
|
|
3859
3597
|
text: " \u2022 "
|
|
@@ -3933,30 +3671,42 @@ function buildDropdownLines(state, width, palette) {
|
|
|
3933
3671
|
], backgroundColor);
|
|
3934
3672
|
});
|
|
3935
3673
|
}
|
|
3936
|
-
function
|
|
3937
|
-
if (
|
|
3938
|
-
|
|
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
|
+
}));
|
|
3939
3685
|
}
|
|
3940
|
-
return
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
});
|
|
3686
|
+
return formatPromptPreview(getActivePrompt(state), 5).flatMap((line) => wrapAndLimit(line, width, 5)).slice(0, 5).map((line) => ({
|
|
3687
|
+
colorKind: "body",
|
|
3688
|
+
text: line
|
|
3689
|
+
}));
|
|
3944
3690
|
}
|
|
3945
3691
|
function buildPromptFooterLines(state, width, palette, cursorVisible, animationTick, now) {
|
|
3946
3692
|
const lines = [];
|
|
3947
3693
|
const modeColor = getModeColor(state.activeMode, palette);
|
|
3948
3694
|
const promptPreviewWidth = Math.max(width - 2, 16);
|
|
3949
|
-
const promptLines =
|
|
3695
|
+
const promptLines = buildPromptPreviewLines(state, promptPreviewWidth);
|
|
3950
3696
|
const inputFieldWidth = Math.max(width - 2, 12);
|
|
3951
3697
|
const inputContentWidth = Math.max(inputFieldWidth - 1, 8);
|
|
3952
3698
|
const composerLines = buildComposerVisualLines(state.composerText, inputContentWidth);
|
|
3953
3699
|
const cursorLineIndex = getComposerCursorLineIndex(composerLines, state.composerCursorOffset);
|
|
3954
3700
|
const placeholder = getVisibleSlashPlaceholder(state);
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
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
|
+
}
|
|
3960
3710
|
lines.push(createTextLine("prompt-top-border", "\u2500".repeat(width), {
|
|
3961
3711
|
backgroundColor: palette.promptBackground,
|
|
3962
3712
|
color: palette.promptBorder ?? palette.muted
|
|
@@ -3975,8 +3725,9 @@ function buildPromptFooterLines(state, width, palette, cursorVisible, animationT
|
|
|
3975
3725
|
},
|
|
3976
3726
|
{
|
|
3977
3727
|
backgroundColor: palette.promptBackground,
|
|
3978
|
-
color: palette.bodyText,
|
|
3979
|
-
|
|
3728
|
+
color: line.colorKind === "muted" ? palette.muted : palette.bodyText,
|
|
3729
|
+
dimColor: line.colorKind === "muted",
|
|
3730
|
+
text: padDisplayWidth(line.text, width - 2)
|
|
3980
3731
|
}
|
|
3981
3732
|
], palette.promptBackground));
|
|
3982
3733
|
});
|
|
@@ -4048,7 +3799,7 @@ function buildPromptFooterLines(state, width, palette, cursorVisible, animationT
|
|
|
4048
3799
|
color: palette.promptBorder ?? palette.muted
|
|
4049
3800
|
}));
|
|
4050
3801
|
lines.push(createSpacerLine("footer-gap", palette.footerBase));
|
|
4051
|
-
lines.push(buildStatusBarLine(state, palette, animationTick, width));
|
|
3802
|
+
lines.push(buildStatusBarLine(state, palette, animationTick, now, width));
|
|
4052
3803
|
lines.push(createSpacerLine("workspace-gap", palette.footerBase));
|
|
4053
3804
|
lines.push(createTextLine("workspace", formatWorkspaceLine(state.runtime.workspace), {
|
|
4054
3805
|
backgroundColor: palette.footerMutedBackground,
|
|
@@ -4060,8 +3811,7 @@ function buildPromptFooterLines(state, width, palette, cursorVisible, animationT
|
|
|
4060
3811
|
function buildBeeScreenLines(state, title, columns, cursorVisible, palette, animationTick, now) {
|
|
4061
3812
|
const bodyLines = buildBodyLines(state, title, columns, palette);
|
|
4062
3813
|
const footerLines = buildPromptFooterLines(state, columns, palette, cursorVisible, animationTick, now);
|
|
4063
|
-
|
|
4064
|
-
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];
|
|
4065
3815
|
}
|
|
4066
3816
|
function buildCodexScreenLines(state, columns, palette) {
|
|
4067
3817
|
const bodyLines = state.runtime.codexBufferLines.map((line, index) => createTextLine(`codex-${index}`, line, {
|
|
@@ -4083,8 +3833,7 @@ function buildCodexScreenLines(state, columns, palette) {
|
|
|
4083
3833
|
dimColor: !palette.muted
|
|
4084
3834
|
})
|
|
4085
3835
|
];
|
|
4086
|
-
|
|
4087
|
-
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];
|
|
4088
3837
|
}
|
|
4089
3838
|
function buildNarrowScreenLines(columns, palette) {
|
|
4090
3839
|
return [
|
|
@@ -4115,7 +3864,7 @@ function InkControlApp({
|
|
|
4115
3864
|
const [isPinnedToBottom, setIsPinnedToBottom] = useState(true);
|
|
4116
3865
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
4117
3866
|
const [clockNow, setClockNow] = useState(() => Date.now());
|
|
4118
|
-
const animationTick =
|
|
3867
|
+
const animationTick = Math.floor(clockNow / LOADER_TICK_MS);
|
|
4119
3868
|
const cursorVisible = true;
|
|
4120
3869
|
const [viewport] = useState(() => ({
|
|
4121
3870
|
columns: stdout.columns ?? 100,
|
|
@@ -4141,17 +3890,14 @@ function InkControlApp({
|
|
|
4141
3890
|
});
|
|
4142
3891
|
}, [allowQuit, controller, exit, state.exitRequested]);
|
|
4143
3892
|
useEffect(() => {
|
|
4144
|
-
if (state.runtime.session.stopAt === null) {
|
|
4145
|
-
return;
|
|
4146
|
-
}
|
|
4147
3893
|
setClockNow(Date.now());
|
|
4148
3894
|
const intervalId = setInterval(() => {
|
|
4149
3895
|
setClockNow(Date.now());
|
|
4150
|
-
},
|
|
3896
|
+
}, LOADER_TICK_MS);
|
|
4151
3897
|
return () => {
|
|
4152
3898
|
clearInterval(intervalId);
|
|
4153
3899
|
};
|
|
4154
|
-
}, [
|
|
3900
|
+
}, []);
|
|
4155
3901
|
const palette = createPalette(stdout.isTTY !== false && !process.env.NO_COLOR && process.env.TERM !== "dumb");
|
|
4156
3902
|
const contentWidth = Math.min(Math.max(viewport.columns, MIN_CONTENT_WIDTH), MAX_CONTENT_WIDTH);
|
|
4157
3903
|
const renderWidth = viewport.columns < MIN_CONTENT_WIDTH ? viewport.columns : contentWidth;
|
|
@@ -4455,6 +4201,7 @@ import { execFile } from "child_process";
|
|
|
4455
4201
|
import process7 from "process";
|
|
4456
4202
|
import { promisify } from "util";
|
|
4457
4203
|
var execFileAsync = promisify(execFile);
|
|
4204
|
+
var OUTPUT_ACTIVITY_NOTIFY_INTERVAL_MS = 250;
|
|
4458
4205
|
var MAX_EVENT_LINES = 8;
|
|
4459
4206
|
function cloneLogEntry(entry) {
|
|
4460
4207
|
return {
|
|
@@ -4533,6 +4280,7 @@ function createInitialSnapshot(options) {
|
|
|
4533
4280
|
session: {
|
|
4534
4281
|
currentTurn: 0,
|
|
4535
4282
|
hookStatus: null,
|
|
4283
|
+
lastCodexOutputAt: null,
|
|
4536
4284
|
stopAt: getStopAt(continuationSnapshot),
|
|
4537
4285
|
stopScript: options.initialVerificationCommand ?? null,
|
|
4538
4286
|
totalTurns: toUiTotalTurns(continuationSnapshot.maxContinues)
|
|
@@ -4549,6 +4297,13 @@ function getNextSleepTimestamp(hours, minutes, now = Date.now()) {
|
|
|
4549
4297
|
}
|
|
4550
4298
|
return nextTarget.getTime();
|
|
4551
4299
|
}
|
|
4300
|
+
function formatTimeLeftSuffix(stopAt) {
|
|
4301
|
+
const label = formatTimeLeft(stopAt);
|
|
4302
|
+
if (!label) {
|
|
4303
|
+
return "";
|
|
4304
|
+
}
|
|
4305
|
+
return ` (${label.replace(/^time left:\s*/u, "")} left)`;
|
|
4306
|
+
}
|
|
4552
4307
|
function createLogEntry(id, kind, text, timestamp) {
|
|
4553
4308
|
return {
|
|
4554
4309
|
id,
|
|
@@ -4664,6 +4419,8 @@ var LiveUiRuntimeHost = class {
|
|
|
4664
4419
|
#listeners = /* @__PURE__ */ new Set();
|
|
4665
4420
|
#recentEvents = [];
|
|
4666
4421
|
#entryCounter = 0;
|
|
4422
|
+
#handledStopFingerprints = /* @__PURE__ */ new Set();
|
|
4423
|
+
#lastOutputActivityNotificationAt = 0;
|
|
4667
4424
|
#pendingPrompts = [];
|
|
4668
4425
|
#snapshot;
|
|
4669
4426
|
#verificationCommand;
|
|
@@ -4695,10 +4452,26 @@ var LiveUiRuntimeHost = class {
|
|
|
4695
4452
|
this.#pushEventLine("Codex session disconnected.");
|
|
4696
4453
|
this.#notify();
|
|
4697
4454
|
}
|
|
4455
|
+
recordPtyOutputActivity(timestamp = Date.now()) {
|
|
4456
|
+
this.#snapshot = {
|
|
4457
|
+
...this.#snapshot,
|
|
4458
|
+
session: {
|
|
4459
|
+
...this.#snapshot.session,
|
|
4460
|
+
lastCodexOutputAt: timestamp
|
|
4461
|
+
}
|
|
4462
|
+
};
|
|
4463
|
+
if (timestamp - this.#lastOutputActivityNotificationAt < OUTPUT_ACTIVITY_NOTIFY_INTERVAL_MS) {
|
|
4464
|
+
return;
|
|
4465
|
+
}
|
|
4466
|
+
this.#lastOutputActivityNotificationAt = timestamp;
|
|
4467
|
+
this.#notify();
|
|
4468
|
+
}
|
|
4698
4469
|
recordAuxiliaryHookEvent(capture) {
|
|
4699
4470
|
const eventName = capture.payload?.hook_event_name ?? null;
|
|
4700
4471
|
if (eventName === "SessionStart") {
|
|
4701
4472
|
const source = capture.payload?.source;
|
|
4473
|
+
this.#handledStopFingerprints.clear();
|
|
4474
|
+
this.#pendingPrompts = [];
|
|
4702
4475
|
this.#snapshot = {
|
|
4703
4476
|
...this.#snapshot,
|
|
4704
4477
|
connectionState: "connected"
|
|
@@ -4719,9 +4492,11 @@ var LiveUiRuntimeHost = class {
|
|
|
4719
4492
|
runState: "running",
|
|
4720
4493
|
session: {
|
|
4721
4494
|
...this.#snapshot.session,
|
|
4722
|
-
currentTurn: this.#snapshot.logs.length
|
|
4495
|
+
currentTurn: this.#snapshot.logs.length,
|
|
4496
|
+
lastCodexOutputAt: submittedAt
|
|
4723
4497
|
}
|
|
4724
4498
|
};
|
|
4499
|
+
this.#lastOutputActivityNotificationAt = submittedAt;
|
|
4725
4500
|
this.#pushEventLine(
|
|
4726
4501
|
promptText ? `UserPromptSubmit received. Codex turn is running: ${formatCodexEcho(promptText)}` : "UserPromptSubmit received. Codex turn is running."
|
|
4727
4502
|
);
|
|
@@ -4746,6 +4521,8 @@ var LiveUiRuntimeHost = class {
|
|
|
4746
4521
|
async executeCommand(command) {
|
|
4747
4522
|
switch (command.id) {
|
|
4748
4523
|
case "clear-log":
|
|
4524
|
+
this.#handledStopFingerprints.clear();
|
|
4525
|
+
this.#pendingPrompts = [];
|
|
4749
4526
|
this.#snapshot = {
|
|
4750
4527
|
...this.#snapshot,
|
|
4751
4528
|
logs: [],
|
|
@@ -4763,9 +4540,11 @@ var LiveUiRuntimeHost = class {
|
|
|
4763
4540
|
maxDurationMs: command.minutes * 60 * 1e3,
|
|
4764
4541
|
prompt: currentContinuation.promptText
|
|
4765
4542
|
});
|
|
4543
|
+
const stopAt = getStopAt(updatedSnapshot);
|
|
4544
|
+
const timeLeftSuffix = formatTimeLeftSuffix(stopAt);
|
|
4766
4545
|
this.#syncContinuationState(updatedSnapshot);
|
|
4767
|
-
this.#pushEventLine(`Updated the session duration to ${command.minutes} minute(s).`);
|
|
4768
|
-
return this.#createUpdate(`Set the live session duration to ${command.minutes} minute(s).`);
|
|
4546
|
+
this.#pushEventLine(`Updated the session duration to ${command.minutes} minute(s)${timeLeftSuffix}.`);
|
|
4547
|
+
return this.#createUpdate(`Set the live session duration to ${command.minutes} minute(s)${timeLeftSuffix}.`);
|
|
4769
4548
|
}
|
|
4770
4549
|
case "quit":
|
|
4771
4550
|
return this.#createUpdate("Exiting the current codex-bee session.", {
|
|
@@ -4780,20 +4559,21 @@ var LiveUiRuntimeHost = class {
|
|
|
4780
4559
|
maxDurationMs: Math.max(stopAt - Date.now(), 1e3),
|
|
4781
4560
|
prompt: currentContinuation.promptText
|
|
4782
4561
|
});
|
|
4562
|
+
const timeLeftSuffix = formatTimeLeftSuffix(getStopAt(updatedSnapshot));
|
|
4783
4563
|
this.#syncContinuationState(updatedSnapshot);
|
|
4784
|
-
this.#pushEventLine(`Set the absolute stop time to ${command.time}.`);
|
|
4785
|
-
return this.#createUpdate(`Set the live stop time to ${command.time}.`);
|
|
4564
|
+
this.#pushEventLine(`Set the absolute stop time to ${command.time}${timeLeftSuffix}.`);
|
|
4565
|
+
return this.#createUpdate(`Set the live stop time to ${command.time}${timeLeftSuffix}.`);
|
|
4786
4566
|
}
|
|
4787
4567
|
case "steps-count": {
|
|
4788
4568
|
const currentContinuation = this.#continuation.getSnapshot();
|
|
4789
4569
|
const updatedSnapshot = this.#continuation.updateInteractiveDraft({
|
|
4790
|
-
maxContinues: command.steps,
|
|
4570
|
+
maxContinues: currentContinuation.injectionsUsed + command.steps,
|
|
4791
4571
|
maxDurationMs: currentContinuation.maxDurationMs,
|
|
4792
4572
|
prompt: currentContinuation.promptText
|
|
4793
4573
|
});
|
|
4794
4574
|
this.#syncContinuationState(updatedSnapshot);
|
|
4795
|
-
this.#pushEventLine(`Updated the max turns count to ${command.steps}.`);
|
|
4796
|
-
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}.`);
|
|
4797
4577
|
}
|
|
4798
4578
|
case "stop-script":
|
|
4799
4579
|
this.#verificationCommand = command.command;
|
|
@@ -4878,6 +4658,12 @@ var LiveUiRuntimeHost = class {
|
|
|
4878
4658
|
}
|
|
4879
4659
|
recordStopCaptureHandled(event) {
|
|
4880
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);
|
|
4881
4667
|
const pendingPrompt = this.#pendingPrompts.shift() ?? null;
|
|
4882
4668
|
const transcriptUserMessage = readUserMessageFromCapture(event.capture);
|
|
4883
4669
|
const existingGroupIndex = event.capture.payload?.turn_id ? this.#snapshot.logs.findIndex((group) => group.id === event.capture.payload?.turn_id) : -1;
|
|
@@ -4900,7 +4686,6 @@ var LiveUiRuntimeHost = class {
|
|
|
4900
4686
|
);
|
|
4901
4687
|
}
|
|
4902
4688
|
entries.push(this.#nextLogEntry("hook", hookSummary.text, completedAt));
|
|
4903
|
-
const terminalStatusMessage = buildTerminalStatusMessage(event);
|
|
4904
4689
|
if (terminalStatusMessage) {
|
|
4905
4690
|
entries.push(this.#nextLogEntry("status", terminalStatusMessage, completedAt));
|
|
4906
4691
|
}
|
|
@@ -4910,23 +4695,26 @@ var LiveUiRuntimeHost = class {
|
|
|
4910
4695
|
durationMs: turnStartedAt !== null ? Math.max(completedAt - turnStartedAt, 0) : existingGroup?.durationMs ?? null,
|
|
4911
4696
|
entries,
|
|
4912
4697
|
id: groupId,
|
|
4913
|
-
totalTurns:
|
|
4698
|
+
totalTurns: null,
|
|
4914
4699
|
turn: turnNumber
|
|
4915
4700
|
};
|
|
4916
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);
|
|
4917
4704
|
this.#snapshot = {
|
|
4918
4705
|
...this.#snapshot,
|
|
4919
4706
|
connectionState: "connected",
|
|
4920
4707
|
announcement: this.#buildAnnouncement(event, terminalStatusMessage),
|
|
4921
|
-
logs:
|
|
4708
|
+
logs: finalizedLogs,
|
|
4922
4709
|
runState: "idle",
|
|
4923
4710
|
session: {
|
|
4924
4711
|
...this.#snapshot.session,
|
|
4925
|
-
currentTurn:
|
|
4712
|
+
currentTurn: finalizedLogs.length,
|
|
4926
4713
|
hookStatus: hookSummary.hookStatus,
|
|
4714
|
+
lastCodexOutputAt: completedAt,
|
|
4927
4715
|
stopAt: getStopAt(continuationSnapshot),
|
|
4928
4716
|
stopScript: this.#verificationCommand,
|
|
4929
|
-
totalTurns:
|
|
4717
|
+
totalTurns: nextTotalTurns
|
|
4930
4718
|
}
|
|
4931
4719
|
};
|
|
4932
4720
|
this.#pushEventLine(
|
|
@@ -4968,8 +4756,8 @@ var LiveUiRuntimeHost = class {
|
|
|
4968
4756
|
if (!promptText || targetGroup.entries.some((entry) => entry.kind === "user")) {
|
|
4969
4757
|
return;
|
|
4970
4758
|
}
|
|
4971
|
-
const
|
|
4972
|
-
|
|
4759
|
+
const nextLogs2 = [...this.#snapshot.logs];
|
|
4760
|
+
nextLogs2[targetIndex] = {
|
|
4973
4761
|
...targetGroup,
|
|
4974
4762
|
entries: [
|
|
4975
4763
|
this.#nextLogEntry("user", promptText, startedAt),
|
|
@@ -4978,7 +4766,10 @@ var LiveUiRuntimeHost = class {
|
|
|
4978
4766
|
};
|
|
4979
4767
|
this.#snapshot = {
|
|
4980
4768
|
...this.#snapshot,
|
|
4981
|
-
logs:
|
|
4769
|
+
logs: this.#applyTotalTurns(
|
|
4770
|
+
nextLogs2,
|
|
4771
|
+
this.#getUiTotalTurns(this.#continuation.getSnapshot(), nextLogs2)
|
|
4772
|
+
)
|
|
4982
4773
|
};
|
|
4983
4774
|
return;
|
|
4984
4775
|
}
|
|
@@ -4992,12 +4783,14 @@ var LiveUiRuntimeHost = class {
|
|
|
4992
4783
|
this.#nextLogEntry("user", promptText, startedAt)
|
|
4993
4784
|
],
|
|
4994
4785
|
id: turnId,
|
|
4995
|
-
totalTurns:
|
|
4786
|
+
totalTurns: null,
|
|
4996
4787
|
turn: turnNumber
|
|
4997
4788
|
};
|
|
4789
|
+
const nextLogs = [...this.#snapshot.logs, nextGroup];
|
|
4790
|
+
const nextTotalTurns = this.#getUiTotalTurns(this.#continuation.getSnapshot(), nextLogs);
|
|
4998
4791
|
this.#snapshot = {
|
|
4999
4792
|
...this.#snapshot,
|
|
5000
|
-
logs:
|
|
4793
|
+
logs: this.#applyTotalTurns(nextLogs, nextTotalTurns)
|
|
5001
4794
|
};
|
|
5002
4795
|
}
|
|
5003
4796
|
#buildAnnouncement(event, terminalStatusMessage) {
|
|
@@ -5057,10 +4850,13 @@ var LiveUiRuntimeHost = class {
|
|
|
5057
4850
|
}
|
|
5058
4851
|
#syncContinuationState(snapshot) {
|
|
5059
4852
|
const connectionState = this.#snapshot.connectionState;
|
|
5060
|
-
const
|
|
4853
|
+
const nextTotalTurns = this.#getUiTotalTurns(snapshot, this.#snapshot.logs);
|
|
4854
|
+
const nextLogs = this.#applyTotalTurns(this.#snapshot.logs, nextTotalTurns);
|
|
4855
|
+
const currentTurn = nextLogs.length;
|
|
5061
4856
|
this.#snapshot = {
|
|
5062
4857
|
...this.#snapshot,
|
|
5063
4858
|
connectionState,
|
|
4859
|
+
logs: nextLogs,
|
|
5064
4860
|
prompts: {
|
|
5065
4861
|
...this.#snapshot.prompts,
|
|
5066
4862
|
static: snapshot.promptText
|
|
@@ -5071,10 +4867,37 @@ var LiveUiRuntimeHost = class {
|
|
|
5071
4867
|
currentTurn,
|
|
5072
4868
|
stopAt: getStopAt(snapshot),
|
|
5073
4869
|
stopScript: this.#verificationCommand,
|
|
5074
|
-
totalTurns:
|
|
4870
|
+
totalTurns: nextTotalTurns
|
|
5075
4871
|
}
|
|
5076
4872
|
};
|
|
5077
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
|
+
}
|
|
5078
4901
|
};
|
|
5079
4902
|
|
|
5080
4903
|
// src/cli.ts
|
|
@@ -5346,9 +5169,8 @@ async function main() {
|
|
|
5346
5169
|
}
|
|
5347
5170
|
const runId = randomUUID();
|
|
5348
5171
|
const runStartedAt = Date.now();
|
|
5349
|
-
const hasContinuation = Boolean(options.continueLoopSource || options.continueOnceSource);
|
|
5350
5172
|
const executionMode = resolveExecutionMode({
|
|
5351
|
-
hasContinuation,
|
|
5173
|
+
hasContinuation: false,
|
|
5352
5174
|
stdinIsTTY: process8.stdin.isTTY,
|
|
5353
5175
|
stdoutIsTTY: process8.stdout.isTTY
|
|
5354
5176
|
});
|
|
@@ -5387,28 +5209,7 @@ async function main() {
|
|
|
5387
5209
|
configPath: hookSetup.activeConfig?.path ?? hookSetup.globalPath
|
|
5388
5210
|
});
|
|
5389
5211
|
}
|
|
5390
|
-
|
|
5391
|
-
enabled: true,
|
|
5392
|
-
maxContinues: options.maxContinues,
|
|
5393
|
-
maxDurationMs: options.maxDurationMs,
|
|
5394
|
-
promptSource: options.continueLoopSource
|
|
5395
|
-
} : options.continueOnceSource ? {
|
|
5396
|
-
enabled: true,
|
|
5397
|
-
maxContinues: 1,
|
|
5398
|
-
maxDurationMs: options.maxDurationMs,
|
|
5399
|
-
promptSource: options.continueOnceSource
|
|
5400
|
-
} : null;
|
|
5401
|
-
if (initialContinuation) {
|
|
5402
|
-
if (isBeeAgentSource(initialContinuation.promptSource)) {
|
|
5403
|
-
readBeeAgentConfig(initialContinuation.promptSource, {
|
|
5404
|
-
cwd: process8.cwd()
|
|
5405
|
-
});
|
|
5406
|
-
} else {
|
|
5407
|
-
readContinuationTemplate(initialContinuation.promptSource, {
|
|
5408
|
-
cwd: process8.cwd()
|
|
5409
|
-
});
|
|
5410
|
-
}
|
|
5411
|
-
}
|
|
5212
|
+
let runtimeHost = null;
|
|
5412
5213
|
const proxy = new PtyProxy({
|
|
5413
5214
|
command: wrappedCommand,
|
|
5414
5215
|
commandArgs: options.commandArgs,
|
|
@@ -5416,20 +5217,21 @@ async function main() {
|
|
|
5416
5217
|
...process8.env,
|
|
5417
5218
|
CODEX_BEE_CAPTURE_ROOT: process8.cwd(),
|
|
5418
5219
|
CODEX_BEE_RUN_ID: runId
|
|
5220
|
+
},
|
|
5221
|
+
onOutputActivity: ({ timestamp }) => {
|
|
5222
|
+
runtimeHost?.recordPtyOutputActivity(timestamp);
|
|
5419
5223
|
}
|
|
5420
5224
|
});
|
|
5421
5225
|
const continuation = new ContinuationController({
|
|
5422
|
-
cwd: process8.cwd()
|
|
5423
|
-
initial: initialContinuation ?? void 0
|
|
5226
|
+
cwd: process8.cwd()
|
|
5424
5227
|
});
|
|
5425
|
-
|
|
5228
|
+
runtimeHost = new LiveUiRuntimeHost({
|
|
5426
5229
|
beeVersion: CLI_VERSION,
|
|
5427
5230
|
codexCommand: wrappedCommand,
|
|
5428
5231
|
continuation,
|
|
5429
5232
|
cwd: process8.cwd(),
|
|
5430
5233
|
initialAnnouncementMessage: startupAnnouncement,
|
|
5431
5234
|
initialConnectionState,
|
|
5432
|
-
initialVerificationCommand: options.verificationCommand,
|
|
5433
5235
|
promptProxy: proxy
|
|
5434
5236
|
});
|
|
5435
5237
|
const controller = new UiController(runtimeHost);
|
|
@@ -5454,17 +5256,6 @@ async function main() {
|
|
|
5454
5256
|
});
|
|
5455
5257
|
await proxy.start();
|
|
5456
5258
|
proxy.setInputInterceptor((chunk) => overlay.handleInput(chunk));
|
|
5457
|
-
if (initialContinuation) {
|
|
5458
|
-
if (initialContinuation.maxContinues > 1) {
|
|
5459
|
-
console.error(
|
|
5460
|
-
`[codex-bee] Auto-continue loop armed for up to ${initialContinuation.maxContinues} injections with ${continuation.getSnapshot().promptSourceLabel}.`
|
|
5461
|
-
);
|
|
5462
|
-
} else {
|
|
5463
|
-
console.error(
|
|
5464
|
-
`[codex-bee] One-shot continuation armed with ${continuation.getSnapshot().promptSourceLabel}.`
|
|
5465
|
-
);
|
|
5466
|
-
}
|
|
5467
|
-
}
|
|
5468
5259
|
if (initialConnectionState === "ready") {
|
|
5469
5260
|
void runHookEventWatcher({
|
|
5470
5261
|
cwd: process8.cwd(),
|
|
@@ -5495,7 +5286,7 @@ async function main() {
|
|
|
5495
5286
|
continuation,
|
|
5496
5287
|
cwd: process8.cwd(),
|
|
5497
5288
|
getVerificationCommand: () => runtimeHost.getVerificationCommand(),
|
|
5498
|
-
injectDelayMs:
|
|
5289
|
+
injectDelayMs: 0,
|
|
5499
5290
|
logger,
|
|
5500
5291
|
onPromptInjected: (event) => {
|
|
5501
5292
|
runtimeHost.recordContinuationPromptInjected(event);
|
|
@@ -5508,7 +5299,7 @@ async function main() {
|
|
|
5508
5299
|
runStartedAt,
|
|
5509
5300
|
runVerificationCommand,
|
|
5510
5301
|
signal: watcherAbortController.signal,
|
|
5511
|
-
verificationCommand:
|
|
5302
|
+
verificationCommand: null
|
|
5512
5303
|
}).catch((error) => {
|
|
5513
5304
|
const message = error instanceof Error ? error.message : String(error);
|
|
5514
5305
|
logger.log("continuation_watcher_stopped", {
|