slotlock 0.5.1 → 0.5.2
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.
- package/CHANGELOG.md +24 -0
- package/README.md +55 -1
- package/agent-server.d.ts +14 -0
- package/agent-server.js +15 -0
- package/assistant.d.ts +263 -0
- package/assistant.js +771 -0
- package/config.d.ts +10 -0
- package/config.js +11 -0
- package/dashboard.d.ts +61 -0
- package/dashboard.js +589 -11
- package/ddl.d.ts +5 -5
- package/ddl.js +199 -2
- package/index.d.ts +4 -2
- package/index.js +2 -1
- package/maintenance.d.ts +2 -1
- package/maintenance.js +22 -0
- package/package.json +1 -1
- package/rate-limit.d.ts +8 -0
- package/rate-limit.js +20 -0
- package/self-host.d.ts +2 -2
- package/self-host.js +34 -3
- package/slotlock.manifest.json +1 -1
- package/store.d.ts +101 -1
- package/store.js +158 -0
- package/webhooks.d.ts +24 -0
- package/webhooks.js +96 -19
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,30 @@
|
|
|
3
3
|
All notable changes follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and semantic
|
|
4
4
|
versioning.
|
|
5
5
|
|
|
6
|
+
## 0.5.2 - 2026-10-28
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- The dashboard assistant: with `SLOTLOCK_ASSISTANT_SECRET`, an organization's owners connect a
|
|
11
|
+
model of their own in Settings (the Anthropic Messages API, or OpenAI-compatible chat
|
|
12
|
+
completions: OpenAI, OpenRouter, vLLM, Ollama, LM Studio), with a connection test, and every
|
|
13
|
+
member can operate Slotlock by chat on a new Assistant page. The server runs the tool loop (up to
|
|
14
|
+
8 model calls a message) with a fixed list of agent tools: reads run through the agent operation
|
|
15
|
+
boundary, and every write is a proposed change the member approves on a form before it runs once,
|
|
16
|
+
recorded as `dashboard-assistant:github:<user id>`, whose card shows the change in full under the
|
|
17
|
+
confirmation question's sentence. A proposal takes no series-write slot or rate token until it
|
|
18
|
+
runs, a message's reads share a budget, maintenance deletes every tenant's expired conversations
|
|
19
|
+
(`slotlock.sweep_assistant_conversations`, over the `assistant_tenants` list), and a new provider
|
|
20
|
+
or base URL takes the key again. The API key is sealed with AES-256-GCM, bound to the
|
|
21
|
+
tenant, and only its last four characters are shown. Two tenant tables under forced row-level
|
|
22
|
+
security, `assistant_settings` and `assistant_conversations`, which erasure deletes.
|
|
23
|
+
`createSlotlockRateLimiter` is the per-key token bucket it limits members' messages with, and
|
|
24
|
+
organization sessions now remember each organization's role (owners are admins). As webhooks
|
|
25
|
+
do, it refuses a model URL whose host is, or resolves to, a loopback, private, link-local, CGNAT,
|
|
26
|
+
unique-local or metadata address, when saved and at each connection (the resolved address is
|
|
27
|
+
checked as the socket connects, against DNS rebinding); `SLOTLOCK_ASSISTANT_PRIVATE_URLS=on`
|
|
28
|
+
allows them for a self-hosted model. `createSlotlockGuardedFetch` is that `fetch`.
|
|
29
|
+
|
|
6
30
|
## 0.5.1 - 2026-10-28
|
|
7
31
|
|
|
8
32
|
### Fixed
|
package/README.md
CHANGED
|
@@ -304,6 +304,59 @@ resources, so you can give each person API keys without running `slotlock key cr
|
|
|
304
304
|
own on the hours page: two windows a day, overnight or all day, or closed. One form sets them
|
|
305
305
|
for every resource at once.
|
|
306
306
|
|
|
307
|
+
#### Assistant: operate Slotlock by chat
|
|
308
|
+
|
|
309
|
+
With `SLOTLOCK_ASSISTANT_SECRET` set (32+ random characters, `openssl rand -hex 32`), the dashboard
|
|
310
|
+
gains an Assistant page where every member chats with a model their organization connects, instead
|
|
311
|
+
of clicking. Each organization brings its own model:
|
|
312
|
+
|
|
313
|
+
- **Connect it** in Settings, Assistant (owners only; with personal tenants, each person): the
|
|
314
|
+
provider (`Anthropic Messages API`, or `OpenAI-compatible`, which covers OpenAI, OpenRouter,
|
|
315
|
+
vLLM, Ollama and LM Studio), the base URL (`https://api.anthropic.com`, or the one ending in
|
|
316
|
+
`/v1`: `https://api.openai.com/v1`, `http://localhost:11434/v1`; https, or http only on
|
|
317
|
+
`localhost` or `127.0.0.1`), the model name (`your-model`) and the API key, which a local model
|
|
318
|
+
may not need. As with webhooks, a URL whose host is, or resolves to, a loopback, private,
|
|
319
|
+
link-local, shared (CGNAT), unique-local or metadata address (`169.254.169.254`) is refused, when
|
|
320
|
+
it is saved and at each connection (the address the socket connects to is checked, so a name
|
|
321
|
+
rebound to one is refused too), and the page says why. For a self-hosted model on the server's
|
|
322
|
+
own machine or network (Ollama, vLLM), the operator sets `SLOTLOCK_ASSISTANT_PRIVATE_URLS=on`.
|
|
323
|
+
On Cloudflare Workers the literal check applies and names are not resolved: a Worker's fetch
|
|
324
|
+
reaches no private network. Changing the provider or the base URL takes the key again (decided in the
|
|
325
|
+
database write, so racing saves cannot get past it), so the stored key is only ever sent where it
|
|
326
|
+
was entered for, even on an origin that serves several accounts. **Test connection** makes one real request
|
|
327
|
+
and checks the model answers with one correct tool call. The organization's messages, and the bookings and resources the assistant
|
|
328
|
+
reads, are sent to the provider it configures.
|
|
329
|
+
- **The key** is stored sealed with AES-256-GCM under a key derived from `SLOTLOCK_ASSISTANT_SECRET`,
|
|
330
|
+
bound to the organization, in `slotlock.assistant_settings` (forced row-level security, erased
|
|
331
|
+
with the tenant). The dashboard shows only that it is set and its last four characters. Changing
|
|
332
|
+
the secret makes stored keys unreadable: owners enter them again.
|
|
333
|
+
- **Reads run, writes wait.** The server runs the tool loop, up to 8 model calls a message, with a
|
|
334
|
+
fixed list of the agent tools (the reads: resources, free-busy, next free time, slots, pool
|
|
335
|
+
availability, events; the writes: holds, bookings, pool and bundle bookings, changes and
|
|
336
|
+
cancellations). A read runs at once through the same operation boundary as an agent's. A write
|
|
337
|
+
never runs when the model asks: it appears as a proposed change the member approves (or
|
|
338
|
+
dismisses) with a form, and only then runs, once (its idempotency key is the dashboard's), with
|
|
339
|
+
the same booking rules, tenant limits and rate limits as an agent, recorded as
|
|
340
|
+
`dashboard-assistant:github:<user id>` (shown as "Assistant for @login"). The card leads with
|
|
341
|
+
the sentence a confirmation would ask (the resource by name, the times in its zone, and for a
|
|
342
|
+
change or cancellation the event's title, resource and time as it stands) and then shows every
|
|
343
|
+
argument in full; a change too large to show (16 KB of arguments) is not proposed, and a
|
|
344
|
+
conversation has at most 4 waiting. A proposal is only checked: the series-write slot and the
|
|
345
|
+
write's rate token are taken when the approved change runs. A message's reads share a budget of
|
|
346
|
+
16 calls and 400 resource-days. API keys, webhooks, booking rules, pools, erasure and
|
|
347
|
+
coverage are never offered; a tool the model names outside the list is refused.
|
|
348
|
+
- Tool results and booking data are data to the model, never instructions, and the system prompt
|
|
349
|
+
says so; the tool list does not change with what the model says. A member may send 10 messages
|
|
350
|
+
a minute; each model call has 30 seconds and a message 90. Conversations are kept per member in
|
|
351
|
+
`slotlock.assistant_conversations` for 2 hours after the last message, then deleted: by
|
|
352
|
+
`serve`'s maintenance, which sweeps every tenant's expired conversations every quarter hour (a
|
|
353
|
+
tenant only the dashboard serves included), by `slotlock maintain`, and whenever the tenant's
|
|
354
|
+
conversations are read (so on Workers too). They are bounded in UTF-8 bytes; **New conversation** forgets one. On Workers, the
|
|
355
|
+
assistant's agent calls are capped at 24 a request and by `tenant_read` and `tenant_write` of
|
|
356
|
+
`SLOTLOCK_LIMITS` in each isolate. A provider's failure is shown as a plain message, never with
|
|
357
|
+
the key or the provider's answer.
|
|
358
|
+
- It works as plain forms: no script is needed.
|
|
359
|
+
|
|
307
360
|
### Dashboard on Cloudflare Workers
|
|
308
361
|
|
|
309
362
|
The same dashboard can run as a Cloudflare Worker at an origin of its own (`workers/dashboard`),
|
|
@@ -337,7 +390,8 @@ this one and read its session secret, which signs in as any tenant.
|
|
|
337
390
|
3. Create the GitHub OAuth app as above, with the callback URL
|
|
338
391
|
`<SLOTLOCK_PUBLIC_URL>/dashboard/callback` on the Worker's origin.
|
|
339
392
|
4. Store the secrets on the Worker: `npx wrangler secret put` for `SLOTLOCK_GITHUB_CLIENT_ID`,
|
|
340
|
-
`SLOTLOCK_GITHUB_CLIENT_SECRET` and `SLOTLOCK_SESSION_SECRET
|
|
393
|
+
`SLOTLOCK_GITHUB_CLIENT_SECRET` and `SLOTLOCK_SESSION_SECRET`, and `SLOTLOCK_ASSISTANT_SECRET`
|
|
394
|
+
for the assistant.
|
|
341
395
|
5. Deploy: `npm ci --omit=dev` at the repository root (the Worker bundles Slotlock's sources), then
|
|
342
396
|
`npx wrangler deploy --config <your configuration>` in `workers/dashboard`.
|
|
343
397
|
|
package/agent-server.d.ts
CHANGED
|
@@ -464,6 +464,20 @@ export declare function parseSlotlockA2ATimestamp(value: unknown): Date | null;
|
|
|
464
464
|
export declare const SLOTLOCK_MCP_RESOURCE_URI_TEMPLATE = "slotlock://resources/{resource_id}";
|
|
465
465
|
/** The MCP resource URI of one calendar resource: `SLOTLOCK_MCP_RESOURCE_URI_TEMPLATE`, expanded. */
|
|
466
466
|
export declare function slotlockCalendarResourceUri(resourceId: string): string;
|
|
467
|
+
/**
|
|
468
|
+
* The sentence a person is asked to confirm for a write, as the confirmation flow words it: the
|
|
469
|
+
* resource by name and the times in its own zone, and for a change or deletion the event as it
|
|
470
|
+
* stands (its title, resource and time), each read as `principal` only where its scopes and
|
|
471
|
+
* `authorize` allow that read. The dashboard assistant shows it on a proposed change.
|
|
472
|
+
*/
|
|
473
|
+
export declare function describeSlotlockAgentWrite(args: {
|
|
474
|
+
backend: SlotlockAgentCalendarBackend;
|
|
475
|
+
authorize: SlotlockAgentServerOptions['authorize'];
|
|
476
|
+
principal: SlotlockAgentPrincipal;
|
|
477
|
+
operation: SlotlockAgentWriteOperation;
|
|
478
|
+
input: Record<string, unknown>;
|
|
479
|
+
signal: AbortSignal;
|
|
480
|
+
}): Promise<string>;
|
|
467
481
|
export declare function createSlotlockAgentServer(options: SlotlockAgentServerOptions): {
|
|
468
482
|
fetch(request: Request): Promise<Response>;
|
|
469
483
|
manifest: Readonly<Record<string, unknown>>;
|
package/agent-server.js
CHANGED
|
@@ -1912,6 +1912,21 @@ function confirmationLookup(options, context) {
|
|
|
1912
1912
|
},
|
|
1913
1913
|
};
|
|
1914
1914
|
}
|
|
1915
|
+
/**
|
|
1916
|
+
* The sentence a person is asked to confirm for a write, as the confirmation flow words it: the
|
|
1917
|
+
* resource by name and the times in its own zone, and for a change or deletion the event as it
|
|
1918
|
+
* stands (its title, resource and time), each read as `principal` only where its scopes and
|
|
1919
|
+
* `authorize` allow that read. The dashboard assistant shows it on a proposed change.
|
|
1920
|
+
*/
|
|
1921
|
+
export async function describeSlotlockAgentWrite(args) {
|
|
1922
|
+
const lookup = confirmationLookup({ backend: args.backend, authorize: args.authorize }, { principal: args.principal, signal: args.signal });
|
|
1923
|
+
try {
|
|
1924
|
+
return await confirmationMessage(args.operation, args.input, lookup);
|
|
1925
|
+
}
|
|
1926
|
+
finally {
|
|
1927
|
+
lookup.close();
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1915
1930
|
/**
|
|
1916
1931
|
* The sentence a person confirms: what changes, where and when. The facts the server establishes
|
|
1917
1932
|
* (the resource, the times in its own zone, every date a repeat books, the event as it stands) come
|
package/assistant.d.ts
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { type SlotlockAgentOperation } from './agent-server.js';
|
|
2
|
+
import type { SlotlockAssistantConversationRecord, SlotlockAssistantSettingsRecord, SlotlockStore } from './store.js';
|
|
3
|
+
export type SlotlockAssistantProvider = 'anthropic' | 'openai';
|
|
4
|
+
/** What the assistant does with each agent operation: run it, propose it, or never offer it. */
|
|
5
|
+
export type SlotlockAssistantToolUse = 'read' | 'propose' | 'excluded';
|
|
6
|
+
/**
|
|
7
|
+
* The tools the model is offered, by agent operation. A record over every operation, so one a later
|
|
8
|
+
* release adds does not compile until it is placed here. Reads run when the model calls them;
|
|
9
|
+
* every write is only proposed, and runs when the member approves it. API keys, webhooks, booking
|
|
10
|
+
* rules, pools, erasure and coverage are application operations, never agent ones, so no entry can
|
|
11
|
+
* offer them.
|
|
12
|
+
*/
|
|
13
|
+
export declare const SLOTLOCK_ASSISTANT_TOOL_USE: Readonly<Record<SlotlockAgentOperation, SlotlockAssistantToolUse>>;
|
|
14
|
+
/** Model calls one message may make before the assistant stops and says so. */
|
|
15
|
+
export declare const SLOTLOCK_ASSISTANT_MAX_STEPS = 8;
|
|
16
|
+
/** Characters of one message from a member. */
|
|
17
|
+
export declare const SLOTLOCK_ASSISTANT_MAX_MESSAGE = 2000;
|
|
18
|
+
/**
|
|
19
|
+
* UTF-8 bytes of the stored conversation as JSON, past which the oldest exchanges go first.
|
|
20
|
+
* PostgreSQL's jsonb text adds at most one space per `:` or `,`, so at most half again: under the
|
|
21
|
+
* table's 262,144-byte CHECK on `messages::text` whatever the text.
|
|
22
|
+
*/
|
|
23
|
+
export declare const SLOTLOCK_ASSISTANT_MAX_STORED_BYTES = 160000;
|
|
24
|
+
/** UTF-8 bytes of one proposed change's arguments as JSON; a larger one is not proposed. */
|
|
25
|
+
export declare const SLOTLOCK_ASSISTANT_MAX_ACTION_BYTES = 16000;
|
|
26
|
+
/** Proposed changes one conversation may have waiting at once (4 of the largest fit the bound). */
|
|
27
|
+
export declare const SLOTLOCK_ASSISTANT_MAX_PENDING = 4;
|
|
28
|
+
/**
|
|
29
|
+
* The Messages API version this sends: the one @anthropic-ai/sdk 1.3.0 (2026-10-23) sends, read
|
|
30
|
+
* from its source on 2026-10-28. Top-level `system`, `tools[].input_schema`, `tool_use` and
|
|
31
|
+
* `tool_result` blocks are as before in it.
|
|
32
|
+
*/
|
|
33
|
+
export declare const SLOTLOCK_ANTHROPIC_VERSION = "2026-10-21";
|
|
34
|
+
/** A model as the assistant calls it, with the key opened. */
|
|
35
|
+
export interface SlotlockAssistantModel {
|
|
36
|
+
provider: SlotlockAssistantProvider;
|
|
37
|
+
baseUrl: string;
|
|
38
|
+
model: string;
|
|
39
|
+
apiKey: string | null;
|
|
40
|
+
}
|
|
41
|
+
export interface SlotlockAssistantCall {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
input: unknown;
|
|
45
|
+
}
|
|
46
|
+
/** One entry of a stored conversation. `note`s are shown to the member and never sent to the model. */
|
|
47
|
+
export type SlotlockAssistantEntry = {
|
|
48
|
+
role: 'user';
|
|
49
|
+
text: string;
|
|
50
|
+
} | {
|
|
51
|
+
role: 'assistant';
|
|
52
|
+
text: string;
|
|
53
|
+
calls: SlotlockAssistantCall[];
|
|
54
|
+
} | {
|
|
55
|
+
role: 'tool';
|
|
56
|
+
results: {
|
|
57
|
+
id: string;
|
|
58
|
+
name: string;
|
|
59
|
+
content: string;
|
|
60
|
+
error: boolean;
|
|
61
|
+
action?: string;
|
|
62
|
+
}[];
|
|
63
|
+
} | {
|
|
64
|
+
role: 'event';
|
|
65
|
+
text: string;
|
|
66
|
+
} | {
|
|
67
|
+
role: 'note';
|
|
68
|
+
text: string;
|
|
69
|
+
};
|
|
70
|
+
/** A proposed write, as the conversation keeps it. */
|
|
71
|
+
export interface SlotlockAssistantAction {
|
|
72
|
+
id: string;
|
|
73
|
+
operation: SlotlockAgentOperation;
|
|
74
|
+
input: Record<string, unknown>;
|
|
75
|
+
status: 'pending' | 'approved' | 'dismissed' | 'done' | 'failed';
|
|
76
|
+
/** What it did (`done`), or the code it failed with (`failed`). */
|
|
77
|
+
outcome?: string;
|
|
78
|
+
/** The change in a sentence, with its resource and event resolved (the confirmation question). */
|
|
79
|
+
summary?: string;
|
|
80
|
+
}
|
|
81
|
+
/** Where the dashboard keeps assistant settings and conversations: the store, under each tenant's RLS. */
|
|
82
|
+
export interface SlotlockAssistantStore {
|
|
83
|
+
getSettings(tenantRef: string): Promise<SlotlockAssistantSettingsRecord | null>;
|
|
84
|
+
putSettings(tenantRef: string, settings: {
|
|
85
|
+
provider: SlotlockAssistantProvider;
|
|
86
|
+
baseUrl: string;
|
|
87
|
+
model: string;
|
|
88
|
+
key: {
|
|
89
|
+
sealed: string;
|
|
90
|
+
hint: string;
|
|
91
|
+
} | null | 'keep';
|
|
92
|
+
}, actor?: string): Promise<{
|
|
93
|
+
saved: boolean;
|
|
94
|
+
}>;
|
|
95
|
+
deleteSettings(tenantRef: string, actor?: string): Promise<boolean>;
|
|
96
|
+
getConversation(tenantRef: string, memberRef: string): Promise<SlotlockAssistantConversationRecord | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Replace a member's messages; keep the stored actions `keep` names, as they stand, and add the
|
|
99
|
+
* `added` ones not stored already (store.ts putAssistantConversation).
|
|
100
|
+
*/
|
|
101
|
+
putConversation(tenantRef: string, memberRef: string, conversation: {
|
|
102
|
+
messages: unknown[];
|
|
103
|
+
added: Record<string, Record<string, unknown>>;
|
|
104
|
+
keep: readonly string[];
|
|
105
|
+
}, ttlMs: number): Promise<void>;
|
|
106
|
+
settleAction(tenantRef: string, memberRef: string, actionId: string, from: string, patch: Record<string, unknown>): Promise<Record<string, unknown> | null>;
|
|
107
|
+
deleteConversation(tenantRef: string, memberRef: string): Promise<boolean>;
|
|
108
|
+
}
|
|
109
|
+
/** The assistant's records in the store, each in a transaction under the tenant's row-level security. */
|
|
110
|
+
export declare function createSlotlockAssistantStore(store: SlotlockStore): SlotlockAssistantStore;
|
|
111
|
+
/** The assistant's records in this process: for tests, or a server that only ever runs as one. */
|
|
112
|
+
export declare function createSlotlockMemoryAssistantStore(now?: () => number): SlotlockAssistantStore;
|
|
113
|
+
/** The key sealed for one tenant: `v1.<iv>.<ciphertext and tag>`, base64url. */
|
|
114
|
+
export declare function sealSlotlockAssistantKey(secret: string, tenantRef: string, apiKey: string): Promise<string>;
|
|
115
|
+
/** The key a seal holds, or null when it is not one this secret sealed for this tenant. */
|
|
116
|
+
export declare function openSlotlockAssistantKey(secret: string, tenantRef: string, sealed: string): Promise<string | null>;
|
|
117
|
+
/**
|
|
118
|
+
* A provider base URL as stored: https, or http on localhost or 127.0.0.1 (a model on the server's
|
|
119
|
+
* own machine); no credentials, query or fragment; no trailing slash. Unless `privateUrls`, a URL
|
|
120
|
+
* whose host is `localhost` or a loopback, private, link-local, shared (CGNAT), unique-local or
|
|
121
|
+
* metadata address (the webhooks' isPrivateAddress) is refused as `private`; a name that resolves
|
|
122
|
+
* to one is refused when it is resolved (checkSlotlockAssistantHost, and at each connection).
|
|
123
|
+
*/
|
|
124
|
+
export declare function readSlotlockAssistantBaseUrl(value: string, options?: {
|
|
125
|
+
privateUrls?: boolean;
|
|
126
|
+
}): {
|
|
127
|
+
ok: true;
|
|
128
|
+
url: string;
|
|
129
|
+
} | {
|
|
130
|
+
ok: false;
|
|
131
|
+
reason: 'invalid' | 'private';
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Whether a base URL's host resolves only to public addresses: `resolve` lists them (dns.lookup's,
|
|
135
|
+
* on Node.js). True when `privateUrls`, or when the host is an IP literal readSlotlockAssistantBaseUrl
|
|
136
|
+
* already checked; false when any address is private. A name that does not resolve is not refused
|
|
137
|
+
* here: the connection fails later, plainly.
|
|
138
|
+
*/
|
|
139
|
+
export declare function checkSlotlockAssistantHost(baseUrl: string, options: {
|
|
140
|
+
privateUrls?: boolean;
|
|
141
|
+
resolve?: (hostname: string) => Promise<string[]>;
|
|
142
|
+
}): Promise<boolean>;
|
|
143
|
+
/** A model name: 1-200 printable characters, no spaces. */
|
|
144
|
+
export declare const SLOTLOCK_ASSISTANT_MODEL_NAME: RegExp;
|
|
145
|
+
/** An API key as typed: 1-4,096 printable characters, no spaces. */
|
|
146
|
+
export declare const SLOTLOCK_ASSISTANT_API_KEY: RegExp;
|
|
147
|
+
export interface SlotlockAssistantTool {
|
|
148
|
+
name: SlotlockAgentOperation;
|
|
149
|
+
title: string;
|
|
150
|
+
description: string;
|
|
151
|
+
inputSchema: Record<string, unknown>;
|
|
152
|
+
use: 'read' | 'propose';
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The tools the model is offered: every agent tool the assistant reads or proposes, with its input
|
|
156
|
+
* schema less `idempotency_key`, which the dashboard sets itself so an approval runs once.
|
|
157
|
+
*/
|
|
158
|
+
export declare function slotlockAssistantTools(): SlotlockAssistantTool[];
|
|
159
|
+
/** Why a provider call failed, in words safe to show (never the key, never the provider's body). */
|
|
160
|
+
export declare class SlotlockAssistantProviderError extends Error {
|
|
161
|
+
readonly reason: 'timeout' | 'refused_key' | 'not_found' | 'rate_limited' | 'unavailable' | 'bad_answer' | 'private_address';
|
|
162
|
+
readonly status?: number | undefined;
|
|
163
|
+
constructor(reason: 'timeout' | 'refused_key' | 'not_found' | 'rate_limited' | 'unavailable' | 'bad_answer' | 'private_address', status?: number | undefined);
|
|
164
|
+
}
|
|
165
|
+
/** What to tell the member a provider failure was. */
|
|
166
|
+
export declare function slotlockAssistantProviderMessage(error: unknown): string;
|
|
167
|
+
/** One answer of the model: its text and the tools it called. */
|
|
168
|
+
export interface SlotlockAssistantModelTurn {
|
|
169
|
+
text: string;
|
|
170
|
+
calls: SlotlockAssistantCall[];
|
|
171
|
+
}
|
|
172
|
+
interface ModelRequest {
|
|
173
|
+
model: SlotlockAssistantModel;
|
|
174
|
+
system: string;
|
|
175
|
+
entries: readonly SlotlockAssistantEntry[];
|
|
176
|
+
tools: readonly {
|
|
177
|
+
name: string;
|
|
178
|
+
description: string;
|
|
179
|
+
inputSchema: Record<string, unknown>;
|
|
180
|
+
}[];
|
|
181
|
+
/** Make the model call this tool (the connection test). */
|
|
182
|
+
force?: string;
|
|
183
|
+
fetch: typeof fetch;
|
|
184
|
+
signal: AbortSignal;
|
|
185
|
+
}
|
|
186
|
+
/** One model call. Throws SlotlockAssistantProviderError, whose message never holds the key. */
|
|
187
|
+
export declare function callSlotlockAssistantModel(request: ModelRequest): Promise<SlotlockAssistantModelTurn>;
|
|
188
|
+
/**
|
|
189
|
+
* The connection test: one request offering one read tool the model must call. True when it
|
|
190
|
+
* answers with exactly one call of that tool with object arguments.
|
|
191
|
+
*/
|
|
192
|
+
export declare function testSlotlockAssistantModel(args: {
|
|
193
|
+
model: SlotlockAssistantModel;
|
|
194
|
+
fetch: typeof fetch;
|
|
195
|
+
timeoutMs: number;
|
|
196
|
+
}): Promise<boolean>;
|
|
197
|
+
/** How a tool call came out, as the loop tells the model. */
|
|
198
|
+
export type SlotlockAssistantToolOutcome = {
|
|
199
|
+
ok: true;
|
|
200
|
+
data: Record<string, unknown>;
|
|
201
|
+
} | {
|
|
202
|
+
ok: false;
|
|
203
|
+
code: string;
|
|
204
|
+
details?: Readonly<Record<string, string>>;
|
|
205
|
+
};
|
|
206
|
+
/** A URL checked again before each call: one stored under looser rules is refused, not reached. */
|
|
207
|
+
export declare function slotlockAssistantUrlAllowed(baseUrl: string, privateUrls: boolean): boolean;
|
|
208
|
+
export interface SlotlockAssistantTurnOptions {
|
|
209
|
+
model: SlotlockAssistantModel;
|
|
210
|
+
system: string;
|
|
211
|
+
/** The conversation so far, ending with the member's new message. */
|
|
212
|
+
entries: readonly SlotlockAssistantEntry[];
|
|
213
|
+
fetch: typeof fetch;
|
|
214
|
+
/** Each model call's limit, and the whole turn's. */
|
|
215
|
+
timeoutMs: number;
|
|
216
|
+
turnTimeoutMs: number;
|
|
217
|
+
maxSteps?: number;
|
|
218
|
+
/** Run a read the model asked for. */
|
|
219
|
+
read(operation: SlotlockAgentOperation, input: unknown): Promise<SlotlockAssistantToolOutcome>;
|
|
220
|
+
/** Check a write the model asked for and keep it for the member to approve: its action id. */
|
|
221
|
+
propose(operation: SlotlockAgentOperation, input: unknown): Promise<{
|
|
222
|
+
ok: true;
|
|
223
|
+
actionId: string;
|
|
224
|
+
} | {
|
|
225
|
+
ok: false;
|
|
226
|
+
code: string;
|
|
227
|
+
}>;
|
|
228
|
+
}
|
|
229
|
+
export interface SlotlockAssistantTurnResult {
|
|
230
|
+
/** The entries this turn added. */
|
|
231
|
+
added: SlotlockAssistantEntry[];
|
|
232
|
+
/** Set when the provider failed; `added` then ends with a note saying so. */
|
|
233
|
+
failure?: unknown;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* One member message: call the model, run what it reads, keep what it would write, and call it
|
|
237
|
+
* again with the results, up to `maxSteps` calls. Only the offered tools run, whatever the model
|
|
238
|
+
* names; everything else is answered `tool_not_available`.
|
|
239
|
+
*/
|
|
240
|
+
export declare function runSlotlockAssistantTurn(options: SlotlockAssistantTurnOptions): Promise<SlotlockAssistantTurnResult>;
|
|
241
|
+
/**
|
|
242
|
+
* The conversation within its bounds: at most MAX_ENTRIES entries and MAX_STORED_CHARACTERS, the
|
|
243
|
+
* oldest exchanges dropped whole, so it always starts at a member's message (a tool result is never
|
|
244
|
+
* kept without the call it answers).
|
|
245
|
+
*/
|
|
246
|
+
export declare function boundSlotlockAssistantEntries(entries: readonly SlotlockAssistantEntry[]): SlotlockAssistantEntry[];
|
|
247
|
+
/**
|
|
248
|
+
* The actions a conversation keeps: those its kept entries show, the oldest settled ones dropped
|
|
249
|
+
* first past MAX_ACTIONS_BYTES (a waiting one is kept: SLOTLOCK_ASSISTANT_MAX_PENDING bounds them).
|
|
250
|
+
*/
|
|
251
|
+
export declare function pruneSlotlockAssistantActions(entries: readonly SlotlockAssistantEntry[], actions: Readonly<Record<string, Record<string, unknown>>>): Record<string, Record<string, unknown>>;
|
|
252
|
+
/** How many of a conversation's actions wait for the member. */
|
|
253
|
+
export declare function pendingSlotlockAssistantActions(actions: Readonly<Record<string, Record<string, unknown>>>): number;
|
|
254
|
+
/** Read stored entries back, dropping anything malformed. */
|
|
255
|
+
export declare function readSlotlockAssistantEntries(value: unknown): SlotlockAssistantEntry[];
|
|
256
|
+
/** The system prompt: who the model works for, and that what it reads is data, not instructions. */
|
|
257
|
+
export declare function slotlockAssistantSystemPrompt(args: {
|
|
258
|
+
brand: string;
|
|
259
|
+
workspace: string;
|
|
260
|
+
login: string;
|
|
261
|
+
now: Date;
|
|
262
|
+
}): string;
|
|
263
|
+
export {};
|