Complete M16 action select circle parity
This commit is contained in:
@@ -12,6 +12,9 @@ export const CONTEXT_LIMITS = Object.freeze({
|
||||
evidenceFiles: 12,
|
||||
evidenceBytes: 8192,
|
||||
contextTokens: 3500,
|
||||
// The printed task bundle is itself part of the model input. Reserve room
|
||||
// for that envelope instead of spending the entire budget on file content.
|
||||
contextEnvelopeTokens: 1024,
|
||||
});
|
||||
|
||||
const read = (file) => fs.readFileSync(file, "utf8");
|
||||
@@ -199,8 +202,9 @@ export function selectInputPaths(candidates, {
|
||||
const unique = [...new Set((candidates ?? []).filter((candidate) => typeof candidate === "string" && candidate.length > 0))];
|
||||
const reserved = new Set(reservedPaths.map(normalizedPathKey));
|
||||
const generated = new Set(generatedPaths.map(normalizedPathKey));
|
||||
const contextRemainingTokens = Math.max(0, effectiveLimits.contextTokens - sourceTokens);
|
||||
const contextRemainingBytes = Math.max(0, effectiveLimits.contextTokens * 4 - sourceBytes);
|
||||
const contentTokenBudget = Math.max(0, effectiveLimits.contextTokens - (effectiveLimits.contextEnvelopeTokens ?? 0));
|
||||
const contextRemainingTokens = Math.max(0, contentTokenBudget - sourceTokens);
|
||||
const contextRemainingBytes = Math.max(0, contentTokenBudget * 4 - sourceBytes);
|
||||
const excluded = [];
|
||||
const selected = [];
|
||||
let selectedBytes = 0;
|
||||
@@ -264,6 +268,45 @@ export function selectInputPaths(candidates, {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Public task context is intentionally smaller than the audit bundle. The
|
||||
* latter is written to task-context.json for governance, while this shape is
|
||||
* what an execution agent should receive and read.
|
||||
*/
|
||||
export function compactTaskContext(context) {
|
||||
const compactText = (value, limit) => {
|
||||
if (typeof value !== "string" || value.length <= limit) return value;
|
||||
return `${value.slice(0, limit - 3)}...`;
|
||||
};
|
||||
const excludedReasons = {};
|
||||
for (const item of context.inputSelection?.excluded ?? []) {
|
||||
excludedReasons[item.reason] = (excludedReasons[item.reason] ?? 0) + 1;
|
||||
}
|
||||
return {
|
||||
schemaVersion: context.schemaVersion,
|
||||
task: context.task,
|
||||
parentTask: context.parentTask,
|
||||
status: context.status,
|
||||
goal: compactText(context.goal, 800),
|
||||
scope: context.scope,
|
||||
commands: context.commands,
|
||||
inputPaths: context.inputPaths,
|
||||
inputSelection: {
|
||||
selected: context.inputSelection?.selected?.map(({ path, bytes, tokens }) => ({ path, bytes, tokens })) ?? [],
|
||||
excludedCount: context.inputSelection?.excluded?.length ?? 0,
|
||||
excludedReasons,
|
||||
totals: context.inputSelection?.totals ?? { files: 0, bytes: 0, tokens: 0 },
|
||||
},
|
||||
exitCriteria: context.exitCriteria,
|
||||
nextTask: context.nextTask,
|
||||
sourceDocuments: context.sourceDocuments,
|
||||
readPolicy: {
|
||||
required: context.readPolicy.required,
|
||||
},
|
||||
parent: { manifest: context.parent.manifest },
|
||||
};
|
||||
}
|
||||
|
||||
function nextFromPlan(task) {
|
||||
const index = loadTaskIndex();
|
||||
if (index?.entries?.[task]) return index.entries[task].next;
|
||||
@@ -375,5 +418,35 @@ export function contextSizeReport(bundle) {
|
||||
const evidenceFiles = context?.inputSelection?.totals?.files ?? 0;
|
||||
const evidenceTokens = context?.inputSelection?.totals?.tokens ?? 0;
|
||||
const totalTokens = sourceTokens + evidenceTokens;
|
||||
return { documents: docs, sourceTokens, evidenceFiles, evidenceBytes, evidenceTokens, totalTokens, withinBudget: docs[0].bytes <= CONTEXT_LIMITS.queueBytes && docs[1].bytes <= CONTEXT_LIMITS.taskBytes && docs[2].bytes <= CONTEXT_LIMITS.parentManifestBytes && docs[3].bytes <= CONTEXT_LIMITS.parentStatusBytes && evidenceFiles <= CONTEXT_LIMITS.evidenceFiles && evidenceBytes <= CONTEXT_LIMITS.evidenceBytes && totalTokens <= CONTEXT_LIMITS.contextTokens };
|
||||
const baseReport = {
|
||||
documents: docs,
|
||||
sourceTokens,
|
||||
evidenceFiles,
|
||||
evidenceBytes,
|
||||
evidenceTokens,
|
||||
totalTokens,
|
||||
reservedEnvelopeTokens: CONTEXT_LIMITS.contextEnvelopeTokens,
|
||||
estimatedContextTokens: totalTokens + CONTEXT_LIMITS.contextEnvelopeTokens,
|
||||
};
|
||||
// Include the pretty-printed report itself: that is what print-task-context
|
||||
// puts on stdout and therefore what the execution model actually receives.
|
||||
let serializedContextTokens = 0;
|
||||
for (let attempt = 0; attempt < 3; attempt += 1) {
|
||||
serializedContextTokens = tokenEstimate(JSON.stringify({
|
||||
...compactTaskContext(context),
|
||||
size: { ...baseReport, serializedContextTokens },
|
||||
}, null, 2));
|
||||
}
|
||||
return {
|
||||
...baseReport,
|
||||
serializedContextTokens,
|
||||
withinBudget: docs[0].bytes <= CONTEXT_LIMITS.queueBytes
|
||||
&& docs[1].bytes <= CONTEXT_LIMITS.taskBytes
|
||||
&& docs[2].bytes <= CONTEXT_LIMITS.parentManifestBytes
|
||||
&& docs[3].bytes <= CONTEXT_LIMITS.parentStatusBytes
|
||||
&& evidenceFiles <= CONTEXT_LIMITS.evidenceFiles
|
||||
&& evidenceBytes <= CONTEXT_LIMITS.evidenceBytes
|
||||
&& serializedContextTokens <= CONTEXT_LIMITS.contextEnvelopeTokens
|
||||
&& baseReport.estimatedContextTokens <= CONTEXT_LIMITS.contextTokens,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user