SandboxProviderJobState sandbox-provider.job-state@1

Generated at from openvibe-contracts v0.177.0 and openvibe-sdk v0.44.0.

Version
1.0.0
Owner
run
Visibility
internal
Status
planned
Compatibility
backward
Decision
ADR-034
Schema
https://openvibe.network/contracts/sandbox-provider/job-state.v1.json

sandbox-provider.job-state@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) state of one job, the body of GET <provider>/jobs/:id and the `job` of sandbox-provider.job-create-result@1. OpenVibe.Run validates it before adopting any of it: a malformed state is never adopted, and the job stays as Run last saw it. A 404 on GET or on cancel means the provider does not know the job (Run fails the job run.job.worker_failed). STATE is queued, running, then one end state: succeeded, failed, cancelled or expired. WALL_MS is the provider's CLAIM of the time the job ran, nothing more: Run bills from its own observed duration and clamps a larger claim (observed plus 5 s), so this field never sets a price. STARTED_MS and FINISHED_MS are the provider's clock, Unix ms, null until they happen. EXIT_REASON uses the reasons of platform.job-frame@1 `job_exit` (exited, cancelled, ttl, limit, stopped, failed) and is null until the job ended. EXIT_CODE is the job's exit status, null when it was killed or never ran. RESULT is the declared output (an object, at most 256 KiB serialized), null unless the job succeeded. STDOUT is the captured output in the ring shape OpenVibe.Bot returns (bot.job-dispatch-result@1) so Run's poller streams it the same way, or null when there is none. There is no node, project or class field: a provider cannot name a node or a project.

Fields

FieldTypeRequiredDescriptionConstraints
idhttps://openvibe.network/contracts/platform/job-frame.v1.json#/$defs/job_idyes
stateenumyesqueued (accepted, not started), running, then one end state: succeeded (exited 0), failed (another exit code, a limit, or the provider could not run it), cancelled (a cancel), expired (the job's lifetime ran out).
  • one of "queued", "running", "succeeded", "failed", "cancelled", "expired"
started_msinteger | nullyesWhen the job started, provider's clock, Unix ms; null until it started.
  • minimum 0
finished_msinteger | nullyesWhen the job ended, provider's clock, Unix ms; null until it ended.
  • minimum 0
wall_msintegeryesThe provider's CLAIM of the running time; Run clamps it to its own observed duration plus 5 s and never bills above that. 0 while the job has not run.
  • minimum 0
exit_reasonenumyesWhy the job ended, as platform.job-frame@1 job_exit reason; null until it ended.
  • one of "exited", "cancelled", "ttl", "limit", "stopped", "failed", null
exit_codeinteger | nullyesThe script's exit status; null when it was killed or never ran.
resultobject | nullyesThe declared output as a JSON object, at most 256 KiB serialized; null unless the job succeeded.
stdoutone ofyesThe captured stdout, or null when the job has none.

Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

Valid: failed-limit
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "failed",
  "started_ms": 1790935200250,
  "finished_ms": 1790935260250,
  "wall_ms": 60000,
  "exit_reason": "limit",
  "exit_code": null,
  "result": null,
  "stdout": null
}
Valid: queued
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "queued",
  "started_ms": null,
  "finished_ms": null,
  "wall_ms": 0,
  "exit_reason": null,
  "exit_code": null,
  "result": null,
  "stdout": null
}
Valid: running-with-stdout
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "running",
  "started_ms": 1790935200250,
  "finished_ms": null,
  "wall_ms": 1200,
  "exit_reason": null,
  "exit_code": null,
  "result": null,
  "stdout": {
    "text": "loaded\n",
    "first_seq": 1,
    "last_seq": 1,
    "truncated": false
  }
}
Valid: succeeded
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "succeeded",
  "started_ms": 1790935200250,
  "finished_ms": 1790935202600,
  "wall_ms": 2350,
  "exit_reason": "exited",
  "exit_code": 0,
  "result": {
    "title": "Example Domain"
  },
  "stdout": {
    "text": "loaded\ndone\n",
    "first_seq": 1,
    "last_seq": 2,
    "truncated": false
  }
}
Rejected: class-field
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "queued",
  "started_ms": null,
  "finished_ms": null,
  "wall_ms": 0,
  "exit_reason": null,
  "exit_code": null,
  "result": null,
  "stdout": null,
  "class": "code"
}
Rejected: missing-wall-ms
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "queued",
  "started_ms": null,
  "finished_ms": null,
  "exit_reason": null,
  "exit_code": null,
  "result": null,
  "stdout": null
}
Rejected: negative-started-ms
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "running",
  "started_ms": -5,
  "finished_ms": null,
  "wall_ms": 1200,
  "exit_reason": null,
  "exit_code": null,
  "result": null,
  "stdout": {
    "text": "loaded\n",
    "first_seq": 1,
    "last_seq": 1,
    "truncated": false
  }
}
Rejected: negative-wall-ms
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "succeeded",
  "started_ms": 1790935200250,
  "finished_ms": 1790935202600,
  "wall_ms": -1,
  "exit_reason": "exited",
  "exit_code": 0,
  "result": {
    "title": "Example Domain"
  },
  "stdout": {
    "text": "loaded\ndone\n",
    "first_seq": 1,
    "last_seq": 2,
    "truncated": false
  }
}
Rejected: node-id
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "queued",
  "started_ms": null,
  "finished_ms": null,
  "wall_ms": 0,
  "exit_reason": null,
  "exit_code": null,
  "result": null,
  "stdout": null,
  "node_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPQ"
}
Rejected: project-id
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "queued",
  "started_ms": null,
  "finished_ms": null,
  "wall_ms": 0,
  "exit_reason": null,
  "exit_code": null,
  "result": null,
  "stdout": null,
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"
}
Rejected: result-not-object
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "succeeded",
  "started_ms": 1790935200250,
  "finished_ms": 1790935202600,
  "wall_ms": 2350,
  "exit_reason": "exited",
  "exit_code": 0,
  "result": "ok",
  "stdout": {
    "text": "loaded\ndone\n",
    "first_seq": 1,
    "last_seq": 2,
    "truncated": false
  }
}
Rejected: stdout-without-seq
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "running",
  "started_ms": 1,
  "finished_ms": null,
  "wall_ms": 1,
  "exit_reason": null,
  "exit_code": null,
  "result": null,
  "stdout": {
    "text": "x"
  }
}
Rejected: unknown-exit-reason
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "succeeded",
  "started_ms": 1790935200250,
  "finished_ms": 1790935202600,
  "wall_ms": 2350,
  "exit_reason": "crashed",
  "exit_code": 0,
  "result": {
    "title": "Example Domain"
  },
  "stdout": {
    "text": "loaded\ndone\n",
    "first_seq": 1,
    "last_seq": 2,
    "truncated": false
  }
}
Rejected: unknown-state
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "placed",
  "started_ms": null,
  "finished_ms": null,
  "wall_ms": 0,
  "exit_reason": null,
  "exit_code": null,
  "result": null,
  "stdout": null
}
Rejected: wall-ms-string
{
  "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "succeeded",
  "started_ms": 1790935200250,
  "finished_ms": 1790935202600,
  "wall_ms": "2350",
  "exit_reason": "exited",
  "exit_code": 0,
  "result": {
    "title": "Example Domain"
  },
  "stdout": {
    "text": "loaded\ndone\n",
    "first_seq": 1,
    "last_seq": 2,
    "truncated": false
  }
}

Validate

const contracts = require('openvibe-contracts');
contracts.validate('sandbox-provider.job-state@1', value);   // { valid, errors: [{ path, message }] }