vpp-ai-platform/packages/domain/src/proposal.ts

76 lines
2.1 KiB
TypeScript
Raw Normal View History

import { z } from 'zod'
import { Curve96, DecimalString, Id, IsoUtc, MarketDate, SnapshotRef, Timescale } from './common.js'
export const ProposalType = z.enum(['BID', 'INVITATION', 'CONTROL_PLAN', 'DISPATCH_PLAN'])
export type ProposalType = z.infer<typeof ProposalType>
export const TriggerKind = z.enum(['MANUAL', 'EVENT', 'SCHEDULED'])
/** Full lifecycle per docs/03 §2 (incl. FRESH_CHECK/STALE/AUTHORIZED from the staleness model). */
export const ProposalStatus = z.enum([
'DRAFT',
'RULE_CHECK',
'SIMULATION',
'ENVELOPE_CHECK',
'PENDING_HUMAN',
'AUTO_APPROVED',
'APPROVED',
'FRESH_CHECK',
'STALE',
'AUTHORIZED',
'RELEASED',
'EXECUTING',
'COMPLETED',
'ROLLED_BACK',
'REJECTED',
])
export const ToolCallRef = z.object({
tool_call_id: Id,
tool: Id,
version: z.string().min(1),
inputs_ref: SnapshotRef,
outputs_ref: SnapshotRef,
})
/**
* Provenance attached to every proposal (docs/03 §1). Rule check enforces that
* every numeric payload field traces to one of these tool calls (invariant/P2).
*/
export const Lineage = z.object({
tool_calls: z.array(ToolCallRef),
data_refs: z.array(SnapshotRef),
ledger_version: z.int().nonnegative(),
policy_pack_version: z.string().min(1),
})
/** Day-ahead bid: per-interval price/quantity pairs. */
export const BidPayload = z.object({
kind: z.literal('BID'),
market_date: MarketDate,
prices_yuan_per_mwh: Curve96,
quantities_mwh: Curve96,
expected_revenue_yuan: DecimalString,
})
const ProposalBase = z.object({
id: Id,
timescale: Timescale,
originator: z.object({ agent: Id, trigger: TriggerKind, task_id: Id }),
lineage: Lineage,
envelope_ref: Id.nullable(),
/** sha256 over canonical payload + lineage. Approvals and permits bind to this (I3/I4). */
digest: SnapshotRef,
status: ProposalStatus,
created_at: IsoUtc,
})
/**
* M1 carries the BID variant only; INVITATION / CONTROL_PLAN / DISPATCH_PLAN
* payloads arrive with their milestones (ROADMAP M4 / phase 2).
*/
export const Proposal = z.discriminatedUnion('type', [
ProposalBase.extend({ type: z.literal('BID'), payload: BidPayload }),
])
export type Proposal = z.infer<typeof Proposal>