@@ -13,6 +13,7 @@ import { nanoid } from "nanoid";
13
13
14
14
import { AsyncLocalStorage } from "node:async_hooks" ;
15
15
import { MCPClientManager } from "./mcp/client" ;
16
+ import { genericObservability , type Observability } from "./observability" ;
16
17
17
18
export type { Connection , WSMessage , ConnectionContext } from "partyserver" ;
18
19
@@ -264,6 +265,11 @@ export class Agent<Env, State = unknown> extends Server<Env> {
264
265
hibernate : true , // default to hibernate
265
266
} ;
266
267
268
+ /**
269
+ * The observability implementation to use for the Agent
270
+ */
271
+ observability ?: Observability = genericObservability ;
272
+
267
273
/**
268
274
* Execute SQL queries against the Agent's database
269
275
* @template T Type of the returned rows
@@ -369,6 +375,35 @@ export class Agent<Env, State = unknown> extends Server<Env> {
369
375
370
376
// For regular methods, execute and send response
371
377
const result = await methodFn . apply ( this , args ) ;
378
+
379
+ const displayArgs = args . map ( a => {
380
+ if ( typeof a === "object" ) {
381
+ if ( Array . isArray ( a ) ) {
382
+ return "[...]"
383
+ }
384
+
385
+ return "{...}" ;
386
+ }
387
+
388
+ return String ( a ) ;
389
+ } ) ;
390
+
391
+ this . observability ?. emit (
392
+ {
393
+ id : nanoid ( ) ,
394
+ type : "rpc" ,
395
+ displayMessage : `RPC call to ${ method } args: ${ displayArgs . join ( ", " ) } ` ,
396
+ timestamp : Date . now ( ) ,
397
+ payload : {
398
+ method,
399
+ args,
400
+ success : true ,
401
+ streaming : metadata ?. streaming ,
402
+ } ,
403
+ } ,
404
+ this . ctx
405
+ ) ;
406
+
372
407
const response : RPCResponse = {
373
408
type : "rpc" ,
374
409
id,
@@ -413,6 +448,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
413
448
} )
414
449
) ;
415
450
}
451
+
452
+ this . observability ?. emit (
453
+ {
454
+ id : nanoid ( ) ,
455
+ type : "connect" ,
456
+ displayMessage : `Connection ${ connection . id } established` ,
457
+ timestamp : Date . now ( ) ,
458
+ payload : {
459
+ connectionId : connection . id ,
460
+ } ,
461
+ } ,
462
+ this . ctx
463
+ ) ;
416
464
return this . #tryCatch( ( ) => _onConnect ( connection , ctx ) ) ;
417
465
} , 20 ) ;
418
466
}
@@ -421,6 +469,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
421
469
}
422
470
423
471
#setStateInternal( state : State , source : Connection | "server" = "server" ) {
472
+ const previousState = this . #state;
424
473
this . #state = state ;
425
474
this . sql `
426
475
INSERT OR REPLACE INTO cf_agents_state (id, state)
@@ -442,6 +491,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
442
491
return agentContext . run (
443
492
{ agent : this , connection, request } ,
444
493
async ( ) => {
494
+ this . observability ?. emit (
495
+ {
496
+ id : nanoid ( ) ,
497
+ type : "state:update" ,
498
+ displayMessage : "State updated" ,
499
+ timestamp : Date . now ( ) ,
500
+ payload : {
501
+ state,
502
+ previousState,
503
+ } ,
504
+ } ,
505
+ this . ctx
506
+ ) ;
445
507
return this . onStateUpdate ( state , source ) ;
446
508
}
447
509
) ;
@@ -535,6 +597,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
535
597
) : Promise < Schedule < T > > {
536
598
const id = nanoid ( 9 ) ;
537
599
600
+ const emitScheduleCreate = ( schedule : Schedule < T > ) =>
601
+ this . observability ?. emit (
602
+ {
603
+ id : nanoid ( ) ,
604
+ type : "schedule:create" ,
605
+ displayMessage : `Schedule ${ schedule . id } created` ,
606
+ timestamp : Date . now ( ) ,
607
+ payload : schedule ,
608
+ } ,
609
+ this . ctx
610
+ ) ;
611
+
538
612
if ( typeof callback !== "string" ) {
539
613
throw new Error ( "Callback must be a string" ) ;
540
614
}
@@ -554,13 +628,17 @@ export class Agent<Env, State = unknown> extends Server<Env> {
554
628
555
629
await this . #scheduleNextAlarm( ) ;
556
630
557
- return {
631
+ const schedule : Schedule < T > = {
558
632
id,
559
633
callback : callback ,
560
634
payload : payload as T ,
561
635
time : timestamp ,
562
636
type : "scheduled" ,
563
637
} ;
638
+
639
+ emitScheduleCreate ( schedule ) ;
640
+
641
+ return schedule ;
564
642
}
565
643
if ( typeof when === "number" ) {
566
644
const time = new Date ( Date . now ( ) + when * 1000 ) ;
@@ -575,14 +653,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
575
653
576
654
await this . #scheduleNextAlarm( ) ;
577
655
578
- return {
656
+ const schedule : Schedule < T > = {
579
657
id,
580
658
callback : callback ,
581
659
payload : payload as T ,
582
660
delayInSeconds : when ,
583
661
time : timestamp ,
584
662
type : "delayed" ,
585
663
} ;
664
+
665
+ emitScheduleCreate ( schedule ) ;
666
+
667
+ return schedule ;
586
668
}
587
669
if ( typeof when === "string" ) {
588
670
const nextExecutionTime = getNextCronTime ( when ) ;
@@ -597,14 +679,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
597
679
598
680
await this . #scheduleNextAlarm( ) ;
599
681
600
- return {
682
+ const schedule : Schedule < T > = {
601
683
id,
602
684
callback : callback ,
603
685
payload : payload as T ,
604
686
cron : when ,
605
687
time : timestamp ,
606
688
type : "cron" ,
607
689
} ;
690
+
691
+ emitScheduleCreate ( schedule ) ;
692
+
693
+ return schedule ;
608
694
}
609
695
throw new Error ( "Invalid schedule type" ) ;
610
696
}
@@ -680,6 +766,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
680
766
* @returns true if the task was cancelled, false otherwise
681
767
*/
682
768
async cancelSchedule ( id : string ) : Promise < boolean > {
769
+ const schedule = await this . getSchedule ( id ) ;
770
+ if ( schedule ) {
771
+ this . observability ?. emit (
772
+ {
773
+ id : nanoid ( ) ,
774
+ type : "schedule:delete" ,
775
+ displayMessage : `Schedule ${ id } deleted` ,
776
+ timestamp : Date . now ( ) ,
777
+ payload : schedule ,
778
+ } ,
779
+ this . ctx
780
+ ) ;
781
+ }
683
782
this . sql `DELETE FROM cf_agents_schedules WHERE id = ${ id } ` ;
684
783
685
784
await this . #scheduleNextAlarm( ) ;
@@ -724,6 +823,17 @@ export class Agent<Env, State = unknown> extends Server<Env> {
724
823
{ agent : this , connection : undefined , request : undefined } ,
725
824
async ( ) => {
726
825
try {
826
+ this . observability ?. emit (
827
+ {
828
+ id : nanoid ( ) ,
829
+ type : "schedule:execute" ,
830
+ displayMessage : `Schedule ${ row . id } executed` ,
831
+ timestamp : Date . now ( ) ,
832
+ payload : row ,
833
+ } ,
834
+ this . ctx
835
+ ) ;
836
+
727
837
await (
728
838
callback as (
729
839
payload : unknown ,
@@ -766,6 +876,17 @@ export class Agent<Env, State = unknown> extends Server<Env> {
766
876
// delete all alarms
767
877
await this . ctx . storage . deleteAlarm ( ) ;
768
878
await this . ctx . storage . deleteAll ( ) ;
879
+
880
+ this . observability ?. emit (
881
+ {
882
+ id : nanoid ( ) ,
883
+ type : "destroy" ,
884
+ displayMessage : "Agent destroyed" ,
885
+ timestamp : Date . now ( ) ,
886
+ payload : { } ,
887
+ } ,
888
+ this . ctx
889
+ ) ;
769
890
}
770
891
771
892
/**
0 commit comments