@spredr/sdk 0.0.0-stage → 0.1.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.
@@ -0,0 +1,162 @@
1
+ import { type Client, type ClientOptions } from './http.js';
2
+ import { Tasks, type Tenant } from './tasks.js';
3
+ import type { AgentInfo, Fact, Freedom, Json, Lesson, LessonVersion, Limits, MemoryUse, Pause, Redeploy, SessionFile, Summary, Task, TenantLimits, TenantUsage, Thread, Traces } from './types.js';
4
+ export interface SpredrOptions extends ClientOptions {
5
+ /** A spredr token (spr_…), from Account › Tokens or `spredr login`; SPREDR_TOKEN when absent. */
6
+ token?: string;
7
+ /** Where ai is; SPREDR_AI_URL, or spredr's own. */
8
+ ai?: string;
9
+ }
10
+ export declare const DEFAULT_AI_URL = "https://api.spredr.cloud/ai";
11
+ export declare class Spredr {
12
+ #private;
13
+ constructor(options?: SpredrOptions);
14
+ agents(): Promise<AgentInfo[]>;
15
+ /** Turns a spredr service into an agent: ai writes its keys into the service and deploys it again. A deploy spredr
16
+ * refuses (redeploy.status «failed») leaves the agent made: deploy the service again once that is fixed. */
17
+ register(name: string, service: string, options?: {
18
+ redeploy?: boolean;
19
+ }): Promise<AgentInfo & {
20
+ redeploy: Redeploy;
21
+ }>;
22
+ /** Leaves a token for ai to wake and put to sleep your agents while nobody is looking. */
23
+ keepToken(token: string): Promise<void>;
24
+ /** What waits for a person across every agent: open questions, tasks that failed in the last `hours`, and agents whose
25
+ * memory is 80 % full or more. */
26
+ inbox(hours?: number): Promise<{
27
+ questions: (Pause & {
28
+ agent: string;
29
+ })[];
30
+ errors: Task[];
31
+ memory: {
32
+ agent: string;
33
+ usedBytes: number;
34
+ maxBytes: number;
35
+ full: boolean;
36
+ }[];
37
+ }>;
38
+ agent(name: string): OwnedAgent;
39
+ }
40
+ export declare class OwnedAgent {
41
+ #private;
42
+ readonly tasks: Tasks;
43
+ constructor(client: Client, name: string);
44
+ /** The agent working for one tenant: its tasks, usage, caps, lessons and freedom, and forgetting it. */
45
+ for(tenant: string): Tenant;
46
+ /** The tenants it worked for in the last `days`, the latest active first, each with what it used. */
47
+ tenants(options?: {
48
+ days?: number;
49
+ limit?: number;
50
+ }): Promise<TenantUsage[]>;
51
+ /** The caps of every tenant without caps of its own. */
52
+ setTenantLimits(limits: TenantLimits): Promise<Required<TenantLimits>>;
53
+ info(): Promise<AgentInfo>;
54
+ /** Its last hours at a glance: tasks, failures, times, tokens, cost, tools, and how free it is. */
55
+ summary(hours?: number): Promise<Summary>;
56
+ /** Its conversations: tasks that share a thread. Read one with tasks.list({ thread }). */
57
+ threads(limit?: number): Promise<Thread[]>;
58
+ /** Every task since a moment with its steps (the export, read line by line). */
59
+ export(since?: Date | string): Promise<(Task & {
60
+ steps: Json[];
61
+ })[]>;
62
+ setLimits(limits: Limits): Promise<AgentInfo>;
63
+ /** Sends each finished task to your OpenTelemetry endpoint (OTLP/HTTP: Langfuse, Datadog, Honeycomb, your collector)
64
+ * as a trace. headers carry its key (sealed by ai, never shown back); withContent sends the input and result too. */
65
+ setTraces(traces: {
66
+ endpoint: string;
67
+ headers?: Record<string, string>;
68
+ withContent?: boolean;
69
+ }): Promise<Traces>;
70
+ /** Where its traces go (the header names only), or null. */
71
+ traces(): Promise<Traces | null>;
72
+ stopTraces(): Promise<void>;
73
+ remove(): Promise<void>;
74
+ /** A new key pair: ai believes the new key at once, whatever the redeploy does (redeploy.status says). */
75
+ rotateKey(options?: {
76
+ redeploy?: boolean;
77
+ }): Promise<AgentInfo & {
78
+ redeploy: Redeploy;
79
+ }>;
80
+ /** A backend that may give this agent tasks. With `service`, ai writes its key there; with `publicKey`, the private
81
+ * one never leaves you; with neither, the private key comes back this once. With `everything` and a `callbackUrl`, it
82
+ * is an events webhook: it hears about every task of the agent (task.created, done, failed, paused, canceled). */
83
+ addCaller(name: string, options?: {
84
+ service?: string;
85
+ publicKey?: string;
86
+ callbackUrl?: string;
87
+ everything?: boolean;
88
+ redeploy?: boolean;
89
+ }): Promise<{
90
+ caller: {
91
+ id: string;
92
+ name: string;
93
+ publicKey: string;
94
+ callbackUrl: string;
95
+ };
96
+ privateKey?: string;
97
+ }>;
98
+ callers(): Promise<{
99
+ id: string;
100
+ name: string;
101
+ service: string;
102
+ callbackUrl: string;
103
+ }[]>;
104
+ removeCaller(id: string): Promise<void>;
105
+ schedule(name: string, kind: string, cron: string, options?: {
106
+ tz?: string;
107
+ input?: Json;
108
+ }): Promise<{
109
+ id: string;
110
+ nextAt: string;
111
+ }>;
112
+ /** A secret address that starts a task with whatever is POSTed to it; the address comes back this once. */
113
+ hook(name: string, kind: string): Promise<{
114
+ hook: {
115
+ id: string;
116
+ name: string;
117
+ };
118
+ url: string;
119
+ }>;
120
+ /** Every file its sessions keep, newest first (each with its tenant, conversation and place in the bucket), and how full
121
+ * its room for them is. */
122
+ storedFiles(options?: {
123
+ before?: string;
124
+ limit?: number;
125
+ }): Promise<{
126
+ files: SessionFile[];
127
+ usedBytes: number;
128
+ maxBytes: number;
129
+ filesBytes: number;
130
+ memoryBytes: number;
131
+ }>;
132
+ /** One of them, as its bytes and type: any session's. */
133
+ downloadFile(id: string): Promise<{
134
+ data: Uint8Array;
135
+ type: string;
136
+ }>;
137
+ /** Removes one, any session's: its card and its bytes. */
138
+ deleteFile(id: string): Promise<void>;
139
+ /** How full its memory is (warn from 80 %, full from 95 %), and whether unchanged facts are forgotten. */
140
+ memoryUse(): Promise<MemoryUse>;
141
+ /** What it remembers; `tenant` narrows it to one tenant's. */
142
+ memory(options?: {
143
+ scope?: string;
144
+ q?: string;
145
+ limit?: number;
146
+ tenant?: string;
147
+ }): Promise<Fact[]>;
148
+ editFact(id: string, text: string, data?: Json): Promise<Fact>;
149
+ /** Erases everything the agent keeps under a scope (a customer who asks to be forgotten): the agent's own, or one
150
+ * tenant's. */
151
+ forget(scope: string, tenant?: string): Promise<number>;
152
+ /** How free it is with each action in its own work (no tenant); a tenant's is `for(tenant).freedom()`. */
153
+ freedom(): Promise<Freedom[]>;
154
+ setFreedom(action: string, level: 'ask' | 'auto'): Promise<Freedom>;
155
+ lessons(): Promise<Lesson[]>;
156
+ writeLesson(text: string, why?: string): Promise<Lesson>;
157
+ decide(lessonId: string, decision: 'accept' | 'refuse' | 'retire'): Promise<Lesson>;
158
+ versions(): Promise<LessonVersion[]>;
159
+ /** Releases the accepted lessons as a new version, tried first on past tasks. */
160
+ release(): Promise<LessonVersion>;
161
+ version(version: number, action: 'publish' | 'rollback' | 'discard'): Promise<LessonVersion>;
162
+ }
@@ -0,0 +1,28 @@
1
+ export interface StandardSchemaV1<Input = unknown, Output = Input> {
2
+ readonly '~standard': StandardProps<Input, Output>;
3
+ }
4
+ export interface StandardProps<Input = unknown, Output = Input> {
5
+ readonly version: 1;
6
+ readonly vendor: string;
7
+ readonly validate: (value: unknown) => StandardResult<Output> | Promise<StandardResult<Output>>;
8
+ readonly types?: {
9
+ readonly input: Input;
10
+ readonly output: Output;
11
+ } | undefined;
12
+ }
13
+ export type StandardResult<Output> = {
14
+ readonly value: Output;
15
+ readonly issues?: undefined;
16
+ } | {
17
+ readonly issues: ReadonlyArray<StandardIssue>;
18
+ };
19
+ export interface StandardIssue {
20
+ readonly message: string;
21
+ readonly path?: ReadonlyArray<PropertyKey | {
22
+ readonly key: PropertyKey;
23
+ }> | undefined;
24
+ }
25
+ export type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];
26
+ export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
27
+ /** The value the schema makes of `input`, or an error that says every place it does not match. */
28
+ export declare function validated<Output>(schema: StandardSchemaV1<unknown, Output>, input: unknown): Promise<Output>;
package/dist/sign.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { type KeyObject } from 'node:crypto';
2
+ export declare const HEADERS: {
3
+ readonly from: "x-spredr-from";
4
+ readonly time: "x-spredr-time";
5
+ readonly nonce: "x-spredr-nonce";
6
+ readonly signature: "x-spredr-signature";
7
+ };
8
+ /** Who ai is when it signs a delivery or a callback. */
9
+ export declare const AI_SIGNER = "ai";
10
+ export declare const privateKeyOf: (base64: string) => KeyObject;
11
+ export declare const publicKeyOf: (base64: string) => KeyObject;
12
+ /** A new key pair for a backend that brings its own public key: keep `privateKey` secret, give spredr `publicKey`. */
13
+ export declare function newKeys(): {
14
+ privateKey: string;
15
+ publicKey: string;
16
+ };
17
+ export declare function signedHeaders(from: string, key: KeyObject, method: string, path: string, body: string | Uint8Array): Record<string, string>;
18
+ export interface Incoming {
19
+ method: string;
20
+ /** The path with its query, exactly as it arrived (Node's request.url). */
21
+ path: string;
22
+ headers: Record<string, string | string[] | undefined>;
23
+ body: string | Uint8Array;
24
+ }
25
+ /** Checks that a request was signed by ai, recently, and never seen before. One instance per process. */
26
+ export declare function aiVerifier(aiPublicKey: string): (request: Incoming) => boolean;
package/dist/tag.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /** The id of the task the code is working on now, or null outside one: for a logger's own fields, e.g.
2
+ * `pino({ mixin: () => ({ task: currentTask() }) })`. */
3
+ export declare function currentTask(): string | null;
4
+ /** Runs `work` as the given task: currentTask() says its id all the way down. */
5
+ export declare function asTask<T>(taskId: string, work: () => T): T;
6
+ /** From now on, console's lines printed while a task works start with its id. Done once per process. */
7
+ export declare function tagConsole(): void;
@@ -0,0 +1,98 @@
1
+ import { type Client } from './http.js';
2
+ import type { Answer, AnyKinds, AskOptions, Feedback, Freedom, Json, Kinds, Lesson, Pause, SessionFile, Step, StreamEvent, Task, TaskEvent, TenantInfo, TenantLimits } from './types.js';
3
+ export declare class Tasks<K extends Kinds = AnyKinds> {
4
+ #private;
5
+ constructor(client: Client, agent: string, tenant?: string);
6
+ /** The same agent, working for one tenant: `caller.for('user_42').result('reply', …)`. One agent serves any number
7
+ * of tenants at once, each with its own memory, questions, freedom, lessons and caps. */
8
+ for(tenant: string): Tenant<K>;
9
+ /** Asks the agent for a task. With `wait`, answers once it is done (or when the wait runs out). With a `key`, asking
10
+ * again is the same task, so it is safe to retry. */
11
+ ask<Name extends keyof K & string>(kind: Name, input: K[Name]['input'], options?: AskOptions): Promise<Task<K[Name]['output']>>;
12
+ /** Asks and waits until the task is done, failed, canceled or paused, however long (in rounds of up to a minute). */
13
+ run<Name extends keyof K & string>(kind: Name, input: K[Name]['input'], options?: Omit<AskOptions, 'wait'> & {
14
+ timeoutMs?: number;
15
+ }): Promise<Task<K[Name]['output']>>;
16
+ /** Asks, waits, and gives back the result itself; a task that fails, is canceled or waits for an answer throws a
17
+ * TaskError carrying the task. */
18
+ result<Name extends keyof K & string>(kind: Name, input: K[Name]['input'], options?: Omit<AskOptions, 'wait'> & {
19
+ timeoutMs?: number;
20
+ }): Promise<K[Name]['output']>;
21
+ get<Result = Json>(id: string, wait?: number): Promise<Task<Result>>;
22
+ /** Waits for a task to leave the line: done, failed, canceled or paused. */
23
+ settle<Result = Json>(id: string, timeoutMs?: number): Promise<Task<Result>>;
24
+ list(options?: {
25
+ status?: Task['status'];
26
+ limit?: number;
27
+ before?: string;
28
+ thread?: string;
29
+ tenant?: string;
30
+ }): Promise<Task[]>;
31
+ /** What happened to a task after `after` (its events' seq); with `wait` seconds, waits for the first new one. */
32
+ events(id: string, after?: number, wait?: number): Promise<TaskEvent[]>;
33
+ /** What the agent did inside a task, step by step: its trace. */
34
+ steps(id: string): Promise<(Step & {
35
+ seq: number;
36
+ at: string;
37
+ })[]>;
38
+ cancel(id: string): Promise<Task>;
39
+ /** The questions waiting for an answer (a tenant's alone, in a tenant session). */
40
+ pauses(): Promise<Pause[]>;
41
+ answer(pauseId: string, answer: Answer): Promise<Pause>;
42
+ /** How a finished task went: what the agent learns from. A bad one also takes back what it did alone. */
43
+ feedback(id: string, verdict: 'good' | 'bad', details?: {
44
+ correction?: Json;
45
+ note?: string;
46
+ }): Promise<Feedback>;
47
+ /** A task as it happens: each piece of its live answer (the agent's ctx.stream), each of its other events, and last the
48
+ * task itself once it is done, paused or failed. A stream that closes before the end is opened again after what
49
+ * was seen, so nothing comes twice. */
50
+ stream(id: string): AsyncGenerator<StreamEvent>;
51
+ /** Asks for a task and gives its live answer as it is written: the pieces, then the task. */
52
+ live<Name extends keyof K & string>(kind: Name, input: K[Name]['input'], options?: AskOptions): AsyncGenerator<StreamEvent>;
53
+ /** Leaves a file in a session (a thread) for the agent's code to read: a price list, a catalogue, a document. Every
54
+ * task of that thread lists it (ctx.files) and the agent downloads it (ctx.file) while it works on one. The same
55
+ * name again replaces it. spredr keeps the bytes and never opens them; how the agent reads them is its code's. */
56
+ attach(thread: string, name: string, data: Uint8Array | string, options?: {
57
+ type?: string;
58
+ tenant?: string;
59
+ }): Promise<SessionFile>;
60
+ /** A session's files, without their bytes, newest first. */
61
+ files(thread: string, options?: {
62
+ tenant?: string;
63
+ }): Promise<SessionFile[]>;
64
+ /** Takes a file out of a session. */
65
+ detach(thread: string, name: string, options?: {
66
+ tenant?: string;
67
+ }): Promise<void>;
68
+ /** The client and agent a tenant session works with. */
69
+ protected get base(): {
70
+ client: Client;
71
+ agent: string;
72
+ };
73
+ }
74
+ /** One tenant of an agent: its tasks, and what is its own. Made with `caller.for(tenant)` or `spredr.agent(name).for(tenant)`. */
75
+ export declare class Tenant<K extends Kinds = AnyKinds> extends Tasks<K> {
76
+ #private;
77
+ readonly name: string;
78
+ constructor(client: Client, agent: string, tenant: string);
79
+ /** What it used today and this month, its caps, the facts it holds and its open questions. */
80
+ usage(): Promise<TenantInfo>;
81
+ /** Caps this tenant on top of the agent's own; null lifts one. */
82
+ setLimits(limits: TenantLimits): Promise<Required<TenantLimits>>;
83
+ /** Forgets this tenant whole: its tasks, memory, questions, freedom, lessons and caps. The others stay. */
84
+ forget(): Promise<{
85
+ tasks: number;
86
+ facts: number;
87
+ }>;
88
+ /** How free the agent is with each action for this tenant. */
89
+ freedom(): Promise<Freedom[]>;
90
+ /** Lets an action run alone for this tenant (auto), or ask every time (ask): never for another tenant. */
91
+ setFreedom(action: string, level: 'ask' | 'auto'): Promise<Freedom>;
92
+ /** This tenant's lessons: its own rules, in every one of its tasks from the moment they are written. */
93
+ lessons(): Promise<Lesson[]>;
94
+ /** Teaches the agent a rule for this tenant alone («we never book on Sundays»). */
95
+ teach(text: string, why?: string): Promise<Lesson>;
96
+ /** Accepts or refuses a lesson the agent proposed for this tenant, or retires one. */
97
+ decide(lessonId: string, decision: 'accept' | 'refuse' | 'retire'): Promise<Lesson>;
98
+ }
@@ -0,0 +1,293 @@
1
+ export type Json = null | boolean | number | string | Json[] | {
2
+ [key: string]: Json;
3
+ };
4
+ export type TaskStatus = 'queued' | 'running' | 'paused' | 'done' | 'failed' | 'canceled';
5
+ /** One kind of task: what goes in, what comes out. */
6
+ export interface Kind {
7
+ input: unknown;
8
+ output: unknown;
9
+ }
10
+ /** An agent's kinds by name. Name none and any kind goes: JSON in, and a result as free as fetch's json(); take them
11
+ * from the agent (KindsOf) and TypeScript checks each name, its input and its result. */
12
+ export type Kinds = {
13
+ [name: string]: Kind;
14
+ };
15
+ export type AnyKinds = {
16
+ [name: string]: {
17
+ input: Json;
18
+ output: any;
19
+ };
20
+ };
21
+ export interface Task<Result = Json> {
22
+ id: string;
23
+ agent: string;
24
+ kind: string;
25
+ status: TaskStatus;
26
+ input: Json;
27
+ result: Result | null;
28
+ error: string;
29
+ attempt: number;
30
+ by: 'owner' | 'caller' | 'schedule' | 'hook' | 'agent' | 'trial' | 'judge';
31
+ key: string | null;
32
+ thread: string | null;
33
+ /** Whom the agent worked for in it ("" is no one in particular). */
34
+ tenant: string;
35
+ trial: boolean;
36
+ createdAt: string;
37
+ startedAt: string | null;
38
+ finishedAt: string | null;
39
+ }
40
+ export interface TaskEvent {
41
+ seq: number;
42
+ type: string;
43
+ at: string;
44
+ data: Json;
45
+ }
46
+ export interface Pause {
47
+ id: string;
48
+ task: string;
49
+ /** Its task's tenant: whose question it is. */
50
+ tenant: string;
51
+ action: string;
52
+ question: string;
53
+ options: string[] | null;
54
+ data: Json;
55
+ status: 'open' | 'answered' | 'canceled';
56
+ answer: Answer | null;
57
+ answeredBy: string;
58
+ auto: boolean;
59
+ createdAt: string;
60
+ answeredAt: string | null;
61
+ }
62
+ export interface Answer {
63
+ yes: boolean;
64
+ /** What to do instead, when the person changed it. */
65
+ value?: Json;
66
+ note?: string;
67
+ }
68
+ export interface Fact {
69
+ id: string;
70
+ tenant: string;
71
+ scope: string;
72
+ key: string | null;
73
+ text: string;
74
+ data: Json;
75
+ updatedAt: string;
76
+ }
77
+ /** The service's redeploy after its keys changed: asked for, not asked for, or refused by spredr (and why). The agent
78
+ * is made, or its new key taken, either way. */
79
+ export type Redeploy = {
80
+ status: 'started' | 'skipped';
81
+ } | {
82
+ status: 'failed';
83
+ error: string;
84
+ };
85
+ export interface AgentInfo {
86
+ id: string;
87
+ name: string;
88
+ service: string;
89
+ url: string;
90
+ state: 'new' | 'awake' | 'waking' | 'asleep';
91
+ description: string;
92
+ tasks: {
93
+ kind: string;
94
+ description: string;
95
+ }[];
96
+ actions: {
97
+ name: string;
98
+ neverAuto: boolean;
99
+ }[];
100
+ /** Quiet minutes before it sleeps, null never: the owner's choice when they made one, else what it declared. */
101
+ sleepAfterMinutes: number | null;
102
+ /** Whether that is the owner's choice (the panel's «Dormir») rather than its own. */
103
+ sleepChosen?: boolean;
104
+ lessonsVersion: number;
105
+ tools?: {
106
+ name: string;
107
+ description: string;
108
+ asks: boolean;
109
+ }[];
110
+ limits?: Limits;
111
+ }
112
+ export interface Freedom {
113
+ action: string;
114
+ tenant: string;
115
+ level: 'ask' | 'auto';
116
+ earned: boolean;
117
+ neverAuto: boolean;
118
+ streak: number;
119
+ approved: number;
120
+ edited: number;
121
+ rejected: number;
122
+ undone: number;
123
+ }
124
+ export interface Lesson {
125
+ id: string;
126
+ tenant: string;
127
+ text: string;
128
+ why: string;
129
+ sources: string[];
130
+ by: string;
131
+ status: 'proposed' | 'accepted' | 'refused' | 'retired';
132
+ }
133
+ export interface LessonVersion {
134
+ version: number;
135
+ lessons: string[];
136
+ status: 'trial' | 'live' | 'superseded' | 'failed' | 'discarded';
137
+ trial: {
138
+ total: number;
139
+ done: number;
140
+ passed: number;
141
+ };
142
+ }
143
+ export interface Feedback {
144
+ seq: number;
145
+ task: string;
146
+ verdict: 'good' | 'bad';
147
+ correction: Json;
148
+ note: string;
149
+ by: string;
150
+ at: string;
151
+ }
152
+ export interface AskOptions {
153
+ /** Your own key for this task: asking twice with the same key is the same task. */
154
+ key?: string;
155
+ /** Seconds to wait for the outcome (up to 60). */
156
+ wait?: number;
157
+ /** When to start (a Date or ISO time); absent: now. */
158
+ at?: Date | string;
159
+ /** The conversation it belongs to (a customer, a ticket): tasks with the same thread read as one. */
160
+ thread?: string;
161
+ /** Whom the agent works for in it (a user, an account, a team, a business): its memory, freedom, lessons and caps are that tenant's.
162
+ * A tenant session (`caller.for(tenant)`) sets it for you. */
163
+ tenant?: string;
164
+ }
165
+ /** One step inside a task: a model call, a tool, a memory read. Its trace and its costs come from these. */
166
+ export interface Step {
167
+ kind: 'model' | 'tool' | 'memory' | 'other';
168
+ name: string;
169
+ ms: number;
170
+ at?: number;
171
+ detail?: string;
172
+ model?: string;
173
+ tokensIn?: number;
174
+ tokensOut?: number;
175
+ costUsd?: number;
176
+ ok?: boolean;
177
+ }
178
+ export interface Summary {
179
+ hours: number;
180
+ tasks: number;
181
+ done: number;
182
+ failed: number;
183
+ failRate: number;
184
+ p50Ms: number | null;
185
+ p95Ms: number | null;
186
+ tokens: number;
187
+ costUsd: number;
188
+ perHour: number[];
189
+ failedPerHour: number[];
190
+ tools: {
191
+ name: string;
192
+ uses: number;
193
+ }[];
194
+ freedom: {
195
+ alone: number;
196
+ asked: number;
197
+ undone: number;
198
+ };
199
+ }
200
+ export interface Thread {
201
+ thread: string;
202
+ tasks: number;
203
+ lastAt: string;
204
+ lastTask: string;
205
+ }
206
+ export interface Limits {
207
+ /** Actions it may take alone in a day; past it, its free actions ask again. null: no limit. */
208
+ autoPerDay?: number | null;
209
+ /** What its model calls may cost in a day (from the steps it reports); past it, new tasks are refused until tomorrow. null: no limit. */
210
+ spendPerDayUsd?: number | null;
211
+ /** Quiet minutes before it sleeps, over what it declares: 0 never, 5 to 1440; null leaves it to the agent. */
212
+ sleepAfterMinutes?: number | null;
213
+ /** Days its finished tasks are kept: 7, 30, 60, 90, 365 or 1825, never past what the plan keeps; null: the plan's. */
214
+ keepDays?: 7 | 30 | 60 | 90 | 365 | 1825 | null;
215
+ /** Days a fact may go unchanged before it is forgotten: 30, 90, 180 or 365; null: never by itself. */
216
+ forgetAfterDays?: 30 | 90 | 180 | 365 | null;
217
+ }
218
+ /** How full an agent's memory is: what its facts hold of what its plan gives it, in bytes. warn from 80 %, full from
219
+ * 95 % (the next fact may not fit: not kept, the task goes on). */
220
+ /** What a task's stream gives, in order: pieces of its live answer, its other events (steps, progress, a question), and
221
+ * last the task as it finished, paused or failed. */
222
+ export type StreamEvent = {
223
+ type: 'delta';
224
+ text: string;
225
+ seq: number;
226
+ } | {
227
+ type: 'event';
228
+ event: TaskEvent;
229
+ } | {
230
+ type: 'end';
231
+ task: Task;
232
+ };
233
+ /** A file in an agent's session (a thread, for one tenant): what a caller left for the agent's code to read. */
234
+ export interface SessionFile {
235
+ id: string;
236
+ name: string;
237
+ type: string;
238
+ bytes: number;
239
+ thread?: string;
240
+ tenant?: string;
241
+ /** Where its bytes are in spredr's files' bucket: owners/<owner>/agents/<agent>/sessions/<tenant>/<thread>/<id>. */
242
+ path?: string;
243
+ createdAt?: string;
244
+ updatedAt?: string;
245
+ }
246
+ /** An agent's space: its memory and its sessions' files share it (its plan's, plus packs bought in spredr). */
247
+ export interface MemoryUse {
248
+ /** Memory and files together. */
249
+ usedBytes: number;
250
+ maxBytes: number;
251
+ memoryBytes: number;
252
+ filesBytes: number;
253
+ share: number;
254
+ warn: boolean;
255
+ full: boolean;
256
+ forgetAfterDays: number | null;
257
+ }
258
+ /** Where an agent's traces go: an OTLP/HTTP endpoint and the names of its headers (their values never come back). */
259
+ export interface Traces {
260
+ endpoint: string;
261
+ headers: string[];
262
+ withContent: boolean;
263
+ }
264
+ /** What one tenant may use, on top of the agent's own caps; null is no cap. */
265
+ export interface TenantLimits {
266
+ /** Actions it may take alone a day for this tenant; past it, they ask again until tomorrow. */
267
+ autoPerDay?: number | null;
268
+ /** Model spend a day for this tenant; past it, its new tasks are refused until tomorrow (the others go on). */
269
+ spendPerDayUsd?: number | null;
270
+ /** Facts it may hold of the agent's memory, which all its tenants share. */
271
+ facts?: number | null;
272
+ }
273
+ /** What one tenant used in a while: its tasks and what their model calls cost. */
274
+ export interface TenantUsage {
275
+ tenant: string;
276
+ tasks: number;
277
+ done: number;
278
+ failed: number;
279
+ costUsd: number;
280
+ tokensIn: number;
281
+ tokensOut: number;
282
+ lastAt: string | null;
283
+ }
284
+ export interface TenantInfo {
285
+ tenant: string;
286
+ /** Its caps as they apply: its own, or every tenant's. */
287
+ caps: Required<TenantLimits>;
288
+ ownCaps: boolean;
289
+ today: TenantUsage;
290
+ month: TenantUsage;
291
+ facts: number;
292
+ openQuestions: number;
293
+ }
@@ -0,0 +1,3 @@
1
+ import type { Step } from './types.js';
2
+ /** The model and its tokens, as far as the answer tells them. */
3
+ export declare function usageOf(answer: unknown): Pick<Step, 'model' | 'tokensIn' | 'tokensOut'>;
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
1
  {
2
2
  "name": "@spredr/sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Talk to agents on spredr: run an agent, give it tasks from your backend, and manage it with your token. No dependencies.",
5
+ "type": "module",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "author": "Candhrly, LLC (https://spredr.cloud)",
8
+ "keywords": [
9
+ "spredr",
10
+ "agents",
11
+ "ai-agents",
12
+ "tasks",
13
+ "sdk",
14
+ "ed25519"
15
+ ],
16
+ "homepage": "https://spredr.cloud",
17
+ "engines": {
18
+ "node": ">=22.12"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "types": "./dist/index.d.ts",
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE",
33
+ "TRADEMARKS.md"
34
+ ],
35
+ "sideEffects": false,
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "scripts": {
40
+ "build": "node scripts/build.ts",
41
+ "check": "tsc --noEmit -p . && node scripts/check-alone.ts",
42
+ "test": "node --test 'test/*.test.ts'",
43
+ "coverage": "node --test --experimental-test-coverage --test-coverage-include='src/**' --test-coverage-lines=95 --test-coverage-branches=85 --test-coverage-functions=90 'test/*.test.ts'",
44
+ "package": "npm run build && publint --strict && node scripts/check-package.ts",
45
+ "prepublishOnly": "node scripts/check-readme.ts && npm run check && npm run coverage && npm run package"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "24.19.2",
49
+ "esbuild": "0.28.2",
50
+ "publint": "^0.3.14",
51
+ "typescript": "5.9.3"
52
+ }
53
+ }