Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 58 additions & 6 deletions packages/core/src/goals/goal-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,62 @@ export const GOAL_CHECKPOINT_STALL_LIMIT = 3;
export const GOAL_CHECKPOINT_STALLED_REASON =
'The current Goal revision ran three consecutive evidence checkpoints without relief: the evidence window overflowed every time, and each check either came back with a full claim list or a result that could not be folded into claims at all, so every turn paid a checkpoint call and lost uncatalogued evidence. Automatic retries cannot recover. Edit or replace the Goal with a narrower objective before resuming it.';

/**
* Default autonomous spend window armed on a newly created Goal, in model
* tokens on the `tokensUsed` metric (`totalTokenCount` summed per model call,
* so a call's full input context counts every time it is sent).
*
* The meter bills Goal-turn model calls only -- per-turn side queries and
* checkpoint-verifier calls are unmetered -- so real provider spend at a
* stop runs above this window.
*
* This is an authorization quantum, not a cost estimate: it bounds how much
* autonomous continuation one explicit user action (create, or a later
* resume) pays for before the Goal stops and asks again. Sized to a few hours
* of continuous turn cadence -- the runaway session this bound exists for
* burned ~8.6M tokens in half an hour before a human killed it, so a healthy
* long run reaches this ceiling late and a stuck loop reaches it unattended.
*/
export const GOAL_DEFAULT_TOKEN_BUDGET = 30_000_000;

/** The `lastReason` a Goal stops with when `tokensUsed` reaches its budget. */
export function goalTokenBudgetReason(tokenBudget: number): string {
return `The Goal spent its autonomous token budget (${tokenBudget.toLocaleString('en-US')} tokens). Resume the Goal to authorize another budget window, or clear it.`;
}

/**
* Whether `tokensUsed` has reached the armed ceiling. One predicate serves
* both the runtime's stop condition and the reducer's re-arm condition, so
* the stop/re-arm cycle cannot desynchronize.
*/
export function isGoalTokenBudgetSpent(
goal: Pick<GoalRecord, 'tokensUsed' | 'tokenBudget'>,
): goal is Pick<GoalRecord, 'tokensUsed' | 'tokenBudget'> & {
tokenBudget: number;
} {
return goal.tokenBudget !== undefined && goal.tokensUsed >= goal.tokenBudget;
}

/**
* Which bound a `usage_limited` Goal ran into.
*
* Only the evidence bounds are enumerated: they are the ones a caller has to
* branch on, because they are the ones a plain resume cannot clear. Every other
* route to `usage_limited` is an operational failure that carries prose in
* `lastReason` and nothing to key off.
* Only the enumerated bounds are typed: they are the ones a caller has to
* branch on. The evidence kinds mark a window a plain resume cannot simply
* re-enter; `token_budget` marks a spent authorization that a resume re-arms.
* Every other route to `usage_limited` is an operational failure that carries
* prose in `lastReason` and nothing to key off.
*/
export type GoalLimitKind = 'evidence_catalog' | 'checkpoint_request';
export type GoalLimitKind =
| 'evidence_catalog'
| 'checkpoint_request'
| 'token_budget';

export function isGoalLimitKind(value: unknown): value is GoalLimitKind {
return value === 'evidence_catalog' || value === 'checkpoint_request';
return (
value === 'evidence_catalog' ||
value === 'checkpoint_request' ||
value === 'token_budget'
Comment thread
qwen-code-dev-bot marked this conversation as resolved.
);
}

/** The limit a `usage_limited` reason denotes, for reasons that denote one. */
Expand Down Expand Up @@ -120,6 +164,14 @@ export interface GoalRecord {
* Zero on Goals recovered from a transcript written before the field existed.
*/
tokensUsed: number;
/**
* The ceiling `tokensUsed` may reach before autonomous continuation stops
* and the Goal waits for the user. Armed at creation from the runtime's
* grant; a resume or edit of a Goal whose ceiling is spent moves it forward
* (`tokensUsed + grant`) -- the spent meter itself is never reset. Absent
* on Goals persisted before budgets existed: those stay unbounded.
*/
tokenBudget?: number;
createdAt: number;
updatedAt: number;
evidenceCheckpoint?: GoalEvidenceCheckpoint;
Expand Down
226 changes: 226 additions & 0 deletions packages/core/src/goals/goal-reducer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
GOAL_CHECKPOINT_REQUEST_TOO_LARGE_REASON,
GOAL_EVIDENCE_CATALOG_EXHAUSTED_REASON,
goalLimitKindForReason,
goalTokenBudgetReason,
goalRequiresExactPermit,
type GoalControlRequest,
type GoalRecord,
Expand Down Expand Up @@ -1106,3 +1107,228 @@ describe('goal reducer', () => {
},
);
});

describe('token budget transitions', () => {
const control = (request: GoalControlRequest, tokenBudgetGrant?: number) => ({
request,
now: 200,
nextGoalId: 'g-next',
cursor: { recordId: 'r-200' },
...(tokenBudgetGrant === undefined ? {} : { tokenBudgetGrant }),
});

const budgetStopped = (overrides: Partial<GoalRecord> = {}): GoalRecord =>
goalRecord({
status: 'usage_limited',
tokensUsed: 1_200,
tokenBudget: 1_000,
lastReason: goalTokenBudgetReason(1_000),
limitKind: 'token_budget',
...overrides,
});

it('stamps the armed grant on create and replace', () => {
const created = reduceGoalControl(
null,
control({ action: 'create', objective: 'ship' }, 1_000),
);
expect(created).toMatchObject({ tokenBudget: 1_000, tokensUsed: 0 });

const replaced = reduceGoalControl(
goalRecord({ tokensUsed: 900, tokenBudget: 1_000 }),
control(
{
action: 'replace',
objective: 'ship again',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
2_000,
),
);
expect(replaced).toMatchObject({ tokenBudget: 2_000, tokensUsed: 0 });
});

it('creates an unbounded Goal when no grant is armed', () => {
const created = reduceGoalControl(
null,
control({ action: 'create', objective: 'ship' }),
);
expect(created).not.toHaveProperty('tokenBudget');
});

it('re-arms a budget-stopped Goal on resume: the ceiling moves ahead of the meter it never resets', () => {
const resumed = reduceGoalControl(
budgetStopped(),
control(
{ action: 'resume', expectedGoalId: 'g-1', expectedRevision: 1 },
1_000,
),
);
expect(resumed).toMatchObject({
status: 'active',
tokensUsed: 1_200,
tokenBudget: 2_200,
revision: 1,
evidenceCursor: { recordId: 'r-100' },
});
Comment thread
qwen-code-dev-bot marked this conversation as resolved.
Comment thread
qwen-code-dev-bot marked this conversation as resolved.
expect(resumed?.lastReason).toBeUndefined();
expect(resumed?.limitKind).toBeUndefined();
});

it('leaves an unspent ceiling alone on resume', () => {
const resumed = reduceGoalControl(
goalRecord({ status: 'paused', tokensUsed: 300, tokenBudget: 1_000 }),
control(
{ action: 'resume', expectedGoalId: 'g-1', expectedRevision: 1 },
1_000,
),
);
expect(resumed).toMatchObject({ status: 'active', tokenBudget: 1_000 });
});

it('re-arms when the spend lands exactly on the ceiling', () => {
const resumed = reduceGoalControl(
budgetStopped({ tokensUsed: 1_000, tokenBudget: 1_000 }),
control(
{ action: 'resume', expectedGoalId: 'g-1', expectedRevision: 1 },
1_000,
),
);
expect(resumed).toMatchObject({
status: 'active',
tokensUsed: 1_000,
tokenBudget: 2_000,
});
});

it.each(['paused', 'blocked'] as const)(
're-arms a spent ceiling when resuming a %s Goal',
(status) => {
const resumed = reduceGoalControl(
goalRecord({ status, tokensUsed: 1_200, tokenBudget: 1_000 }),
control(
{ action: 'resume', expectedGoalId: 'g-1', expectedRevision: 1 },
1_000,
),
);
expect(resumed).toMatchObject({
status: 'active',
tokensUsed: 1_200,
tokenBudget: 2_200,
});
},
);

it('clears a spent ceiling on resume or edit when the runtime opts out', () => {
const resumed = reduceGoalControl(
budgetStopped(),
control(
{ action: 'resume', expectedGoalId: 'g-1', expectedRevision: 1 },
Number.POSITIVE_INFINITY,
),
);
expect(resumed).toMatchObject({ status: 'active', tokensUsed: 1_200 });
expect(resumed).not.toHaveProperty('tokenBudget');

const edited = reduceGoalControl(
budgetStopped(),
control(
{
action: 'edit',
objective: 'ship without a budget',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
Number.POSITIVE_INFINITY,
),
);
expect(edited).toMatchObject({
status: 'usage_limited',
objective: 'ship without a budget',
tokensUsed: 1_200,
});
expect(edited).not.toHaveProperty('tokenBudget');
});

it('re-arms a spent ceiling on edit, so the edited Goal can actually run', () => {
const edited = reduceGoalControl(
budgetStopped(),
control(
{
action: 'edit',
objective: 'ship the rest',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
1_000,
),
);
expect(edited).toMatchObject({
status: 'usage_limited',
revision: 2,
tokensUsed: 1_200,
tokenBudget: 2_200,
});
});

it('never retrofits a budget onto an unbounded Goal', () => {
const edited = reduceGoalControl(
goalRecord({ tokensUsed: 5_000_000 }),
control(
{
action: 'edit',
objective: 'keep going',
expectedGoalId: 'g-1',
expectedRevision: 1,
},
1_000,
),
);
expect(edited).not.toHaveProperty('tokenBudget');
});

it('resumes an evidence-limited Goal through the fresh window, re-arming a spent budget on the way', () => {
const resumed = reduceGoalControl(
goalRecord({
status: 'usage_limited',
tokensUsed: 1_200,
tokenBudget: 1_000,
lastReason: GOAL_EVIDENCE_CATALOG_EXHAUSTED_REASON,
limitKind: 'evidence_catalog',
}),
control(
{ action: 'resume', expectedGoalId: 'g-1', expectedRevision: 1 },
1_000,
),
);
expect(resumed).toMatchObject({
status: 'active',
tokensUsed: 1_200,
tokenBudget: 2_200,
evidenceCursor: { recordId: 'r-200' },
});
expect(resumed?.lastReason).toBeUndefined();
expect(resumed?.limitKind).toBeUndefined();
});

it('restores a persisted budget and rejects a malformed one', () => {
const stored = snapshot(
goalRecord({
status: 'usage_limited',
tokensUsed: 1_200,
tokenBudget: 1_000,
lastReason: goalTokenBudgetReason(1_000),
limitKind: 'token_budget',
}),
);
expect(parseGoalSnapshotV2(stored)).toEqual(stored);
expect(
parseGoalSnapshotV2(snapshot(goalRecord({ tokenBudget: -1 }))),
).toBeUndefined();
// A Goal from before budgets existed restores unbounded, not defaulted.
expect(parseGoalSnapshotV2(snapshot(goalRecord()))).toEqual(
snapshot(goalRecord()),
);
});
});
Loading
Loading