@danypops/vehicle-core 0.12.3 → 0.12.5
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({
|
|
@@ -29,6 +29,19 @@ export declare class WatchLimitExceeded extends Error {
|
|
|
29
29
|
readonly max: number;
|
|
30
30
|
constructor(scope: string, max: number);
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Raised when watchId already identifies a registration, in this scope or any other.
|
|
34
|
+
* add() previously let a duplicate id silently overwrite byId's entry while leaving
|
|
35
|
+
* the id behind in its original scope's byScope Set, corrupting both indexes: the
|
|
36
|
+
* original scope kept counting a watch whose byId lookup now pointed at the wrong
|
|
37
|
+
* scope/resource/topic (or, after remove(), a permanent phantom entry that could
|
|
38
|
+
* never itself be looked up but still consumed that scope's own bound forever).
|
|
39
|
+
* Fails closed instead, before either index is touched.
|
|
40
|
+
*/
|
|
41
|
+
export declare class WatchIdConflict extends Error {
|
|
42
|
+
readonly watchId: string;
|
|
43
|
+
constructor(watchId: string);
|
|
44
|
+
}
|
|
32
45
|
export interface WatchRegistryOptions {
|
|
33
46
|
/** Defaults to DEFAULT_MAX_WATCHES_PER_SCOPE. */
|
|
34
47
|
readonly maxWatchesPerScope?: number;
|
package/dist/vehicle-watchers.js
CHANGED
|
@@ -28,6 +28,23 @@ export class WatchLimitExceeded extends Error {
|
|
|
28
28
|
this.name = "WatchLimitExceeded";
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Raised when watchId already identifies a registration, in this scope or any other.
|
|
33
|
+
* add() previously let a duplicate id silently overwrite byId's entry while leaving
|
|
34
|
+
* the id behind in its original scope's byScope Set, corrupting both indexes: the
|
|
35
|
+
* original scope kept counting a watch whose byId lookup now pointed at the wrong
|
|
36
|
+
* scope/resource/topic (or, after remove(), a permanent phantom entry that could
|
|
37
|
+
* never itself be looked up but still consumed that scope's own bound forever).
|
|
38
|
+
* Fails closed instead, before either index is touched.
|
|
39
|
+
*/
|
|
40
|
+
export class WatchIdConflict extends Error {
|
|
41
|
+
watchId;
|
|
42
|
+
constructor(watchId) {
|
|
43
|
+
super(`watchId "${watchId}" is already registered -- watch ids must be unique across every scope`);
|
|
44
|
+
this.watchId = watchId;
|
|
45
|
+
this.name = "WatchIdConflict";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
31
48
|
export class WatchRegistry {
|
|
32
49
|
byId = new Map();
|
|
33
50
|
byScope = new Map();
|
|
@@ -36,6 +53,8 @@ export class WatchRegistry {
|
|
|
36
53
|
this.maxWatchesPerScope = options.maxWatchesPerScope ?? DEFAULT_MAX_WATCHES_PER_SCOPE;
|
|
37
54
|
}
|
|
38
55
|
add(scope, resource, watchId, topic) {
|
|
56
|
+
if (this.byId.has(watchId))
|
|
57
|
+
throw new WatchIdConflict(watchId);
|
|
39
58
|
const existing = this.byScope.get(scope) ?? new Set();
|
|
40
59
|
if (existing.size >= this.maxWatchesPerScope)
|
|
41
60
|
throw new WatchLimitExceeded(scope, this.maxWatchesPerScope);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/vehicle-core",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.5",
|
|
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.
|
|
22
|
+
"typescript": "^5.9.3"
|
|
23
23
|
},
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
package/src/vehicle-approvals.ts
CHANGED
|
@@ -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
|
|
package/src/vehicle-watchers.ts
CHANGED
|
@@ -37,6 +37,22 @@ export class WatchLimitExceeded extends Error {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Raised when watchId already identifies a registration, in this scope or any other.
|
|
42
|
+
* add() previously let a duplicate id silently overwrite byId's entry while leaving
|
|
43
|
+
* the id behind in its original scope's byScope Set, corrupting both indexes: the
|
|
44
|
+
* original scope kept counting a watch whose byId lookup now pointed at the wrong
|
|
45
|
+
* scope/resource/topic (or, after remove(), a permanent phantom entry that could
|
|
46
|
+
* never itself be looked up but still consumed that scope's own bound forever).
|
|
47
|
+
* Fails closed instead, before either index is touched.
|
|
48
|
+
*/
|
|
49
|
+
export class WatchIdConflict extends Error {
|
|
50
|
+
constructor(readonly watchId: string) {
|
|
51
|
+
super(`watchId "${watchId}" is already registered -- watch ids must be unique across every scope`);
|
|
52
|
+
this.name = "WatchIdConflict";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
export interface WatchRegistryOptions {
|
|
41
57
|
/** Defaults to DEFAULT_MAX_WATCHES_PER_SCOPE. */
|
|
42
58
|
readonly maxWatchesPerScope?: number;
|
|
@@ -52,6 +68,7 @@ export class WatchRegistry {
|
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
add(scope: string, resource: string, watchId: string, topic: string): WatchRegistration {
|
|
71
|
+
if (this.byId.has(watchId)) throw new WatchIdConflict(watchId);
|
|
55
72
|
const existing = this.byScope.get(scope) ?? new Set();
|
|
56
73
|
if (existing.size >= this.maxWatchesPerScope) throw new WatchLimitExceeded(scope, this.maxWatchesPerScope);
|
|
57
74
|
const registration: WatchRegistration = { watchId, scope, resource, topic };
|