Merge remote-tracking branch 'origin/codex/enforce-tool-cancellation' into worktree/explicit-turn-signal
# Conflicts: # docs/architecture.md # docs/cordis-catalog/events.md # docs/core-data-structures/core.md # docs/event-producer-consumer.md # packages/core/agent-loop/README.md # packages/core/agent-loop/src/agent.ts # packages/core/agent/README.md # packages/core/agent/src/types.ts # packages/ui/acp/src/index.ts # packages/ui/tui/src/index.ts # packages/ui/tui/tests/harness.ts
This commit is contained in:
53
packages/goal/goal-session/src/outcome.ts
Normal file
53
packages/goal/goal-session/src/outcome.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/** Typed settlement policy for one admitted same-session goal round. */
|
||||
|
||||
import type { TurnEndReason } from '@deepseek-ai/dsh-session'
|
||||
|
||||
/** Driver action derived from one closed goal-owned turn. */
|
||||
export type GoalRoundOutcome =
|
||||
| { readonly kind: 'continue' }
|
||||
| { readonly kind: 'pause'; readonly reason: string }
|
||||
| {
|
||||
readonly kind: 'blocked'
|
||||
readonly code: 'usage-limited' | 'turn-error' | 'max-tokens' | 'prompt-rejected' | 'unknown-turn-outcome'
|
||||
readonly message: string
|
||||
}
|
||||
| { readonly kind: 'disarm'; readonly reason: 'durability-failed' | 'disposed' | 'interrupted' }
|
||||
|
||||
/**
|
||||
* Classify one closed goal round without mutating goal state.
|
||||
* @param reason - durable reason from the round's `turn/end`.
|
||||
* @param durable - whether the closing flush reached its durability checkpoint.
|
||||
* @returns the single driver action; no abnormal outcome requests an automatic retry.
|
||||
*/
|
||||
export function classifyGoalRound(reason: TurnEndReason, durable: boolean): GoalRoundOutcome {
|
||||
if (!durable) return { kind: 'disarm', reason: 'durability-failed' }
|
||||
const extensibleReason: { readonly kind: string } = reason
|
||||
switch (reason.kind) {
|
||||
case 'completed':
|
||||
return { kind: 'continue' }
|
||||
case 'aborted':
|
||||
return { kind: 'pause', reason: 'cancelled' }
|
||||
case 'error': {
|
||||
const { code, message } = reason.failure ?? reason
|
||||
return code === 'RATE_LIMIT' || code === 'QUOTA'
|
||||
? { kind: 'blocked', code: 'usage-limited', message }
|
||||
: { kind: 'blocked', code: 'turn-error', message }
|
||||
}
|
||||
case 'max-tokens':
|
||||
return { kind: 'blocked', code: 'max-tokens', message: 'model output reached max tokens' }
|
||||
case 'rejected':
|
||||
return { kind: 'blocked', code: 'prompt-rejected', message: reason.reason }
|
||||
case 'disposed':
|
||||
return { kind: 'disarm', reason: 'disposed' }
|
||||
case 'interrupted':
|
||||
return { kind: 'disarm', reason: 'interrupted' }
|
||||
// TurnEndReason is merge-extensible. An unknown producer cannot opt into
|
||||
// automatic retry merely by adding a tag; stop for inspection instead.
|
||||
default:
|
||||
return {
|
||||
kind: 'blocked',
|
||||
code: 'unknown-turn-outcome',
|
||||
message: `unknown turn outcome: ${extensibleReason.kind}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user