@basou/core 0.34.0 → 0.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +14 -5
- package/dist/index.js +280 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -857,11 +857,20 @@ type CodexRolloutToPayloadOptions = {
|
|
|
857
857
|
* provenance-level events from the rollout's message-level records:
|
|
858
858
|
*
|
|
859
859
|
* - `session_started` / `session_ended` from the first / last timestamped record.
|
|
860
|
-
* - `command_executed` from each
|
|
861
|
-
*
|
|
862
|
-
*
|
|
863
|
-
*
|
|
864
|
-
*
|
|
860
|
+
* - `command_executed` from each shell execution, recorded as `bash -c "<cmd>"`.
|
|
861
|
+
* Codex has written these two different ways, and BOTH are read (an operator's
|
|
862
|
+
* `~/.codex/sessions` holds a mix across a CLI upgrade):
|
|
863
|
+
* - one `function_call` named `exec_command` per command, with JSON
|
|
864
|
+
* `arguments` (`{ cmd, workdir }`) and a `function_call_output` whose text
|
|
865
|
+
* carries `Process exited with code N` / `Wall time: X seconds`;
|
|
866
|
+
* - a scripted `custom_tool_call` (see
|
|
867
|
+
* {@link readExecCommandsFromScript}) whose `input` is a JS program that
|
|
868
|
+
* calls `tools.exec_command({ cmd, workdir })` — possibly several times in
|
|
869
|
+
* one call — and whose output reports only `Wall time X seconds` for the
|
|
870
|
+
* whole script, with no per-command exit code.
|
|
871
|
+
* Reading only the first shape made every session recorded by a CLI that had
|
|
872
|
+
* moved to the second derive ZERO commands, so the whole session was dropped
|
|
873
|
+
* as "no actions" — capture went silently blind rather than degrading.
|
|
865
874
|
*
|
|
866
875
|
* Per-session `metrics` are also derived: token totals from the cumulative
|
|
867
876
|
* `token_count` events; active time from the real `task_started` ->
|
package/dist/index.js
CHANGED
|
@@ -701,6 +701,32 @@ function codexRolloutToImportPayload(records, options) {
|
|
|
701
701
|
continue;
|
|
702
702
|
}
|
|
703
703
|
if (readString4(record.type) !== "response_item") continue;
|
|
704
|
+
if (readString4(payload2.type) === "custom_tool_call") {
|
|
705
|
+
if (readString4(payload2.name) !== SCRIPT_TOOL_NAME) continue;
|
|
706
|
+
const scan = scanScript(readString4(payload2.input));
|
|
707
|
+
if (scan.commands.length === 0) continue;
|
|
708
|
+
const output2 = readCallId(payload2.call_id, outputsByCallId);
|
|
709
|
+
const durationMs = scan.toolCallCount === 1 ? parseWallTimeMs(output2) : 0;
|
|
710
|
+
const scriptTsMs = Date.parse(ts);
|
|
711
|
+
if (Number.isFinite(scriptTsMs)) engagementTsMs.push(scriptTsMs);
|
|
712
|
+
for (const command2 of scan.commands) {
|
|
713
|
+
derived.push(
|
|
714
|
+
// The per-command exit code is absent from this format (the script
|
|
715
|
+
// would have to print it), so it stays null — "unknown", not "0".
|
|
716
|
+
commandExecutedEvent2(
|
|
717
|
+
ts,
|
|
718
|
+
placeholderSessionId,
|
|
719
|
+
command2.cmd,
|
|
720
|
+
command2.workdir ?? workingDir ?? ".",
|
|
721
|
+
{
|
|
722
|
+
exitCode: null,
|
|
723
|
+
durationMs
|
|
724
|
+
}
|
|
725
|
+
)
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
704
730
|
if (readString4(payload2.type) !== "function_call") continue;
|
|
705
731
|
if (readString4(payload2.name) !== "exec_command") continue;
|
|
706
732
|
const command = readExecCommand(payload2.arguments);
|
|
@@ -840,6 +866,250 @@ function readCallId(value, outputs) {
|
|
|
840
866
|
const callId = readString4(value);
|
|
841
867
|
return callId !== void 0 ? outputs.get(callId) : void 0;
|
|
842
868
|
}
|
|
869
|
+
var SCRIPT_TOOL_NAME = "exec";
|
|
870
|
+
var TOOL_CALL_PREFIX = "tools.";
|
|
871
|
+
var EXEC_TOOL = "exec_command";
|
|
872
|
+
function scanScript(script) {
|
|
873
|
+
const scan = { commands: [], toolCallCount: 0 };
|
|
874
|
+
if (script === void 0) return scan;
|
|
875
|
+
try {
|
|
876
|
+
collectScriptCalls(script, scan);
|
|
877
|
+
} catch {
|
|
878
|
+
}
|
|
879
|
+
return scan;
|
|
880
|
+
}
|
|
881
|
+
function collectScriptCalls(script, scan) {
|
|
882
|
+
let i = 0;
|
|
883
|
+
while (i < script.length) {
|
|
884
|
+
const skipped = skipNonCode(script, i);
|
|
885
|
+
if (skipped !== i) {
|
|
886
|
+
if (skipped === -1) return;
|
|
887
|
+
i = skipped;
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
if (!script.startsWith(TOOL_CALL_PREFIX, i) || isIdentifierChar(script[i - 1])) {
|
|
891
|
+
i++;
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
894
|
+
const nameStart = i + TOOL_CALL_PREFIX.length;
|
|
895
|
+
const nameEnd = readIdentifierEnd(script, nameStart);
|
|
896
|
+
const open = skipWhitespaceAndComments(script, nameEnd);
|
|
897
|
+
if (nameEnd === nameStart || open === -1 || script[open] !== "(") {
|
|
898
|
+
i = nameStart;
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
scan.toolCallCount++;
|
|
902
|
+
const argsStart = skipWhitespaceAndComments(script, open + 1);
|
|
903
|
+
i = open + 1;
|
|
904
|
+
if (argsStart === -1 || script[argsStart] !== "{") continue;
|
|
905
|
+
const argsEnd = findObjectEnd(script, argsStart);
|
|
906
|
+
if (argsEnd === -1) return;
|
|
907
|
+
i = argsStart + 1;
|
|
908
|
+
if (script.slice(nameStart, nameEnd) !== EXEC_TOOL) continue;
|
|
909
|
+
const command = readExecArguments(script.slice(argsStart, argsEnd + 1));
|
|
910
|
+
if (command !== void 0) scan.commands.push(command);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
function isIdentifierChar(ch) {
|
|
914
|
+
return ch !== void 0 && /[A-Za-z0-9_$]/.test(ch);
|
|
915
|
+
}
|
|
916
|
+
function skipNonCode(script, at) {
|
|
917
|
+
const ch = script[at];
|
|
918
|
+
if (ch === '"' || ch === "'" || ch === "`") return skipStringLiteral(script, at);
|
|
919
|
+
if (ch !== "/") return at;
|
|
920
|
+
const next = script[at + 1];
|
|
921
|
+
if (next === "/") {
|
|
922
|
+
const eol = script.indexOf("\n", at + 2);
|
|
923
|
+
return eol === -1 ? script.length : eol + 1;
|
|
924
|
+
}
|
|
925
|
+
if (next === "*") {
|
|
926
|
+
const end = script.indexOf("*/", at + 2);
|
|
927
|
+
return end === -1 ? -1 : end + 2;
|
|
928
|
+
}
|
|
929
|
+
return at;
|
|
930
|
+
}
|
|
931
|
+
function skipStringLiteral(script, at) {
|
|
932
|
+
const quote = script[at];
|
|
933
|
+
for (let i = at + 1; i < script.length; i++) {
|
|
934
|
+
const ch = script[i];
|
|
935
|
+
if (ch === "\\") {
|
|
936
|
+
i++;
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
if (ch === quote) return i + 1;
|
|
940
|
+
if (quote === "`" && ch === "$" && script[i + 1] === "{") {
|
|
941
|
+
const end = findObjectEnd(script, i + 1);
|
|
942
|
+
if (end === -1) return -1;
|
|
943
|
+
i = end;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return -1;
|
|
947
|
+
}
|
|
948
|
+
function skipWhitespaceAndComments(script, at) {
|
|
949
|
+
let i = at;
|
|
950
|
+
while (i < script.length) {
|
|
951
|
+
if (/\s/.test(script[i] ?? "")) {
|
|
952
|
+
i++;
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
if (script[i] !== "/") return i;
|
|
956
|
+
const skipped = skipNonCode(script, i);
|
|
957
|
+
if (skipped === -1) return -1;
|
|
958
|
+
if (skipped === i) return i;
|
|
959
|
+
i = skipped;
|
|
960
|
+
}
|
|
961
|
+
return i;
|
|
962
|
+
}
|
|
963
|
+
function readIdentifierEnd(script, start) {
|
|
964
|
+
let i = start;
|
|
965
|
+
while (i < script.length && /[A-Za-z0-9_$]/.test(script[i] ?? "")) i++;
|
|
966
|
+
return i;
|
|
967
|
+
}
|
|
968
|
+
function findObjectEnd(script, start) {
|
|
969
|
+
const closers = { "{": "}", "(": ")", "[": "]" };
|
|
970
|
+
const stack = [];
|
|
971
|
+
let i = start;
|
|
972
|
+
while (i < script.length) {
|
|
973
|
+
const skipped = skipNonCode(script, i);
|
|
974
|
+
if (skipped === -1) return -1;
|
|
975
|
+
if (skipped !== i) {
|
|
976
|
+
i = skipped;
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
const ch = script[i] ?? "";
|
|
980
|
+
const closer = closers[ch];
|
|
981
|
+
if (closer !== void 0) {
|
|
982
|
+
stack.push(closer);
|
|
983
|
+
} else if (ch === "}" || ch === ")" || ch === "]") {
|
|
984
|
+
if (stack.pop() !== ch) return -1;
|
|
985
|
+
if (stack.length === 0) return i;
|
|
986
|
+
}
|
|
987
|
+
i++;
|
|
988
|
+
}
|
|
989
|
+
return -1;
|
|
990
|
+
}
|
|
991
|
+
function readExecArguments(literal) {
|
|
992
|
+
const values = /* @__PURE__ */ new Map();
|
|
993
|
+
let i = 1;
|
|
994
|
+
while (i < literal.length) {
|
|
995
|
+
const at = skipWhitespaceAndComments(literal, i);
|
|
996
|
+
if (at === -1) return void 0;
|
|
997
|
+
const ch = literal[at];
|
|
998
|
+
if (ch === "}" || ch === void 0) break;
|
|
999
|
+
if (ch === ",") {
|
|
1000
|
+
i = at + 1;
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
if (literal.startsWith("...", at)) return void 0;
|
|
1004
|
+
let key;
|
|
1005
|
+
let afterKey;
|
|
1006
|
+
if (ch === '"' || ch === "'") {
|
|
1007
|
+
const end = skipStringLiteral(literal, at);
|
|
1008
|
+
if (end === -1) return void 0;
|
|
1009
|
+
key = literal.slice(at + 1, end - 1);
|
|
1010
|
+
afterKey = end;
|
|
1011
|
+
} else {
|
|
1012
|
+
afterKey = readIdentifierEnd(literal, at);
|
|
1013
|
+
if (afterKey === at) return void 0;
|
|
1014
|
+
key = literal.slice(at, afterKey);
|
|
1015
|
+
}
|
|
1016
|
+
const colon = skipWhitespaceAndComments(literal, afterKey);
|
|
1017
|
+
if (colon === -1) return void 0;
|
|
1018
|
+
if (literal[colon] !== ":") {
|
|
1019
|
+
if (key === "cmd" || key === "workdir") return void 0;
|
|
1020
|
+
i = colon;
|
|
1021
|
+
const next2 = skipToPropertyEnd(literal, colon);
|
|
1022
|
+
if (next2 === -1) return void 0;
|
|
1023
|
+
i = next2;
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
const valueAt = skipWhitespaceAndComments(literal, colon + 1);
|
|
1027
|
+
if (valueAt === -1) return void 0;
|
|
1028
|
+
const valueChar = literal[valueAt];
|
|
1029
|
+
let value;
|
|
1030
|
+
let afterValue = valueAt;
|
|
1031
|
+
if (valueChar === '"' || valueChar === "'" || valueChar === "`") {
|
|
1032
|
+
const end = skipStringLiteral(literal, valueAt);
|
|
1033
|
+
if (end === -1) return void 0;
|
|
1034
|
+
const decoded = unescapeScriptString(literal.slice(valueAt, end));
|
|
1035
|
+
afterValue = end;
|
|
1036
|
+
const after = skipWhitespaceAndComments(literal, end);
|
|
1037
|
+
if (after === -1) return void 0;
|
|
1038
|
+
const terminator = literal[after];
|
|
1039
|
+
if (terminator === "," || terminator === "}") {
|
|
1040
|
+
value = decoded.length > 0 ? decoded : void 0;
|
|
1041
|
+
afterValue = after;
|
|
1042
|
+
} else if (key === "cmd" || key === "workdir") {
|
|
1043
|
+
return void 0;
|
|
1044
|
+
}
|
|
1045
|
+
} else if (key === "cmd" || key === "workdir") {
|
|
1046
|
+
return void 0;
|
|
1047
|
+
}
|
|
1048
|
+
if (key === "cmd" || key === "workdir") {
|
|
1049
|
+
if (values.has(key)) return void 0;
|
|
1050
|
+
values.set(key, value);
|
|
1051
|
+
}
|
|
1052
|
+
const next = skipToPropertyEnd(literal, afterValue);
|
|
1053
|
+
if (next === -1) return void 0;
|
|
1054
|
+
i = next;
|
|
1055
|
+
}
|
|
1056
|
+
const cmd = values.get("cmd");
|
|
1057
|
+
if (cmd === void 0) return void 0;
|
|
1058
|
+
return { cmd, workdir: values.get("workdir") };
|
|
1059
|
+
}
|
|
1060
|
+
function skipToPropertyEnd(literal, at) {
|
|
1061
|
+
let i = at;
|
|
1062
|
+
while (i < literal.length) {
|
|
1063
|
+
const skipped = skipNonCode(literal, i);
|
|
1064
|
+
if (skipped === -1) return -1;
|
|
1065
|
+
if (skipped !== i) {
|
|
1066
|
+
i = skipped;
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
const ch = literal[i];
|
|
1070
|
+
if (ch === "," || ch === "}") return i;
|
|
1071
|
+
if (ch === "{" || ch === "(" || ch === "[") {
|
|
1072
|
+
const end = findObjectEnd(literal, i);
|
|
1073
|
+
if (end === -1) return -1;
|
|
1074
|
+
i = end + 1;
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
i++;
|
|
1078
|
+
}
|
|
1079
|
+
return -1;
|
|
1080
|
+
}
|
|
1081
|
+
var SCRIPT_STRING_ESCAPES = {
|
|
1082
|
+
n: "\n",
|
|
1083
|
+
r: "\r",
|
|
1084
|
+
t: " ",
|
|
1085
|
+
b: "\b",
|
|
1086
|
+
f: "\f",
|
|
1087
|
+
v: "\v",
|
|
1088
|
+
"0": "\0"
|
|
1089
|
+
};
|
|
1090
|
+
function unescapeScriptString(quoted) {
|
|
1091
|
+
return quoted.slice(1, -1).replace(
|
|
1092
|
+
/\\(u\{[0-9a-fA-F]{1,6}\}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{2}|\r\n|[\s\S])/g,
|
|
1093
|
+
(match, sequence) => {
|
|
1094
|
+
if (sequence.length <= 2) {
|
|
1095
|
+
if (sequence === "\n" || sequence === "\r" || sequence === "\r\n") return "";
|
|
1096
|
+
if (sequence === "u" || sequence === "x") return match;
|
|
1097
|
+
return SCRIPT_STRING_ESCAPES[sequence] ?? sequence;
|
|
1098
|
+
}
|
|
1099
|
+
const hex = sequence.startsWith("u{") ? sequence.slice(2, -1) : sequence.slice(1);
|
|
1100
|
+
return codePointOrLiteral(hex, match);
|
|
1101
|
+
}
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
function codePointOrLiteral(hex, literal) {
|
|
1105
|
+
const code = Number.parseInt(hex, 16);
|
|
1106
|
+
if (!Number.isInteger(code) || code < 0 || code > 1114111) return literal;
|
|
1107
|
+
try {
|
|
1108
|
+
return String.fromCodePoint(code);
|
|
1109
|
+
} catch {
|
|
1110
|
+
return literal;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
843
1113
|
function turnIntervalFromComplete(endTs, payload, startMsByTurnId) {
|
|
844
1114
|
const endMs = Date.parse(endTs);
|
|
845
1115
|
if (!Number.isFinite(endMs)) return void 0;
|
|
@@ -871,7 +1141,7 @@ function parseExitCode(output) {
|
|
|
871
1141
|
}
|
|
872
1142
|
function parseWallTimeMs(output) {
|
|
873
1143
|
if (output === void 0) return 0;
|
|
874
|
-
const match = output.match(/Wall time
|
|
1144
|
+
const match = output.match(/Wall time:?\s*([\d.]+)\s*seconds/);
|
|
875
1145
|
if (match?.[1] === void 0) return 0;
|
|
876
1146
|
const seconds = Number.parseFloat(match[1]);
|
|
877
1147
|
return Number.isFinite(seconds) ? Math.round(seconds * 1e3) : 0;
|
|
@@ -882,13 +1152,20 @@ function indexOutputs(records) {
|
|
|
882
1152
|
if (readString4(record.type) !== "response_item") continue;
|
|
883
1153
|
const payload = isObject4(record.payload) ? record.payload : void 0;
|
|
884
1154
|
if (payload === void 0) continue;
|
|
885
|
-
|
|
1155
|
+
const payloadType = readString4(payload.type);
|
|
1156
|
+
const output = payloadType === "function_call_output" ? readString4(payload.output) : payloadType === "custom_tool_call_output" ? readOutputParts(payload.output) : void 0;
|
|
886
1157
|
const callId = readString4(payload.call_id);
|
|
887
|
-
const output = readString4(payload.output);
|
|
888
1158
|
if (callId !== void 0 && output !== void 0) byId.set(callId, output);
|
|
889
1159
|
}
|
|
890
1160
|
return byId;
|
|
891
1161
|
}
|
|
1162
|
+
function readOutputParts(value) {
|
|
1163
|
+
const asString = readString4(value);
|
|
1164
|
+
if (asString !== void 0) return asString;
|
|
1165
|
+
if (!Array.isArray(value)) return void 0;
|
|
1166
|
+
const texts = value.map((part) => isObject4(part) ? readString4(part.text) : void 0).filter((text) => text !== void 0);
|
|
1167
|
+
return texts.length > 0 ? texts.join("\n") : void 0;
|
|
1168
|
+
}
|
|
892
1169
|
|
|
893
1170
|
// src/approval/approval-store.ts
|
|
894
1171
|
import { readdir } from "fs/promises";
|