@electric-ax/agents-server 0.4.16 → 0.4.17
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/dist/entrypoint.js +55 -4
- package/dist/index.cjs +54 -3
- package/dist/index.d.cts +258 -236
- package/dist/index.d.ts +258 -236
- package/dist/index.js +55 -4
- package/drizzle/0014_entity_type_slash_commands.sql +1 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +5 -5
- package/src/db/schema.ts +1 -0
- package/src/electric-agents-types.ts +4 -0
- package/src/entity-manager.ts +46 -1
- package/src/entity-registry.ts +7 -0
- package/src/routing/entities-router.ts +3 -0
- package/src/routing/entity-types-router.ts +23 -0
- package/src/utils/server-utils.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import postgres from "postgres";
|
|
|
8
8
|
import { and, desc, eq, inArray, lt, ne, sql } from "drizzle-orm";
|
|
9
9
|
import { bigint, bigserial, boolean, check, index, integer, jsonb, pgTable, primaryKey, serial, text, timestamp, unique } from "drizzle-orm/pg-core";
|
|
10
10
|
import { createHash, createPrivateKey, createPublicKey, generateKeyPairSync, randomUUID, sign } from "node:crypto";
|
|
11
|
-
import { appendPathToUrl, assertTags, buildEventSourceManifestEntry, buildTagsIndex, entityStateSchema, eventSourceSubscriptionManifestKey, getCronStreamPath, getCronStreamPathFromSpec, getEntitiesStreamPath, getNextCronFireAt, getSharedStateStreamPath, getWebhookStreamPath, hashString, manifestChildKey, manifestSharedStateKey, manifestSourceKey, normalizeTags, parseCronStreamPath, resolveCronScheduleSpec, resolveEventSourceSubscription, sourceRefForTags, verifyWebhookSignature } from "@electric-ax/agents-runtime";
|
|
11
|
+
import { COMPOSER_INPUT_MESSAGE_TYPE, appendPathToUrl, assertTags, buildEventSourceManifestEntry, buildTagsIndex, entityStateSchema, eventSourceSubscriptionManifestKey, getCronStreamPath, getCronStreamPathFromSpec, getEntitiesStreamPath, getNextCronFireAt, getSharedStateStreamPath, getWebhookStreamPath, hashString, manifestChildKey, manifestSharedStateKey, manifestSourceKey, normalizeTags, parseCronStreamPath, resolveCronScheduleSpec, resolveEventSourceSubscription, sourceRefForTags, validateComposerInputPayload, validateSlashCommandDefinitions, verifyWebhookSignature } from "@electric-ax/agents-runtime";
|
|
12
12
|
import { DurableStream, DurableStreamError, FetchError, IdempotentProducer } from "@durable-streams/client";
|
|
13
13
|
import { ShapeStream, isChangeMessage, isControlMessage } from "@electric-sql/client";
|
|
14
14
|
import pino from "pino";
|
|
@@ -49,6 +49,7 @@ const entityTypes = pgTable(`entity_types`, {
|
|
|
49
49
|
creationSchema: jsonb(`creation_schema`),
|
|
50
50
|
inboxSchemas: jsonb(`inbox_schemas`),
|
|
51
51
|
stateSchemas: jsonb(`state_schemas`),
|
|
52
|
+
slashCommands: jsonb(`slash_commands`),
|
|
52
53
|
serveEndpoint: text(`serve_endpoint`),
|
|
53
54
|
defaultDispatchPolicy: jsonb(`default_dispatch_policy`),
|
|
54
55
|
revision: integer(`revision`).notNull().default(1),
|
|
@@ -820,6 +821,7 @@ var PostgresRegistry = class {
|
|
|
820
821
|
creationSchema: et.creation_schema ?? null,
|
|
821
822
|
inboxSchemas: et.inbox_schemas ?? null,
|
|
822
823
|
stateSchemas: et.state_schemas ?? null,
|
|
824
|
+
slashCommands: et.slash_commands ?? null,
|
|
823
825
|
serveEndpoint: et.serve_endpoint ?? null,
|
|
824
826
|
defaultDispatchPolicy: et.default_dispatch_policy ?? null,
|
|
825
827
|
revision: et.revision,
|
|
@@ -832,6 +834,7 @@ var PostgresRegistry = class {
|
|
|
832
834
|
creationSchema: et.creation_schema ?? null,
|
|
833
835
|
inboxSchemas: et.inbox_schemas ?? null,
|
|
834
836
|
stateSchemas: et.state_schemas ?? null,
|
|
837
|
+
slashCommands: et.slash_commands ?? null,
|
|
835
838
|
serveEndpoint: et.serve_endpoint ?? null,
|
|
836
839
|
defaultDispatchPolicy: et.default_dispatch_policy ?? null,
|
|
837
840
|
revision: et.revision,
|
|
@@ -849,6 +852,7 @@ var PostgresRegistry = class {
|
|
|
849
852
|
creationSchema: et.creation_schema ?? null,
|
|
850
853
|
inboxSchemas: et.inbox_schemas ?? null,
|
|
851
854
|
stateSchemas: et.state_schemas ?? null,
|
|
855
|
+
slashCommands: et.slash_commands ?? null,
|
|
852
856
|
serveEndpoint: et.serve_endpoint ?? null,
|
|
853
857
|
defaultDispatchPolicy: et.default_dispatch_policy ?? null,
|
|
854
858
|
revision: et.revision,
|
|
@@ -875,6 +879,7 @@ var PostgresRegistry = class {
|
|
|
875
879
|
creationSchema: et.creation_schema ?? null,
|
|
876
880
|
inboxSchemas: et.inbox_schemas ?? null,
|
|
877
881
|
stateSchemas: et.state_schemas ?? null,
|
|
882
|
+
slashCommands: et.slash_commands ?? null,
|
|
878
883
|
serveEndpoint: et.serve_endpoint ?? null,
|
|
879
884
|
defaultDispatchPolicy: et.default_dispatch_policy ?? null,
|
|
880
885
|
revision: et.revision,
|
|
@@ -1465,6 +1470,7 @@ var PostgresRegistry = class {
|
|
|
1465
1470
|
creation_schema: row.creationSchema,
|
|
1466
1471
|
inbox_schemas: row.inboxSchemas,
|
|
1467
1472
|
state_schemas: row.stateSchemas,
|
|
1473
|
+
slash_commands: row.slashCommands ?? void 0,
|
|
1468
1474
|
serve_endpoint: row.serveEndpoint ?? void 0,
|
|
1469
1475
|
default_dispatch_policy: row.defaultDispatchPolicy ?? void 0,
|
|
1470
1476
|
revision: row.revision,
|
|
@@ -3358,6 +3364,7 @@ var EntityManager = class {
|
|
|
3358
3364
|
this.validateSchema(req.creation_schema);
|
|
3359
3365
|
this.validateSchemaMap(req.inbox_schemas);
|
|
3360
3366
|
this.validateSchemaMap(req.state_schemas);
|
|
3367
|
+
this.validateSlashCommands(req.slash_commands);
|
|
3361
3368
|
const defaultDispatchPolicy = req.default_dispatch_policy ? this.validateDispatchPolicy(req.default_dispatch_policy, { label: `default_dispatch_policy` }) : void 0;
|
|
3362
3369
|
const existing = await this.registry.getEntityType(req.name);
|
|
3363
3370
|
const now = new Date().toISOString();
|
|
@@ -3367,6 +3374,7 @@ var EntityManager = class {
|
|
|
3367
3374
|
creation_schema: req.creation_schema,
|
|
3368
3375
|
inbox_schemas: req.inbox_schemas,
|
|
3369
3376
|
state_schemas: req.state_schemas,
|
|
3377
|
+
slash_commands: req.slash_commands,
|
|
3370
3378
|
serve_endpoint: req.serve_endpoint,
|
|
3371
3379
|
default_dispatch_policy: defaultDispatchPolicy,
|
|
3372
3380
|
revision: existing ? existing.revision + 1 : 1,
|
|
@@ -3528,6 +3536,18 @@ var EntityManager = class {
|
|
|
3528
3536
|
}
|
|
3529
3537
|
});
|
|
3530
3538
|
const initialEvents = [createdEvent];
|
|
3539
|
+
const slashCommandTimestamp = new Date().toISOString();
|
|
3540
|
+
for (const command of entityType.slash_commands ?? []) {
|
|
3541
|
+
const slashCommandEvent = entityStateSchema.slashCommands.insert({
|
|
3542
|
+
key: command.name,
|
|
3543
|
+
value: {
|
|
3544
|
+
...command,
|
|
3545
|
+
source: `static`,
|
|
3546
|
+
updated_at: slashCommandTimestamp
|
|
3547
|
+
}
|
|
3548
|
+
});
|
|
3549
|
+
initialEvents.push(slashCommandEvent);
|
|
3550
|
+
}
|
|
3531
3551
|
if (req.initialMessage !== void 0) {
|
|
3532
3552
|
const msgNow = new Date().toISOString();
|
|
3533
3553
|
const inboxEvent = entityStateSchema.inbox.insert({
|
|
@@ -3535,6 +3555,7 @@ var EntityManager = class {
|
|
|
3535
3555
|
value: {
|
|
3536
3556
|
from: req.created_by ?? req.parent ?? `spawn`,
|
|
3537
3557
|
payload: req.initialMessage,
|
|
3558
|
+
message_type: req.initialMessageType,
|
|
3538
3559
|
timestamp: msgNow
|
|
3539
3560
|
}
|
|
3540
3561
|
});
|
|
@@ -4980,7 +5001,9 @@ var EntityManager = class {
|
|
|
4980
5001
|
creation_schema: existing.creation_schema,
|
|
4981
5002
|
inbox_schemas: mergedInbox,
|
|
4982
5003
|
state_schemas: mergedState,
|
|
5004
|
+
slash_commands: existing.slash_commands,
|
|
4983
5005
|
serve_endpoint: existing.serve_endpoint,
|
|
5006
|
+
default_dispatch_policy: existing.default_dispatch_policy,
|
|
4984
5007
|
revision: nextRevision,
|
|
4985
5008
|
created_at: existing.created_at,
|
|
4986
5009
|
updated_at: now
|
|
@@ -5034,11 +5057,19 @@ var EntityManager = class {
|
|
|
5034
5057
|
throw new ElectricAgentsError(ErrCodeInvalidRequest, error instanceof Error ? error.message : `Invalid tags`, 400);
|
|
5035
5058
|
}
|
|
5036
5059
|
}
|
|
5060
|
+
validateSlashCommands(input) {
|
|
5061
|
+
const validationError = validateSlashCommandDefinitions(input);
|
|
5062
|
+
if (!validationError) return;
|
|
5063
|
+
throw new ElectricAgentsError(ErrCodeSchemaValidationFailed, validationError.message, 422, validationError.details);
|
|
5064
|
+
}
|
|
5037
5065
|
async validateSendRequest(entityUrl, req) {
|
|
5038
5066
|
const entity = await this.registry.getEntity(entityUrl);
|
|
5039
5067
|
if (!entity) throw new ElectricAgentsError(ErrCodeNotFound, `Entity not found`, 404);
|
|
5040
5068
|
if (rejectsNormalWrites(entity.status)) throw new ElectricAgentsError(ErrCodeNotRunning, `Entity is not accepting writes`, 409);
|
|
5041
|
-
if (req.type
|
|
5069
|
+
if (req.type === COMPOSER_INPUT_MESSAGE_TYPE) {
|
|
5070
|
+
const valErr = validateComposerInputPayload(req.payload);
|
|
5071
|
+
if (valErr) throw new ElectricAgentsError(ErrCodeSchemaValidationFailed, valErr.message, 422, valErr.details);
|
|
5072
|
+
} else if (req.type && entity.type) {
|
|
5042
5073
|
const { inboxSchemas } = await this.getEffectiveSchemas(entity);
|
|
5043
5074
|
if (inboxSchemas) {
|
|
5044
5075
|
const schema = inboxSchemas[req.type];
|
|
@@ -7111,7 +7142,7 @@ function buildElectricProxyTarget(options) {
|
|
|
7111
7142
|
permissionBypass: options.permissionBypass
|
|
7112
7143
|
}));
|
|
7113
7144
|
} else if (table === `entity_types`) {
|
|
7114
|
-
target.searchParams.set(`columns`, `"tenant_id","name","description","creation_schema","inbox_schemas","state_schemas","serve_endpoint","default_dispatch_policy","revision","created_at","updated_at"`);
|
|
7145
|
+
target.searchParams.set(`columns`, `"tenant_id","name","description","creation_schema","inbox_schemas","state_schemas","slash_commands","serve_endpoint","default_dispatch_policy","revision","created_at","updated_at"`);
|
|
7115
7146
|
applyShapeWhere(target, buildSpawnableEntityTypesWhere({
|
|
7116
7147
|
tenantId: options.tenantId,
|
|
7117
7148
|
principalUrl: options.principalUrl ?? ``,
|
|
@@ -7919,6 +7950,7 @@ const spawnBodySchema = Type.Object({
|
|
|
7919
7950
|
sandbox: Type.Optional(sandboxChoiceSchema),
|
|
7920
7951
|
initialMessage: Type.Optional(Type.Unknown()),
|
|
7921
7952
|
grants: Type.Optional(Type.Array(entityPermissionGrantInputSchema)),
|
|
7953
|
+
initialMessageType: Type.Optional(Type.String()),
|
|
7922
7954
|
wake: Type.Optional(Type.Object({
|
|
7923
7955
|
subscriberUrl: Type.String(),
|
|
7924
7956
|
condition: wakeConditionSchema,
|
|
@@ -8474,6 +8506,7 @@ async function spawnEntity(request, ctx) {
|
|
|
8474
8506
|
dispatch_policy: dispatchPolicy,
|
|
8475
8507
|
sandbox: parsed.sandbox,
|
|
8476
8508
|
initialMessage: void 0,
|
|
8509
|
+
initialMessageType: void 0,
|
|
8477
8510
|
wake: parsed.wake,
|
|
8478
8511
|
created_by: principal.url
|
|
8479
8512
|
});
|
|
@@ -8492,7 +8525,8 @@ async function spawnEntity(request, ctx) {
|
|
|
8492
8525
|
if (linkBeforeInitialMessage) await linkEntityDispatchSubscription(ctx, entity);
|
|
8493
8526
|
if (parsed.initialMessage !== void 0) await ctx.entityManager.send(entity.url, {
|
|
8494
8527
|
from: principal.url,
|
|
8495
|
-
payload: parsed.initialMessage
|
|
8528
|
+
payload: parsed.initialMessage,
|
|
8529
|
+
type: parsed.initialMessageType
|
|
8496
8530
|
});
|
|
8497
8531
|
if (!linkBeforeInitialMessage) await linkEntityDispatchSubscription(ctx, entity);
|
|
8498
8532
|
return json({
|
|
@@ -8539,6 +8573,21 @@ async function signalEntity(request, ctx) {
|
|
|
8539
8573
|
//#region src/routing/entity-types-router.ts
|
|
8540
8574
|
const jsonObjectSchema = Type.Record(Type.String(), Type.Unknown());
|
|
8541
8575
|
const schemaMapSchema = Type.Record(Type.String(), jsonObjectSchema);
|
|
8576
|
+
const slashCommandArgumentSchema = Type.Object({
|
|
8577
|
+
name: Type.String(),
|
|
8578
|
+
type: Type.Union([
|
|
8579
|
+
Type.Literal(`string`),
|
|
8580
|
+
Type.Literal(`number`),
|
|
8581
|
+
Type.Literal(`boolean`)
|
|
8582
|
+
]),
|
|
8583
|
+
required: Type.Optional(Type.Boolean()),
|
|
8584
|
+
description: Type.Optional(Type.String())
|
|
8585
|
+
}, { additionalProperties: false });
|
|
8586
|
+
const slashCommandSchema = Type.Object({
|
|
8587
|
+
name: Type.String(),
|
|
8588
|
+
description: Type.Optional(Type.String()),
|
|
8589
|
+
arguments: Type.Optional(Type.Array(slashCommandArgumentSchema))
|
|
8590
|
+
}, { additionalProperties: false });
|
|
8542
8591
|
const typePermissionGrantInputSchema = Type.Object({
|
|
8543
8592
|
subject_kind: Type.Union([Type.Literal(`principal`), Type.Literal(`principal_kind`)]),
|
|
8544
8593
|
subject_value: Type.String(),
|
|
@@ -8551,6 +8600,7 @@ const registerEntityTypeBodySchema = Type.Object({
|
|
|
8551
8600
|
creation_schema: Type.Optional(jsonObjectSchema),
|
|
8552
8601
|
inbox_schemas: Type.Optional(schemaMapSchema),
|
|
8553
8602
|
state_schemas: Type.Optional(schemaMapSchema),
|
|
8603
|
+
slash_commands: Type.Optional(Type.Array(slashCommandSchema)),
|
|
8554
8604
|
serve_endpoint: Type.Optional(Type.String()),
|
|
8555
8605
|
default_dispatch_policy: Type.Optional(dispatchPolicySchema),
|
|
8556
8606
|
permission_grants: Type.Optional(Type.Array(typePermissionGrantInputSchema))
|
|
@@ -8692,6 +8742,7 @@ function normalizeEntityTypeRequest(parsed) {
|
|
|
8692
8742
|
creation_schema: parsed.creation_schema,
|
|
8693
8743
|
inbox_schemas: parsed.inbox_schemas,
|
|
8694
8744
|
state_schemas: parsed.state_schemas,
|
|
8745
|
+
slash_commands: parsed.slash_commands,
|
|
8695
8746
|
serve_endpoint: serveEndpoint,
|
|
8696
8747
|
default_dispatch_policy: parsed.default_dispatch_policy ?? (serveEndpoint ? { targets: [{
|
|
8697
8748
|
type: `webhook`,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "entity_types" ADD COLUMN "slash_commands" jsonb;
|
|
@@ -99,6 +99,13 @@
|
|
|
99
99
|
"when": 1780588695000,
|
|
100
100
|
"tag": "0013_worker_user_manage_permission",
|
|
101
101
|
"breakpoints": true
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"idx": 14,
|
|
105
|
+
"version": "7",
|
|
106
|
+
"when": 1780600000000,
|
|
107
|
+
"tag": "0014_entity_type_slash_commands",
|
|
108
|
+
"breakpoints": true
|
|
102
109
|
}
|
|
103
110
|
]
|
|
104
111
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@electric-ax/agents-server",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.17",
|
|
4
4
|
"description": "Electric Agents entity runtime server",
|
|
5
5
|
"author": "Durable Stream contributors",
|
|
6
6
|
"bin": {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"pino-pretty": "^13.0.0",
|
|
55
55
|
"postgres": "^3.4.0",
|
|
56
56
|
"undici": "^7.24.7",
|
|
57
|
-
"@electric-ax/agents-runtime": "0.3.
|
|
57
|
+
"@electric-ax/agents-runtime": "0.3.10"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "^22.19.15",
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"tsx": "^4.19.0",
|
|
66
66
|
"typescript": "^5.0.0",
|
|
67
67
|
"vitest": "^4.1.0",
|
|
68
|
-
"@electric-ax/agents": "0.4.
|
|
69
|
-
"@electric-ax/agents-server-
|
|
70
|
-
"@electric-ax/agents-server-
|
|
68
|
+
"@electric-ax/agents": "0.4.14",
|
|
69
|
+
"@electric-ax/agents-server-ui": "0.4.17",
|
|
70
|
+
"@electric-ax/agents-server-conformance-tests": "0.1.11"
|
|
71
71
|
},
|
|
72
72
|
"files": [
|
|
73
73
|
"dist",
|
package/src/db/schema.ts
CHANGED
|
@@ -24,6 +24,7 @@ export const entityTypes = pgTable(
|
|
|
24
24
|
creationSchema: jsonb(`creation_schema`),
|
|
25
25
|
inboxSchemas: jsonb(`inbox_schemas`),
|
|
26
26
|
stateSchemas: jsonb(`state_schemas`),
|
|
27
|
+
slashCommands: jsonb(`slash_commands`),
|
|
27
28
|
serveEndpoint: text(`serve_endpoint`),
|
|
28
29
|
defaultDispatchPolicy: jsonb(`default_dispatch_policy`),
|
|
29
30
|
revision: integer(`revision`).notNull().default(1),
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
6
|
PullWakeRunnerHealth,
|
|
7
|
+
SlashCommandDefinition,
|
|
7
8
|
WebhookNotification,
|
|
8
9
|
} from '@electric-ax/agents-runtime'
|
|
9
10
|
import type { Principal } from './principal.js'
|
|
@@ -499,6 +500,7 @@ export interface ElectricAgentsEntityType {
|
|
|
499
500
|
creation_schema?: Record<string, unknown>
|
|
500
501
|
inbox_schemas?: Record<string, Record<string, unknown>>
|
|
501
502
|
state_schemas?: Record<string, Record<string, unknown>>
|
|
503
|
+
slash_commands?: Array<SlashCommandDefinition>
|
|
502
504
|
serve_endpoint?: string
|
|
503
505
|
default_dispatch_policy?: DispatchPolicy
|
|
504
506
|
revision: number
|
|
@@ -512,6 +514,7 @@ export interface RegisterEntityTypeRequest {
|
|
|
512
514
|
creation_schema?: Record<string, unknown>
|
|
513
515
|
inbox_schemas?: Record<string, Record<string, unknown>>
|
|
514
516
|
state_schemas?: Record<string, Record<string, unknown>>
|
|
517
|
+
slash_commands?: Array<SlashCommandDefinition>
|
|
515
518
|
serve_endpoint?: string
|
|
516
519
|
default_dispatch_policy?: DispatchPolicy
|
|
517
520
|
permission_grants?: Array<EntityTypePermissionGrantInput>
|
|
@@ -530,6 +533,7 @@ export interface TypedSpawnRequest {
|
|
|
530
533
|
*/
|
|
531
534
|
sandbox?: SandboxChoice
|
|
532
535
|
initialMessage?: unknown
|
|
536
|
+
initialMessageType?: string
|
|
533
537
|
created_by?: string
|
|
534
538
|
wake?: {
|
|
535
539
|
subscriberUrl: string
|
package/src/entity-manager.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto'
|
|
2
2
|
import fastq from 'fastq'
|
|
3
3
|
import {
|
|
4
|
+
COMPOSER_INPUT_MESSAGE_TYPE,
|
|
4
5
|
assertTags,
|
|
5
6
|
entityStateSchema,
|
|
6
7
|
getCronStreamPath,
|
|
@@ -11,6 +12,8 @@ import {
|
|
|
11
12
|
manifestSharedStateKey,
|
|
12
13
|
manifestSourceKey,
|
|
13
14
|
resolveCronScheduleSpec,
|
|
15
|
+
validateComposerInputPayload,
|
|
16
|
+
validateSlashCommandDefinitions,
|
|
14
17
|
} from '@electric-ax/agents-runtime'
|
|
15
18
|
import type { EventPointer } from '@electric-ax/agents-runtime'
|
|
16
19
|
import {
|
|
@@ -430,6 +433,7 @@ export class EntityManager {
|
|
|
430
433
|
this.validateSchema(req.creation_schema)
|
|
431
434
|
this.validateSchemaMap(req.inbox_schemas)
|
|
432
435
|
this.validateSchemaMap(req.state_schemas)
|
|
436
|
+
this.validateSlashCommands(req.slash_commands)
|
|
433
437
|
const defaultDispatchPolicy = req.default_dispatch_policy
|
|
434
438
|
? this.validateDispatchPolicy(req.default_dispatch_policy, {
|
|
435
439
|
label: `default_dispatch_policy`,
|
|
@@ -444,6 +448,7 @@ export class EntityManager {
|
|
|
444
448
|
creation_schema: req.creation_schema,
|
|
445
449
|
inbox_schemas: req.inbox_schemas,
|
|
446
450
|
state_schemas: req.state_schemas,
|
|
451
|
+
slash_commands: req.slash_commands,
|
|
447
452
|
serve_endpoint: req.serve_endpoint,
|
|
448
453
|
default_dispatch_policy: defaultDispatchPolicy,
|
|
449
454
|
revision: existing ? existing.revision + 1 : 1,
|
|
@@ -735,6 +740,19 @@ export class EntityManager {
|
|
|
735
740
|
createdEvent as Record<string, unknown>,
|
|
736
741
|
]
|
|
737
742
|
|
|
743
|
+
const slashCommandTimestamp = new Date().toISOString()
|
|
744
|
+
for (const command of entityType.slash_commands ?? []) {
|
|
745
|
+
const slashCommandEvent = entityStateSchema.slashCommands.insert({
|
|
746
|
+
key: command.name,
|
|
747
|
+
value: {
|
|
748
|
+
...command,
|
|
749
|
+
source: `static`,
|
|
750
|
+
updated_at: slashCommandTimestamp,
|
|
751
|
+
},
|
|
752
|
+
} as any)
|
|
753
|
+
initialEvents.push(slashCommandEvent as Record<string, unknown>)
|
|
754
|
+
}
|
|
755
|
+
|
|
738
756
|
if (req.initialMessage !== undefined) {
|
|
739
757
|
const msgNow = new Date().toISOString()
|
|
740
758
|
const inboxEvent = entityStateSchema.inbox.insert({
|
|
@@ -742,6 +760,7 @@ export class EntityManager {
|
|
|
742
760
|
value: {
|
|
743
761
|
from: req.created_by ?? req.parent ?? `spawn`,
|
|
744
762
|
payload: req.initialMessage,
|
|
763
|
+
message_type: req.initialMessageType,
|
|
745
764
|
timestamp: msgNow,
|
|
746
765
|
},
|
|
747
766
|
} as any)
|
|
@@ -3512,7 +3531,9 @@ export class EntityManager {
|
|
|
3512
3531
|
creation_schema: existing.creation_schema,
|
|
3513
3532
|
inbox_schemas: mergedInbox,
|
|
3514
3533
|
state_schemas: mergedState,
|
|
3534
|
+
slash_commands: existing.slash_commands,
|
|
3515
3535
|
serve_endpoint: existing.serve_endpoint,
|
|
3536
|
+
default_dispatch_policy: existing.default_dispatch_policy,
|
|
3516
3537
|
revision: nextRevision,
|
|
3517
3538
|
created_at: existing.created_at,
|
|
3518
3539
|
updated_at: now,
|
|
@@ -3601,6 +3622,20 @@ export class EntityManager {
|
|
|
3601
3622
|
}
|
|
3602
3623
|
}
|
|
3603
3624
|
|
|
3625
|
+
private validateSlashCommands(input: unknown): void {
|
|
3626
|
+
const validationError = validateSlashCommandDefinitions(input)
|
|
3627
|
+
if (!validationError) {
|
|
3628
|
+
return
|
|
3629
|
+
}
|
|
3630
|
+
|
|
3631
|
+
throw new ElectricAgentsError(
|
|
3632
|
+
ErrCodeSchemaValidationFailed,
|
|
3633
|
+
validationError.message,
|
|
3634
|
+
422,
|
|
3635
|
+
validationError.details
|
|
3636
|
+
)
|
|
3637
|
+
}
|
|
3638
|
+
|
|
3604
3639
|
private async validateSendRequest(
|
|
3605
3640
|
entityUrl: string,
|
|
3606
3641
|
req: SendRequest
|
|
@@ -3617,7 +3652,17 @@ export class EntityManager {
|
|
|
3617
3652
|
)
|
|
3618
3653
|
}
|
|
3619
3654
|
|
|
3620
|
-
if (req.type
|
|
3655
|
+
if (req.type === COMPOSER_INPUT_MESSAGE_TYPE) {
|
|
3656
|
+
const valErr = validateComposerInputPayload(req.payload)
|
|
3657
|
+
if (valErr) {
|
|
3658
|
+
throw new ElectricAgentsError(
|
|
3659
|
+
ErrCodeSchemaValidationFailed,
|
|
3660
|
+
valErr.message,
|
|
3661
|
+
422,
|
|
3662
|
+
valErr.details
|
|
3663
|
+
)
|
|
3664
|
+
}
|
|
3665
|
+
} else if (req.type && entity.type) {
|
|
3621
3666
|
const { inboxSchemas } = await this.getEffectiveSchemas(entity)
|
|
3622
3667
|
if (inboxSchemas) {
|
|
3623
3668
|
const schema = inboxSchemas[req.type]
|
package/src/entity-registry.ts
CHANGED
|
@@ -633,6 +633,7 @@ export class PostgresRegistry {
|
|
|
633
633
|
creationSchema: et.creation_schema ?? null,
|
|
634
634
|
inboxSchemas: et.inbox_schemas ?? null,
|
|
635
635
|
stateSchemas: et.state_schemas ?? null,
|
|
636
|
+
slashCommands: et.slash_commands ?? null,
|
|
636
637
|
serveEndpoint: et.serve_endpoint ?? null,
|
|
637
638
|
defaultDispatchPolicy: et.default_dispatch_policy ?? null,
|
|
638
639
|
revision: et.revision,
|
|
@@ -646,6 +647,7 @@ export class PostgresRegistry {
|
|
|
646
647
|
creationSchema: et.creation_schema ?? null,
|
|
647
648
|
inboxSchemas: et.inbox_schemas ?? null,
|
|
648
649
|
stateSchemas: et.state_schemas ?? null,
|
|
650
|
+
slashCommands: et.slash_commands ?? null,
|
|
649
651
|
serveEndpoint: et.serve_endpoint ?? null,
|
|
650
652
|
defaultDispatchPolicy: et.default_dispatch_policy ?? null,
|
|
651
653
|
revision: et.revision,
|
|
@@ -668,6 +670,7 @@ export class PostgresRegistry {
|
|
|
668
670
|
creationSchema: et.creation_schema ?? null,
|
|
669
671
|
inboxSchemas: et.inbox_schemas ?? null,
|
|
670
672
|
stateSchemas: et.state_schemas ?? null,
|
|
673
|
+
slashCommands: et.slash_commands ?? null,
|
|
671
674
|
serveEndpoint: et.serve_endpoint ?? null,
|
|
672
675
|
defaultDispatchPolicy: et.default_dispatch_policy ?? null,
|
|
673
676
|
revision: et.revision,
|
|
@@ -709,6 +712,7 @@ export class PostgresRegistry {
|
|
|
709
712
|
creationSchema: et.creation_schema ?? null,
|
|
710
713
|
inboxSchemas: et.inbox_schemas ?? null,
|
|
711
714
|
stateSchemas: et.state_schemas ?? null,
|
|
715
|
+
slashCommands: et.slash_commands ?? null,
|
|
712
716
|
serveEndpoint: et.serve_endpoint ?? null,
|
|
713
717
|
defaultDispatchPolicy: et.default_dispatch_policy ?? null,
|
|
714
718
|
revision: et.revision,
|
|
@@ -1829,6 +1833,9 @@ export class PostgresRegistry {
|
|
|
1829
1833
|
state_schemas: row.stateSchemas as
|
|
1830
1834
|
| Record<string, Record<string, unknown>>
|
|
1831
1835
|
| undefined,
|
|
1836
|
+
slash_commands:
|
|
1837
|
+
(row.slashCommands as ElectricAgentsEntityType[`slash_commands`]) ??
|
|
1838
|
+
undefined,
|
|
1832
1839
|
serve_endpoint: row.serveEndpoint ?? undefined,
|
|
1833
1840
|
default_dispatch_policy:
|
|
1834
1841
|
(row.defaultDispatchPolicy as ElectricAgentsEntityType[`default_dispatch_policy`]) ??
|
|
@@ -135,6 +135,7 @@ const spawnBodySchema = Type.Object({
|
|
|
135
135
|
sandbox: Type.Optional(sandboxChoiceSchema),
|
|
136
136
|
initialMessage: Type.Optional(Type.Unknown()),
|
|
137
137
|
grants: Type.Optional(Type.Array(entityPermissionGrantInputSchema)),
|
|
138
|
+
initialMessageType: Type.Optional(Type.String()),
|
|
138
139
|
wake: Type.Optional(
|
|
139
140
|
Type.Object({
|
|
140
141
|
subscriberUrl: Type.String(),
|
|
@@ -1293,6 +1294,7 @@ async function spawnEntity(
|
|
|
1293
1294
|
dispatch_policy: dispatchPolicy,
|
|
1294
1295
|
sandbox: parsed.sandbox,
|
|
1295
1296
|
initialMessage: undefined,
|
|
1297
|
+
initialMessageType: undefined,
|
|
1296
1298
|
wake: parsed.wake,
|
|
1297
1299
|
created_by: principal.url,
|
|
1298
1300
|
})
|
|
@@ -1325,6 +1327,7 @@ async function spawnEntity(
|
|
|
1325
1327
|
await ctx.entityManager.send(entity.url, {
|
|
1326
1328
|
from: principal.url,
|
|
1327
1329
|
payload: parsed.initialMessage,
|
|
1330
|
+
type: parsed.initialMessageType,
|
|
1328
1331
|
})
|
|
1329
1332
|
}
|
|
1330
1333
|
if (!linkBeforeInitialMessage) {
|
|
@@ -45,6 +45,27 @@ type PublicEntityTypeResponse = ElectricAgentsEntityType & {
|
|
|
45
45
|
|
|
46
46
|
const jsonObjectSchema = Type.Record(Type.String(), Type.Unknown())
|
|
47
47
|
const schemaMapSchema = Type.Record(Type.String(), jsonObjectSchema)
|
|
48
|
+
const slashCommandArgumentSchema = Type.Object(
|
|
49
|
+
{
|
|
50
|
+
name: Type.String(),
|
|
51
|
+
type: Type.Union([
|
|
52
|
+
Type.Literal(`string`),
|
|
53
|
+
Type.Literal(`number`),
|
|
54
|
+
Type.Literal(`boolean`),
|
|
55
|
+
]),
|
|
56
|
+
required: Type.Optional(Type.Boolean()),
|
|
57
|
+
description: Type.Optional(Type.String()),
|
|
58
|
+
},
|
|
59
|
+
{ additionalProperties: false }
|
|
60
|
+
)
|
|
61
|
+
const slashCommandSchema = Type.Object(
|
|
62
|
+
{
|
|
63
|
+
name: Type.String(),
|
|
64
|
+
description: Type.Optional(Type.String()),
|
|
65
|
+
arguments: Type.Optional(Type.Array(slashCommandArgumentSchema)),
|
|
66
|
+
},
|
|
67
|
+
{ additionalProperties: false }
|
|
68
|
+
)
|
|
48
69
|
|
|
49
70
|
const typePermissionGrantInputSchema = Type.Object(
|
|
50
71
|
{
|
|
@@ -66,6 +87,7 @@ const registerEntityTypeBodySchema = Type.Object(
|
|
|
66
87
|
creation_schema: Type.Optional(jsonObjectSchema),
|
|
67
88
|
inbox_schemas: Type.Optional(schemaMapSchema),
|
|
68
89
|
state_schemas: Type.Optional(schemaMapSchema),
|
|
90
|
+
slash_commands: Type.Optional(Type.Array(slashCommandSchema)),
|
|
69
91
|
serve_endpoint: Type.Optional(Type.String()),
|
|
70
92
|
default_dispatch_policy: Type.Optional(dispatchPolicySchema),
|
|
71
93
|
permission_grants: Type.Optional(
|
|
@@ -433,6 +455,7 @@ function normalizeEntityTypeRequest(
|
|
|
433
455
|
creation_schema: parsed.creation_schema,
|
|
434
456
|
inbox_schemas: parsed.inbox_schemas,
|
|
435
457
|
state_schemas: parsed.state_schemas,
|
|
458
|
+
slash_commands: parsed.slash_commands,
|
|
436
459
|
serve_endpoint: serveEndpoint,
|
|
437
460
|
default_dispatch_policy:
|
|
438
461
|
parsed.default_dispatch_policy ??
|
|
@@ -135,7 +135,7 @@ export function buildElectricProxyTarget(options: {
|
|
|
135
135
|
} else if (table === `entity_types`) {
|
|
136
136
|
target.searchParams.set(
|
|
137
137
|
`columns`,
|
|
138
|
-
`"tenant_id","name","description","creation_schema","inbox_schemas","state_schemas","serve_endpoint","default_dispatch_policy","revision","created_at","updated_at"`
|
|
138
|
+
`"tenant_id","name","description","creation_schema","inbox_schemas","state_schemas","slash_commands","serve_endpoint","default_dispatch_policy","revision","created_at","updated_at"`
|
|
139
139
|
)
|
|
140
140
|
applyShapeWhere(
|
|
141
141
|
target,
|