@clawcrony/claw-crony 1.0.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.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +82 -0
  3. package/dist/index.d.ts +17 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +720 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/src/agent-card.d.ts +4 -0
  8. package/dist/src/agent-card.d.ts.map +1 -0
  9. package/dist/src/agent-card.js +61 -0
  10. package/dist/src/agent-card.js.map +1 -0
  11. package/dist/src/audit.d.ts +36 -0
  12. package/dist/src/audit.d.ts.map +1 -0
  13. package/dist/src/audit.js +88 -0
  14. package/dist/src/audit.js.map +1 -0
  15. package/dist/src/client.d.ts +53 -0
  16. package/dist/src/client.d.ts.map +1 -0
  17. package/dist/src/client.js +322 -0
  18. package/dist/src/client.js.map +1 -0
  19. package/dist/src/executor.d.ts +34 -0
  20. package/dist/src/executor.d.ts.map +1 -0
  21. package/dist/src/executor.js +994 -0
  22. package/dist/src/executor.js.map +1 -0
  23. package/dist/src/file-security.d.ts +63 -0
  24. package/dist/src/file-security.d.ts.map +1 -0
  25. package/dist/src/file-security.js +350 -0
  26. package/dist/src/file-security.js.map +1 -0
  27. package/dist/src/hub-match.d.ts +73 -0
  28. package/dist/src/hub-match.d.ts.map +1 -0
  29. package/dist/src/hub-match.js +120 -0
  30. package/dist/src/hub-match.js.map +1 -0
  31. package/dist/src/hub-registration.d.ts +24 -0
  32. package/dist/src/hub-registration.d.ts.map +1 -0
  33. package/dist/src/hub-registration.js +242 -0
  34. package/dist/src/hub-registration.js.map +1 -0
  35. package/dist/src/internal/envelope.d.ts +33 -0
  36. package/dist/src/internal/envelope.d.ts.map +1 -0
  37. package/dist/src/internal/envelope.js +152 -0
  38. package/dist/src/internal/envelope.js.map +1 -0
  39. package/dist/src/internal/idempotency.d.ts +48 -0
  40. package/dist/src/internal/idempotency.d.ts.map +1 -0
  41. package/dist/src/internal/idempotency.js +82 -0
  42. package/dist/src/internal/idempotency.js.map +1 -0
  43. package/dist/src/internal/metrics.d.ts +38 -0
  44. package/dist/src/internal/metrics.d.ts.map +1 -0
  45. package/dist/src/internal/metrics.js +83 -0
  46. package/dist/src/internal/metrics.js.map +1 -0
  47. package/dist/src/internal/outbox.d.ts +49 -0
  48. package/dist/src/internal/outbox.d.ts.map +1 -0
  49. package/dist/src/internal/outbox.js +149 -0
  50. package/dist/src/internal/outbox.js.map +1 -0
  51. package/dist/src/internal/routing.d.ts +28 -0
  52. package/dist/src/internal/routing.d.ts.map +1 -0
  53. package/dist/src/internal/routing.js +57 -0
  54. package/dist/src/internal/routing.js.map +1 -0
  55. package/dist/src/internal/security.d.ts +53 -0
  56. package/dist/src/internal/security.d.ts.map +1 -0
  57. package/dist/src/internal/security.js +122 -0
  58. package/dist/src/internal/security.js.map +1 -0
  59. package/dist/src/internal/transport.d.ts +49 -0
  60. package/dist/src/internal/transport.d.ts.map +1 -0
  61. package/dist/src/internal/transport.js +207 -0
  62. package/dist/src/internal/transport.js.map +1 -0
  63. package/dist/src/internal/types-internal.d.ts +95 -0
  64. package/dist/src/internal/types-internal.d.ts.map +1 -0
  65. package/dist/src/internal/types-internal.js +9 -0
  66. package/dist/src/internal/types-internal.js.map +1 -0
  67. package/dist/src/peer-health.d.ts +47 -0
  68. package/dist/src/peer-health.d.ts.map +1 -0
  69. package/dist/src/peer-health.js +169 -0
  70. package/dist/src/peer-health.js.map +1 -0
  71. package/dist/src/peer-retry.d.ts +16 -0
  72. package/dist/src/peer-retry.d.ts.map +1 -0
  73. package/dist/src/peer-retry.js +75 -0
  74. package/dist/src/peer-retry.js.map +1 -0
  75. package/dist/src/queueing-executor.d.ts +23 -0
  76. package/dist/src/queueing-executor.d.ts.map +1 -0
  77. package/dist/src/queueing-executor.js +179 -0
  78. package/dist/src/queueing-executor.js.map +1 -0
  79. package/dist/src/routing-rules.d.ts +53 -0
  80. package/dist/src/routing-rules.d.ts.map +1 -0
  81. package/dist/src/routing-rules.js +130 -0
  82. package/dist/src/routing-rules.js.map +1 -0
  83. package/dist/src/task-cleanup.d.ts +21 -0
  84. package/dist/src/task-cleanup.d.ts.map +1 -0
  85. package/dist/src/task-cleanup.js +77 -0
  86. package/dist/src/task-cleanup.js.map +1 -0
  87. package/dist/src/task-store.d.ts +16 -0
  88. package/dist/src/task-store.d.ts.map +1 -0
  89. package/dist/src/task-store.js +80 -0
  90. package/dist/src/task-store.js.map +1 -0
  91. package/dist/src/telemetry.d.ts +88 -0
  92. package/dist/src/telemetry.d.ts.map +1 -0
  93. package/dist/src/telemetry.js +235 -0
  94. package/dist/src/telemetry.js.map +1 -0
  95. package/dist/src/transport-fallback.d.ts +29 -0
  96. package/dist/src/transport-fallback.d.ts.map +1 -0
  97. package/dist/src/transport-fallback.js +81 -0
  98. package/dist/src/transport-fallback.js.map +1 -0
  99. package/dist/src/types.d.ts +160 -0
  100. package/dist/src/types.d.ts.map +1 -0
  101. package/dist/src/types.js +7 -0
  102. package/dist/src/types.js.map +1 -0
  103. package/openclaw.plugin.json +272 -0
  104. package/package.json +56 -0
  105. package/skill/SKILL.md +230 -0
  106. package/skill/references/tools-md-template.md +57 -0
  107. package/skill/scripts/a2a-send.mjs +357 -0
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Manages per-peer health checks and circuit breaker state.
3
+ *
4
+ * Health checks periodically probe each peer's Agent Card endpoint.
5
+ * The circuit breaker follows the standard three-state pattern:
6
+ * closed → open → half-open → closed
7
+ */
8
+ export class PeerHealthManager {
9
+ states = new Map();
10
+ peers;
11
+ healthConfig;
12
+ cbConfig;
13
+ probe;
14
+ log;
15
+ timer = null;
16
+ halfOpenInFlight = new Set();
17
+ /** Cached skills per peer, refreshed on each successful health check probe. */
18
+ peerSkills = new Map();
19
+ constructor(peers, healthConfig, cbConfig, probe, log) {
20
+ this.peers = peers;
21
+ this.healthConfig = healthConfig;
22
+ this.cbConfig = cbConfig;
23
+ this.probe = probe;
24
+ this.log = log;
25
+ // Initialize state for each peer
26
+ for (const peer of peers) {
27
+ this.states.set(peer.name, {
28
+ health: "unknown",
29
+ circuit: "closed",
30
+ consecutiveFailures: 0,
31
+ lastFailureAt: null,
32
+ lastCheckAt: null,
33
+ });
34
+ }
35
+ }
36
+ /** Start periodic health checks. */
37
+ start() {
38
+ if (!this.healthConfig.enabled || this.peers.length === 0)
39
+ return;
40
+ this.log("info", "peer.health.start", {
41
+ peers: this.peers.map((p) => p.name),
42
+ interval_ms: this.healthConfig.intervalMs,
43
+ });
44
+ // Run immediately, then on interval
45
+ this.runHealthChecks();
46
+ this.timer = setInterval(() => this.runHealthChecks(), this.healthConfig.intervalMs);
47
+ }
48
+ /** Stop periodic health checks. */
49
+ stop() {
50
+ if (this.timer) {
51
+ clearInterval(this.timer);
52
+ this.timer = null;
53
+ }
54
+ }
55
+ /** Check if a peer is available for requests (circuit is not open). */
56
+ isAvailable(peerName) {
57
+ const state = this.states.get(peerName);
58
+ if (!state)
59
+ return true; // Unknown peer → allow (fail at send)
60
+ if (state.circuit === "closed")
61
+ return true;
62
+ if (state.circuit === "open") {
63
+ // Check if cooldown has elapsed → transition to half-open
64
+ if (state.lastFailureAt &&
65
+ Date.now() - state.lastFailureAt >= this.cbConfig.resetTimeoutMs) {
66
+ state.circuit = "half-open";
67
+ this.halfOpenInFlight.add(peerName); // Only one probe allowed
68
+ this.log("info", "peer.circuit.half-open", { peer: peerName });
69
+ return true;
70
+ }
71
+ return false;
72
+ }
73
+ // half-open: allow one request at a time
74
+ if (this.halfOpenInFlight.has(peerName))
75
+ return false;
76
+ this.halfOpenInFlight.add(peerName);
77
+ return true;
78
+ }
79
+ /** Record a successful call to a peer. */
80
+ recordSuccess(peerName) {
81
+ const state = this.states.get(peerName);
82
+ if (!state)
83
+ return;
84
+ const prevCircuit = state.circuit;
85
+ state.consecutiveFailures = 0;
86
+ state.health = "healthy";
87
+ this.halfOpenInFlight.delete(peerName);
88
+ if (state.circuit !== "closed") {
89
+ state.circuit = "closed";
90
+ this.log("info", "peer.circuit.closed", {
91
+ peer: peerName,
92
+ previous: prevCircuit,
93
+ });
94
+ }
95
+ }
96
+ /** Record a failed call to a peer. May trigger circuit open. */
97
+ recordFailure(peerName) {
98
+ const state = this.states.get(peerName);
99
+ if (!state)
100
+ return;
101
+ state.consecutiveFailures += 1;
102
+ state.lastFailureAt = Date.now();
103
+ this.halfOpenInFlight.delete(peerName);
104
+ // half-open failure → back to open
105
+ if (state.circuit === "half-open") {
106
+ state.circuit = "open";
107
+ this.log("warn", "peer.circuit.open", {
108
+ peer: peerName,
109
+ reason: "half-open probe failed",
110
+ consecutive_failures: state.consecutiveFailures,
111
+ });
112
+ return;
113
+ }
114
+ // closed: check threshold
115
+ if (state.circuit === "closed" &&
116
+ state.consecutiveFailures >= this.cbConfig.failureThreshold) {
117
+ state.circuit = "open";
118
+ state.health = "unhealthy";
119
+ this.log("warn", "peer.circuit.open", {
120
+ peer: peerName,
121
+ reason: "failure threshold reached",
122
+ consecutive_failures: state.consecutiveFailures,
123
+ });
124
+ }
125
+ }
126
+ /** Get state for a single peer. */
127
+ getState(peerName) {
128
+ return this.states.get(peerName);
129
+ }
130
+ /** Get states for all peers. */
131
+ getAllStates() {
132
+ return new Map(this.states);
133
+ }
134
+ /** Get cached skills for all peers (populated during health checks). */
135
+ getPeerSkills() {
136
+ return new Map(this.peerSkills);
137
+ }
138
+ /** Update cached skills for a peer (called by the health probe). */
139
+ setPeerSkills(peerName, skills) {
140
+ this.peerSkills.set(peerName, skills);
141
+ }
142
+ /** Run health checks for all peers. */
143
+ runHealthChecks() {
144
+ for (const peer of this.peers) {
145
+ this.checkPeer(peer).catch((err) => {
146
+ this.log("error", "peer.health.check-error", {
147
+ peer: peer.name,
148
+ error: err instanceof Error ? err.message : String(err),
149
+ });
150
+ });
151
+ }
152
+ }
153
+ /** Probe a single peer and update its state. */
154
+ async checkPeer(peer) {
155
+ const state = this.states.get(peer.name);
156
+ if (!state)
157
+ return;
158
+ const healthy = await this.probe(peer);
159
+ state.lastCheckAt = Date.now();
160
+ if (healthy) {
161
+ this.recordSuccess(peer.name);
162
+ }
163
+ else {
164
+ state.health = "unhealthy";
165
+ this.recordFailure(peer.name);
166
+ }
167
+ }
168
+ }
169
+ //# sourceMappingURL=peer-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-health.js","sourceRoot":"","sources":["../../src/peer-health.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { RetryConfig, OutboundSendResult } from "./types.js";
2
+ type LogFn = (level: "info" | "warn", msg: string, details?: Record<string, unknown>) => void;
3
+ /**
4
+ * Determine whether an error or failed result is retryable.
5
+ * Delegates to isRetryableTransportError for comprehensive transport-level error classification.
6
+ */
7
+ export declare function isRetryable(errorOrResult: unknown): boolean;
8
+ /**
9
+ * Wrap an async operation with configurable retry + exponential backoff.
10
+ *
11
+ * The function `fn` should throw on network errors or return an
12
+ * OutboundSendResult. Non-retryable failures are returned immediately.
13
+ */
14
+ export declare function withRetry(fn: () => Promise<OutboundSendResult>, config: RetryConfig, log?: LogFn, peerName?: string): Promise<OutboundSendResult>;
15
+ export {};
16
+ //# sourceMappingURL=peer-retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-retry.d.ts","sourceRoot":"","sources":["../../src/peer-retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGlE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAE9F;;;GAGG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,OAAO,GAAG,OAAO,CAa3D;AAeD;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,EAAE,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,EACrC,MAAM,EAAE,WAAW,EACnB,GAAG,CAAC,EAAE,KAAK,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC,CA6C7B"}
@@ -0,0 +1,75 @@
1
+ import { isRetryableTransportError } from "./transport-fallback.js";
2
+ /**
3
+ * Determine whether an error or failed result is retryable.
4
+ * Delegates to isRetryableTransportError for comprehensive transport-level error classification.
5
+ */
6
+ export function isRetryable(errorOrResult) {
7
+ if (errorOrResult &&
8
+ typeof errorOrResult === "object" &&
9
+ "ok" in errorOrResult &&
10
+ "statusCode" in errorOrResult) {
11
+ const result = errorOrResult;
12
+ if (result.ok)
13
+ return false;
14
+ return isRetryableTransportError(result);
15
+ }
16
+ return isRetryableTransportError(errorOrResult);
17
+ }
18
+ /**
19
+ * Calculate delay with exponential backoff + jitter.
20
+ */
21
+ function calcDelay(attempt, config) {
22
+ const exponential = Math.min(config.baseDelayMs * Math.pow(2, attempt), config.maxDelayMs);
23
+ // Add 0–10% jitter to prevent thundering herd
24
+ const jitter = Math.random() * exponential * 0.1;
25
+ return exponential + jitter;
26
+ }
27
+ /**
28
+ * Wrap an async operation with configurable retry + exponential backoff.
29
+ *
30
+ * The function `fn` should throw on network errors or return an
31
+ * OutboundSendResult. Non-retryable failures are returned immediately.
32
+ */
33
+ export async function withRetry(fn, config, log, peerName) {
34
+ let lastResult;
35
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
36
+ try {
37
+ const result = await fn();
38
+ // Success → return immediately
39
+ if (result.ok)
40
+ return result;
41
+ // Non-retryable failure → return immediately
42
+ if (!isRetryable(result))
43
+ return result;
44
+ lastResult = result;
45
+ }
46
+ catch (error) {
47
+ if (!isRetryable(error)) {
48
+ return {
49
+ ok: false,
50
+ statusCode: 500,
51
+ response: { error: error instanceof Error ? error.message : String(error) },
52
+ };
53
+ }
54
+ lastResult = {
55
+ ok: false,
56
+ statusCode: 500,
57
+ response: { error: error instanceof Error ? error.message : String(error) },
58
+ };
59
+ }
60
+ // If we have retries left, wait before the next attempt
61
+ if (attempt < config.maxRetries) {
62
+ const delay = calcDelay(attempt, config);
63
+ log?.("warn", "peer.retry", {
64
+ peer: peerName,
65
+ attempt: attempt + 1,
66
+ max_retries: config.maxRetries,
67
+ delay_ms: Math.round(delay),
68
+ });
69
+ await new Promise((resolve) => setTimeout(resolve, delay));
70
+ }
71
+ }
72
+ // All retries exhausted
73
+ return lastResult;
74
+ }
75
+ //# sourceMappingURL=peer-retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-retry.js","sourceRoot":"","sources":["../../src/peer-retry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAIpE;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,aAAsB;IAChD,IACE,aAAa;QACb,OAAO,aAAa,KAAK,QAAQ;QACjC,IAAI,IAAI,aAAa;QACrB,YAAY,IAAI,aAAa,EAC7B,CAAC;QACD,MAAM,MAAM,GAAG,aAAmC,CAAC;QACnD,IAAI,MAAM,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC;QAC5B,OAAO,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,yBAAyB,CAAC,aAAa,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,OAAe,EAAE,MAAmB;IACrD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAC1B,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EACzC,MAAM,CAAC,UAAU,CAClB,CAAC;IACF,8CAA8C;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,WAAW,GAAG,GAAG,CAAC;IACjD,OAAO,WAAW,GAAG,MAAM,CAAC;AAC9B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAAqC,EACrC,MAAmB,EACnB,GAAW,EACX,QAAiB;IAEjB,IAAI,UAA0C,CAAC;IAE/C,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QAC9D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC;YAE1B,+BAA+B;YAC/B,IAAI,MAAM,CAAC,EAAE;gBAAE,OAAO,MAAM,CAAC;YAE7B,6CAA6C;YAC7C,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAM,CAAC;YAExC,UAAU,GAAG,MAAM,CAAC;QACtB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,UAAU,EAAE,GAAG;oBACf,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;iBAC5E,CAAC;YACJ,CAAC;YAED,UAAU,GAAG;gBACX,EAAE,EAAE,KAAK;gBACT,UAAU,EAAE,GAAG;gBACf,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;aAC5E,CAAC;QACJ,CAAC;QAED,wDAAwD;QACxD,IAAI,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACzC,GAAG,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE;gBAC1B,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,OAAO,GAAG,CAAC;gBACpB,WAAW,EAAE,MAAM,CAAC,UAAU;gBAC9B,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;aAC5B,CAAC,CAAC;YACH,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,wBAAwB;IACxB,OAAO,UAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { AgentExecutor, ExecutionEventBus, RequestContext } from "@a2a-js/sdk/server";
2
+ import { GatewayTelemetry } from "./telemetry.js";
3
+ interface QueueingExecutorOptions {
4
+ maxConcurrentTasks: number;
5
+ maxQueuedTasks: number;
6
+ }
7
+ export declare class QueueingAgentExecutor implements AgentExecutor {
8
+ private readonly delegate;
9
+ private readonly telemetry;
10
+ private readonly options;
11
+ private readonly queue;
12
+ private readonly pendingByTaskId;
13
+ private activeTasks;
14
+ constructor(delegate: AgentExecutor, telemetry: GatewayTelemetry, options: QueueingExecutorOptions);
15
+ execute(requestContext: RequestContext, eventBus: ExecutionEventBus): Promise<void>;
16
+ cancelTask(taskId: string, eventBus: ExecutionEventBus): Promise<void>;
17
+ private runEntry;
18
+ private drainQueue;
19
+ private queueDelete;
20
+ private pickAgentId;
21
+ }
22
+ export {};
23
+ //# sourceMappingURL=queueing-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queueing-executor.d.ts","sourceRoot":"","sources":["../../src/queueing-executor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,aAAa,EACb,iBAAiB,EACjB,cAAc,EACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,UAAU,uBAAuB;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;CACxB;AAwED,qBAAa,qBAAsB,YAAW,aAAa;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAClD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsC;IACtE,OAAO,CAAC,WAAW,CAAK;gBAEZ,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,OAAO,EAAE,uBAAuB;IASlG,OAAO,CAAC,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsD7E,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;YA0B9D,QAAQ;IAwEtB,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,WAAW;CAIpB"}
@@ -0,0 +1,179 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+ function statusMessage(contextId, text) {
3
+ return {
4
+ kind: "message",
5
+ messageId: uuidv4(),
6
+ role: "agent",
7
+ contextId,
8
+ parts: [{ kind: "text", text }],
9
+ };
10
+ }
11
+ function taskEvent(taskId, contextId, state, text) {
12
+ return {
13
+ kind: "task",
14
+ id: taskId,
15
+ contextId,
16
+ status: {
17
+ state,
18
+ message: text ? statusMessage(contextId, text) : undefined,
19
+ timestamp: new Date().toISOString(),
20
+ },
21
+ };
22
+ }
23
+ function createObservedEventBus(eventBus, observer) {
24
+ const wrapped = {
25
+ publish(event) {
26
+ observer(event);
27
+ eventBus.publish(event);
28
+ },
29
+ on(eventName, listener) {
30
+ eventBus.on(eventName, listener);
31
+ return wrapped;
32
+ },
33
+ off(eventName, listener) {
34
+ eventBus.off(eventName, listener);
35
+ return wrapped;
36
+ },
37
+ once(eventName, listener) {
38
+ eventBus.once(eventName, listener);
39
+ return wrapped;
40
+ },
41
+ removeAllListeners(eventName) {
42
+ eventBus.removeAllListeners(eventName);
43
+ return wrapped;
44
+ },
45
+ finished() {
46
+ eventBus.finished();
47
+ },
48
+ };
49
+ return wrapped;
50
+ }
51
+ export class QueueingAgentExecutor {
52
+ delegate;
53
+ telemetry;
54
+ options;
55
+ queue = [];
56
+ pendingByTaskId = new Map();
57
+ activeTasks = 0;
58
+ constructor(delegate, telemetry, options) {
59
+ this.delegate = delegate;
60
+ this.telemetry = telemetry;
61
+ this.options = {
62
+ maxConcurrentTasks: Math.max(1, options.maxConcurrentTasks),
63
+ maxQueuedTasks: Math.max(0, options.maxQueuedTasks),
64
+ };
65
+ }
66
+ execute(requestContext, eventBus) {
67
+ return new Promise((resolve, reject) => {
68
+ const entry = {
69
+ requestContext,
70
+ eventBus,
71
+ resolve,
72
+ reject,
73
+ };
74
+ this.pendingByTaskId.set(requestContext.taskId, entry);
75
+ if (this.activeTasks < this.options.maxConcurrentTasks) {
76
+ void this.runEntry(entry);
77
+ return;
78
+ }
79
+ if (this.queue.length >= this.options.maxQueuedTasks) {
80
+ this.pendingByTaskId.delete(requestContext.taskId);
81
+ this.telemetry.recordQueueRejected(requestContext.taskId, requestContext.contextId, this.queue.length);
82
+ eventBus.publish(taskEvent(requestContext.taskId, requestContext.contextId, "rejected", "Gateway is overloaded; queue limit reached"));
83
+ eventBus.finished();
84
+ resolve();
85
+ return;
86
+ }
87
+ this.queue.push(entry);
88
+ this.telemetry.recordTaskQueued(requestContext.taskId, requestContext.contextId, this.queue.length, this.queue.length);
89
+ eventBus.publish(taskEvent(requestContext.taskId, requestContext.contextId, "submitted", `Queued for execution (position ${this.queue.length})`));
90
+ });
91
+ }
92
+ async cancelTask(taskId, eventBus) {
93
+ const queuedIndex = this.queue.findIndex((entry) => entry.requestContext.taskId === taskId);
94
+ if (queuedIndex !== -1) {
95
+ const [entry] = this.queue.splice(queuedIndex, 1);
96
+ if (entry) {
97
+ this.pendingByTaskId.delete(taskId);
98
+ entry.eventBus.publish(taskEvent(taskId, entry.requestContext.contextId, "canceled", "Task canceled while queued"));
99
+ entry.eventBus.finished();
100
+ entry.resolve();
101
+ this.telemetry.recordTaskFinish(taskId, entry.requestContext.contextId, "canceled", 0, this.activeTasks, this.queue.length);
102
+ }
103
+ return;
104
+ }
105
+ await this.delegate.cancelTask(taskId, eventBus);
106
+ }
107
+ async runEntry(entry) {
108
+ const { requestContext } = entry;
109
+ const startedAt = Date.now();
110
+ let finalState;
111
+ let finalErrorMessage;
112
+ this.queueDelete(requestContext.taskId);
113
+ this.activeTasks += 1;
114
+ this.telemetry.recordTaskStart(requestContext.taskId, requestContext.contextId, this.pickAgentId(requestContext), this.activeTasks, this.queue.length);
115
+ const observedBus = createObservedEventBus(entry.eventBus, (event) => {
116
+ const status = event.kind === "task" || event.kind === "status-update" ? event.status : undefined;
117
+ if (!status) {
118
+ return;
119
+ }
120
+ if (status.state === "completed" ||
121
+ status.state === "failed" ||
122
+ status.state === "canceled" ||
123
+ status.state === "rejected") {
124
+ finalState = status.state;
125
+ if (status.state !== "completed") {
126
+ finalErrorMessage =
127
+ typeof status.message?.parts?.[0] === "object" && status.message.parts[0]?.kind === "text"
128
+ ? status.message.parts[0].text
129
+ : finalErrorMessage;
130
+ }
131
+ }
132
+ });
133
+ try {
134
+ await this.delegate.execute(requestContext, observedBus);
135
+ entry.resolve();
136
+ }
137
+ catch (error) {
138
+ const message = error instanceof Error ? error.message : String(error);
139
+ finalState = finalState || "failed";
140
+ finalErrorMessage = finalErrorMessage || message;
141
+ entry.reject(error instanceof Error ? error : new Error(message));
142
+ return;
143
+ }
144
+ finally {
145
+ this.pendingByTaskId.delete(requestContext.taskId);
146
+ this.activeTasks = Math.max(0, this.activeTasks - 1);
147
+ this.telemetry.recordTaskFinish(requestContext.taskId, requestContext.contextId, finalState || "failed", Date.now() - startedAt, this.activeTasks, this.queue.length, finalErrorMessage);
148
+ // Ensure eventBus.finished() is always called so the SDK's
149
+ // DefaultRequestHandler does not hang waiting for the signal.
150
+ try {
151
+ entry.eventBus.finished();
152
+ }
153
+ catch {
154
+ // already finished — safe to ignore
155
+ }
156
+ this.drainQueue();
157
+ }
158
+ }
159
+ drainQueue() {
160
+ while (this.activeTasks < this.options.maxConcurrentTasks && this.queue.length > 0) {
161
+ const next = this.queue.shift();
162
+ if (!next) {
163
+ break;
164
+ }
165
+ void this.runEntry(next);
166
+ }
167
+ }
168
+ queueDelete(taskId) {
169
+ const index = this.queue.findIndex((entry) => entry.requestContext.taskId === taskId);
170
+ if (index !== -1) {
171
+ this.queue.splice(index, 1);
172
+ }
173
+ }
174
+ pickAgentId(requestContext) {
175
+ const message = requestContext.userMessage;
176
+ return typeof message?.agentId === "string" ? message.agentId : "default";
177
+ }
178
+ }
179
+ //# sourceMappingURL=queueing-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queueing-executor.js","sourceRoot":"","sources":["../../src/queueing-executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Rule-based routing — choose peer + agentId based on message content.
3
+ *
4
+ * Rules are evaluated at send-time in priority order (higher priority first).
5
+ * The first matching rule wins.
6
+ */
7
+ export interface RoutingRule {
8
+ name: string;
9
+ match: {
10
+ /** Regex pattern to match against message text (case-insensitive). */
11
+ pattern?: string;
12
+ /** Match if message contains any of these tags (OR logic). */
13
+ tags?: string[];
14
+ /** Match if target peer has any of these skills. */
15
+ skills?: string[];
16
+ };
17
+ target: {
18
+ peer: string;
19
+ agentId?: string;
20
+ };
21
+ /** Higher = checked first (default: 0). */
22
+ priority?: number;
23
+ }
24
+ export interface RoutingMatch {
25
+ peer: string;
26
+ agentId?: string;
27
+ }
28
+ /**
29
+ * Parse and validate routing rules from raw config.
30
+ *
31
+ * Invalid entries (missing name, target.peer, or at least one match criterion)
32
+ * are silently dropped.
33
+ */
34
+ export declare function parseRoutingRules(raw: unknown): RoutingRule[];
35
+ /**
36
+ * Evaluate rules in priority order against a message.
37
+ *
38
+ * Match criteria within a single rule are AND-combined:
39
+ * - pattern: regex test against message text
40
+ * - tags: any overlap between message tags and rule tags
41
+ * - skills: target peer advertises at least one required skill
42
+ *
43
+ * Returns the first match or null.
44
+ *
45
+ * The `peerSkills` map is populated from Agent Card discovery during
46
+ * periodic health checks. Skills are cached per peer and refreshed
47
+ * on each successful health check probe.
48
+ */
49
+ export declare function matchRule(rules: RoutingRule[], message: {
50
+ text: string;
51
+ tags?: string[];
52
+ }, peerSkills?: Map<string, string[]>): RoutingMatch | null;
53
+ //# sourceMappingURL=routing-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing-rules.d.ts","sourceRoot":"","sources":["../../src/routing-rules.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QACL,sEAAsE;QACtE,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,8DAA8D;QAC9D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,oDAAoD;QACpD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,EAAE,CAuD7D;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,WAAW,EAAE,EACpB,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,EAC1C,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACjC,YAAY,GAAG,IAAI,CAWrB"}