slotlock 0.6.3 → 0.7.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +467 -0
  2. package/CONTRIBUTING.md +12 -5
  3. package/README.md +50 -1551
  4. package/SECURITY.md +5 -2
  5. package/SPEC.md +1 -1
  6. package/agent-server.d.ts +160 -8
  7. package/agent-server.js +559 -44
  8. package/agent-store-backend.d.ts +34 -1
  9. package/agent-store-backend.js +1399 -38
  10. package/api-keys.d.ts +69 -3
  11. package/api-keys.js +217 -28
  12. package/application-api.d.ts +56 -3
  13. package/application-api.js +826 -15
  14. package/approvals.d.ts +88 -0
  15. package/approvals.js +293 -0
  16. package/assistant.d.ts +40 -0
  17. package/assistant.js +115 -0
  18. package/billing.d.ts +114 -0
  19. package/billing.js +258 -0
  20. package/config.d.ts +25 -4
  21. package/config.js +72 -3
  22. package/dashboard-icon.d.ts +1 -0
  23. package/dashboard-icon.js +5 -0
  24. package/dashboard.d.ts +203 -7
  25. package/dashboard.js +1545 -64
  26. package/ddl.d.ts +51 -12
  27. package/ddl.js +2566 -54
  28. package/event-feed.d.ts +30 -4
  29. package/event-feed.js +171 -19
  30. package/index.d.ts +15 -4
  31. package/index.js +8 -2
  32. package/maintenance.d.ts +10 -0
  33. package/maintenance.js +16 -2
  34. package/metrics.d.ts +41 -0
  35. package/metrics.js +22 -0
  36. package/openapi.json +14675 -5957
  37. package/otel.d.ts +86 -0
  38. package/otel.js +273 -0
  39. package/overage.d.ts +33 -0
  40. package/overage.js +115 -0
  41. package/package.json +1 -1
  42. package/partner-billing.d.ts +81 -0
  43. package/partner-billing.js +333 -0
  44. package/partner-names.d.ts +8 -0
  45. package/partner-names.js +10 -0
  46. package/partners.d.ts +147 -0
  47. package/partners.js +375 -0
  48. package/plans.d.ts +92 -0
  49. package/plans.js +115 -0
  50. package/quotas.d.ts +108 -0
  51. package/quotas.js +258 -0
  52. package/rate-limit.d.ts +21 -1
  53. package/rate-limit.js +82 -15
  54. package/resource-profile.d.ts +135 -0
  55. package/resource-profile.js +263 -0
  56. package/rest-api.d.ts +2 -0
  57. package/rest-api.js +68 -3
  58. package/sandbox.d.ts +70 -0
  59. package/sandbox.js +295 -0
  60. package/self-host.d.ts +33 -5
  61. package/self-host.js +950 -22
  62. package/slotlock.manifest.json +1 -1
  63. package/store.d.ts +373 -4
  64. package/store.js +1212 -118
  65. package/teams.d.ts +25 -0
  66. package/teams.js +63 -0
  67. package/timezone.js +13 -1
  68. package/types.d.ts +95 -1
  69. package/usage.d.ts +106 -0
  70. package/usage.js +238 -0
package/SECURITY.md CHANGED
@@ -45,7 +45,10 @@ The [Acceptable Use Policy](ACCEPTABLE-USE.md) permits research within these rul
45
45
  Slotlock treats all external calendar content as untrusted. The parser has explicit size, component,
46
46
  occurrence, exception, work, and expansion-window bounds. Reservation conflicts are arbitrated by a
47
47
  PostgreSQL exclusion constraint. Tenant policies are forced and fail closed when their transaction
48
- context is missing. Embedding applications remain responsible for authentication, authorization,
48
+ context is missing. A key limited to locations or groups is held to its resources by a second,
49
+ restrictive policy (`resource_scope_v1`) on each table keyed by resource, which can only narrow what
50
+ the tenant policy allows; the deployment check accepts exactly those two policies on those tables
51
+ and the tenant policy alone on the rest. Embedding applications remain responsible for authentication, authorization,
49
52
  provider-secret storage, webhook verification, network egress, and erasure.
50
53
 
51
54
  Upgrading repairs data across tenants (expired holds in a turnaround buffer, duplicate resources,
@@ -163,7 +166,7 @@ it is not an authorization boundary. Scopes and `authorize` decide who may write
163
166
 
164
167
  ## API keys and the application API
165
168
 
166
- An API key is `slk_`, 40 base62 characters from the operating system's random source (about 238
169
+ An API key is `slk_` (`slk_test_` in a test environment, `<tenant>~test`), 40 base62 characters from the operating system's random source (about 238
167
170
  bits) and a 6-character checksum, and the database keeps only its SHA-256. A slow password hash
168
171
  (bcrypt, scrypt, Argon2) defends a secret a person chose against offline guessing; nobody can guess
169
172
  238 random bits, so a fast hash
package/SPEC.md CHANGED
@@ -341,7 +341,7 @@ at most 100 active keys and 1,000 in all. The digest of a rotated or erased key
341
341
  is never valid again. A server's own bearer token is never `admin`.
342
342
 
343
343
  A dashboard signs a person in with GitHub, asking for no scope, to the tenant `github:<user id>`,
344
- and manages that tenant's keys, resources (at most 100) and weekly hours.
344
+ and manages that tenant's keys, resources (at most 500) and weekly hours.
345
345
 
346
346
  ### Maintenance
347
347
 
package/agent-server.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { z } from 'zod';
1
2
  import { type SlotlockTraceContext } from './mcp-modern.js';
2
3
  import { type SlotlockApplicationBackend, type SlotlockApplicationOperation } from './application-api.js';
3
4
  import { type SlotlockAgentChangeSource } from './change-watch.js';
@@ -38,11 +39,30 @@ export declare const SLOTLOCK_A2A_TASK_PAGE_SIZE: Readonly<{
38
39
  }>;
39
40
  /** The A2A 1.0 `TaskState` names, as a `ListTasks` status filter spells them in JSON. */
40
41
  export declare const SLOTLOCK_A2A_TASK_STATES: readonly ["TASK_STATE_UNSPECIFIED", "TASK_STATE_SUBMITTED", "TASK_STATE_WORKING", "TASK_STATE_COMPLETED", "TASK_STATE_FAILED", "TASK_STATE_CANCELED", "TASK_STATE_INPUT_REQUIRED", "TASK_STATE_REJECTED", "TASK_STATE_AUTH_REQUIRED"];
42
+ /** The tools' copy of the profile's kinds and bounds, for the test that holds it to resource-profile.ts. */
43
+ export declare const SLOTLOCK_AGENT_RESOURCE_PROFILE: Readonly<{
44
+ kinds: ("desk" | "equipment" | "machine" | "other" | "person" | "room" | "service" | "space" | "vehicle")[];
45
+ label: z.ZodString;
46
+ labels: 20;
47
+ name: 200;
48
+ groupingRef: z.ZodString;
49
+ groups: 20;
50
+ }>;
51
+ /** A free time offered with a refusal (RFC 0004 A1): a slot, not a hold. */
52
+ export interface SlotlockAlternative {
53
+ resource_id: string;
54
+ start: string;
55
+ end: string;
56
+ /** For a bundle or a pool booking: the resources free together then. */
57
+ resource_ids?: string[];
58
+ }
59
+ /** At most this many alternatives, the earliest first. */
60
+ export declare const SLOTLOCK_MAX_ALTERNATIVES = 3;
41
61
  /**
42
62
  * Operation (MCP tool / A2A skill) names. They use only `[a-z_]`, so they are valid tool names for the
43
63
  * Claude Messages API (`^[a-zA-Z0-9_-]{1,128}$`), OpenAI function calling and every MCP host.
44
64
  */
45
- export type SlotlockAgentOperation = 'slotlock_list_resources' | 'slotlock_get_free_busy' | 'slotlock_find_next_available' | 'slotlock_find_available_slots' | 'slotlock_hold_slots' | 'slotlock_release_slots' | 'slotlock_book_together' | 'slotlock_book_pool' | 'slotlock_get_pool_availability' | 'slotlock_create_event' | 'slotlock_get_event' | 'slotlock_list_events' | 'slotlock_update_event' | 'slotlock_delete_event';
65
+ export type SlotlockAgentOperation = 'slotlock_list_resources' | 'slotlock_get_free_busy' | 'slotlock_find_next_available' | 'slotlock_find_available_slots' | 'slotlock_hold_slots' | 'slotlock_release_slots' | 'slotlock_confirm_slot' | 'slotlock_reschedule' | 'slotlock_cancel' | 'slotlock_book_together' | 'slotlock_book_pool' | 'slotlock_get_pool_availability' | 'slotlock_create_event' | 'slotlock_get_event' | 'slotlock_list_events' | 'slotlock_update_event' | 'slotlock_delete_event';
46
66
  /**
47
67
  * Names earlier builds published, which every entry point still accepts and dispatches as the same
48
68
  * operation; nothing advertises them. The dotted names came first, before tool names had to be
@@ -71,6 +91,36 @@ export interface SlotlockAgentPrincipal {
71
91
  * each change the principal makes (`createSlotlockApiKeyAuthenticator` sets it).
72
92
  */
73
93
  keyId?: string;
94
+ /**
95
+ * For a key limited to part of its tenant (RFC 0003 G6), the locations and groups it reaches: a
96
+ * backend runs its operations with the store's `resourceScope`, so nothing outside is found.
97
+ */
98
+ resourceScope?: {
99
+ readonly locations: readonly string[];
100
+ readonly groups: readonly string[];
101
+ };
102
+ /**
103
+ * For a key held below the server's limits (RFC 0004 A5): its live holds and held hours (the store
104
+ * backend lowers the store's caps to them), its requests a minute (`slotlock serve`'s limiter),
105
+ * and whether its bookings wait for a person (the store backend's approval policies).
106
+ */
107
+ limits?: {
108
+ readonly maxHolds?: number;
109
+ readonly maxHeldHours?: number;
110
+ readonly requestsPerMinute?: number;
111
+ readonly requiresApproval?: boolean;
112
+ };
113
+ /**
114
+ * For a partner key (RFC 0003 P1), its partner: with the `partner` scope, the partner API acts
115
+ * for this partner's accounts and no other (partners.ts).
116
+ */
117
+ partnerId?: string;
118
+ /**
119
+ * For a partner key acting for one of its accounts with `Slotlock-Account` (RFC 0003 P3, where the
120
+ * contract allows it), the partner: the principal is the account's tenant with the contract's key
121
+ * scopes, and never reaches the partner API.
122
+ */
123
+ onBehalfOf?: string;
74
124
  }
75
125
  export interface SlotlockAgentInvocationContext {
76
126
  principal: SlotlockAgentPrincipal;
@@ -86,6 +136,12 @@ export interface SlotlockAgentInvocationContext {
86
136
  * to record with its changes (withTenant's `origin`).
87
137
  */
88
138
  origin?: SlotlockChangeOrigin;
139
+ /**
140
+ * A dry run (RFC 0004 A3): make the write with every check, the database's constraints
141
+ * included, then roll it back, and answer what it would have done. A backend that cannot undo
142
+ * all it would write refuses with `dry_run_not_supported`.
143
+ */
144
+ dryRun?: boolean;
89
145
  }
90
146
  /** A bounded domain failure a backend may deliberately expose through a protocol tool result. */
91
147
  export declare class SlotlockAgentOperationError extends Error {
@@ -119,6 +175,15 @@ export interface SlotlockAgentCalendarBackend {
119
175
  name: string | null;
120
176
  timezone: string | null;
121
177
  } | null>;
178
+ /**
179
+ * Optional. The ids of the resources a search selects instead of naming them (RFC 0003 G4): the
180
+ * principal's shared, unarchived resources that match every criterion given, in a stable order (by
181
+ * id). More than `limit` is refused with `selection_too_large`, so a search is never silently
182
+ * partial. Without it, a search by selection answers `selection_not_supported`.
183
+ */
184
+ selectResources?(context: SlotlockAgentInvocationContext, selection: SlotlockResourceSelection & {
185
+ limit: number;
186
+ }): Promise<string[]>;
122
187
  /**
123
188
  * Optional. Hold up to five candidate slots for this principal, as alternatives in one group
124
189
  * (`slotlock_hold_slots`); a tool without it answers `holds_unavailable`.
@@ -138,6 +203,31 @@ export interface SlotlockAgentCalendarBackend {
138
203
  bookTogether?(context: SlotlockAgentInvocationContext, input: Record<string, unknown>): Promise<Record<string, unknown>>;
139
204
  /** Optional. Release one of this principal's holds with its group (`slotlock_release_slots`). */
140
205
  releaseSlots?(context: SlotlockAgentInvocationContext, input: Record<string, unknown>): Promise<Record<string, unknown>>;
206
+ /**
207
+ * Optional. Book the resource and time one of this principal's holds holds, as
208
+ * `slotlock_create_event` with its `hold_id` does (`slotlock_confirm_slot`), else
209
+ * `holds_unavailable`.
210
+ */
211
+ confirmHold?(context: SlotlockAgentInvocationContext, input: Record<string, unknown>): Promise<Record<string, unknown>>;
212
+ /**
213
+ * Optional. Up to `limit` successive free slots of one resource from `start`, each the earliest
214
+ * after the last one ends, as as many `findNextAvailable` calls would find them, from one read.
215
+ * `certain` false when coverage is incomplete. `slotlock_find_available_slots` and refusals'
216
+ * alternatives use it where present.
217
+ */
218
+ findNextSlots?(context: SlotlockAgentInvocationContext, input: {
219
+ resource_id: string;
220
+ start: string;
221
+ end: string;
222
+ duration_minutes: number;
223
+ limit: number;
224
+ }): Promise<{
225
+ slots: {
226
+ start: string;
227
+ end: string;
228
+ }[];
229
+ certain: boolean;
230
+ }>;
141
231
  }
142
232
  /**
143
233
  * OAuth 2.0 discovery for the MCP endpoint (MCP 2025-11-25 authorization, RFC 9728). Slotlock only
@@ -286,6 +376,8 @@ export interface SlotlockAgentServerOptions {
286
376
  consumeRateLimit?: (args: {
287
377
  principal: SlotlockAgentPrincipal;
288
378
  operation: SlotlockAgentOperation | 'protocol';
379
+ /** A dry run of a write: charged as a read (RFC 0004 A3). */
380
+ dryRun?: boolean;
289
381
  }) => Promise<boolean>;
290
382
  allowInsecureLocalhost?: boolean;
291
383
  maxRequestBytes?: number;
@@ -332,6 +424,22 @@ export interface SlotlockAgentServerOptions {
332
424
  * rejects with, is ignored.
333
425
  */
334
426
  onEvent?: (event: SlotlockAgentServerEvent) => void;
427
+ /**
428
+ * For tracing only (otel.ts): called with a call's event and the caller's span from its valid
429
+ * `traceparent`, so the call's own span can be its child. Kept apart from `onEvent`, whose events
430
+ * go to logs and never carry the caller's span. A throw is ignored.
431
+ */
432
+ onCallTrace?: (event: Extract<SlotlockAgentServerEvent, {
433
+ type: 'call' | 'rest';
434
+ }>, trace: {
435
+ parentSpanId: string;
436
+ }) => void;
437
+ /**
438
+ * Called for each operation a principal ran (MCP, A2A or REST, the application API included), as
439
+ * it reaches the backend, with the principal: for metering a tenant's use (usage.ts). Unlike
440
+ * `onEvent`, it names the tenant and key, so it must not feed a public metric. A throw is ignored.
441
+ */
442
+ onUsage?: (usage: SlotlockAgentUsage) => void;
335
443
  /**
336
444
  * Called with a failure no client is told about: an operation's backend threw something other
337
445
  * than a `SlotlockAgentOperationError`, or returned a result outside the operation's schema. The
@@ -346,6 +454,17 @@ export interface SlotlockAgentErrorContext {
346
454
  /** The operation that failed; `null` when the failure came before one was resolved. */
347
455
  operation: SlotlockAgentOperation | SlotlockApplicationOperation | null;
348
456
  }
457
+ /**
458
+ * One operation a principal ran, for usage metering (`onUsage`): reported as it reaches the backend,
459
+ * once its arguments, scopes, authorization, rate limit and any confirmation have passed, whatever it
460
+ * then returns. Never its arguments or its result.
461
+ */
462
+ export interface SlotlockAgentUsage {
463
+ principal: SlotlockAgentPrincipal;
464
+ operation: SlotlockAgentOperation | SlotlockApplicationOperation;
465
+ /** The surface it came through (`library` when the caller named none). */
466
+ origin: SlotlockChangeOrigin;
467
+ }
349
468
  export interface SlotlockAgentOperationDispatchOptions {
350
469
  backend: SlotlockAgentCalendarBackend;
351
470
  authenticate(request: Request): Promise<SlotlockAgentPrincipal | null>;
@@ -357,6 +476,8 @@ export interface SlotlockAgentOperationDispatchOptions {
357
476
  consumeRateLimit?: (args: {
358
477
  principal: SlotlockAgentPrincipal;
359
478
  operation: SlotlockAgentOperation;
479
+ /** A dry run of a write: charged as a read (RFC 0004 A3). */
480
+ dryRun?: boolean;
360
481
  }) => Promise<boolean>;
361
482
  /**
362
483
  * A gate after authorization and the rate limit (so every round of a guarded write is charged):
@@ -373,6 +494,21 @@ export interface SlotlockAgentOperationDispatchOptions {
373
494
  } | null>;
374
495
  /** As `SlotlockAgentServerOptions.onError`: a failure the result reports only as internal. */
375
496
  onError?: (error: unknown, context: SlotlockAgentErrorContext) => void;
497
+ /** As `SlotlockAgentServerOptions.onUsage`: each operation that reaches the backend. */
498
+ onUsage?: (usage: SlotlockAgentUsage) => void;
499
+ }
500
+ /** What a search may select resources by, in place of naming them (RFC 0003 G4). */
501
+ export interface SlotlockResourceSelection {
502
+ /** At this location or one in it. */
503
+ location?: string;
504
+ /** In this group. */
505
+ group?: string;
506
+ /** With every one of these labels. */
507
+ labels?: string[];
508
+ /** Of this kind. */
509
+ kind?: string;
510
+ /** In this pool. */
511
+ pool?: string;
376
512
  }
377
513
  /** One reason `invalid_arguments` refused a call: where in the arguments, and what is wrong there. */
378
514
  export interface SlotlockArgumentIssue {
@@ -390,6 +526,12 @@ export type SlotlockAgentOperationDispatchResult = {
390
526
  details?: Readonly<Record<string, string>>;
391
527
  /** With `invalid_arguments` from argument validation: the fields it refused. */
392
528
  issues?: readonly SlotlockArgumentIssue[];
529
+ /**
530
+ * With a refusal another time can answer (`overlap`, `pool_full`, a booking rule): the
531
+ * earliest free times of the same length, at most three (RFC 0004 A1). Empty when none was
532
+ * found within seven days.
533
+ */
534
+ alternatives?: readonly SlotlockAlternative[];
393
535
  };
394
536
  /** Runtime type guard for the current operation names (legacy dotted names are not included). */
395
537
  export declare function isSlotlockAgentOperation(name: unknown): name is SlotlockAgentOperation;
@@ -428,14 +570,16 @@ export declare function slotlockMcpToolResult(outcome: {
428
570
  code: string;
429
571
  details?: Readonly<Record<string, string | number | boolean | null>>;
430
572
  issues?: readonly SlotlockArgumentIssue[];
573
+ alternatives?: readonly SlotlockAlternative[];
431
574
  }): SlotlockMcpToolResult;
432
575
  /** The application operations with their input and output as JSON Schema, as REST serves them. */
433
576
  export declare function slotlockApplicationOperations(): {
434
577
  name: SlotlockApplicationOperation;
435
578
  title: string;
436
579
  description: string;
437
- method: "DELETE" | "GET" | "POST" | "PUT";
580
+ method: "DELETE" | "GET" | "PATCH" | "POST" | "PUT";
438
581
  path: string;
582
+ scope?: "partner";
439
583
  inputSchema: Readonly<Record<string, unknown>>;
440
584
  outputSchema: Readonly<Record<string, unknown>>;
441
585
  }[];
@@ -449,13 +593,11 @@ export declare function slotlockOpenApi(options?: {
449
593
  application?: boolean;
450
594
  }): Record<string, unknown>;
451
595
  export declare function slotlockAgentTools(): {
452
- name: "slotlock_book_pool" | "slotlock_book_together" | "slotlock_create_event" | "slotlock_delete_event" | "slotlock_find_available_slots" | "slotlock_find_next_available" | "slotlock_get_event" | "slotlock_get_free_busy" | "slotlock_get_pool_availability" | "slotlock_hold_slots" | "slotlock_list_events" | "slotlock_list_resources" | "slotlock_release_slots" | "slotlock_update_event";
453
- title: "Book from a pool" | "Book resources together" | "Create resource event" | "Delete resource event" | "Find available slots" | "Find next available interval" | "Get free-busy" | "Get pool availability" | "Get resource event" | "Hold candidate slots" | "List calendar resources" | "List resource events" | "Release held slots" | "Update resource event";
454
- description: "Apply an expected-revision event patch and rematerialize occupancy atomically. With expected_resource_id, the resource you read the event on, an event since moved elsewhere is not found." | "Book any one member of a pool (a vehicle class, a room type) for a time: members are tried in a fixed order and the first that is free and allows the booking is booked, in one transaction, so a class sells out exactly at its number of members. The event names the member booked. 409 pool_full when none is free; 404 pool_not_found when the pool has no member you may book. A retry with the same idempotency_key answers the same event." | "Book the same time on 2-20 resources at once, all or none: a van with its driver, a room with its projector. If any of them is taken, nothing is booked, and the refusal names that resource_id. Find such a time with slotlock_find_available_slots and require_free. A retry with the same idempotency_key answers the same events." | "Create an event of up to 3,660 days (a booking, lease or long rental); opaque occurrences use the database overlap arbiter." | "Find the earliest certain interval that satisfies the requested duration (up to the 367-day search window, so a multi-month rental slot can be found)." | "Hold up to five candidate slots for a short time (30-900 seconds) while a person chooses, as alternatives in one group: each free one is held, the others are listed with why not. Book one by passing its hold_id to slotlock_create_event, which releases the rest; unbooked holds expire on their own. A hold is yours alone, and you may have 20 live at once." | "How many members of a pool (a vehicle class, a room type) are free across a window, each only where its booking rules let a booking start: the window split where that number changes, each part with how many are free, and coverage certainty. Book one with slotlock_book_pool. 404 pool_not_found when the pool has no member you may book." | "List bounded events intersecting a time window." | "List resources visible to the authenticated tenant principal, with the pool each belongs to (a vehicle class, a room type). With pool, only that pool's members, in the order slotlock_book_pool tries them." | "List up to `limit` certain slots of the requested duration, earliest first across the resources, each one free of busy time and inside the resource's hours, to offer a person a choice. With require_free, only times when at least that many of the resources are free together, each slot naming them in resource_ids: every resource for a booking that needs them all (book it with slotlock_book_together), or any n of them. No slot is listed unless every resource's coverage is certain." | "Read one tenant-scoped event by stable identifier." | "Release one of your holds, and the rest of its group, at once instead of letting them expire. `released` is false when it was already gone." | "Return privacy-minimized busy intervals and explicit coverage certainty." | "Tombstone an event at an expected revision so stale sync cannot resurrect it.";
455
- inputSchema: Readonly<Record<string, unknown>>;
456
- outputSchema: Readonly<Record<string, unknown>>;
596
+ name: "slotlock_book_pool" | "slotlock_book_together" | "slotlock_cancel" | "slotlock_confirm_slot" | "slotlock_create_event" | "slotlock_delete_event" | "slotlock_find_available_slots" | "slotlock_find_next_available" | "slotlock_get_event" | "slotlock_get_free_busy" | "slotlock_get_pool_availability" | "slotlock_hold_slots" | "slotlock_list_events" | "slotlock_list_resources" | "slotlock_release_slots" | "slotlock_reschedule" | "slotlock_update_event";
597
+ title: "Book from a pool" | "Book resources together" | "Cancel an event" | "Confirm a held slot" | "Create resource event" | "Delete resource event" | "Find available slots" | "Find next available interval" | "Get free-busy" | "Get pool availability" | "Get resource event" | "Hold candidate slots" | "List calendar resources" | "List resource events" | "Release held slots" | "Reschedule an event" | "Update resource event";
598
+ description: "Book a resource for a time, as an event of up to 3,660 days (a booking, lease or long rental). The database refuses a time already taken: 409 overlap, with alternatives (the earliest free times of the same length) and, when the booking in the way is yours, its id. A booking rule that refuses the time answers booking_rule_… with alternatives too. To book a time you hold, pass its hold_id, or use slotlock_confirm_slot. A retry with the same idempotency_key answers the same event." | "Book a time you hold (a hold_id from slotlock_hold_slots): the held resource and time exactly, with the title and details you give, and the rest of its group released. 409 hold_expired when the hold lapsed first; 404 hold_not_found when it was released, booked or is not yours. A retry answers the same event (replayed)." | "Book any one member of a pool (a vehicle class, a room type) for a time: members are tried in the pool's order (by id, fewest bookings in the last 30 days first, or in turn, as the tenant sets it) and the first that is free and allows the booking is booked, in one transaction, so a class sells out exactly at its number of members. The event names the member booked. 409 pool_full when none is free; 404 pool_not_found when the pool has no member you may book. A retry with the same idempotency_key answers the same event." | "Book the same time on 2-20 resources at once, all or none: a van with its driver, a room with its projector. If any of them is taken, nothing is booked, and the refusal names that resource_id. Find such a time with slotlock_find_available_slots and require_free. A retry with the same idempotency_key answers the same events." | "Cancel one of your events at the revision you last read (expected_revision); a stale one is 409 revision_conflict. It cannot come back at that revision, so an old copy synced later does not restore it. slotlock_cancel does the same with fewer fields." | "Cancel one of your events at the revision you last read (expected_revision); a stale one is 409 revision_conflict. Its time is free at once. A retry answers the same cancellation (replayed)." | "Change one of your events at the revision you last read (expected_revision): its time, resource or details, all or nothing. A time already taken is 409 overlap with alternatives; a stale revision is 409 revision_conflict (read the event again). With expected_resource_id, the resource you read it on, an event since moved elsewhere is not found. To change only the time, slotlock_reschedule is simpler." | "Find the earliest certain interval that satisfies the requested duration (up to the 367-day search window, so a multi-month rental slot can be found). Name the resources with resource_ids, or select them as slotlock_get_free_busy does: at most 100." | "Hold up to five candidate slots for a short time (30-900 seconds) while a person chooses, as alternatives in one group: each free one is held, the others are listed with why not. Book one by passing its hold_id to slotlock_create_event, which releases the rest; unbooked holds expire on their own. A hold is yours alone, and you may have 20 live at once. A candidate refused for its time comes with alternatives (the earliest free times of its length). slotlock_confirm_slot books a held time." | "How many members of a pool (a vehicle class, a room type) are free across a window, each only where its booking rules let a booking start: the window split where that number changes, each part with how many are free, and coverage certainty. Book one with slotlock_book_pool. 404 pool_not_found when the pool has no member you may book." | "List resources visible to the authenticated tenant principal, with what each says about itself: its name (show it rather than the reference), description, kind, capacity and labels, where it is (location), the groups it is in (a team, a department), and the pool it belongs to (a vehicle class, a room type). With kind, only resources of that kind; with labels, only those with every label given; with location, only those there or at a location in it; with group, only that group's. With pool, only that pool's members, in the order slotlock_book_pool tries them." | "List up to `limit` certain slots of the requested duration, earliest first across the resources, each one free of busy time and inside the resource's hours, to offer a person a choice. With require_free, only times when at least that many of the resources are free together, each slot naming them in resource_ids: every resource for a booking that needs them all (book it with slotlock_book_together), or any n of them. No slot is listed unless every resource's coverage is certain. Name the resources with resource_ids, or select them as slotlock_get_free_busy does: at most 20." | "List your events that meet a time window, earliest first." | "Move one of your events to a new time, and to another resource if you give one, at the revision you last read (expected_revision). A time already taken is 409 overlap with alternatives; a stale revision 409 revision_conflict. A retry answers the same move (replayed)." | "Read one of your events by its id, with its revision (pass it as expected_revision to change or cancel it)." | "Release one of your holds, and the rest of its group, at once instead of letting them expire. `released` is false when it was already gone." | "When each resource is busy in a window, as busy intervals only (never whose or what), and whether that is certain (coverage: \"uncertain\" where a calendar feeding it has not been read lately). Name the resources with resource_ids, or select them instead by location (and every location in it), group, labels (every one), kind or pool: at most 100 (more answers selection_too_large, none selection_empty), named in selected_resource_ids.";
457
599
  annotations: {
458
- title: "Book from a pool" | "Book resources together" | "Create resource event" | "Delete resource event" | "Find available slots" | "Find next available interval" | "Get free-busy" | "Get pool availability" | "Get resource event" | "Hold candidate slots" | "List calendar resources" | "List resource events" | "Release held slots" | "Update resource event";
600
+ title: "Book from a pool" | "Book resources together" | "Cancel an event" | "Confirm a held slot" | "Create resource event" | "Delete resource event" | "Find available slots" | "Find next available interval" | "Get free-busy" | "Get pool availability" | "Get resource event" | "Hold candidate slots" | "List calendar resources" | "List resource events" | "Release held slots" | "Reschedule an event" | "Update resource event";
459
601
  readOnlyHint: boolean;
460
602
  destructiveHint: boolean;
461
603
  idempotentHint: true;
@@ -472,7 +614,17 @@ export declare function slotlockAgentTools(): {
472
614
  visibility: readonly ["model"];
473
615
  };
474
616
  };
617
+ inputSchema: Readonly<Record<string, unknown>>;
618
+ outputSchema: Readonly<Record<string, unknown>>;
475
619
  }[];
620
+ /**
621
+ * A write's input and output schemas with `dry_run` (RFC 0004 A3): an optional flag in, and
622
+ * `dry_run: true` on a dry run's answer. Other operations' are as they are.
623
+ */
624
+ export declare function slotlockDryRunSchemas(dryRunnable: boolean, inputSchema: Readonly<Record<string, unknown>>, outputSchema: Readonly<Record<string, unknown>>): {
625
+ inputSchema: Readonly<Record<string, unknown>>;
626
+ outputSchema: Readonly<Record<string, unknown>>;
627
+ };
476
628
  /**
477
629
  * Canonical transport-neutral operation boundary. Every host shares the package's schemas,
478
630
  * principal validation, authorization, per-operation limiter and bounded backend error mapping.