@danypops/vehicle-core 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import type { VehicleJobWakeBudget } from "./vehicle-jobs.js";
1
+ import type { VehicleJobSnapshot, VehicleJobSubmitOptions, VehicleJobSubmitResult, VehicleJobTailResult, VehicleJobWakeBudget } from "./vehicle-jobs.js";
2
2
  export type JsonPrimitive = string | number | boolean | null;
3
3
  export type JsonValue = JsonPrimitive | readonly JsonValue[] | {
4
4
  readonly [key: string]: JsonValue;
@@ -324,6 +324,24 @@ export interface VehicleClient {
324
324
  manifest(): Promise<VehicleManifest>;
325
325
  invoke<Output = unknown>(name: string, version: number, input: unknown, options?: VehicleInvocationOptions): Promise<Output>;
326
326
  close(): Promise<void>;
327
+ /**
328
+ * Vehicle Jobs -- submit a background-capable operation (one whose descriptor declares
329
+ * `background`, see {@link VehicleBackgroundCapability}) and get its jobId back immediately,
330
+ * without waiting for the operation itself to make any progress. Optional: a client that
331
+ * never talks to a job-capable Vehicle (or a hand-rolled test double) simply omits these five
332
+ * methods, exactly like this interface's own long-standing `subscribe()`-shaped extras --
333
+ * present on both LocalVehicleClient and RemoteVehicleClient, absent elsewhere. Feature-detect
334
+ * via the operation's own manifest `background` capability, not by probing for these methods.
335
+ */
336
+ submitJob?(name: string, version: number, input: unknown, options?: VehicleJobSubmitOptions): Promise<VehicleJobSubmitResult>;
337
+ /** Never blocks -- current status, plus output/error once terminal. */
338
+ pollJob?(jobId: string): Promise<VehicleJobSnapshot>;
339
+ /** Progress entries strictly after `cursor` (0 for everything so far), plus the next cursor. Never blocks. */
340
+ tailJob?(jobId: string, cursor?: number): Promise<VehicleJobTailResult>;
341
+ /** Pushes new input to an already-running job's handler, if it opted in via context.steerInputs. */
342
+ steerJob?(jobId: string, input: unknown): Promise<void>;
343
+ /** Best-effort cancellation of a still-running job -- a no-op against an already-terminal one. */
344
+ cancelJob?(jobId: string): Promise<void>;
327
345
  }
328
346
  export declare function defineVehicleOperation<Input, Output>(options: DefineVehicleOperationOptions<Input, Output>): VehicleOperation<Input, Output>;
329
347
  export declare function bindVehicleOperation<Input, Output>(operation: VehicleOperation<Input, Output>, bind: () => VehicleOperationHandler<Input, Output>): VehicleOperationBinding<Input, Output>;
@@ -1,4 +1,6 @@
1
1
  /** Pure pieces of Vehicle Jobs: a termination-reason resolver and a bounded wake-log accumulator. Orchestration lives in vehicle-server's VehicleJobStore. */
2
+ import type { VehiclePrincipal } from "./vehicle-contract.js";
3
+ import type { VehicleFailure } from "./vehicle-errors.js";
2
4
  export type VehicleJobStatus = "running" | "succeeded" | "failed" | "canceled";
3
5
  /** Highest precedence first -- an explicit cancel always wins even if the handler also settled around the same time. "orphaned" is a restart-reconciliation outcome: a job that was still "running" when its process died, so nothing ever really failed or succeeded -- the record's own status just goes stale. */
4
6
  export declare const VEHICLE_JOB_TERMINATION_PRECEDENCE: readonly ["canceled", "timeout", "orphaned", "failed", "succeeded"];
@@ -101,6 +103,49 @@ export interface VehicleJobRetentionOptions {
101
103
  readonly deliveredRetentionMs: number;
102
104
  readonly now: number;
103
105
  }
106
+ /**
107
+ * The client-facing wire shapes for Vehicle Jobs -- submit/poll/tail options and results, shared by
108
+ * vehicle-server's VehicleJobStore (the orchestration side) and vehicle-client's job-capable clients
109
+ * (the calling side), so both halves of the wire agree on one definition instead of two structurally
110
+ *-identical copies drifting apart. Every field type referenced here already lives in vehicle-core
111
+ * (VehiclePrincipal, VehicleFailure, VehicleJobStatus, ...), which is what makes it safe for these
112
+ * shapes to live here too, alongside the rest of Vehicle Jobs' pure pieces.
113
+ */
114
+ export interface VehicleJobSubmitOptions {
115
+ readonly permissions?: readonly string[];
116
+ readonly principal?: VehiclePrincipal;
117
+ readonly idempotencyKey?: string;
118
+ readonly expectedRevision?: string | number;
119
+ readonly approvalCapability?: string;
120
+ readonly correlationId?: string;
121
+ readonly callerSessionId?: string;
122
+ readonly callerProjectRoot?: string;
123
+ /** Defaults to "transition". */
124
+ readonly notifyMode?: VehicleJobNotifyMode;
125
+ /** Defaults to background.defaultWakeBudget; clamped to background.maxWakeBudget either way. */
126
+ readonly wakeBudget?: VehicleJobWakeBudget;
127
+ /** No default -- unset means the job runs until it settles or is canceled. */
128
+ readonly maxLifetimeMs?: number;
129
+ }
130
+ export interface VehicleJobSubmitResult {
131
+ readonly jobId: string;
132
+ }
133
+ export interface VehicleJobSnapshot {
134
+ readonly jobId: string;
135
+ readonly operationName: string;
136
+ readonly operationVersion: number;
137
+ readonly status: VehicleJobStatus;
138
+ readonly createdAt: number;
139
+ readonly updatedAt: number;
140
+ readonly delivered: boolean;
141
+ readonly terminationReason?: VehicleJobTerminationReason;
142
+ readonly output?: unknown;
143
+ readonly error?: VehicleFailure;
144
+ }
145
+ export interface VehicleJobTailResult {
146
+ readonly entries: readonly VehicleJobWakeEntry[];
147
+ readonly cursor: number;
148
+ }
104
149
  /**
105
150
  * Pure eviction-selection policy, kept separate from VehicleJobStore's own
106
151
  * bookkeeping so the bounded-retention rule is independently testable.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/vehicle-core",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Vehicle's runtime-neutral wire contract: operation descriptors, schema codecs, failure shapes. Zero runtime dependencies, zero Bun-specific code -- the one thing every Vehicle client and server package depends on.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,4 +1,10 @@
1
- import type { VehicleJobWakeBudget } from "./vehicle-jobs.js";
1
+ import type {
2
+ VehicleJobSnapshot,
3
+ VehicleJobSubmitOptions,
4
+ VehicleJobSubmitResult,
5
+ VehicleJobTailResult,
6
+ VehicleJobWakeBudget,
7
+ } from "./vehicle-jobs.js";
2
8
 
3
9
  export type JsonPrimitive = string | number | boolean | null;
4
10
  export type JsonValue = JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue };
@@ -438,6 +444,24 @@ export interface VehicleClient {
438
444
  manifest(): Promise<VehicleManifest>;
439
445
  invoke<Output = unknown>(name: string, version: number, input: unknown, options?: VehicleInvocationOptions): Promise<Output>;
440
446
  close(): Promise<void>;
447
+ /**
448
+ * Vehicle Jobs -- submit a background-capable operation (one whose descriptor declares
449
+ * `background`, see {@link VehicleBackgroundCapability}) and get its jobId back immediately,
450
+ * without waiting for the operation itself to make any progress. Optional: a client that
451
+ * never talks to a job-capable Vehicle (or a hand-rolled test double) simply omits these five
452
+ * methods, exactly like this interface's own long-standing `subscribe()`-shaped extras --
453
+ * present on both LocalVehicleClient and RemoteVehicleClient, absent elsewhere. Feature-detect
454
+ * via the operation's own manifest `background` capability, not by probing for these methods.
455
+ */
456
+ submitJob?(name: string, version: number, input: unknown, options?: VehicleJobSubmitOptions): Promise<VehicleJobSubmitResult>;
457
+ /** Never blocks -- current status, plus output/error once terminal. */
458
+ pollJob?(jobId: string): Promise<VehicleJobSnapshot>;
459
+ /** Progress entries strictly after `cursor` (0 for everything so far), plus the next cursor. Never blocks. */
460
+ tailJob?(jobId: string, cursor?: number): Promise<VehicleJobTailResult>;
461
+ /** Pushes new input to an already-running job's handler, if it opted in via context.steerInputs. */
462
+ steerJob?(jobId: string, input: unknown): Promise<void>;
463
+ /** Best-effort cancellation of a still-running job -- a no-op against an already-terminal one. */
464
+ cancelJob?(jobId: string): Promise<void>;
441
465
  }
442
466
 
443
467
  export function defineVehicleOperation<Input, Output>(
@@ -1,5 +1,8 @@
1
1
  /** Pure pieces of Vehicle Jobs: a termination-reason resolver and a bounded wake-log accumulator. Orchestration lives in vehicle-server's VehicleJobStore. */
2
2
 
3
+ import type { VehiclePrincipal } from "./vehicle-contract.js";
4
+ import type { VehicleFailure } from "./vehicle-errors.js";
5
+
3
6
  export type VehicleJobStatus = "running" | "succeeded" | "failed" | "canceled";
4
7
 
5
8
  /** Highest precedence first -- an explicit cancel always wins even if the handler also settled around the same time. "orphaned" is a restart-reconciliation outcome: a job that was still "running" when its process died, so nothing ever really failed or succeeded -- the record's own status just goes stale. */
@@ -212,6 +215,53 @@ export interface VehicleJobRetentionOptions {
212
215
  readonly now: number;
213
216
  }
214
217
 
218
+ /**
219
+ * The client-facing wire shapes for Vehicle Jobs -- submit/poll/tail options and results, shared by
220
+ * vehicle-server's VehicleJobStore (the orchestration side) and vehicle-client's job-capable clients
221
+ * (the calling side), so both halves of the wire agree on one definition instead of two structurally
222
+ *-identical copies drifting apart. Every field type referenced here already lives in vehicle-core
223
+ * (VehiclePrincipal, VehicleFailure, VehicleJobStatus, ...), which is what makes it safe for these
224
+ * shapes to live here too, alongside the rest of Vehicle Jobs' pure pieces.
225
+ */
226
+ export interface VehicleJobSubmitOptions {
227
+ readonly permissions?: readonly string[];
228
+ readonly principal?: VehiclePrincipal;
229
+ readonly idempotencyKey?: string;
230
+ readonly expectedRevision?: string | number;
231
+ readonly approvalCapability?: string;
232
+ readonly correlationId?: string;
233
+ readonly callerSessionId?: string;
234
+ readonly callerProjectRoot?: string;
235
+ /** Defaults to "transition". */
236
+ readonly notifyMode?: VehicleJobNotifyMode;
237
+ /** Defaults to background.defaultWakeBudget; clamped to background.maxWakeBudget either way. */
238
+ readonly wakeBudget?: VehicleJobWakeBudget;
239
+ /** No default -- unset means the job runs until it settles or is canceled. */
240
+ readonly maxLifetimeMs?: number;
241
+ }
242
+
243
+ export interface VehicleJobSubmitResult {
244
+ readonly jobId: string;
245
+ }
246
+
247
+ export interface VehicleJobSnapshot {
248
+ readonly jobId: string;
249
+ readonly operationName: string;
250
+ readonly operationVersion: number;
251
+ readonly status: VehicleJobStatus;
252
+ readonly createdAt: number;
253
+ readonly updatedAt: number;
254
+ readonly delivered: boolean;
255
+ readonly terminationReason?: VehicleJobTerminationReason;
256
+ readonly output?: unknown;
257
+ readonly error?: VehicleFailure;
258
+ }
259
+
260
+ export interface VehicleJobTailResult {
261
+ readonly entries: readonly VehicleJobWakeEntry[];
262
+ readonly cursor: number;
263
+ }
264
+
215
265
  /**
216
266
  * Pure eviction-selection policy, kept separate from VehicleJobStore's own
217
267
  * bookkeeping so the bounded-retention rule is independently testable.