@danypops/vehicle-core 0.12.2 → 0.12.4

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.
@@ -9,6 +9,17 @@
9
9
  import type { VehicleEffect, VehiclePrincipal } from "./vehicle-contract.js";
10
10
  /** Set once at registry-configuration time (VehicleRegistry.configureApprovals()); never on a per-invoke basis. */
11
11
  export declare const DEFAULT_APPROVAL_EFFECTS: readonly VehicleEffect[];
12
+ /**
13
+ * The name VehicleRegistry.configureApprovals() registers its built-in
14
+ * grant/deny operation under. Shared so vehicle-client-pi can recognize and
15
+ * exclude it from Pi tool projection by exact name (see its own use site):
16
+ * it is invoked only via this package's own approval-required retry dance
17
+ * using the extension's already-fixed permissions, never meant to be a
18
+ * model-callable tool -- a model that could call it directly would be able
19
+ * to grant its own pending approval requests, defeating the human-in-the-
20
+ * loop point of the gate entirely.
21
+ */
22
+ export declare const VEHICLE_APPROVAL_RESOLVE_OPERATION_NAME = "vehicle.approval.resolve";
12
23
  /** How long a request stays resolvable before it lapses and must be re-requested. */
13
24
  export declare const DEFAULT_APPROVAL_TIMEOUT_MS: number;
14
25
  /** Emitted (as a Vehicle Event) the moment a gated-effect invoke() has no valid capability -- durable-first, before any interactive prompt is attempted. */
@@ -1,6 +1,17 @@
1
1
  import { defineVehicleEvent, defineVehicleSchema } from "./vehicle-contract.js";
2
2
  /** Set once at registry-configuration time (VehicleRegistry.configureApprovals()); never on a per-invoke basis. */
3
3
  export const DEFAULT_APPROVAL_EFFECTS = ["destructive", "open-world"];
4
+ /**
5
+ * The name VehicleRegistry.configureApprovals() registers its built-in
6
+ * grant/deny operation under. Shared so vehicle-client-pi can recognize and
7
+ * exclude it from Pi tool projection by exact name (see its own use site):
8
+ * it is invoked only via this package's own approval-required retry dance
9
+ * using the extension's already-fixed permissions, never meant to be a
10
+ * model-callable tool -- a model that could call it directly would be able
11
+ * to grant its own pending approval requests, defeating the human-in-the-
12
+ * loop point of the gate entirely.
13
+ */
14
+ export const VEHICLE_APPROVAL_RESOLVE_OPERATION_NAME = "vehicle.approval.resolve";
4
15
  /** How long a request stays resolvable before it lapses and must be re-requested. */
5
16
  export const DEFAULT_APPROVAL_TIMEOUT_MS = 5 * 60_000;
6
17
  const requestedPayloadSchema = defineVehicleSchema({
@@ -59,6 +59,8 @@ export declare class VehicleError extends Error {
59
59
  constructor(code: string, message: string, options: VehicleErrorOptions);
60
60
  toFailure(): VehicleFailure;
61
61
  }
62
+ /** Recognizes VehicleError instances across duplicated package installations in one process. */
63
+ export declare function isVehicleError(value: unknown): value is VehicleError;
62
64
  /** Extracts a bounded, wire-safe message from an unknown cause -- never the full stack trace, never an unbounded payload. */
63
65
  export declare function boundedCauseMessage(cause: unknown): string | undefined;
64
66
  export declare function boundedValidationDetails(issues: readonly VehicleSchemaIssue[] | undefined): JsonValue | undefined;
@@ -1,3 +1,4 @@
1
+ const VEHICLE_ERROR_BRAND = Symbol.for("@danypops/vehicle-core/VehicleError");
1
2
  /** Maps reviewed domain errors into wire-safe Vehicle errors while preserving already-mapped failures. */
2
3
  export function defineErrorMapping(rules, options = {}) {
3
4
  return async (run) => {
@@ -5,7 +6,7 @@ export function defineErrorMapping(rules, options = {}) {
5
6
  return await run();
6
7
  }
7
8
  catch (error) {
8
- if (error instanceof VehicleError)
9
+ if (isVehicleError(error))
9
10
  throw error;
10
11
  const rule = rules.find((candidate) => "errorClass" in candidate ? error instanceof candidate.errorClass : candidate.matches(error));
11
12
  const causeMessage = error instanceof Error ? error.message : String(error);
@@ -30,6 +31,7 @@ export class VehicleError extends Error {
30
31
  constructor(code, message, options) {
31
32
  super(message, options.cause === undefined ? undefined : { cause: options.cause });
32
33
  this.code = code;
34
+ Object.defineProperty(this, VEHICLE_ERROR_BRAND, { value: true });
33
35
  this.name = "VehicleError";
34
36
  this.category = options.category;
35
37
  this.retryable = options.retryable ?? false;
@@ -54,6 +56,10 @@ export class VehicleError extends Error {
54
56
  };
55
57
  }
56
58
  }
59
+ /** Recognizes VehicleError instances across duplicated package installations in one process. */
60
+ export function isVehicleError(value) {
61
+ return value instanceof Error && Reflect.get(value, VEHICLE_ERROR_BRAND) === true;
62
+ }
57
63
  const MAX_CAUSE_MESSAGE_LENGTH = 500;
58
64
  /** Extracts a bounded, wire-safe message from an unknown cause -- never the full stack trace, never an unbounded payload. */
59
65
  export function boundedCauseMessage(cause) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/vehicle-core",
3
- "version": "0.12.2",
3
+ "version": "0.12.4",
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",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/node": "^22.0.0",
22
- "typescript": "^5.9.2"
22
+ "typescript": "^5.9.3"
23
23
  },
24
24
  "repository": {
25
25
  "type": "git",
@@ -12,6 +12,18 @@ import { defineVehicleEvent, defineVehicleSchema } from "./vehicle-contract.js";
12
12
  /** Set once at registry-configuration time (VehicleRegistry.configureApprovals()); never on a per-invoke basis. */
13
13
  export const DEFAULT_APPROVAL_EFFECTS: readonly VehicleEffect[] = ["destructive", "open-world"];
14
14
 
15
+ /**
16
+ * The name VehicleRegistry.configureApprovals() registers its built-in
17
+ * grant/deny operation under. Shared so vehicle-client-pi can recognize and
18
+ * exclude it from Pi tool projection by exact name (see its own use site):
19
+ * it is invoked only via this package's own approval-required retry dance
20
+ * using the extension's already-fixed permissions, never meant to be a
21
+ * model-callable tool -- a model that could call it directly would be able
22
+ * to grant its own pending approval requests, defeating the human-in-the-
23
+ * loop point of the gate entirely.
24
+ */
25
+ export const VEHICLE_APPROVAL_RESOLVE_OPERATION_NAME = "vehicle.approval.resolve";
26
+
15
27
  /** How long a request stays resolvable before it lapses and must be re-requested. */
16
28
  export const DEFAULT_APPROVAL_TIMEOUT_MS = 5 * 60_000;
17
29
 
@@ -1,5 +1,7 @@
1
1
  import type { JsonValue, VehicleSchemaIssue } from "./vehicle-contract.js";
2
2
 
3
+ const VEHICLE_ERROR_BRAND = Symbol.for("@danypops/vehicle-core/VehicleError");
4
+
3
5
  export type VehicleFailureCategory =
4
6
  | "validation"
5
7
  | "not_found"
@@ -92,7 +94,7 @@ export function defineErrorMapping(
92
94
  try {
93
95
  return await run();
94
96
  } catch (error) {
95
- if (error instanceof VehicleError) throw error;
97
+ if (isVehicleError(error)) throw error;
96
98
  const rule = rules.find((candidate) =>
97
99
  "errorClass" in candidate ? error instanceof candidate.errorClass : candidate.matches(error),
98
100
  );
@@ -122,6 +124,7 @@ export class VehicleError extends Error {
122
124
  options: VehicleErrorOptions,
123
125
  ) {
124
126
  super(message, options.cause === undefined ? undefined : { cause: options.cause });
127
+ Object.defineProperty(this, VEHICLE_ERROR_BRAND, { value: true });
125
128
  this.name = "VehicleError";
126
129
  this.category = options.category;
127
130
  this.retryable = options.retryable ?? false;
@@ -148,6 +151,11 @@ export class VehicleError extends Error {
148
151
  }
149
152
  }
150
153
 
154
+ /** Recognizes VehicleError instances across duplicated package installations in one process. */
155
+ export function isVehicleError(value: unknown): value is VehicleError {
156
+ return value instanceof Error && Reflect.get(value, VEHICLE_ERROR_BRAND) === true;
157
+ }
158
+
151
159
  const MAX_CAUSE_MESSAGE_LENGTH = 500;
152
160
 
153
161
  /** Extracts a bounded, wire-safe message from an unknown cause -- never the full stack trace, never an unbounded payload. */