@segmentstream-cloud/ontology 0.14.0 → 0.16.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.
- package/README.md +74 -10
- package/dist/agent-chat.d.ts +75 -0
- package/dist/agent-chat.js +176 -0
- package/dist/analyze.d.ts +6 -2
- package/dist/analyze.js +29 -18
- package/dist/authoring.d.ts +6 -3
- package/dist/authoring.js +6 -3
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/migrate/apply.js +2 -0
- package/dist/migrate/plan.js +14 -4
- package/dist/permissions.d.ts +11 -2
- package/dist/permissions.js +15 -3
- package/dist/replication/baseline.js +1 -1
- package/dist/replication/model.js +2 -2
- package/dist/replication/schema.js +1 -1
- package/dist/runtime/agents.d.ts +1 -0
- package/dist/runtime/agents.js +160 -0
- package/dist/runtime/generated-actions.d.ts +2 -1
- package/dist/runtime/generated-actions.js +18 -13
- package/dist/runtime/service-binding.js +3 -0
- package/dist/service/agent/attachments.d.ts +37 -0
- package/dist/service/agent/attachments.js +225 -0
- package/dist/service/agent/channel.d.ts +16 -0
- package/dist/service/agent/channel.js +88 -0
- package/dist/service/agent/protocol.d.ts +14 -0
- package/dist/service/agent/protocol.js +26 -0
- package/dist/service/agent/server.d.ts +2 -1
- package/dist/service/agent/server.js +24 -2
- package/dist/service/aggregate-platform.d.ts +5 -0
- package/dist/service/aggregate-platform.js +1 -1
- package/dist/service/config.d.ts +10 -0
- package/dist/service/config.js +8 -0
- package/dist/service/handlers.d.ts +2 -1
- package/dist/service/handlers.js +2 -2
- package/dist/service/mcp/server.d.ts +3 -0
- package/dist/service/mcp/server.js +27 -4
- package/dist/service/mcp/tools.d.ts +6 -0
- package/dist/service/mcp/tools.js +33 -1
- package/dist/service/operations.d.ts +6 -2
- package/dist/service/operations.js +13 -5
- package/dist/service/runtime-contract.d.ts +29 -0
- package/dist/service/service.js +18 -2
- package/dist/storage/attachments.d.ts +117 -0
- package/dist/storage/attachments.js +536 -0
- package/dist/storage/maintenance.d.ts +4 -1
- package/dist/storage/maintenance.js +7 -2
- package/dist/storage/neon.js +14 -0
- package/dist/storage/provider.d.ts +2 -0
- package/dist/storage/tables.d.ts +8 -0
- package/dist/storage/tables.js +32 -0
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import { isAllowed, permissionOf, READ_VERB } from "../../permissions.js";
|
|
|
7
7
|
import { queryJsonSchema } from "../../query.js";
|
|
8
8
|
import { measuresFor, RECORDS_OR_AGGREGATES } from "../../render.js";
|
|
9
9
|
import { actionInputJsonSchema } from "../../runtime/generated-actions.js";
|
|
10
|
+
import { uploadFields } from "../../storage/attachments.js";
|
|
10
11
|
import { actionDescription, requires } from "../descriptions.js";
|
|
11
12
|
import { AGGREGATE_FILTER_OPERATORS, AGGREGATE_GRANULARITIES, AGGREGATE_LIMITS, HistoryPage, Me, } from "../schemas.js";
|
|
12
13
|
const DEFS = "#/$defs/";
|
|
@@ -160,6 +161,37 @@ function readTools(planned, schemas, built) {
|
|
|
160
161
|
},
|
|
161
162
|
];
|
|
162
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* The argument an action that consumes an upload takes beside its input: the idempotency key the
|
|
166
|
+
* runtime requires of it, and the create intent the upload was begun with. The service removes it
|
|
167
|
+
* before the input is validated.
|
|
168
|
+
*/
|
|
169
|
+
export const ACTION_OPTIONS_ARGUMENT = "_options";
|
|
170
|
+
const ACTION_OPTIONS_SCHEMA = {
|
|
171
|
+
type: "object",
|
|
172
|
+
description: "Options of an action that stores an uploaded file: idempotency_key, required with an upload, " +
|
|
173
|
+
"and create_intent, the one the upload was begun with when it creates a record.",
|
|
174
|
+
properties: {
|
|
175
|
+
idempotency_key: { type: "string", minLength: 1, maxLength: 128 },
|
|
176
|
+
create_intent: { type: "string", format: "uuid" },
|
|
177
|
+
},
|
|
178
|
+
additionalProperties: false,
|
|
179
|
+
};
|
|
180
|
+
/** The input schema of an action tool; one that consumes an upload also takes its options. */
|
|
181
|
+
function actionToolInput(action) {
|
|
182
|
+
const schema = rootObject(actionInputJsonSchema(action.input));
|
|
183
|
+
const properties = schema.properties;
|
|
184
|
+
if (uploadFields(action.input).length === 0 ||
|
|
185
|
+
schema.type !== "object" ||
|
|
186
|
+
properties === undefined ||
|
|
187
|
+
ACTION_OPTIONS_ARGUMENT in properties) {
|
|
188
|
+
return schema;
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
...schema,
|
|
192
|
+
properties: { ...properties, [ACTION_OPTIONS_ARGUMENT]: ACTION_OPTIONS_SCHEMA },
|
|
193
|
+
};
|
|
194
|
+
}
|
|
163
195
|
function actionTool(planned, plannedAction) {
|
|
164
196
|
const { name, action, permissions } = plannedAction;
|
|
165
197
|
const hints = hintsOf(planned, plannedAction);
|
|
@@ -167,7 +199,7 @@ function actionTool(planned, plannedAction) {
|
|
|
167
199
|
name: mcpToolName(planned.name, name),
|
|
168
200
|
title: `${planned.resolved.entity.definition.label}: ${humanize(name).toLowerCase()}`,
|
|
169
201
|
description: actionDescription(plannedAction),
|
|
170
|
-
inputSchema:
|
|
202
|
+
inputSchema: actionToolInput(action),
|
|
171
203
|
annotations: {
|
|
172
204
|
readOnlyHint: false,
|
|
173
205
|
destructiveHint: hints.destructive,
|
|
@@ -5,6 +5,7 @@ import type { Query, QueryResult } from "../query.js";
|
|
|
5
5
|
import { CurrentPrincipal } from "./authentication.js";
|
|
6
6
|
import type { AfterCommitHook } from "./config.js";
|
|
7
7
|
import type { ReplicationKick } from "./replication.js";
|
|
8
|
+
import type { AgentChannelFactory } from "./agent/channel.js";
|
|
8
9
|
import { type ConnectionTransportFactory } from "./connections.js";
|
|
9
10
|
import { InvalidActionInput, RequestId } from "./errors.js";
|
|
10
11
|
import type { ApiPlan, PlannedAction, PlannedEntity, Traversal } from "./plan.js";
|
|
@@ -59,8 +60,11 @@ export interface Caller {
|
|
|
59
60
|
readonly permissions: ReadonlyArray<string>;
|
|
60
61
|
}
|
|
61
62
|
type Backend = OntologyBackend["Service"];
|
|
62
|
-
/**
|
|
63
|
-
|
|
63
|
+
/**
|
|
64
|
+
* The operations of a planned ontology, bound to its backend, after-commit hooks, connections and,
|
|
65
|
+
* on deployments with one, the workspace's agent.
|
|
66
|
+
*/
|
|
67
|
+
export declare function makeOperations(backend: Backend, plan: ApiPlan, hooks: ReadonlyMap<string, AfterCommitHook>, connections: ConnectionTransportFactory, replicate: ReplicationKick, agentChannel?: AgentChannelFactory): {
|
|
64
68
|
/** A page of records. Requires `read` on the entity and on every entity the query reaches. */
|
|
65
69
|
find: (planned: PlannedEntity, query: Query) => Effect.Effect<QueryResult<Readonly<Record<string, unknown>>>, PermissionDenied | import("../index.js").InvalidQuery, CurrentPrincipal>;
|
|
66
70
|
/** One record. Requires `read` on the entity. */
|
|
@@ -6,6 +6,7 @@ import { PermissionDenied } from "../errors.js";
|
|
|
6
6
|
import { toCamelCase } from "../names.js";
|
|
7
7
|
import { missingPermission, permissionOf, READ_VERB } from "../permissions.js";
|
|
8
8
|
import { CurrentPrincipal } from "./authentication.js";
|
|
9
|
+
import { AgentChannel } from "../agent-chat.js";
|
|
9
10
|
import { OIDC_TOKEN_HEADER } from "./connections.js";
|
|
10
11
|
import { InvalidActionInput, RequestId } from "./errors.js";
|
|
11
12
|
/**
|
|
@@ -145,12 +146,12 @@ function traverseOf(backend, targets, planned, traversal, id, query) {
|
|
|
145
146
|
return page(yield* backend.find({ entity: traversal.target, query: { ...query, where } }));
|
|
146
147
|
});
|
|
147
148
|
}
|
|
148
|
-
function runActionOf(backend, ontology, hooks, connections, replicate) {
|
|
149
|
+
function runActionOf(backend, ontology, hooks, connections, replicate, agentChannel) {
|
|
149
150
|
return (planned, { name, generated }, call) => Effect.gen(function* () {
|
|
150
151
|
const principal = yield* CurrentPrincipal;
|
|
151
152
|
const requestId = yield* RequestId;
|
|
152
153
|
const hook = hooks.get(`${planned.name}.${name}`);
|
|
153
|
-
const
|
|
154
|
+
const run = backend.runAction({
|
|
154
155
|
entity: planned.name,
|
|
155
156
|
action: name,
|
|
156
157
|
input: call.input,
|
|
@@ -175,6 +176,10 @@ function runActionOf(backend, ontology, hooks, connections, replicate) {
|
|
|
175
176
|
}
|
|
176
177
|
: {}),
|
|
177
178
|
});
|
|
179
|
+
// Built-in actions reach the workspace's agent through it, when the deployment has one.
|
|
180
|
+
const outcome = yield* agentChannel === undefined
|
|
181
|
+
? run
|
|
182
|
+
: run.pipe(Effect.provideService(AgentChannel, agentChannel({ identityToken: call.identityToken, store: backend.agents })));
|
|
178
183
|
const { issues } = outcome.validation;
|
|
179
184
|
if (!outcome.validation.ok && !call.validateOnly) {
|
|
180
185
|
return yield* new InvalidActionInput({ issues });
|
|
@@ -208,8 +213,11 @@ function callerOf(backend) {
|
|
|
208
213
|
};
|
|
209
214
|
});
|
|
210
215
|
}
|
|
211
|
-
/**
|
|
212
|
-
|
|
216
|
+
/**
|
|
217
|
+
* The operations of a planned ontology, bound to its backend, after-commit hooks, connections and,
|
|
218
|
+
* on deployments with one, the workspace's agent.
|
|
219
|
+
*/
|
|
220
|
+
export function makeOperations(backend, plan, hooks, connections, replicate, agentChannel) {
|
|
213
221
|
const targets = relationshipTargets(plan);
|
|
214
222
|
return {
|
|
215
223
|
/** A page of records. Requires `read` on the entity and on every entity the query reaches. */
|
|
@@ -221,7 +229,7 @@ export function makeOperations(backend, plan, hooks, connections, replicate) {
|
|
|
221
229
|
/** The records a relationship reaches from one record, as a page of the target entity. */
|
|
222
230
|
traverse: (planned, traversal, id, query) => traverseOf(backend, targets, planned, traversal, id, query),
|
|
223
231
|
/** Runs an action as the caller, with the action's after-commit hook and connections. */
|
|
224
|
-
runAction: runActionOf(backend, plan.ontology, hooks, connections, replicate),
|
|
232
|
+
runAction: runActionOf(backend, plan.ontology, hooks, connections, replicate, agentChannel),
|
|
225
233
|
/** The caller's user, roles and permissions. It needs no permission. */
|
|
226
234
|
caller: callerOf(backend),
|
|
227
235
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Context, Schema, type Effect, type Layer, type Redacted } from "effect";
|
|
2
|
+
import type { AttachmentRuntime } from "../storage/attachments.js";
|
|
2
3
|
import type { BlobRuntime } from "../storage/sessions.js";
|
|
3
4
|
import type { BlobStorageBinding } from "../storage/provider.js";
|
|
4
5
|
import type { AnyAction } from "../action.js";
|
|
@@ -180,6 +181,13 @@ export interface AgentMember {
|
|
|
180
181
|
readonly name: string;
|
|
181
182
|
readonly principal: Principal;
|
|
182
183
|
}
|
|
184
|
+
/** A group chat the agent saw, as `POST /api/ontology/agent/chats/seen` answers. */
|
|
185
|
+
export interface SeenChat {
|
|
186
|
+
readonly id: string;
|
|
187
|
+
readonly title: string;
|
|
188
|
+
readonly purpose: string | null;
|
|
189
|
+
readonly whenToUse: string | null;
|
|
190
|
+
}
|
|
183
191
|
/**
|
|
184
192
|
* Where the app's agent keeps pairing codes and the identities they create: the runtime's agent
|
|
185
193
|
* tables in the workspace database. Codes are stored only as SHA-256 hashes. Expiry is checked
|
|
@@ -218,9 +226,30 @@ export interface AgentStore {
|
|
|
218
226
|
readonly member: (userId: string) => Effect.Effect<AgentMember | undefined>;
|
|
219
227
|
/** The member a runtime caller acts for: active, and with a live identity. */
|
|
220
228
|
readonly actingMember: (userId: string) => Effect.Effect<Principal | undefined>;
|
|
229
|
+
/**
|
|
230
|
+
* Records a message the agent saw in a group chat: creates the `agent_chat` or keeps its title
|
|
231
|
+
* current, records when the chat last had a message (at most every 15 minutes, so that history
|
|
232
|
+
* keeps it without a row per message), links the writer to its members when they are an active
|
|
233
|
+
* member, and unlinks `leftMemberId`, a member who left, when linked. With `agentLeft`, the agent
|
|
234
|
+
* itself left the chat: every member is unlinked, and the chat stays. With `migratedFrom`, the
|
|
235
|
+
* chat had that id before it became a supergroup: its record moves to `externalId`, or, when both
|
|
236
|
+
* ids have one, the new record takes the old one's members, and its purpose and when to use where
|
|
237
|
+
* it has none. Concurrent calls for one chat leave one record.
|
|
238
|
+
*/
|
|
239
|
+
readonly seeChat: (input: {
|
|
240
|
+
readonly provider: string;
|
|
241
|
+
readonly externalId: string;
|
|
242
|
+
readonly title: string;
|
|
243
|
+
readonly memberId?: string;
|
|
244
|
+
readonly leftMemberId?: string;
|
|
245
|
+
readonly agentLeft?: true;
|
|
246
|
+
readonly migratedFrom?: string;
|
|
247
|
+
}) => Effect.Effect<SeenChat>;
|
|
221
248
|
}
|
|
222
249
|
declare const OntologyBackend_base: Context.ServiceClass<OntologyBackend, "@segmentstream-cloud/ontology/OntologyBackend", {
|
|
223
250
|
readonly blobs?: BlobRuntime;
|
|
251
|
+
/** The agent's temporary attachments, on deployments with file storage. */
|
|
252
|
+
readonly attachments?: AttachmentRuntime;
|
|
224
253
|
readonly maintainBlobs?: Effect.Effect<{
|
|
225
254
|
readonly removed: number;
|
|
226
255
|
}, unknown>;
|
package/dist/service/service.js
CHANGED
|
@@ -4,6 +4,8 @@ import { HttpApiBuilder } from "effect/unstable/httpapi";
|
|
|
4
4
|
import { storageMaintenanceAuthenticationLayer } from "../storage/maintenance-auth.js";
|
|
5
5
|
import { nameToWire } from "../authoring-names.js";
|
|
6
6
|
import { sqlServiceRuntime } from "../runtime/service-binding.js";
|
|
7
|
+
import { attachmentsLayer } from "./agent/attachments.js";
|
|
8
|
+
import { deployedAgentChannel } from "./agent/channel.js";
|
|
7
9
|
import { agentLayer } from "./agent/server.js";
|
|
8
10
|
import { makeAggregates } from "./aggregate.js";
|
|
9
11
|
import { platformAggregates } from "./aggregate-platform.js";
|
|
@@ -89,11 +91,15 @@ export function createOntologyService(ontology, config) {
|
|
|
89
91
|
? deployedConnections(config)
|
|
90
92
|
: localConnections(config.localConnections));
|
|
91
93
|
const replicate = deployedReplication(config);
|
|
94
|
+
// The agent's runtime identity is configured only on deployments that have an agent.
|
|
95
|
+
const agentChannel = config.runtimeIdentity === undefined || config.agentUrl === undefined
|
|
96
|
+
? undefined
|
|
97
|
+
: deployedAgentChannel(config.agentUrl);
|
|
92
98
|
const aggregates = makeAggregates({
|
|
93
99
|
manifest: ontology.manifest,
|
|
94
100
|
transport: platformAggregates({ ...config, manifestHash: ontology.manifest.hash }),
|
|
95
101
|
});
|
|
96
|
-
const routes = HttpApiBuilder.layer(built.api).pipe(Layer.provide(handlersLayer(built, hooks, connections, replicate, aggregates)), Layer.provide([
|
|
102
|
+
const routes = HttpApiBuilder.layer(built.api).pipe(Layer.provide(handlersLayer(built, hooks, connections, replicate, aggregates, agentChannel)), Layer.provide([
|
|
97
103
|
authenticationLayer(verifyToken, exchangeSession, verifyRuntime),
|
|
98
104
|
ErrorResponsesLive,
|
|
99
105
|
storageMaintenanceAuthenticationLayer(config.storageMaintenanceKey),
|
|
@@ -108,9 +114,19 @@ export function createOntologyService(ontology, config) {
|
|
|
108
114
|
verifyRuntime,
|
|
109
115
|
replicate,
|
|
110
116
|
aggregates,
|
|
117
|
+
agentChannel,
|
|
111
118
|
}), oauthLayer({ verifyToken }));
|
|
112
119
|
const agent = agentLayer({ verifyToken, verifyRuntime, exchangeSession });
|
|
113
|
-
const
|
|
120
|
+
const attachments = attachmentsLayer({
|
|
121
|
+
plan: built.plan,
|
|
122
|
+
hooks,
|
|
123
|
+
connections,
|
|
124
|
+
replicate,
|
|
125
|
+
agentChannel,
|
|
126
|
+
verifyToken,
|
|
127
|
+
verifyRuntime,
|
|
128
|
+
});
|
|
129
|
+
const application = Layer.mergeAll(routes, mcp, agent, attachments, DocsRoute, UnmatchedRoutes).pipe(Layer.provide(backend), Layer.provide(HttpServer.layerServices));
|
|
114
130
|
const web = HttpRouter.toWebHandler(application, { disableLogger: true });
|
|
115
131
|
const hash = ontology.manifest.hash;
|
|
116
132
|
return {
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import type { SqlClient } from "effect/unstable/sql";
|
|
3
|
+
import { ActionRejected, Conflict, NotFound, type PermissionDenied } from "../errors.js";
|
|
4
|
+
import type { Ontology } from "../ontology.js";
|
|
5
|
+
import type { Principal } from "../runtime.js";
|
|
6
|
+
import type { BlobProvider, TransferGrant } from "./provider.js";
|
|
7
|
+
/**
|
|
8
|
+
* The agent's temporary attachments: a file a member sends the app's agent is stored once, as a
|
|
9
|
+
* `_blobs` revision in the ontology's private file storage, and belongs to that member, the chat it
|
|
10
|
+
* arrived in and the agent session that first reads it. Nothing about it is an ontology record. It
|
|
11
|
+
* expires 24 hours after receipt; within that time the member's agent may read it, discard it, or
|
|
12
|
+
* promote it into a record through an action, which adopts the same stored object without copying
|
|
13
|
+
* it. Every operation re-derives the owner, chat and session from the caller, never from input.
|
|
14
|
+
*/
|
|
15
|
+
/** The largest file the agent takes: what Telegram's Bot API lets a bot download. */
|
|
16
|
+
export declare const MAX_ATTACHMENT_BYTES: number;
|
|
17
|
+
/** How long an attachment stays readable after it arrives; reads never extend it. */
|
|
18
|
+
export declare const ATTACHMENT_TTL_HOURS = 24;
|
|
19
|
+
/** Live attachments one member may hold at once, so a busy chat cannot fill the storage. */
|
|
20
|
+
export declare const MAX_LIVE_ATTACHMENTS = 50;
|
|
21
|
+
export declare const ATTACHMENT_SOURCES: readonly ["telegram"];
|
|
22
|
+
/** Who asks, as the agent's runtime knows it: the member, their chat and the agent session. */
|
|
23
|
+
export interface AttachmentCaller {
|
|
24
|
+
readonly principal: Principal;
|
|
25
|
+
/** The chat the attachment arrived in, such as `telegram:12345`. */
|
|
26
|
+
readonly conversation: string;
|
|
27
|
+
/**
|
|
28
|
+
* The agent session the request comes from. The first session that reads an attachment owns it;
|
|
29
|
+
* other sessions of the same chat, such as one after a reset, never see it.
|
|
30
|
+
*/
|
|
31
|
+
readonly sessionId?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ReceiveAttachment {
|
|
34
|
+
readonly source: (typeof ATTACHMENT_SOURCES)[number];
|
|
35
|
+
/** Identifies the file at its source, such as the message and attachment index; retries reuse it. */
|
|
36
|
+
readonly sourceRef: string;
|
|
37
|
+
readonly filename: string;
|
|
38
|
+
readonly contentType: string;
|
|
39
|
+
readonly size: number;
|
|
40
|
+
readonly sha256: string;
|
|
41
|
+
}
|
|
42
|
+
export interface AttachmentView {
|
|
43
|
+
readonly id: string;
|
|
44
|
+
readonly filename: string;
|
|
45
|
+
readonly contentType: string;
|
|
46
|
+
readonly size: number;
|
|
47
|
+
readonly sha256: string;
|
|
48
|
+
readonly state: string;
|
|
49
|
+
readonly createdAt: string;
|
|
50
|
+
readonly expiresAt: string;
|
|
51
|
+
readonly promotion: unknown;
|
|
52
|
+
}
|
|
53
|
+
export interface PromotionTarget {
|
|
54
|
+
/** The entity whose action adopts the file, such as `document`. */
|
|
55
|
+
readonly entity: string;
|
|
56
|
+
/** The action, such as `upload` or `create`. */
|
|
57
|
+
readonly action: string;
|
|
58
|
+
/** The action's input without the file; the field that takes the upload is filled in. */
|
|
59
|
+
readonly input: Readonly<Record<string, unknown>>;
|
|
60
|
+
/** The input field that takes the upload, when the action has more than one. */
|
|
61
|
+
readonly field?: string;
|
|
62
|
+
/** The record the action updates, for an action that sets the file on an existing record. */
|
|
63
|
+
readonly recordId?: string;
|
|
64
|
+
}
|
|
65
|
+
/** Runs the promotion's action as the caller, with the upload's options. */
|
|
66
|
+
export type PromotionRunner = (input: {
|
|
67
|
+
readonly entity: string;
|
|
68
|
+
readonly action: string;
|
|
69
|
+
readonly input: Readonly<Record<string, unknown>>;
|
|
70
|
+
readonly options: {
|
|
71
|
+
readonly idempotencyKey: string;
|
|
72
|
+
readonly createIntent?: string;
|
|
73
|
+
};
|
|
74
|
+
}) => Effect.Effect<{
|
|
75
|
+
readonly result: unknown;
|
|
76
|
+
readonly edits: ReadonlyArray<unknown>;
|
|
77
|
+
}, unknown>;
|
|
78
|
+
/** The fields of an action's input that take an upload, with the property each one writes. */
|
|
79
|
+
export declare function uploadFields(input: Schema.Top): ReadonlyArray<{
|
|
80
|
+
readonly field: string;
|
|
81
|
+
readonly entity: string;
|
|
82
|
+
readonly property: string;
|
|
83
|
+
}>;
|
|
84
|
+
/** Persisted phases surround every provider call; no external I/O holds a database transaction. */
|
|
85
|
+
export declare function attachmentRuntime(ontology: Ontology, sql: SqlClient.SqlClient, provider: BlobProvider["Service"]): {
|
|
86
|
+
receive: (input: ReceiveAttachment, caller: AttachmentCaller) => Effect.Effect<{
|
|
87
|
+
attachment: AttachmentView;
|
|
88
|
+
upload?: undefined;
|
|
89
|
+
} | {
|
|
90
|
+
attachment: AttachmentView;
|
|
91
|
+
upload: TransferGrant;
|
|
92
|
+
}, ActionRejected | PermissionDenied | Conflict | NotFound | import("effect/unstable/sql/SqlError").SqlError | import("./provider.js").BlobStorageError, never>;
|
|
93
|
+
complete: (id: string, caller: AttachmentCaller) => Effect.Effect<AttachmentView, ActionRejected | PermissionDenied | Conflict | NotFound | import("effect/unstable/sql/SqlError").SqlError | import("./provider.js").BlobStorageError, never>;
|
|
94
|
+
list: (caller: AttachmentCaller) => Effect.Effect<AttachmentView[], PermissionDenied | import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
95
|
+
read: (id: string, caller: AttachmentCaller) => Effect.Effect<{
|
|
96
|
+
attachment: AttachmentView;
|
|
97
|
+
download: TransferGrant;
|
|
98
|
+
}, PermissionDenied | Conflict | NotFound | import("effect/unstable/sql/SqlError").SqlError | import("./provider.js").BlobStorageError, never>;
|
|
99
|
+
discard: (id: string, caller: AttachmentCaller) => Effect.Effect<AttachmentView, PermissionDenied | Conflict | NotFound | import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
100
|
+
promote: (id: string, target: PromotionTarget, caller: AttachmentCaller, run: PromotionRunner) => Effect.Effect<{
|
|
101
|
+
attachment: AttachmentView;
|
|
102
|
+
deduplicated: boolean;
|
|
103
|
+
result: {} | null;
|
|
104
|
+
}, unknown, never>;
|
|
105
|
+
};
|
|
106
|
+
export type AttachmentRuntime = ReturnType<typeof attachmentRuntime>;
|
|
107
|
+
export type AttachmentFailure = ActionRejected | Conflict | NotFound | PermissionDenied;
|
|
108
|
+
/**
|
|
109
|
+
* Expires attachments past their deadline and removes the bytes of every expired or discarded one
|
|
110
|
+
* that no record adopted. A revision a promotion ticket still points at is left to the upload
|
|
111
|
+
* maintenance, once the ticket is canceled. Runs from the storage maintenance schedule, so files
|
|
112
|
+
* expire while the agent is idle.
|
|
113
|
+
*/
|
|
114
|
+
export declare function maintainAttachments(sql: SqlClient.SqlClient, provider: BlobProvider["Service"], limit?: number): Effect.Effect<{
|
|
115
|
+
expired: number;
|
|
116
|
+
removed: number;
|
|
117
|
+
}, import("effect/unstable/sql/SqlError").SqlError | import("./provider.js").BlobStorageError, never>;
|