MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / classifySufficiency

Function classifySufficiency

scripts/agent-eval/parse-run.mjs:853–922  ·  view source on GitHub ↗
(events)

Source from the content-addressed store, hash-verified

851 * `message.id`, and tool results as `tool_result` blocks in user messages.
852 */
853export function classifySufficiency(events) {
854 // One action list PER THREAD: 'main', plus one per subagent (keyed by the
855 // delegating tool_use id, which is what `parent_tool_use_id` carries).
856 const threads = new Map();
857 const nameById = new Map();
858 const textById = new Map(); // explore tool_use_id -> response text
859 for (const ev of events) {
860 const content = ev?.message?.content;
861 if (!Array.isArray(content)) continue;
862 const thread = ev.parent_tool_use_id ?? 'main';
863 if (ev.type === 'assistant') {
864 if (!threads.has(thread)) threads.set(thread, []);
865 const list = threads.get(thread);
866 for (const b of content) {
867 if (b.type !== 'tool_use') continue;
868 nameById.set(b.id, b.name);
869 // No message.id (never seen on a real log) degrades to "every call is
870 // its own message", i.e. same-message calls read as reactions.
871 list.push({ msgId: ev.message.id || `#${thread}-${list.length}`, id: b.id, name: b.name, input: b.input || {} });
872 }
873 } else if (ev.type === 'user') {
874 for (const b of content) {
875 if (b.type !== 'tool_result') continue;
876 const name = nameById.get(b.tool_use_id) || '';
877 if (/codegraph_explore/.test(name) && !b.is_error) textById.set(b.tool_use_id, textOf(b.content));
878 }
879 }
880 }
881
882 const calls = [];
883 let errors = 0, concurrent = 0;
884 // What a delegation really did: the subagent's first substantive call. A
885 // nested delegation is skipped rather than followed, so a subagent that only
886 // spawns another subagent leaves the call as "moved on".
887 const throughDelegation = (action, returned, mentioned, earlier) => {
888 const first = (threads.get(action.id) || []).find((x) => !TRANSPARENT_TOOLS.has(x.name) && !DELEGATION_TOOLS.has(x.name));
889 if (!first) return { bucket: 'sufficient', next: action.name };
890 const r = reactionOf(first, returned, mentioned, earlier);
891 return { ...r, next: `${action.name} → ${r.next}` };
892 };
893 for (const [thread, actions] of threads) {
894 const earlier = []; // files previous explores in THIS thread already shipped
895 for (let i = 0; i < actions.length; i++) {
896 const a = actions[i];
897 if (!/codegraph_explore/.test(a.name)) continue;
898 const text = textById.get(a.id);
899 // No response text = the call errored, or the run ended before it
900 // returned. Nothing to judge the sufficiency of; count it and move on.
901 if (text === undefined) { errors++; continue; }
902 const returned = exploreReturnedFiles(text);
903 const mentioned = text.match(PATH_TOKEN_RE) || [];
904 let reaction = { bucket: 'sufficient', next: '(final answer)' };
905 for (let j = i + 1; j < actions.length; j++) {
906 const b = actions[j];
907 if (b.msgId === a.msgId) { if (isFileAccess(b)) concurrent++; continue; }
908 if (TRANSPARENT_TOOLS.has(b.name)) continue;
909 reaction = DELEGATION_TOOLS.has(b.name)
910 ? throughDelegation(b, returned, mentioned, earlier)

Callers 3

parse-session.mjsFile · 0.90
parseSessionFunction · 0.85
suffFunction · 0.85

Calls 8

textOfFunction · 0.85
exploreReturnedFilesFunction · 0.85
isFileAccessFunction · 0.85
throughDelegationFunction · 0.85
reactionOfFunction · 0.85
hasMethod · 0.80
getMethod · 0.65
setMethod · 0.45

Tested by

no test coverage detected