@looopy-ai/core 1.1.0 → 1.1.1

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.
@@ -26,8 +26,6 @@ export declare class Agent {
26
26
  private readonly config;
27
27
  private _state;
28
28
  private logger;
29
- private sigtermHandler?;
30
- private sigintHandler?;
31
29
  private shuttingDown;
32
30
  private shutdownComplete;
33
31
  constructor(config: AgentConfig);
@@ -48,6 +46,4 @@ export declare class Agent {
48
46
  private loadPersistedState;
49
47
  private loadMessages;
50
48
  private checkAndCompact;
51
- private registerSignalHandlers;
52
- private removeSignalHandlers;
53
49
  }
@@ -2,15 +2,12 @@ import { catchError, concat, Observable, of, tap } from 'rxjs';
2
2
  import { createTaskStatusEvent } from '../events';
3
3
  import { addMessagesCompactedEvent, addMessagesLoadedEvent, completeAgentInitializeSpan, completeAgentTurnSpan, failAgentInitializeSpan, failAgentTurnSpan, setResumeAttributes, setTurnCountAttribute, startAgentInitializeSpan, startAgentTurnSpan, } from '../observability/spans';
4
4
  import { serializeError } from '../utils/error';
5
- import { registerSignalListener, unregisterSignalListener } from '../utils/process-signals';
6
5
  import { getLogger } from './logger';
7
6
  import { runLoop } from './loop';
8
7
  export class Agent {
9
8
  config;
10
9
  _state;
11
10
  logger;
12
- sigtermHandler;
13
- sigintHandler;
14
11
  shuttingDown = false;
15
12
  shutdownComplete = false;
16
13
  constructor(config) {
@@ -31,7 +28,6 @@ export class Agent {
31
28
  };
32
29
  this.logger.debug('Agent created');
33
30
  this.persistStateSafely();
34
- this.registerSignalHandlers();
35
31
  }
36
32
  get state() {
37
33
  return { ...this._state };
@@ -286,7 +282,6 @@ export class Agent {
286
282
  this._state.status = 'shutdown';
287
283
  this._state.lastActivity = new Date();
288
284
  await this.persistState();
289
- this.removeSignalHandlers();
290
285
  this.shutdownComplete = true;
291
286
  this.config.logger.info('Agent shutdown complete');
292
287
  }
@@ -371,46 +366,4 @@ export class Agent {
371
366
  });
372
367
  }
373
368
  }
374
- registerSignalHandlers() {
375
- if (typeof process === 'undefined') {
376
- return;
377
- }
378
- if (this.sigtermHandler || typeof process === 'undefined') {
379
- return;
380
- }
381
- if (!this.sigtermHandler) {
382
- this.sigtermHandler = async () => {
383
- this.logger.info('Received SIGTERM signal. Initiating graceful shutdown.');
384
- try {
385
- await this.shutdown();
386
- }
387
- catch (error) {
388
- this.logger.error({ error }, 'Failed to shutdown agent after SIGTERM signal');
389
- }
390
- };
391
- registerSignalListener('SIGTERM', this.sigtermHandler);
392
- }
393
- if (!this.sigintHandler) {
394
- const sigintHandler = async () => {
395
- this.logger.info('Received SIGINT signal. Initiating graceful shutdown.');
396
- try {
397
- await this.shutdown();
398
- }
399
- catch (error) {
400
- this.logger.error({ error }, 'Failed to shutdown agent after SIGINT signal');
401
- }
402
- };
403
- registerSignalListener('SIGINT', sigintHandler);
404
- }
405
- }
406
- removeSignalHandlers() {
407
- if (this.sigtermHandler) {
408
- unregisterSignalListener('SIGTERM', this.sigtermHandler);
409
- this.sigtermHandler = undefined;
410
- }
411
- if (this.sigintHandler) {
412
- unregisterSignalListener('SIGINT', this.sigintHandler);
413
- this.sigintHandler = undefined;
414
- }
415
- }
416
369
  }
package/dist/index.d.ts CHANGED
@@ -6,3 +6,4 @@ export * from './server';
6
6
  export * from './stores';
7
7
  export * from './tools';
8
8
  export * from './types';
9
+ export * from './utils';
package/dist/index.js CHANGED
@@ -6,3 +6,4 @@ export * from './server';
6
6
  export * from './stores';
7
7
  export * from './tools';
8
8
  export * from './types';
9
+ export * from './utils';
@@ -1,3 +1,4 @@
1
1
  export { type BufferedEvent, EventBuffer, type EventBufferConfig, } from './event-buffer';
2
2
  export { type EventFilter, EventRouter, type Subscriber, type SubscriptionConfig, } from './event-router';
3
+ export * from './shutdown';
3
4
  export { SSEConnection, type SSEConnectionConfig, type SSEResponse, SSEServer, type SSEServerConfig, } from './sse';
@@ -1,3 +1,4 @@
1
1
  export { EventBuffer, } from './event-buffer';
2
2
  export { EventRouter, } from './event-router';
3
+ export * from './shutdown';
3
4
  export { SSEConnection, SSEServer, } from './sse';
@@ -0,0 +1,7 @@
1
+ export declare class ShutdownManager {
2
+ private watchers;
3
+ constructor();
4
+ registerWatcher(handleShutdown: () => Promise<void>, order?: number): void;
5
+ initiateShutdown(): Promise<void>;
6
+ private signalHandler;
7
+ }
@@ -0,0 +1,21 @@
1
+ import { getLogger } from '../core';
2
+ export class ShutdownManager {
3
+ watchers = [];
4
+ constructor() {
5
+ process.on('SIGINT', this.signalHandler.bind(this));
6
+ process.on('SIGTERM', this.signalHandler.bind(this));
7
+ }
8
+ registerWatcher(handleShutdown, order = 100) {
9
+ this.watchers.push({ handleShutdown, order });
10
+ this.watchers.sort((a, b) => a.order - b.order);
11
+ }
12
+ async initiateShutdown() {
13
+ for (const watcher of this.watchers) {
14
+ await watcher.handleShutdown();
15
+ }
16
+ }
17
+ signalHandler(signal) {
18
+ getLogger({ component: 'shutdown-manager' }).info({ signal, watchers: this.watchers.length }, 'Received shutdown signal');
19
+ this.initiateShutdown();
20
+ }
21
+ }
@@ -0,0 +1 @@
1
+ export * from './error';
@@ -0,0 +1 @@
1
+ export * from './error';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@looopy-ai/core",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "RxJS-based AI agent framework",
5
5
  "keywords": [
6
6
  "agent",
@@ -1,3 +0,0 @@
1
- export type SignalListener = () => void | Promise<void>;
2
- export declare function registerSignalListener(signal: NodeJS.Signals, listener: SignalListener): void;
3
- export declare function unregisterSignalListener(signal: NodeJS.Signals, listener: SignalListener): void;
@@ -1,67 +0,0 @@
1
- import { getLogger } from '../core/logger';
2
- import { serializeError } from './error';
3
- const listeners = new Map();
4
- const processHandlers = new Map();
5
- const signalLogger = getLogger({ component: 'process-signal-coordinator' });
6
- function isProcessAvailable() {
7
- return typeof process !== 'undefined' && typeof process.on === 'function';
8
- }
9
- export function registerSignalListener(signal, listener) {
10
- if (!isProcessAvailable()) {
11
- return;
12
- }
13
- const signalListeners = listeners.get(signal) ?? new Set();
14
- signalListeners.add(listener);
15
- listeners.set(signal, signalListeners);
16
- ensureProcessHandler(signal);
17
- }
18
- export function unregisterSignalListener(signal, listener) {
19
- if (!isProcessAvailable()) {
20
- return;
21
- }
22
- const signalListeners = listeners.get(signal);
23
- if (!signalListeners) {
24
- return;
25
- }
26
- signalListeners.delete(listener);
27
- if (signalListeners.size === 0) {
28
- listeners.delete(signal);
29
- removeProcessHandler(signal);
30
- }
31
- }
32
- function ensureProcessHandler(signal) {
33
- if (processHandlers.has(signal)) {
34
- return;
35
- }
36
- const handler = () => {
37
- const signalListeners = listeners.get(signal);
38
- if (!signalListeners || signalListeners.size === 0) {
39
- return;
40
- }
41
- signalLogger.info({ signal, listenerCount: signalListeners.size }, 'Received process signal; notifying registered listeners');
42
- const listenersSnapshot = Array.from(signalListeners);
43
- void Promise.allSettled(listenersSnapshot.map(async (listener) => {
44
- try {
45
- await listener();
46
- }
47
- catch (error) {
48
- signalLogger.error({ signal, error: serializeError(error) }, 'Signal listener failed');
49
- }
50
- }));
51
- };
52
- process.on(signal, handler);
53
- processHandlers.set(signal, handler);
54
- }
55
- function removeProcessHandler(signal) {
56
- const handler = processHandlers.get(signal);
57
- if (!handler) {
58
- return;
59
- }
60
- if (typeof process.off === 'function') {
61
- process.off(signal, handler);
62
- }
63
- else if (typeof process.removeListener === 'function') {
64
- process.removeListener(signal, handler);
65
- }
66
- processHandlers.delete(signal);
67
- }