@moda-ai/cli 1.55.15 → 1.55.17
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/{agent-a2xwfeeh.js → agent-cha3kg9d.js} +9 -9
- package/dist/{cli-86q35fpq.js → cli-0e8bfvn7.js} +8 -8
- package/dist/{cli-3gwbg0xp.js → cli-1exddmc0.js} +2 -2
- package/dist/{cli-b3cxcxgn.js → cli-3ezzeqcy.js} +669 -37
- package/dist/{cli-kf3d6y8c.js → cli-5hq827jt.js} +8 -2
- package/dist/{cli-xy6zn80e.js → cli-5q1gyvxn.js} +5 -5
- package/dist/{cli-7fc44hhq.js → cli-a1mv0ypm.js} +5 -5
- package/dist/{cli-zdeywyn9.js → cli-av3dqgj5.js} +4 -4
- package/dist/cli-bb7133m7.js +144 -0
- package/dist/{cli-2ars875d.js → cli-c8p69n55.js} +2 -2
- package/dist/{cli-atx672xv.js → cli-eazeanvt.js} +1 -1
- package/dist/{cli-53mvsfgn.js → cli-kg0kz05e.js} +3 -3
- package/dist/{cli-6ca1sz0a.js → cli-qmjnh4z9.js} +1 -1
- package/dist/cli.js +299 -103
- package/dist/{doctor-e2xrbpmp.js → doctor-kg0yq1wf.js} +9 -9
- package/dist/{fix-v8xka487.js → fix-q8er8egx.js} +7 -7
- package/dist/{harness-github-actions-301nak95.js → harness-github-actions-ver6cprq.js} +6 -6
- package/dist/{harness-28wbzncq.js → harness-v9s0k9s7.js} +6 -6
- package/dist/{index-ccdpy9ma.js → index-7zqa7t5c.js} +11 -11
- package/dist/{production-intelligence-0wqcvvxk.js → production-intelligence-k36jsvqx.js} +13 -13
- package/dist/{profile-commands-f5kysjrv.js → profile-commands-88sdwzbg.js} +3 -3
- package/dist/prompt-source-mcp.js +6 -6
- package/dist/{prompts-jg0stjfb.js → prompts-2r756vtd.js} +7 -7
- package/dist/{provision-25bw5rd7.js → provision-n3r7yway.js} +4 -4
- package/dist/{registry-39xzetsc.js → registry-5v63ga01.js} +4 -4
- package/dist/{skills-gjesa81c.js → skills-fed17et3.js} +6 -6
- package/dist/{sync-7ts9psky.js → sync-r4tncne4.js} +8 -8
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +20 -8
- package/dist/cli-438g4nwh.js +0 -465
|
@@ -678,6 +678,427 @@ function resetApiRequestCount() {
|
|
|
678
678
|
apiRequestCount = 0;
|
|
679
679
|
}
|
|
680
680
|
|
|
681
|
+
// src/batch-reads.ts
|
|
682
|
+
import { readFile } from "node:fs/promises";
|
|
683
|
+
var BATCH_MAX_TARGETS = 25;
|
|
684
|
+
var BATCH_CONCURRENCY = 4;
|
|
685
|
+
var ANCHOR_SUFFIX = /^(.+)@(\d+)$/;
|
|
686
|
+
function parseBatchTarget(text, anchors, usage) {
|
|
687
|
+
const raw = text.trim();
|
|
688
|
+
if (raw.startsWith("{")) {
|
|
689
|
+
let parsed;
|
|
690
|
+
try {
|
|
691
|
+
parsed = JSON.parse(raw);
|
|
692
|
+
} catch {
|
|
693
|
+
throw new CliInputError(`Could not parse the JSON target ${JSON.stringify(raw)}.`, usage, "bad_batch_target");
|
|
694
|
+
}
|
|
695
|
+
const record = parsed;
|
|
696
|
+
const id = record?.id ?? record?.conversation_id ?? record?.trace_id;
|
|
697
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
698
|
+
throw new CliInputError(`JSON target ${JSON.stringify(raw)} has no "id" string.`, usage, "bad_batch_target");
|
|
699
|
+
}
|
|
700
|
+
const index = record.msg_index ?? record.message_index;
|
|
701
|
+
if (index !== undefined && index !== null) {
|
|
702
|
+
if (!anchors) {
|
|
703
|
+
throw new CliInputError(`JSON target ${JSON.stringify(raw)} has a msg_index, which this command does not take.`, usage, "bad_batch_target");
|
|
704
|
+
}
|
|
705
|
+
if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) {
|
|
706
|
+
throw new CliInputError(`JSON target ${JSON.stringify(raw)} has an invalid msg_index (want an integer >= 0).`, usage, "bad_batch_target");
|
|
707
|
+
}
|
|
708
|
+
return { raw, id, msgIndex: index };
|
|
709
|
+
}
|
|
710
|
+
return { raw, id };
|
|
711
|
+
}
|
|
712
|
+
if (anchors) {
|
|
713
|
+
const match = ANCHOR_SUFFIX.exec(raw);
|
|
714
|
+
if (match) {
|
|
715
|
+
const msgIndex = Number(match[2]);
|
|
716
|
+
if (!Number.isSafeInteger(msgIndex)) {
|
|
717
|
+
throw new CliInputError(`Target ${JSON.stringify(raw)} has an @msg_index past ${Number.MAX_SAFE_INTEGER}. ` + 'If the id itself ends in @<digits>, pass it as {"id": "..."}.', usage, "bad_batch_target");
|
|
718
|
+
}
|
|
719
|
+
return { raw, id: match[1], msgIndex };
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return { raw, id: raw };
|
|
723
|
+
}
|
|
724
|
+
function parseTargetLines(text, anchors, usage) {
|
|
725
|
+
return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line !== "").map((line) => parseBatchTarget(line, anchors, usage));
|
|
726
|
+
}
|
|
727
|
+
async function readStdinText() {
|
|
728
|
+
if (process.stdin.isTTY) {
|
|
729
|
+
throw new CliInputError("--ids-from=- reads targets from stdin, but stdin is a terminal. Pipe ids in (one per line) or pass a file path.", undefined, "ids_from_tty");
|
|
730
|
+
}
|
|
731
|
+
const chunks = [];
|
|
732
|
+
for await (const chunk of process.stdin)
|
|
733
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
734
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
735
|
+
}
|
|
736
|
+
async function readIdsFrom(source, usage) {
|
|
737
|
+
if (source === "" || source === "true") {
|
|
738
|
+
throw new CliInputError("--ids-from needs a value: - (stdin) or a file path.", usage, "missing_flag_value");
|
|
739
|
+
}
|
|
740
|
+
if (source === "-")
|
|
741
|
+
return readStdinText();
|
|
742
|
+
try {
|
|
743
|
+
return await readFile(source, "utf8");
|
|
744
|
+
} catch (error) {
|
|
745
|
+
const code = error.code;
|
|
746
|
+
throw new CliInputError(`Could not read --ids-from file ${JSON.stringify(source)}${typeof code === "string" ? ` (${code})` : ""}.`, usage, "ids_from_unreadable");
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
async function collectBatchTargets(positionals, idsFrom, options) {
|
|
750
|
+
const targets = positionals.map((value) => parseBatchTarget(value, options.anchors, options.usage));
|
|
751
|
+
if (idsFrom !== undefined) {
|
|
752
|
+
targets.push(...parseTargetLines(await readIdsFrom(idsFrom, options.usage), options.anchors, options.usage));
|
|
753
|
+
}
|
|
754
|
+
const seen = new Set;
|
|
755
|
+
const unique = targets.filter((target) => {
|
|
756
|
+
const key = `${target.id}\x00${target.msgIndex ?? ""}`;
|
|
757
|
+
if (seen.has(key))
|
|
758
|
+
return false;
|
|
759
|
+
seen.add(key);
|
|
760
|
+
return true;
|
|
761
|
+
});
|
|
762
|
+
if (unique.length === 0) {
|
|
763
|
+
throw new CliInputError(`No targets: --ids-from gave no ids (one per line).`, options.usage, "missing_positional");
|
|
764
|
+
}
|
|
765
|
+
if (unique.length > BATCH_MAX_TARGETS) {
|
|
766
|
+
throw new CliInputError(`Got ${unique.length} targets; 'moda ${options.command}' reads at most ${BATCH_MAX_TARGETS} per call, so nothing was read. ` + `Split the ids into calls of ${BATCH_MAX_TARGETS} or fewer.`, options.usage, "batch_too_large");
|
|
767
|
+
}
|
|
768
|
+
return unique;
|
|
769
|
+
}
|
|
770
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
771
|
+
const results = new Array(items.length);
|
|
772
|
+
let next = 0;
|
|
773
|
+
let failure;
|
|
774
|
+
const worker = async () => {
|
|
775
|
+
while (!failure && next < items.length) {
|
|
776
|
+
const index = next++;
|
|
777
|
+
try {
|
|
778
|
+
results[index] = await fn(items[index], index);
|
|
779
|
+
} catch (error) {
|
|
780
|
+
failure ??= { error };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, worker));
|
|
785
|
+
if (failure)
|
|
786
|
+
throw failure.error;
|
|
787
|
+
return results;
|
|
788
|
+
}
|
|
789
|
+
function idNeedsJsonTarget(id) {
|
|
790
|
+
return ANCHOR_SUFFIX.test(id);
|
|
791
|
+
}
|
|
792
|
+
function contextTarget(id) {
|
|
793
|
+
const verbatim = id === id.trim() && !id.startsWith("{") && !idNeedsJsonTarget(id);
|
|
794
|
+
return verbatim ? id : JSON.stringify({ id });
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// src/transforms.ts
|
|
798
|
+
function asRecord(value) {
|
|
799
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
800
|
+
return value;
|
|
801
|
+
}
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
804
|
+
function asString(value) {
|
|
805
|
+
return typeof value === "string" ? value : null;
|
|
806
|
+
}
|
|
807
|
+
function asNumber(value) {
|
|
808
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
809
|
+
}
|
|
810
|
+
function frustrationAnchorIndex(row) {
|
|
811
|
+
if (!row)
|
|
812
|
+
return null;
|
|
813
|
+
const userQuotes = Array.isArray(row.user_quotes) ? row.user_quotes : [];
|
|
814
|
+
const firstQuote = asRecord(userQuotes[0]);
|
|
815
|
+
const resolved = asNumber(firstQuote?.msg_index) ?? asNumber(row.anchor_msg_index);
|
|
816
|
+
if (resolved !== null)
|
|
817
|
+
return resolved;
|
|
818
|
+
const keyTurns = Array.isArray(row.key_turns) ? row.key_turns : [];
|
|
819
|
+
const firstTurn = keyTurns.find((t) => typeof t === "number");
|
|
820
|
+
if (firstTurn !== undefined)
|
|
821
|
+
return firstTurn;
|
|
822
|
+
return asNumber(firstQuote?.turn);
|
|
823
|
+
}
|
|
824
|
+
function buildFrustrationAnchor(row) {
|
|
825
|
+
const conversation_id = asString(row.conversation_id) ?? "";
|
|
826
|
+
const userQuotes = Array.isArray(row.user_quotes) ? row.user_quotes : [];
|
|
827
|
+
const firstQuote = asRecord(userQuotes[0]);
|
|
828
|
+
const keyTurnsRaw = Array.isArray(row.key_turns) ? row.key_turns : [];
|
|
829
|
+
const all_turns = keyTurnsRaw.filter((t) => typeof t === "number");
|
|
830
|
+
const msg_index = frustrationAnchorIndex(row);
|
|
831
|
+
let signal = null;
|
|
832
|
+
let quote_preview = null;
|
|
833
|
+
if (firstQuote) {
|
|
834
|
+
signal = asString(firstQuote.signal);
|
|
835
|
+
const quote = asString(firstQuote.quote);
|
|
836
|
+
if (quote !== null) {
|
|
837
|
+
let end = 0;
|
|
838
|
+
let points = 0;
|
|
839
|
+
for (const ch of quote) {
|
|
840
|
+
if (points++ === 200)
|
|
841
|
+
break;
|
|
842
|
+
end += ch.length;
|
|
843
|
+
}
|
|
844
|
+
quote_preview = quote.slice(0, end);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
return {
|
|
848
|
+
kind: "frustration",
|
|
849
|
+
conversation_id,
|
|
850
|
+
msg_index,
|
|
851
|
+
signal,
|
|
852
|
+
quote_preview,
|
|
853
|
+
all_turns,
|
|
854
|
+
no_anchor: msg_index === null
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
function buildToolFailureAnchor(row, toolName) {
|
|
858
|
+
const conversation_id = asString(row.conversation_id) ?? "";
|
|
859
|
+
const msg_index = asNumber(row.msg_index);
|
|
860
|
+
const tool_use_id = asString(row.tool_use_id);
|
|
861
|
+
const error_subtype = asString(row.error_subtype);
|
|
862
|
+
return {
|
|
863
|
+
kind: "tool_failure",
|
|
864
|
+
conversation_id,
|
|
865
|
+
msg_index,
|
|
866
|
+
tool_name: toolName,
|
|
867
|
+
tool_use_id,
|
|
868
|
+
error_subtype,
|
|
869
|
+
no_anchor: msg_index === null
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
function frustrationsFromEmotions(response) {
|
|
873
|
+
if (!Array.isArray(response.detections) || Array.isArray(response.frustrations))
|
|
874
|
+
return response;
|
|
875
|
+
const summary = asRecord(response.summary) ?? {};
|
|
876
|
+
const pagination = asRecord(response.pagination) ?? {};
|
|
877
|
+
const byFamily = asRecord(summary.by_family) ?? {};
|
|
878
|
+
const totalAnalyzed = asNumber(summary.conversations_analyzed) ?? 0;
|
|
879
|
+
const byFamilyConversations = asRecord(summary.conversations_by_family);
|
|
880
|
+
const frustrated = asNumber(byFamilyConversations?.frustration) ?? asNumber(byFamily.frustration) ?? 0;
|
|
881
|
+
const frustrations = response.detections.map((item) => asRecord(item)).filter((row) => row !== null && (row.family === undefined || row.family === "frustration")).map(({ conversation_context, score, is_detected, ...row }) => ({
|
|
882
|
+
...row,
|
|
883
|
+
is_frustrated: is_detected === true,
|
|
884
|
+
frustration_score: score,
|
|
885
|
+
target: row.elicitor === "bot" || row.elicitor === "external" ? row.elicitor : "ambiguous",
|
|
886
|
+
conversation: conversation_context ?? null
|
|
887
|
+
}));
|
|
888
|
+
const atRisk = asNumber(pagination.at_risk) ?? Math.max(0, (asNumber(pagination.total) ?? 0) - frustrated);
|
|
889
|
+
const signalBreakdown = {};
|
|
890
|
+
for (const item of Array.isArray(response.signal_breakdown) ? response.signal_breakdown : []) {
|
|
891
|
+
const row = asRecord(item);
|
|
892
|
+
if (row?.family === "frustration" && typeof row.signal === "string") {
|
|
893
|
+
signalBreakdown[row.signal] = asNumber(row.count) ?? 0;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
const { detections: _detections, ...rest } = response;
|
|
897
|
+
return {
|
|
898
|
+
...rest,
|
|
899
|
+
summary: {
|
|
900
|
+
total_analyzed: totalAnalyzed,
|
|
901
|
+
frustrated_count: frustrated,
|
|
902
|
+
at_risk_count: atRisk,
|
|
903
|
+
frustration_rate_pct: totalAnalyzed > 0 ? Math.round(frustrated / totalAnalyzed * 1000) / 10 : 0
|
|
904
|
+
},
|
|
905
|
+
signal_breakdown: signalBreakdown,
|
|
906
|
+
frustrations,
|
|
907
|
+
source: "emotion_detections"
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function withFrustrationAnchors(response) {
|
|
911
|
+
if (!Array.isArray(response.frustrations))
|
|
912
|
+
return response;
|
|
913
|
+
const frustrations = response.frustrations.map((item) => {
|
|
914
|
+
const row = asRecord(item);
|
|
915
|
+
if (!row)
|
|
916
|
+
return item;
|
|
917
|
+
const anchor = buildFrustrationAnchor(row);
|
|
918
|
+
return { ...row, anchor };
|
|
919
|
+
});
|
|
920
|
+
return { ...response, frustrations };
|
|
921
|
+
}
|
|
922
|
+
var TOOL_FAILURE_CONTEXT_RADIUS = 2;
|
|
923
|
+
function shellWord(value) {
|
|
924
|
+
return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
925
|
+
}
|
|
926
|
+
function trimFailureWindow(value, radius, fallbackCenter) {
|
|
927
|
+
const conv = asRecord(value);
|
|
928
|
+
if (!conv)
|
|
929
|
+
return value ?? null;
|
|
930
|
+
const { coverage: _coverage, context, ...convRest } = conv;
|
|
931
|
+
const ctx = asRecord(context);
|
|
932
|
+
if (!ctx)
|
|
933
|
+
return convRest;
|
|
934
|
+
const center = asNumber(ctx.center_index) ?? fallbackCenter;
|
|
935
|
+
const messages = (Array.isArray(ctx.messages) ? ctx.messages : []).map((message) => asRecord(message)).filter((message) => {
|
|
936
|
+
const index = asNumber(message?.index);
|
|
937
|
+
return message !== null && (center === null || index === null || Math.abs(index - center) <= radius);
|
|
938
|
+
}).map(({ content_blocks, ...message }) => {
|
|
939
|
+
const covered = typeof message.content === "string" && message.content.length > 0 || Array.isArray(message.tool_calls) && message.tool_calls.length > 0 || Array.isArray(message.tool_results) && message.tool_results.length > 0;
|
|
940
|
+
return covered || content_blocks === undefined ? message : { ...message, content_blocks };
|
|
941
|
+
});
|
|
942
|
+
const indexes = messages.map((message) => asNumber(message.index)).filter((index) => index !== null);
|
|
943
|
+
return {
|
|
944
|
+
...convRest,
|
|
945
|
+
context: {
|
|
946
|
+
...ctx,
|
|
947
|
+
...indexes.length > 0 ? { from_index: Math.min(...indexes), to_index: Math.max(...indexes) } : {},
|
|
948
|
+
messages
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
function trimToolFailureConversations(response, opts) {
|
|
953
|
+
if (!Array.isArray(response.examples))
|
|
954
|
+
return response;
|
|
955
|
+
const examples = response.examples.map((item) => {
|
|
956
|
+
const row = asRecord(item);
|
|
957
|
+
if (!row)
|
|
958
|
+
return item;
|
|
959
|
+
const { conversation, window, ...rest } = row;
|
|
960
|
+
const conversationId = asString(row.conversation_id);
|
|
961
|
+
const msgIndex = asNumber(asRecord(row.anchor)?.msg_index) ?? asNumber(row.msg_index);
|
|
962
|
+
return {
|
|
963
|
+
...rest,
|
|
964
|
+
...conversationId && msgIndex !== null ? { context_command: `moda context ${shellWord(contextTarget(conversationId))} --msg-index=${msgIndex} --window=${TOOL_FAILURE_CONTEXT_RADIUS}` } : {},
|
|
965
|
+
...opts.drop ? {} : { conversation: trimFailureWindow(conversation, TOOL_FAILURE_CONTEXT_RADIUS, msgIndex) },
|
|
966
|
+
..."window" in row ? { window: trimFailureWindow(window, opts.windowRadius ?? TOOL_FAILURE_CONTEXT_RADIUS, msgIndex) } : {}
|
|
967
|
+
};
|
|
968
|
+
});
|
|
969
|
+
return { ...response, examples };
|
|
970
|
+
}
|
|
971
|
+
function withToolFailureAnchors(response) {
|
|
972
|
+
if (!Array.isArray(response.examples))
|
|
973
|
+
return response;
|
|
974
|
+
const toolName = asString(response.tool_name) ?? "";
|
|
975
|
+
const examples = response.examples.map((item) => {
|
|
976
|
+
const row = asRecord(item);
|
|
977
|
+
if (!row)
|
|
978
|
+
return item;
|
|
979
|
+
const anchor = buildToolFailureAnchor(row, toolName);
|
|
980
|
+
return { ...row, anchor };
|
|
981
|
+
});
|
|
982
|
+
return { ...response, examples };
|
|
983
|
+
}
|
|
984
|
+
function embeddedWindowSize(conversation) {
|
|
985
|
+
if (!conversation)
|
|
986
|
+
return null;
|
|
987
|
+
const ctx = asRecord(conversation.context);
|
|
988
|
+
if (!ctx)
|
|
989
|
+
return null;
|
|
990
|
+
const center = asNumber(ctx.center_index);
|
|
991
|
+
const from = asNumber(ctx.from_index);
|
|
992
|
+
const to = asNumber(ctx.to_index);
|
|
993
|
+
if (center === null || from === null || to === null)
|
|
994
|
+
return null;
|
|
995
|
+
return Math.min(center - from, to - center);
|
|
996
|
+
}
|
|
997
|
+
var DETECTION_CONTEXT_RADIUS = 2;
|
|
998
|
+
function withoutDetectionWindows(response, listKey, windowKey) {
|
|
999
|
+
const list = response[listKey];
|
|
1000
|
+
if (!Array.isArray(list))
|
|
1001
|
+
return response;
|
|
1002
|
+
const rows = list.map((item) => {
|
|
1003
|
+
const row = asRecord(item);
|
|
1004
|
+
if (!row)
|
|
1005
|
+
return item;
|
|
1006
|
+
const { [windowKey]: _window, ...rest } = row;
|
|
1007
|
+
const conversationId = asString(row.conversation_id);
|
|
1008
|
+
const msgIndex = asNumber(asRecord(row.anchor)?.msg_index) ?? frustrationAnchorIndex(row);
|
|
1009
|
+
return {
|
|
1010
|
+
...rest,
|
|
1011
|
+
...conversationId && msgIndex !== null ? { context_command: `moda context ${shellWord(contextTarget(conversationId))} --msg-index=${msgIndex} --window=${DETECTION_CONTEXT_RADIUS}` } : {}
|
|
1012
|
+
};
|
|
1013
|
+
});
|
|
1014
|
+
return { ...response, [listKey]: rows };
|
|
1015
|
+
}
|
|
1016
|
+
var PROBLEM_COMPACT_EVIDENCE = 5;
|
|
1017
|
+
var PROBLEM_COMPACT_OMITTED_BLOCKS = [
|
|
1018
|
+
"rubric",
|
|
1019
|
+
"trend",
|
|
1020
|
+
"sub_problems",
|
|
1021
|
+
"affected_conversations",
|
|
1022
|
+
"remainder",
|
|
1023
|
+
"confidence",
|
|
1024
|
+
"change_history",
|
|
1025
|
+
"stories"
|
|
1026
|
+
];
|
|
1027
|
+
function readCommand(command, purpose) {
|
|
1028
|
+
return { command, purpose, mutability: "read", requires_approval: false };
|
|
1029
|
+
}
|
|
1030
|
+
function evidenceMsgIndex(row) {
|
|
1031
|
+
return asNumber(row.anchor_msg_index) ?? asNumber(asRecord(asRecord(row.snippet_parsed)?.anchor)?.msg_index);
|
|
1032
|
+
}
|
|
1033
|
+
function compactProblemDossier(response, problemId) {
|
|
1034
|
+
const id = shellWord(asString(response.problem_id) ?? problemId);
|
|
1035
|
+
const evidence = (Array.isArray(response.evidence) ? response.evidence : []).map((item) => asRecord(item)).filter((row) => row !== null);
|
|
1036
|
+
const topEvidence = evidence.slice(0, PROBLEM_COMPACT_EVIDENCE).map((row) => {
|
|
1037
|
+
const conversationId = asString(row.conversation_id);
|
|
1038
|
+
const msgIndex = evidenceMsgIndex(row);
|
|
1039
|
+
return {
|
|
1040
|
+
attribution_id: row.attribution_id ?? null,
|
|
1041
|
+
conversation_id: conversationId,
|
|
1042
|
+
msg_index: msgIndex,
|
|
1043
|
+
signal_family: row.signal_family ?? null,
|
|
1044
|
+
signal_type: row.signal_type ?? null,
|
|
1045
|
+
confidence: row.confidence ?? null,
|
|
1046
|
+
causal_rationale: row.causal_rationale ?? null,
|
|
1047
|
+
door: row.door ?? null,
|
|
1048
|
+
replay_verified: row.replay_verified ?? null,
|
|
1049
|
+
...conversationId && msgIndex !== null ? { context_command: `moda context ${shellWord(contextTarget(conversationId))} --msg-index=${msgIndex}` } : {}
|
|
1050
|
+
};
|
|
1051
|
+
});
|
|
1052
|
+
const reportsBlock = asRecord(response.investigation_reports);
|
|
1053
|
+
const reports = (Array.isArray(reportsBlock?.items) ? reportsBlock.items : []).map((item) => asRecord(item)).filter((row) => row !== null).map((row) => ({
|
|
1054
|
+
report_id: row.report_id ?? null,
|
|
1055
|
+
conversation_id: row.conversation_id ?? null,
|
|
1056
|
+
mechanism: row.mechanism ?? null,
|
|
1057
|
+
suspected_surface: row.suspected_surface ?? null,
|
|
1058
|
+
severity: row.severity ?? null,
|
|
1059
|
+
problem_found: row.problem_found ?? null
|
|
1060
|
+
}));
|
|
1061
|
+
const verification = asRecord(response.verification);
|
|
1062
|
+
const { samples: _samples, ...verificationCounts } = verification ?? {};
|
|
1063
|
+
const dossier = asRecord(response.dossier);
|
|
1064
|
+
const { representative_story_keys: _storyKeys, ...dossierSummary } = dossier ?? {};
|
|
1065
|
+
const trend = Array.isArray(response.trend) ? response.trend : [];
|
|
1066
|
+
const data = {
|
|
1067
|
+
found: response.found ?? true,
|
|
1068
|
+
problem_id: response.problem_id ?? problemId,
|
|
1069
|
+
requested_problem_id: response.requested_problem_id ?? problemId,
|
|
1070
|
+
alias_resolved: response.alias_resolved ?? false,
|
|
1071
|
+
view: "compact",
|
|
1072
|
+
header: response.header ?? null,
|
|
1073
|
+
dossier: dossier ? dossierSummary : null,
|
|
1074
|
+
verification: verification ? verificationCounts : null,
|
|
1075
|
+
...response.impact_identity !== undefined ? { impact_identity: response.impact_identity } : {},
|
|
1076
|
+
...response.hierarchy !== undefined ? { hierarchy: response.hierarchy } : {},
|
|
1077
|
+
...trend.length > 0 ? { trend_latest: trend[trend.length - 1], trend_points: trend.length } : {},
|
|
1078
|
+
top_evidence: topEvidence,
|
|
1079
|
+
evidence_in_dossier: evidence.length,
|
|
1080
|
+
reports,
|
|
1081
|
+
omitted: {
|
|
1082
|
+
evidence_rows: Math.max(0, evidence.length - topEvidence.length),
|
|
1083
|
+
report_narratives: reports.length,
|
|
1084
|
+
reports_has_more: reportsBlock?.has_more === true,
|
|
1085
|
+
blocks: PROBLEM_COMPACT_OMITTED_BLOCKS.filter((key) => (key in response))
|
|
1086
|
+
},
|
|
1087
|
+
full_command: `moda problem ${id} --full`
|
|
1088
|
+
};
|
|
1089
|
+
const nextCommands = [
|
|
1090
|
+
readCommand(`moda problem ${id} --evidence --limit=20`, "Page every evidence row with its anchor (follow next_cursor)."),
|
|
1091
|
+
readCommand(`moda problem ${id} --reports`, "Page investigation reports with narratives and citations."),
|
|
1092
|
+
readCommand(`moda problem ${id} --traces`, "Page affected traces with their latest anchor."),
|
|
1093
|
+
readCommand(`moda problem ${id} --full`, "The complete dossier, every block (large).")
|
|
1094
|
+
];
|
|
1095
|
+
const first = topEvidence.find((row) => typeof row.context_command === "string");
|
|
1096
|
+
if (first?.context_command) {
|
|
1097
|
+
nextCommands.push(readCommand(first.context_command, "Read the trace around the top evidence anchor."));
|
|
1098
|
+
}
|
|
1099
|
+
return { data, nextCommands };
|
|
1100
|
+
}
|
|
1101
|
+
|
|
681
1102
|
// ../../packages/harness-snapshot/src/policy.ts
|
|
682
1103
|
var MAX_SCANNED_FILE_BYTES = 512 * 1024;
|
|
683
1104
|
var DEFAULT_MAX_INVENTORY_FILES = 12000;
|
|
@@ -1053,6 +1474,25 @@ var AGENT_ERROR_CODES = [
|
|
|
1053
1474
|
"output_error",
|
|
1054
1475
|
"not_found"
|
|
1055
1476
|
];
|
|
1477
|
+
var MODA_COMMAND = /^(?:moda |.*\| moda )/;
|
|
1478
|
+
function withProfileFlag(command, flag) {
|
|
1479
|
+
return MODA_COMMAND.test(command) && !/(?:^|\s)--profile(?:[=\s]|$)/.test(command) ? `${command} ${flag}` : command;
|
|
1480
|
+
}
|
|
1481
|
+
function withProfileOnCommands(value, profile) {
|
|
1482
|
+
const flag = `--profile=${shellWord(profile)}`;
|
|
1483
|
+
const visit = (node, key) => {
|
|
1484
|
+
const isCommandKey = key !== undefined && /commands?$/.test(key);
|
|
1485
|
+
if (typeof node === "string")
|
|
1486
|
+
return isCommandKey ? withProfileFlag(node, flag) : node;
|
|
1487
|
+
if (Array.isArray(node))
|
|
1488
|
+
return node.map((item) => visit(item, isCommandKey ? key : undefined));
|
|
1489
|
+
const record = asRecord2(node);
|
|
1490
|
+
if (!record)
|
|
1491
|
+
return node;
|
|
1492
|
+
return Object.fromEntries(Object.entries(record).map(([entryKey, entry]) => [entryKey, visit(entry, entryKey)]));
|
|
1493
|
+
};
|
|
1494
|
+
return visit(value, undefined);
|
|
1495
|
+
}
|
|
1056
1496
|
var FALLBACK_RUN_ID = randomUUID();
|
|
1057
1497
|
var fallbackSeq = 0;
|
|
1058
1498
|
var stdoutClosed = false;
|
|
@@ -1217,7 +1657,8 @@ function createCliOutput(options) {
|
|
|
1217
1657
|
nextCommands: writeOptions.nextCommands,
|
|
1218
1658
|
artifacts: writeOptions.artifacts,
|
|
1219
1659
|
warnings: writeOptions.warnings,
|
|
1220
|
-
errors: writeOptions.errors
|
|
1660
|
+
errors: writeOptions.errors,
|
|
1661
|
+
invocation: options.invocation
|
|
1221
1662
|
});
|
|
1222
1663
|
};
|
|
1223
1664
|
const output = {
|
|
@@ -1227,6 +1668,12 @@ function createCliOutput(options) {
|
|
|
1227
1668
|
const bypassQuiet = writeOptions.bypassQuiet === true;
|
|
1228
1669
|
if (options.quiet && !bypassQuiet)
|
|
1229
1670
|
return;
|
|
1671
|
+
const profile = options.invocation?.flags.profile;
|
|
1672
|
+
if (profile) {
|
|
1673
|
+
data = withProfileOnCommands(data, profile);
|
|
1674
|
+
if (writeOptions.nextCommands)
|
|
1675
|
+
writeOptions = { ...writeOptions, nextCommands: withProfileOnCommands(writeOptions.nextCommands, profile) };
|
|
1676
|
+
}
|
|
1230
1677
|
if (options.mode === "agent" || options.mode === "agent-stream") {
|
|
1231
1678
|
if (state.terminal)
|
|
1232
1679
|
return;
|
|
@@ -1368,7 +1815,7 @@ function createAgentEnvelope(options) {
|
|
|
1368
1815
|
findings: text.findings,
|
|
1369
1816
|
evidence_refs: text.evidence_refs,
|
|
1370
1817
|
recommended_actions: text.recommended_actions,
|
|
1371
|
-
next_commands: text.next_commands,
|
|
1818
|
+
next_commands: [...text.next_commands, ...bounded.nextCommands ?? []],
|
|
1372
1819
|
artifacts: [
|
|
1373
1820
|
...options.artifacts ?? [],
|
|
1374
1821
|
...bounded.fullOutputPath ? [{ kind: "full_output", path: bounded.fullOutputPath, description: "Complete command output (the same JSON --json prints); data in this envelope was bounded." }] : [],
|
|
@@ -1396,9 +1843,9 @@ function createAgentEnvelope(options) {
|
|
|
1396
1843
|
const sizeOf = (value) => Buffer.byteLength(JSON.stringify(value) ?? "", "utf8");
|
|
1397
1844
|
const rebound = (current) => {
|
|
1398
1845
|
const overhead = sizeOf(current) - sizeOf(current.data);
|
|
1399
|
-
return build(boundForAgent(safeData, Math.max(1024, maxBytes - overhead - 1024), { writeFullOutput }));
|
|
1846
|
+
return build(boundForAgent(safeData, Math.max(1024, maxBytes - overhead - 1024), { writeFullOutput, invocation: options.invocation }));
|
|
1400
1847
|
};
|
|
1401
|
-
let envelope = build(boundForAgent(safeData, maxBytes, { writeFullOutput }));
|
|
1848
|
+
let envelope = build(boundForAgent(safeData, maxBytes, { writeFullOutput, invocation: options.invocation }));
|
|
1402
1849
|
if (sizeOf(envelope) > maxBytes)
|
|
1403
1850
|
envelope = rebound(envelope);
|
|
1404
1851
|
if (sizeOf(envelope) > maxBytes && !saveFailed) {
|
|
@@ -1750,11 +2197,11 @@ function normalizeSummary(summary, command, status, data) {
|
|
|
1750
2197
|
return inferSummary(command, status, data);
|
|
1751
2198
|
}
|
|
1752
2199
|
function inferSummary(command, status, data) {
|
|
1753
|
-
const record =
|
|
2200
|
+
const record = asRecord2(data);
|
|
1754
2201
|
if (status === "error") {
|
|
1755
2202
|
return { text: `${command} failed.`, confidence: "high" };
|
|
1756
2203
|
}
|
|
1757
|
-
const summary =
|
|
2204
|
+
const summary = asRecord2(record?.summary);
|
|
1758
2205
|
if (summary) {
|
|
1759
2206
|
const statusValue = typeof summary.status === "string" ? summary.status : undefined;
|
|
1760
2207
|
const fail = typeof summary.fail === "number" ? summary.fail : undefined;
|
|
@@ -1806,18 +2253,21 @@ function inferMutability(command) {
|
|
|
1806
2253
|
return "read";
|
|
1807
2254
|
}
|
|
1808
2255
|
function inferWarnings(data) {
|
|
1809
|
-
const record =
|
|
2256
|
+
const record = asRecord2(data);
|
|
1810
2257
|
const direct = record?.warnings;
|
|
1811
2258
|
if (Array.isArray(direct))
|
|
1812
2259
|
return direct.filter((item) => typeof item === "string");
|
|
1813
|
-
const validation =
|
|
2260
|
+
const validation = asRecord2(record?.validation);
|
|
1814
2261
|
if (Array.isArray(validation?.warnings)) {
|
|
1815
2262
|
return validation.warnings.filter((item) => typeof item === "string");
|
|
1816
2263
|
}
|
|
1817
2264
|
return [];
|
|
1818
2265
|
}
|
|
2266
|
+
var DEFAULT_AGENT_MAX_OUTPUT_BYTES = 24000;
|
|
1819
2267
|
function agentMaxOutputBytes() {
|
|
1820
|
-
|
|
2268
|
+
const raw = process.env.MODA_AGENT_MAX_OUTPUT_BYTES;
|
|
2269
|
+
const value = raw === undefined || raw.trim() === "" ? Number.NaN : Number(raw);
|
|
2270
|
+
return Number.isFinite(value) && value > 0 ? value : DEFAULT_AGENT_MAX_OUTPUT_BYTES;
|
|
1821
2271
|
}
|
|
1822
2272
|
var AGENT_FULL_OUTPUT_FILE = /-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z-[0-9a-f]{8}\.json$/;
|
|
1823
2273
|
function agentOutputRetentionMs() {
|
|
@@ -1860,6 +2310,158 @@ function writeAgentFullOutput(command, data) {
|
|
|
1860
2310
|
return;
|
|
1861
2311
|
}
|
|
1862
2312
|
}
|
|
2313
|
+
function contentBlocksRedundant(message) {
|
|
2314
|
+
const blocks = message.content_blocks;
|
|
2315
|
+
if (!Array.isArray(blocks))
|
|
2316
|
+
return false;
|
|
2317
|
+
const content = typeof message.content === "string" ? message.content : typeof message.text === "string" ? message.text : "";
|
|
2318
|
+
const hasToolCalls = Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
|
|
2319
|
+
const hasToolResults = Array.isArray(message.tool_results) && message.tool_results.length > 0;
|
|
2320
|
+
const texts = [];
|
|
2321
|
+
for (const item of blocks) {
|
|
2322
|
+
const block = asRecord2(item);
|
|
2323
|
+
if (!block)
|
|
2324
|
+
return false;
|
|
2325
|
+
if (block.type === "text") {
|
|
2326
|
+
if (typeof block.text !== "string")
|
|
2327
|
+
return false;
|
|
2328
|
+
texts.push(block.text);
|
|
2329
|
+
continue;
|
|
2330
|
+
}
|
|
2331
|
+
if (block.type === "tool_use" && hasToolCalls)
|
|
2332
|
+
continue;
|
|
2333
|
+
if (block.type === "tool_result" && hasToolResults)
|
|
2334
|
+
continue;
|
|
2335
|
+
return false;
|
|
2336
|
+
}
|
|
2337
|
+
if (texts.length === 0)
|
|
2338
|
+
return true;
|
|
2339
|
+
const trimmed = content.trim();
|
|
2340
|
+
return [texts.join(""), texts.join(`
|
|
2341
|
+
`), texts.join(`
|
|
2342
|
+
|
|
2343
|
+
`)].some((joined) => joined.trim() === trimmed);
|
|
2344
|
+
}
|
|
2345
|
+
function shedRedundantContentBlocks(value) {
|
|
2346
|
+
let shed = 0;
|
|
2347
|
+
const visit = (node) => {
|
|
2348
|
+
if (Array.isArray(node)) {
|
|
2349
|
+
for (const item of node)
|
|
2350
|
+
if (item && typeof item === "object")
|
|
2351
|
+
visit(item);
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
const record = asRecord2(node);
|
|
2355
|
+
if (!record)
|
|
2356
|
+
return;
|
|
2357
|
+
if ("content_blocks" in record && contentBlocksRedundant(record)) {
|
|
2358
|
+
delete record.content_blocks;
|
|
2359
|
+
shed += 1;
|
|
2360
|
+
}
|
|
2361
|
+
for (const item of Object.values(record))
|
|
2362
|
+
if (item && typeof item === "object")
|
|
2363
|
+
visit(item);
|
|
2364
|
+
};
|
|
2365
|
+
visit(value);
|
|
2366
|
+
return shed;
|
|
2367
|
+
}
|
|
2368
|
+
var CONTINUATION_SECRET_FLAGS = new Set(["api-key", "api_key", "token"]);
|
|
2369
|
+
function continuationCommand(invocation, overrides) {
|
|
2370
|
+
const normalize2 = (key) => key.replace(/_/g, "-");
|
|
2371
|
+
const overridden = new Set(Object.keys(overrides).map(normalize2));
|
|
2372
|
+
const bare = new Set((invocation.bareFlags ?? []).map(normalize2));
|
|
2373
|
+
const parts = ["moda", invocation.command, ...invocation.positionals.map((value) => shellWord(value))];
|
|
2374
|
+
for (const [key, value] of Object.entries(invocation.flags)) {
|
|
2375
|
+
const flag = normalize2(key);
|
|
2376
|
+
if (overridden.has(flag) || CONTINUATION_SECRET_FLAGS.has(flag))
|
|
2377
|
+
continue;
|
|
2378
|
+
parts.push(bare.has(flag) && value === "true" ? `--${flag}` : `--${flag}=${shellWord(String(value))}`);
|
|
2379
|
+
}
|
|
2380
|
+
for (const [key, value] of Object.entries(overrides))
|
|
2381
|
+
parts.push(`--${normalize2(key)}=${shellWord(String(value))}`);
|
|
2382
|
+
return parts.join(" ");
|
|
2383
|
+
}
|
|
2384
|
+
var PAGED_LIST_BY_COMMAND = {
|
|
2385
|
+
"tool-failure-detail": "examples",
|
|
2386
|
+
emotions: "detections",
|
|
2387
|
+
frustrations: "frustrations",
|
|
2388
|
+
"problems remainder": "groups"
|
|
2389
|
+
};
|
|
2390
|
+
function pagedListFor(invocation) {
|
|
2391
|
+
const subcommand = invocation.positionals[0];
|
|
2392
|
+
return (subcommand !== undefined ? PAGED_LIST_BY_COMMAND[`${invocation.command} ${subcommand}`] : undefined) ?? PAGED_LIST_BY_COMMAND[invocation.command];
|
|
2393
|
+
}
|
|
2394
|
+
var MAX_PAGE_OFFSET = 1e4;
|
|
2395
|
+
function rewritePagination(list, isTopLevel, invocation) {
|
|
2396
|
+
const { parent, array, originalLength } = list;
|
|
2397
|
+
if (!parent)
|
|
2398
|
+
return;
|
|
2399
|
+
const nested = asRecord2(parent.pagination);
|
|
2400
|
+
const inline = !nested && !isTopLevel && (("has_more" in parent) || ("next_cursor" in parent)) ? parent : undefined;
|
|
2401
|
+
const pagination = nested ?? inline;
|
|
2402
|
+
if (!pagination)
|
|
2403
|
+
return;
|
|
2404
|
+
const listKey = list.path.split(".").pop() ?? list.path;
|
|
2405
|
+
const arrayKeys = Object.keys(parent).filter((key) => Array.isArray(parent[key]));
|
|
2406
|
+
if (arrayKeys.length > 1) {
|
|
2407
|
+
const paged = invocation ? pagedListFor(invocation) : undefined;
|
|
2408
|
+
if (paged === undefined || paged !== listKey)
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2411
|
+
if (typeof pagination.returned === "number" ? pagination.returned !== originalLength : typeof pagination.limit === "number" && originalLength > pagination.limit) {
|
|
2412
|
+
return;
|
|
2413
|
+
}
|
|
2414
|
+
const kept = array.length;
|
|
2415
|
+
const omitted = originalLength - kept;
|
|
2416
|
+
const offset = typeof pagination.offset === "number" && Number.isFinite(pagination.offset) ? pagination.offset : undefined;
|
|
2417
|
+
const hadCursor = typeof pagination.next_cursor === "string" && pagination.next_cursor.length > 0;
|
|
2418
|
+
if (typeof pagination.limit === "number") {
|
|
2419
|
+
pagination.requested_limit = pagination.limit;
|
|
2420
|
+
pagination.limit = kept;
|
|
2421
|
+
}
|
|
2422
|
+
pagination.returned = kept;
|
|
2423
|
+
pagination.omitted_for_byte_limit = omitted;
|
|
2424
|
+
pagination.has_more = true;
|
|
2425
|
+
const pastOffsetCap = offset !== undefined && offset + kept > MAX_PAGE_OFFSET;
|
|
2426
|
+
if (offset !== undefined && !pastOffsetCap)
|
|
2427
|
+
pagination.next_offset = offset + kept;
|
|
2428
|
+
if (hadCursor)
|
|
2429
|
+
pagination.next_cursor = null;
|
|
2430
|
+
const where = `data.${list.path}`;
|
|
2431
|
+
if (pastOffsetCap) {
|
|
2432
|
+
return {
|
|
2433
|
+
note: `${where} holds ${kept} of ${originalLength} row(s); the omitted rows start past --offset=${MAX_PAGE_OFFSET}, ` + "the deepest offset the API pages to, so no --offset command can fetch them. Read them from the saved full output " + "(truncation.data.full_output_path) or rerun this page with --json"
|
|
2434
|
+
};
|
|
2435
|
+
}
|
|
2436
|
+
if (!isTopLevel || !invocation) {
|
|
2437
|
+
return { note: `${where} holds ${kept} of ${originalLength} row(s)` };
|
|
2438
|
+
}
|
|
2439
|
+
if (offset !== undefined) {
|
|
2440
|
+
const command = continuationCommand(invocation, { offset: offset + kept, limit: omitted });
|
|
2441
|
+
return {
|
|
2442
|
+
note: `${where} holds ${kept} of ${originalLength} row(s); pagination.next_offset=${offset + kept}. Fetch the ${omitted} omitted row(s) with: ${command}`,
|
|
2443
|
+
nextCommand: {
|
|
2444
|
+
command,
|
|
2445
|
+
purpose: `Fetch the ${omitted} row(s) of this page that were left out to fit the agent byte limit.`,
|
|
2446
|
+
mutability: "read",
|
|
2447
|
+
requires_approval: false
|
|
2448
|
+
}
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
if (hadCursor || "next_cursor" in pagination || "cursor" in invocation.flags) {
|
|
2452
|
+
const command = continuationCommand(invocation, { limit: kept });
|
|
2453
|
+
return {
|
|
2454
|
+
note: `${where} holds ${kept} of ${originalLength} row(s); next_cursor was cleared because it skipped the omitted rows. Rerun ${command} for a next_cursor that starts at the first omitted row`,
|
|
2455
|
+
nextCommand: {
|
|
2456
|
+
command,
|
|
2457
|
+
purpose: `Re-read this page at --limit=${kept} (what fits) to get a next_cursor that starts at the first omitted row.`,
|
|
2458
|
+
mutability: "read",
|
|
2459
|
+
requires_approval: false
|
|
2460
|
+
}
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
return { note: `${where} holds ${kept} of ${originalLength} row(s)` };
|
|
2464
|
+
}
|
|
1863
2465
|
function boundForAgent(data, maxBytes = agentMaxOutputBytes(), options = {}) {
|
|
1864
2466
|
const sizeOf = (value) => Buffer.byteLength(JSON.stringify(value) ?? "", "utf8");
|
|
1865
2467
|
const serialized = JSON.stringify(data) ?? "";
|
|
@@ -1870,46 +2472,56 @@ function boundForAgent(data, maxBytes = agentMaxOutputBytes(), options = {}) {
|
|
|
1870
2472
|
if (options.writeFullOutput && !fullOutputPath)
|
|
1871
2473
|
return { data, saveFailed: true };
|
|
1872
2474
|
const bounded = JSON.parse(serialized);
|
|
1873
|
-
|
|
2475
|
+
const shedContentBlocks = shedRedundantContentBlocks(bounded);
|
|
2476
|
+
const shedFields = shedContentBlocks > 0 ? { content_blocks: shedContentBlocks } : {};
|
|
2477
|
+
let boundedBytes = shedContentBlocks > 0 ? sizeOf(bounded) : originalBytes;
|
|
1874
2478
|
const droppedItems = {};
|
|
1875
|
-
const
|
|
2479
|
+
const droppedLists = new Map;
|
|
2480
|
+
const collectArrays = (value, path, parent, out) => {
|
|
1876
2481
|
if (Array.isArray(value)) {
|
|
1877
2482
|
if (value.length > 1)
|
|
1878
|
-
out.push({ path, array: value, itemBytes: value.map((item) => sizeOf(item) + 1) });
|
|
2483
|
+
out.push({ path, array: value, itemBytes: value.map((item) => sizeOf(item) + 1), parent });
|
|
1879
2484
|
value.forEach((item, index) => {
|
|
1880
2485
|
if (item && typeof item === "object")
|
|
1881
|
-
collectArrays(item, `${path}[${index}]`, out);
|
|
2486
|
+
collectArrays(item, `${path}[${index}]`, undefined, out);
|
|
1882
2487
|
});
|
|
1883
2488
|
} else if (value && typeof value === "object") {
|
|
1884
|
-
|
|
2489
|
+
const record = value;
|
|
2490
|
+
for (const [key, item] of Object.entries(record)) {
|
|
1885
2491
|
if (item && typeof item === "object")
|
|
1886
|
-
collectArrays(item, path ? `${path}.${key}` : key, out);
|
|
2492
|
+
collectArrays(item, path ? `${path}.${key}` : key, record, out);
|
|
1887
2493
|
}
|
|
1888
2494
|
}
|
|
1889
2495
|
};
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
2496
|
+
if (boundedBytes > maxBytes) {
|
|
2497
|
+
for (let round = 0;round < 25 && boundedBytes > maxBytes; round++) {
|
|
2498
|
+
const arrays = [];
|
|
2499
|
+
collectArrays(bounded, "", undefined, arrays);
|
|
2500
|
+
const largest = arrays.map((entry) => ({ ...entry, total: entry.itemBytes.reduce((sum, bytes) => sum + bytes, 0) })).sort((left, right) => right.total - left.total)[0];
|
|
2501
|
+
if (!largest)
|
|
2502
|
+
break;
|
|
2503
|
+
let overshoot = boundedBytes - maxBytes;
|
|
2504
|
+
let drop = 0;
|
|
2505
|
+
for (let index = largest.array.length - 1;index >= 1 && overshoot > 0; index--) {
|
|
2506
|
+
overshoot -= largest.itemBytes[index];
|
|
2507
|
+
drop += 1;
|
|
2508
|
+
}
|
|
2509
|
+
if (drop === 0)
|
|
2510
|
+
break;
|
|
2511
|
+
const key = largest.path || "(root)";
|
|
2512
|
+
if (!droppedLists.has(key)) {
|
|
2513
|
+
droppedLists.set(key, { path: key, parent: largest.parent, array: largest.array, originalLength: largest.array.length });
|
|
2514
|
+
}
|
|
2515
|
+
largest.array.splice(largest.array.length - drop, drop);
|
|
2516
|
+
droppedItems[key] = (droppedItems[key] ?? 0) + drop;
|
|
2517
|
+
boundedBytes = sizeOf(bounded);
|
|
1901
2518
|
}
|
|
1902
|
-
if (drop === 0)
|
|
1903
|
-
break;
|
|
1904
|
-
largest.array.splice(largest.array.length - drop, drop);
|
|
1905
|
-
const key = largest.path || "(root)";
|
|
1906
|
-
droppedItems[key] = (droppedItems[key] ?? 0) + drop;
|
|
1907
|
-
boundedBytes = sizeOf(bounded);
|
|
1908
2519
|
}
|
|
1909
2520
|
const dropped = Object.entries(droppedItems);
|
|
1910
2521
|
const whereIsTheRest = fullOutputPath ? `The complete output (every item, no text shortened) is in ${fullOutputPath}; read it in line ranges or query it with jq. ` : "The complete output could not be saved to a file. ";
|
|
1911
2522
|
const howToGetMore = whereIsTheRest + "Alternatively narrow the query (a smaller --limit, with --offset or --cursor paging where the command supports it), " + "rerun with --json (no byte limit), or raise MODA_AGENT_MAX_OUTPUT_BYTES.";
|
|
1912
2523
|
const pathField = fullOutputPath ? { full_output_path: fullOutputPath } : {};
|
|
2524
|
+
const shedNote = shedContentBlocks > 0 ? `removed content_blocks from ${shedContentBlocks} message(s) whose content already holds the same text` : "";
|
|
1913
2525
|
if (boundedBytes > maxBytes) {
|
|
1914
2526
|
return {
|
|
1915
2527
|
data: { truncated: true, original_bytes: originalBytes, max_bytes: maxBytes, ...pathField },
|
|
@@ -1920,25 +2532,45 @@ function boundForAgent(data, maxBytes = agentMaxOutputBytes(), options = {}) {
|
|
|
1920
2532
|
fullOutputPath
|
|
1921
2533
|
};
|
|
1922
2534
|
}
|
|
2535
|
+
const root = asRecord2(bounded);
|
|
2536
|
+
const notes = [];
|
|
2537
|
+
const nextCommands = [];
|
|
2538
|
+
for (const list of droppedLists.values()) {
|
|
2539
|
+
const rewritten = rewritePagination(list, root !== undefined && list.parent === root, options.invocation);
|
|
2540
|
+
if (!rewritten)
|
|
2541
|
+
continue;
|
|
2542
|
+
notes.push(rewritten.note);
|
|
2543
|
+
if (rewritten.nextCommand)
|
|
2544
|
+
nextCommands.push(rewritten.nextCommand);
|
|
2545
|
+
}
|
|
2546
|
+
const omittedTotal = dropped.reduce((sum, [, count]) => sum + count, 0);
|
|
2547
|
+
const actions = [
|
|
2548
|
+
...shedNote ? [shedNote] : [],
|
|
2549
|
+
...dropped.map(([path, count]) => `dropped the last ${count} item(s) of data.${path}`)
|
|
2550
|
+
];
|
|
2551
|
+
const warning = `Output was ${originalBytes} bytes, over the ${maxBytes}-byte agent limit: ${actions.join("; ")}.` + (notes.length > 0 ? ` ${notes.join(". ")}.` : "") + (omittedTotal > 0 ? ` ${howToGetMore}` : ` Nothing else was left out. ${whereIsTheRest}`);
|
|
1923
2552
|
return {
|
|
1924
2553
|
data: bounded,
|
|
1925
|
-
warning
|
|
2554
|
+
warning,
|
|
1926
2555
|
truncation: {
|
|
1927
2556
|
data: {
|
|
1928
2557
|
original_bytes: originalBytes,
|
|
1929
2558
|
bounded_bytes: boundedBytes,
|
|
1930
2559
|
max_bytes: maxBytes,
|
|
1931
2560
|
reason: "agent_output_byte_limit",
|
|
2561
|
+
...Object.keys(shedFields).length > 0 ? { shed_fields: shedFields } : {},
|
|
1932
2562
|
dropped_items: droppedItems,
|
|
2563
|
+
...omittedTotal > 0 ? { omitted_rows: omittedTotal } : {},
|
|
1933
2564
|
structure_preserved: true,
|
|
1934
|
-
complete:
|
|
2565
|
+
complete: omittedTotal === 0,
|
|
1935
2566
|
...pathField
|
|
1936
2567
|
}
|
|
1937
2568
|
},
|
|
1938
|
-
fullOutputPath
|
|
2569
|
+
fullOutputPath,
|
|
2570
|
+
...nextCommands.length > 0 ? { nextCommands } : {}
|
|
1939
2571
|
};
|
|
1940
2572
|
}
|
|
1941
|
-
function
|
|
2573
|
+
function asRecord2(value) {
|
|
1942
2574
|
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
1943
2575
|
}
|
|
1944
2576
|
function extractSuggestedCommands(text) {
|
|
@@ -1946,4 +2578,4 @@ function extractSuggestedCommands(text) {
|
|
|
1946
2578
|
return commands.length > 0 ? commands : undefined;
|
|
1947
2579
|
}
|
|
1948
2580
|
|
|
1949
|
-
export { PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, describeBaseUrlSource, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, CliInputError, CliAccessError, CliInputRequiredError, CliAuthError, CliNotFoundError, CliCancellationError, isNetworkLikeError, isApiLikeError, apiErrorStatus, VERSION, MODA_BASE_URL, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveApiTenantId, resolveModaBaseUrl, validateConfig, recordApiRequest, getApiRequestCount, resetApiRequestCount, MAX_SCANNED_FILE_BYTES, DEFAULT_MAX_INVENTORY_FILES, DEFAULT_MAX_INVENTORY_TEXT_BYTES, MAX_RECORDED_SKIPS, SKIPPED_DIRS, isSkippedDir, isSkippedPathPrefix, isEvalLikePath, shouldInventoryFile, isTextFile, isSecretishPath, SECRET_KEYS, redactDeep, redactString, packSnapshot, setAgentUpdateNotice, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, explicitOutputMode, outputModeFromFlags, resolveOutputMode, createCliOutput, agentEnvelopeFits, errorToAgentError, agentMaxOutputBytes };
|
|
2581
|
+
export { PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, describeBaseUrlSource, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, CliInputError, CliAccessError, CliInputRequiredError, CliAuthError, CliNotFoundError, CliCancellationError, isNetworkLikeError, isApiLikeError, apiErrorStatus, VERSION, MODA_BASE_URL, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveApiTenantId, resolveModaBaseUrl, validateConfig, recordApiRequest, getApiRequestCount, resetApiRequestCount, BATCH_MAX_TARGETS, BATCH_CONCURRENCY, parseBatchTarget, collectBatchTargets, mapWithConcurrency, contextTarget, asRecord, asString, asNumber, frustrationAnchorIndex, frustrationsFromEmotions, withFrustrationAnchors, shellWord, trimToolFailureConversations, withToolFailureAnchors, embeddedWindowSize, withoutDetectionWindows, compactProblemDossier, MAX_SCANNED_FILE_BYTES, DEFAULT_MAX_INVENTORY_FILES, DEFAULT_MAX_INVENTORY_TEXT_BYTES, MAX_RECORDED_SKIPS, SKIPPED_DIRS, isSkippedDir, isSkippedPathPrefix, isEvalLikePath, shouldInventoryFile, isTextFile, isSecretishPath, SECRET_KEYS, redactDeep, redactString, packSnapshot, setAgentUpdateNotice, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, explicitOutputMode, outputModeFromFlags, resolveOutputMode, createCliOutput, agentEnvelopeFits, errorToAgentError, agentMaxOutputBytes };
|