@electric-ax/agents-server 0.4.6 → 0.4.7

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.
@@ -18,6 +18,7 @@ import {
18
18
  backfillEntityDispatchPolicy,
19
19
  linkEntityDispatchSubscription,
20
20
  resolveEffectiveDispatchPolicyForSpawn,
21
+ shouldLinkDispatchBeforeInitialMessage,
21
22
  unlinkEntityDispatchSubscription,
22
23
  } from './dispatch-policy.js'
23
24
  import { routeBody, withSchema } from './schema.js'
@@ -133,6 +134,22 @@ const setTagBodySchema = Type.Object({
133
134
  value: Type.String(),
134
135
  })
135
136
 
137
+ const entitySignalSchema = Type.Union([
138
+ Type.Literal(`SIGINT`),
139
+ Type.Literal(`SIGHUP`),
140
+ Type.Literal(`SIGTERM`),
141
+ Type.Literal(`SIGKILL`),
142
+ Type.Literal(`SIGSTOP`),
143
+ Type.Literal(`SIGCONT`),
144
+ Type.Literal(`SIGUSR`),
145
+ ])
146
+
147
+ const signalBodySchema = Type.Object({
148
+ signal: entitySignalSchema,
149
+ reason: Type.Optional(Type.String()),
150
+ payload: Type.Optional(Type.Unknown()),
151
+ })
152
+
136
153
  const scheduleBodySchema = Type.Union([
137
154
  Type.Object({
138
155
  scheduleType: Type.Literal(`cron`),
@@ -161,6 +178,7 @@ type SendBody = Static<typeof sendBodySchema>
161
178
  type InboxMessageBody = Static<typeof inboxMessageBodySchema>
162
179
  type ForkBody = Static<typeof forkBodySchema>
163
180
  type SetTagBody = Static<typeof setTagBodySchema>
181
+ type SignalBody = Static<typeof signalBodySchema>
164
182
  type ScheduleBody = Static<typeof scheduleBodySchema>
165
183
  type EntitiesRegisterBody = Static<typeof entitiesRegisterBodySchema>
166
184
 
@@ -187,6 +205,12 @@ entitiesRouter.put(
187
205
  entitiesRouter.get(`/:type/:instanceId`, withExistingEntity, getEntity)
188
206
  entitiesRouter.head(`/:type/:instanceId`, withExistingEntity, headEntity)
189
207
  entitiesRouter.delete(`/:type/:instanceId`, withExistingEntity, killEntity)
208
+ entitiesRouter.post(
209
+ `/:type/:instanceId/signal`,
210
+ withExistingEntity,
211
+ withSchema(signalBodySchema),
212
+ signalEntity
213
+ )
190
214
  entitiesRouter.post(
191
215
  `/:type/:instanceId/send`,
192
216
  withExistingEntity,
@@ -614,13 +638,21 @@ async function spawnEntity(
614
638
  wake: parsed.wake,
615
639
  created_by: principal.url,
616
640
  })
617
- await linkEntityDispatchSubscription(ctx, entity)
641
+ const linkBeforeInitialMessage =
642
+ parsed.initialMessage !== undefined &&
643
+ shouldLinkDispatchBeforeInitialMessage(dispatchPolicy)
644
+ if (linkBeforeInitialMessage) {
645
+ await linkEntityDispatchSubscription(ctx, entity)
646
+ }
618
647
  if (parsed.initialMessage !== undefined) {
619
648
  await ctx.entityManager.send(entity.url, {
620
649
  from: principal.url,
621
650
  payload: parsed.initialMessage,
622
651
  })
623
652
  }
653
+ if (!linkBeforeInitialMessage) {
654
+ await linkEntityDispatchSubscription(ctx, entity)
655
+ }
624
656
 
625
657
  return json(
626
658
  { ...toPublicEntity(entity), txid: entity.txid },
@@ -655,3 +687,27 @@ async function killEntity(
655
687
  ctx.runtime.claimWriteTokens.clearStream(ctx.service, entity.streams.main)
656
688
  return json(result)
657
689
  }
690
+
691
+ async function signalEntity(
692
+ request: AgentsRouteRequest,
693
+ ctx: TenantContext
694
+ ): Promise<Response> {
695
+ const principalMutationError = rejectPrincipalEntityMutation(
696
+ request,
697
+ `signaled`
698
+ )
699
+ if (principalMutationError) return principalMutationError
700
+
701
+ const parsed = routeBody<SignalBody>(request)
702
+ const { entityUrl, entity } = requireExistingEntityRoute(request)
703
+ const result = await ctx.entityManager.signal(entityUrl, {
704
+ signal: parsed.signal,
705
+ reason: parsed.reason,
706
+ payload: parsed.payload,
707
+ })
708
+ if (result.new_state === `stopped` || result.new_state === `killed`) {
709
+ await unlinkEntityDispatchSubscription(ctx, entity)
710
+ ctx.runtime.claimWriteTokens.clearStream(ctx.service, entity.streams.main)
711
+ }
712
+ return json(result)
713
+ }
@@ -479,7 +479,7 @@ async function webhookForward(
479
479
  enrichPromise,
480
480
  ])
481
481
 
482
- if (entity?.status === `stopped`) {
482
+ if (entity?.status === `stopped` || entity?.status === `paused`) {
483
483
  if (upsertPromise) await upsertPromise
484
484
  return json({ done: true })
485
485
  }
@@ -764,10 +764,13 @@ async function callbackForward(
764
764
  // the token is intact even though entityDispatchState may diverge.
765
765
  // If both are false, a newer wake owns the entity — leave status as-is.
766
766
  if (entity && (entityCleared || stillOwnsClaim)) {
767
- await ctx.entityManager.registry.updateStatus(entity.url, `idle`)
767
+ await ctx.entityManager.registry.updateStatus(
768
+ entity.url,
769
+ entity.status === `stopping` ? `stopped` : `idle`
770
+ )
768
771
  await ctx.entityBridgeManager.onEntityChanged(entity.url)
769
772
  serverLog.info(
770
- `[callback-forward] status updated to idle for ${entity.url}`
773
+ `[callback-forward] status updated after done for ${entity.url}`
771
774
  )
772
775
  } else if (!entity) {
773
776
  serverLog.warn(
@@ -575,7 +575,7 @@ async function notificationFromClaim(
575
575
  404
576
576
  )
577
577
  }
578
- if (entity.status === `stopped`) {
578
+ if (entity.status === `stopped` || entity.status === `paused`) {
579
579
  await ctx.streamClient.releaseSubscription(
580
580
  input.subscriptionId,
581
581
  input.claim.token,
package/src/runtime.ts CHANGED
@@ -533,7 +533,11 @@ export class ElectricAgentsTenantRuntime {
533
533
  return
534
534
  }
535
535
 
536
- await this.manager.registry.updateStatus(entityUrl, `idle`)
536
+ const entity = await this.manager.registry.getEntity(entityUrl)
537
+ await this.manager.registry.updateStatus(
538
+ entityUrl,
539
+ entity?.status === `stopping` ? `stopped` : `idle`
540
+ )
537
541
  await this.entityBridgeManager.onEntityChanged(entityUrl)
538
542
  }
539
543
  }