{"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n *                 Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: store the sealed result and complete with its object key, hash, and size.\n * GW -> Builder: return inline or polled status with an object-storage handle.\n * Builder: decrypt and verify the result's job, scope, and version bindings.\n * Full flow: personal-server-ts `docs/260903-jobs-contract.md`, section 1.\n *\n * @category Protocol\n */\n\nimport type { Address, Hex } from \"viem\";\nimport type { SealedEnvelope } from \"./identity\";\n\nexport const JOB_PROTOCOL_VERSION = 1;\nexport const JOB_OPERATIONS = [\"raw_read\", \"inference\"] as const;\nexport type JobOperation = (typeof JOB_OPERATIONS)[number];\nexport const JOB_STATES = [\n  \"queued\",\n  \"claimed\",\n  \"running\",\n  \"completed\",\n  \"failed\",\n  \"expired\",\n  \"cancelled\",\n] as const;\nexport type JobState = (typeof JOB_STATES)[number];\n/** Payment lifecycle recorded for a queued job. */\n/**\n * What actually happened to the money for one read.\n *\n * `none` is the pre-fee literal every job carried before job fees and stays in\n * the union for those rows. `free` is a chain whose `data_access` fee is\n * disabled or zero — priced honestly at nothing, not a placeholder. `unbilled`\n * is a read the Gateway priced but did not charge, either during the fee\n * rollout or on a path with no payer.\n */\nexport type PaymentState =\n  | \"none\"\n  | \"free\"\n  | \"unbilled\"\n  | \"reserved\"\n  | \"settling\"\n  | \"settled\"\n  | \"released\";\n\n/**\n * The builder's signed authorization for the price of one read.\n *\n * The same EIP-712 `GenericPayment` a legacy Personal Server read signs into\n * its `X-PAYMENT` header, over `opType: \"job_access\"` and\n * `opId: keccak256(jobId)`. The Gateway stores it verbatim on the payment row.\n */\nexport interface JobPaymentAuthorization {\n  signature: Hex;\n  /** uint256 decimal; must equal the Gateway's quote for this chain. */\n  amount: string;\n  asset: Address;\n  /** uint256 decimal, unique per (payer, kind). */\n  paymentNonce: string;\n}\n\n/** The price of one delivered read, as `GET /v1/jobs/quote` reports it. */\nexport interface JobQuote {\n  chainId: number;\n  /** uint256 decimal; \"0\" when this chain charges nothing. */\n  price: string;\n  asset: Address;\n  /** False when the fee is disabled or zero, so no payment is needed. */\n  payable: boolean;\n  /** Whether the Gateway refuses an unpaid submission today. */\n  enforced: boolean;\n}\n\n/** RecordDataAccess receipt fields plus the enclave signature over them. */\nexport interface JobAccessRecord {\n  dataPointId: Hex;\n  /** uint256 decimal — the data-point version actually served. */\n  version: string;\n  accessor: Address;\n  /** bytes32 per-event replay nonce; the contract pins it. */\n  recordId: Hex;\n  signature: Hex;\n}\nexport const DEFAULT_LEASE_SECONDS = 30;\nexport const MAX_LEASE_SECONDS = 300;\nexport const MAX_ATTEMPTS = 3;\nexport const MAX_WAIT_SECONDS = 25;\nexport const CLAIM_POLL_FLOOR_MS = 1000;\nexport const DEFAULT_JOB_DEADLINE_SECONDS = 600;\nexport const MAX_JOB_DEADLINE_SECONDS = 3600;\n\n/** Inner plaintext of the request box (ECIES to the enclave publicKey). */\nexport interface JobRequest {\n  v: 1;\n  jobId: string;\n  owner: Address;\n  builder: Address;\n  builderPublicKey: Hex;\n  grantId: Hex;\n  scope: string;\n  operation: JobOperation;\n  pinnedVersion: string | null;\n  deadline: string; /* ISO */\n}\n/** Plaintext of the request box; the Gateway never sees it. */\nexport interface JobRequestEnvelope {\n  request: JobRequest;\n  /**\n   * `Web3Signed <b64>.<sig>` by the builder: `aud` = Gateway origin,\n   * `uri` = `/v1/jobs/execute`,\n   * `bodyHash` = `sha256(canonicalJobRequestBytes(request))`.\n   */\n  auth: string;\n}\n/** Outer body of POST /v1/jobs (signed Web3Signed by the builder). */\nexport interface JobSubmission {\n  owner: Address;\n  grantId: Hex;\n  scope: string;\n  operation: JobOperation;\n  idempotencyKey: string;\n  /** Client UUID, echoed in `JobRequest.jobId`. */\n  jobId: string;\n  deadline?: string;\n  /** Base64 ECIES from `sealJobRequest`. */\n  requestCiphertext: string;\n  /**\n   * The most the builder accepts for this read, uint256 decimal. The Gateway\n   * refuses a quote above it rather than charging a price nobody agreed to.\n   *\n   * @remarks\n   * This submission is authenticated by a Web3Signed body hash, not by an\n   * EIP-712 struct, so these three fields are covered by the builder's existing\n   * request signature. No typed-data version changes.\n   */\n  maxPrice?: string;\n  priceAsset?: Address;\n  payment?: JobPaymentAuthorization;\n}\n/** Where a completed job's sealed result lives. Bytes never transit the Gateway. */\nexport interface ResultHandle {\n  /** Object key in vana-storage, `jobresults/{chainId}/{jobId}`. */\n  objectKey: string;\n  /** Absolute URL the builder GETs. The Gateway builds it from its storage origin. */\n  url: string;\n  /** Byte length of the sealed object. */\n  size: number;\n  /** sha256 of the sealed bytes, 0x-prefixed. */\n  hash: Hex;\n  /** Logical expiry. After this the Gateway stops serving the handle. */\n  expiresAt: string;\n}\n/** Response from `GET /v1/jobs/:id`. */\nexport interface JobStatus {\n  jobId: string;\n  state: JobState;\n  operation: JobOperation;\n  owner: Address;\n  grantId: Hex;\n  scope: string;\n  pinnedVersion: string | null;\n  attempt: number;\n  price: string;\n  /**\n   * Null exactly when the read is free; nothing owed is owed to nobody.\n   * Optional so a consumer's existing `JobStatus` fixtures keep compiling; the\n   * Gateway always sends it.\n   */\n  priceAsset?: Address | null;\n  payer: \"builder\";\n  paymentState: PaymentState;\n  createdAt: string;\n  claimedAt: string | null;\n  completedAt: string | null;\n  failureReason: string | null;\n  /** Present only when `state === \"completed\"`. */\n  result?: ResultHandle;\n}\n/** Request body for `POST /v1/jobs/claim`. */\nexport interface ClaimRequest {\n  leaseSeconds?: number;\n  capacity?: number;\n}\n/** Claimed job and owner identity returned by `POST /v1/jobs/claim`. */\nexport interface ClaimResponse {\n  job: {\n    jobId: string;\n    owner: Address;\n    builder: Address;\n    grantId: Hex;\n    scope: string;\n    operation: JobOperation;\n    pinnedVersion: string | null;\n    requestCiphertext: string;\n    attempt: number;\n    deadlineAt: string | null;\n    claimExpiresAt: string;\n    fencingToken: number; /* = attempt; every node write echoes it */\n  };\n  identity: {\n    userPsId: Hex;\n    epoch: number;\n    enclaveAddress: Address;\n    enclavePublicKey: Hex;\n    sealedEnvelope: SealedEnvelope;\n  };\n}\n/** Request body for `POST /v1/jobs/:id/heartbeat`. */\nexport interface HeartbeatRequest {\n  leaseSeconds?: number;\n  fencingToken: number;\n}\n/** Request body for `POST /v1/jobs/:id/complete`. */\nexport interface CompleteRequest {\n  fencingToken: number;\n  resultHash: Hex;\n  resultSize: number;\n  /** `jobresults/{chainId}/{jobId}`. */\n  resultObjectKey: string;\n  /**\n   * Server-signed delivery receipt for the read this job served, minted by the\n   * node agent with the owner's enclave wallet. Binds the reserved payment to\n   * an on-chain `recordDataAccess`, exactly as the legacy paid read does.\n   * Required whenever the job reserved a fee.\n   */\n  accessRecord?: JobAccessRecord;\n}\n/** Request body for `POST /v1/jobs/:id/fail`. */\nexport interface FailRequest {\n  fencingToken: number;\n  reason: string; /* <= 1024, fail.ts:16 */\n}\n/** Successful response from a fenced job write endpoint. */\nexport interface FencedResponse {\n  success: true;\n  jobId: string;\n  state: JobState;\n  claimExpiresAt: string | null;\n}\n/** Inner plaintext of the result box (ECIES to builderPublicKey). */\nexport interface JobResult {\n  v: 1;\n  jobId: string;\n  scope: string;\n  version: string | null;\n  contentType: string;\n  /** Raw result bytes; callers decode text explicitly when appropriate. */\n  body: Uint8Array;\n}\n/** Admission lifecycle of a registered TEE node. */\nexport type TeeNodeState = \"pending\" | \"admitted\" | \"draining\" | \"removed\";\n/** Request body for `POST /v1/tee-nodes`. */\nexport interface TeeNodeRegistration {\n  nodeId: string;\n  appId: Hex;\n  composeHash: Hex;\n  publicUrl: string;\n  capacity: number;\n  secret: string;\n}\n/** Request body for `POST /v1/tee-nodes/:id/heartbeat`. */\nexport interface TeeNodeHeartbeat {\n  composeHash: Hex;\n  instanceId: string;\n  activeSandboxes: number;\n  capacity: number;\n}\n/** Public registration and capacity state for a TEE node. */\nexport interface TeeNode {\n  nodeId: string;\n  appId: Hex;\n  composeHash: Hex;\n  publicUrl: string;\n  state: TeeNodeState;\n  capacity: number;\n  activeSandboxes: number;\n  lastHeartbeatAt: string | null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBO,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB,CAAC,YAAY,WAAW;AAE/C,MAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA2DO,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;AACrC,MAAM,2BAA2B;","names":[]}