mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 06:24:41 +01:00
v1.91.124.0 feat(cso): helper run clock (remainingSeconds), /cso time discipline, content-free producer tool timeline (#3242)
* fix(cso): comprehensive verify can patch read-only snapshots and accepts runtime-plan's profile * feat(cso): helper replies carry remainingSeconds; producer receipts record a content-free tool timeline * fix(cso): count remainingSeconds to the investigation deadline; bound and validate the tool timeline remainingSeconds now measures to deadline - 60s, the point where scan, verify and record-review refuse with DEADLINE, and inspect carries it. The post-command clock lookup can only omit the field, never fail a command whose state change is already saved. The stream timeline records end times from tool_result, indexes only recorded calls so it stays at the 4096 cap, and receipts reject malformed timeline entries. * docs(changelog): unreleased entry for the /cso run clock * v1.91.124.0 stamp
This commit is contained in:
1 parent
56f25ef82b
commit
501ff8310a
16 files changed
+144
-14
No files matched your search
@@ -686,6 +686,7 @@ export async function runProducerCell(inputPath: string, receiptPath: string, op
|
||||
durationMs: run.durationMs,
|
||||
firstUsefulResultMs: null,
|
||||
toolCalls: run.toolCalls,
|
||||
...(run.toolTimeline ? { toolTimeline: run.toolTimeline } : {}),
|
||||
output,
|
||||
outputHash: sha256(output),
|
||||
usage: {
|
||||
|
||||
@@ -130,6 +130,8 @@ export interface ProducerReceipt {
|
||||
durationMs: number;
|
||||
firstUsefulResultMs: null;
|
||||
toolCalls: number;
|
||||
/** Content-free tool timeline (name, offset, thread) for diagnosing overruns; absent when the adapter has none. */
|
||||
toolTimeline?: Array<{ atMs: number; endMs?: number; tool: string; agent: 'main' | 'subagent' }>;
|
||||
output: string;
|
||||
outputHash: string;
|
||||
usage: {
|
||||
@@ -148,7 +150,7 @@ export interface ProducerReceipt {
|
||||
* Compact trusted index; raw output remains in the separately retained receipt.
|
||||
* Collection adds the matrix cell that the receipt's cellRef names.
|
||||
*/
|
||||
export type ProducerReceiptIndex = Omit<ProducerReceipt, 'output' | 'error'> & {
|
||||
export type ProducerReceiptIndex = Omit<ProducerReceipt, 'output' | 'error' | 'toolTimeline'> & {
|
||||
cell: ProducerCell;
|
||||
error?: { code: string };
|
||||
};
|
||||
|
||||
+3
-1
@@ -400,6 +400,8 @@ function validateReceipt(receipt: ProducerReceipt, cell: EvalCell, inputHash: st
|
||||
validateInstallationIdentity(receipt.installationIdentity, 'INVALID_PRODUCER_RECEIPT');
|
||||
validateProviderIdentity(receipt.providerIdentity, cell.host === 'codex' ? 'gpt' : cell.host, 'INVALID_PRODUCER_RECEIPT');
|
||||
validateArtifactInventory(receipt.artifacts, 'INVALID_PRODUCER_RECEIPT');
|
||||
const timeline = receipt.toolTimeline;
|
||||
if (timeline !== undefined && (!Array.isArray(timeline) || timeline.length > 4096 || timeline.some(entry => !entry || !Number.isSafeInteger(entry.atMs) || entry.atMs < 0 || (entry.endMs !== undefined && (!Number.isSafeInteger(entry.endMs) || entry.endMs < entry.atMs)) || typeof entry.tool !== 'string' || !/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(entry.tool) || !['main', 'subagent'].includes(entry.agent) || Object.keys(entry).some(key => !['atMs', 'endMs', 'tool', 'agent'].includes(key))))) throw new Error('INVALID_PRODUCER_RECEIPT');
|
||||
}
|
||||
|
||||
function validateInstallationIdentity(identity: ProducerInstallationIdentity, error: string): void {
|
||||
@@ -530,7 +532,7 @@ export function collectProducerReceipts(matrix: EvalMatrix, schedule: PreparedEv
|
||||
estimatedCost: { measured: costs.length, denominator: expected.length, totalUSD: costs.length ? costs.reduce((sum, value) => sum + value, 0) : null, source: 'pricing-table-estimate' },
|
||||
});
|
||||
}
|
||||
const indexes: ProducerReceiptIndex[] = bound.map(({ output: _output, error, ...receipt }) => ({ ...receipt, ...(error ? { error: { code: error.code } } : {}) }));
|
||||
const indexes: ProducerReceiptIndex[] = bound.map(({ output: _output, toolTimeline: _toolTimeline, error, ...receipt }) => ({ ...receipt, ...(error ? { error: { code: error.code } } : {}) }));
|
||||
const base = { schemaVersion: 1 as const, matrixHash: matrixHash(matrix), scheduleHash: digest(JSON.stringify(schedule)), receipts: indexes.sort((left, right) => left.cell.id.localeCompare(right.cell.id)), summary: {
|
||||
scheduled: matrix.cells.length, prepared: schedule.preparedCells, submitted: bound.length, missing: matrix.cells.length - bound.length,
|
||||
matchedPairsExpected: expectedMatchedPairs(matrix), matchedPairsSubmitted: completePairs.length, modelMismatches, groups,
|
||||
|
||||
Reference in new issue
Block a user