@axiom-lattice/core 2.1.76 → 2.1.78
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/README.md +258 -0
- package/dist/index.d.mts +539 -30
- package/dist/index.d.ts +539 -30
- package/dist/index.js +678 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +658 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1665,6 +1665,7 @@ var InMemoryProjectStore = class {
|
|
|
1665
1665
|
workspaceId,
|
|
1666
1666
|
name: data.name,
|
|
1667
1667
|
description: data.description,
|
|
1668
|
+
config: data.config,
|
|
1668
1669
|
createdAt: now,
|
|
1669
1670
|
updatedAt: now
|
|
1670
1671
|
};
|
|
@@ -1672,6 +1673,13 @@ var InMemoryProjectStore = class {
|
|
|
1672
1673
|
this.projects.set(key, project);
|
|
1673
1674
|
return project;
|
|
1674
1675
|
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Update an existing project
|
|
1678
|
+
*
|
|
1679
|
+
* @remarks
|
|
1680
|
+
* - The `config` field uses **replace** semantics: if provided, it completely
|
|
1681
|
+
* overwrites the existing config. To preserve the current config, omit this field.
|
|
1682
|
+
*/
|
|
1675
1683
|
async updateProject(tenantId, id, updates) {
|
|
1676
1684
|
const key = this.getKey(tenantId, id);
|
|
1677
1685
|
const existing = this.projects.get(key);
|
|
@@ -2463,14 +2471,35 @@ var InMemoryChannelInstallationStore = class {
|
|
|
2463
2471
|
constructor() {
|
|
2464
2472
|
this.installations = /* @__PURE__ */ new Map();
|
|
2465
2473
|
}
|
|
2474
|
+
/**
|
|
2475
|
+
* Retrieves a channel installation by its unique ID.
|
|
2476
|
+
*
|
|
2477
|
+
* @param installationId - The installation identifier
|
|
2478
|
+
* @returns The {@link ChannelInstallation} or `null` if not found
|
|
2479
|
+
*/
|
|
2466
2480
|
async getInstallationById(installationId) {
|
|
2467
2481
|
return this.installations.get(installationId) || null;
|
|
2468
2482
|
}
|
|
2483
|
+
/**
|
|
2484
|
+
* Lists all channel installations for a tenant, optionally filtered by channel type.
|
|
2485
|
+
*
|
|
2486
|
+
* @param tenantId - Tenant identifier
|
|
2487
|
+
* @param channel - Optional channel type filter (`"lark"`, `"email"`, `"slack"`)
|
|
2488
|
+
* @returns Array of matching {@link ChannelInstallation} objects
|
|
2489
|
+
*/
|
|
2469
2490
|
async getInstallationsByTenant(tenantId, channel) {
|
|
2470
2491
|
return Array.from(this.installations.values()).filter(
|
|
2471
2492
|
(inst) => inst.tenantId === tenantId && (!channel || inst.channel === channel)
|
|
2472
2493
|
);
|
|
2473
2494
|
}
|
|
2495
|
+
/**
|
|
2496
|
+
* Creates a new channel installation for a tenant.
|
|
2497
|
+
*
|
|
2498
|
+
* @param tenantId - Tenant identifier
|
|
2499
|
+
* @param installationId - Unique installation ID (caller-defined)
|
|
2500
|
+
* @param data - {@link CreateChannelInstallationRequest} containing channel type, name, config, and fallback settings
|
|
2501
|
+
* @returns The created {@link ChannelInstallation}
|
|
2502
|
+
*/
|
|
2474
2503
|
async createInstallation(tenantId, installationId, data) {
|
|
2475
2504
|
const now = /* @__PURE__ */ new Date();
|
|
2476
2505
|
const installation = {
|
|
@@ -2488,6 +2517,14 @@ var InMemoryChannelInstallationStore = class {
|
|
|
2488
2517
|
this.installations.set(installationId, installation);
|
|
2489
2518
|
return installation;
|
|
2490
2519
|
}
|
|
2520
|
+
/**
|
|
2521
|
+
* Updates an existing channel installation. Config fields are shallow-merged.
|
|
2522
|
+
*
|
|
2523
|
+
* @param tenantId - Tenant identifier (used for isolation check)
|
|
2524
|
+
* @param installationId - Installation ID to update
|
|
2525
|
+
* @param updates - {@link UpdateChannelInstallationRequest} with fields to patch
|
|
2526
|
+
* @returns The updated {@link ChannelInstallation} or `null` if not found or tenant mismatch
|
|
2527
|
+
*/
|
|
2491
2528
|
async updateInstallation(tenantId, installationId, updates) {
|
|
2492
2529
|
const existing = this.installations.get(installationId);
|
|
2493
2530
|
if (!existing || existing.tenantId !== tenantId) return null;
|
|
@@ -2503,11 +2540,21 @@ var InMemoryChannelInstallationStore = class {
|
|
|
2503
2540
|
this.installations.set(installationId, updated);
|
|
2504
2541
|
return updated;
|
|
2505
2542
|
}
|
|
2543
|
+
/**
|
|
2544
|
+
* Deletes a channel installation.
|
|
2545
|
+
*
|
|
2546
|
+
* @param tenantId - Tenant identifier (used for isolation check)
|
|
2547
|
+
* @param installationId - Installation ID to delete
|
|
2548
|
+
* @returns `true` if deleted, `false` if not found or tenant mismatch
|
|
2549
|
+
*/
|
|
2506
2550
|
async deleteInstallation(tenantId, installationId) {
|
|
2507
2551
|
const existing = this.installations.get(installationId);
|
|
2508
2552
|
if (!existing || existing.tenantId !== tenantId) return false;
|
|
2509
2553
|
return this.installations.delete(installationId);
|
|
2510
2554
|
}
|
|
2555
|
+
/**
|
|
2556
|
+
* Clears all in-memory installations. Primarily used in tests.
|
|
2557
|
+
*/
|
|
2511
2558
|
clear() {
|
|
2512
2559
|
this.installations.clear();
|
|
2513
2560
|
}
|
|
@@ -2519,6 +2566,16 @@ var InMemoryBindingStore = class {
|
|
|
2519
2566
|
constructor() {
|
|
2520
2567
|
this.bindings = /* @__PURE__ */ new Map();
|
|
2521
2568
|
}
|
|
2569
|
+
/**
|
|
2570
|
+
* Resolves a binding for the given sender across a specific channel installation.
|
|
2571
|
+
*
|
|
2572
|
+
* Called by `MessageRouter.dispatch()` during inbound message processing.
|
|
2573
|
+
* Matches on `channel + senderId + channelInstallationId + tenantId` and
|
|
2574
|
+
* requires `enabled === true`.
|
|
2575
|
+
*
|
|
2576
|
+
* @param params - Resolution parameters
|
|
2577
|
+
* @returns The matching {@link Binding} or `null` if none found.
|
|
2578
|
+
*/
|
|
2522
2579
|
async resolve(params) {
|
|
2523
2580
|
for (const binding of this.bindings.values()) {
|
|
2524
2581
|
if (binding.channel === params.channel && binding.senderId === params.senderId && binding.channelInstallationId === params.channelInstallationId && binding.tenantId === params.tenantId && binding.enabled) {
|
|
@@ -2527,6 +2584,12 @@ var InMemoryBindingStore = class {
|
|
|
2527
2584
|
}
|
|
2528
2585
|
return null;
|
|
2529
2586
|
}
|
|
2587
|
+
/**
|
|
2588
|
+
* Creates a new sender-to-agent binding.
|
|
2589
|
+
*
|
|
2590
|
+
* @param input - {@link CreateBindingInput} defining channel, sender, target agent, and thread mode.
|
|
2591
|
+
* @returns The newly created {@link Binding}.
|
|
2592
|
+
*/
|
|
2530
2593
|
async create(input) {
|
|
2531
2594
|
const now = /* @__PURE__ */ new Date();
|
|
2532
2595
|
const binding = {
|
|
@@ -2549,6 +2612,14 @@ var InMemoryBindingStore = class {
|
|
|
2549
2612
|
this.bindings.set(binding.id, binding);
|
|
2550
2613
|
return binding;
|
|
2551
2614
|
}
|
|
2615
|
+
/**
|
|
2616
|
+
* Updates an existing binding (e.g. changing the target agent or thread mode).
|
|
2617
|
+
*
|
|
2618
|
+
* @param id - Binding ID
|
|
2619
|
+
* @param patch - Partial {@link Binding} fields to update
|
|
2620
|
+
* @returns The updated {@link Binding}
|
|
2621
|
+
* @throws {Error} If the binding does not exist
|
|
2622
|
+
*/
|
|
2552
2623
|
async update(id, patch) {
|
|
2553
2624
|
const existing = this.bindings.get(id);
|
|
2554
2625
|
if (!existing) throw new Error(`Binding ${id} not found`);
|
|
@@ -2560,9 +2631,20 @@ var InMemoryBindingStore = class {
|
|
|
2560
2631
|
this.bindings.set(id, updated);
|
|
2561
2632
|
return updated;
|
|
2562
2633
|
}
|
|
2634
|
+
/**
|
|
2635
|
+
* Deletes a binding by ID.
|
|
2636
|
+
*
|
|
2637
|
+
* @param id - Binding ID
|
|
2638
|
+
*/
|
|
2563
2639
|
async delete(id) {
|
|
2564
2640
|
this.bindings.delete(id);
|
|
2565
2641
|
}
|
|
2642
|
+
/**
|
|
2643
|
+
* Lists bindings filtered by tenant and optional channel/agent/installation.
|
|
2644
|
+
*
|
|
2645
|
+
* @param params - Filter and pagination parameters
|
|
2646
|
+
* @returns Array of matching {@link Binding} objects
|
|
2647
|
+
*/
|
|
2566
2648
|
async list(params) {
|
|
2567
2649
|
let results = Array.from(this.bindings.values()).filter((b) => {
|
|
2568
2650
|
if (b.tenantId !== params.tenantId) return false;
|
|
@@ -2575,6 +2657,12 @@ var InMemoryBindingStore = class {
|
|
|
2575
2657
|
const limit = params.limit ?? 50;
|
|
2576
2658
|
return results.slice(offset, offset + limit);
|
|
2577
2659
|
}
|
|
2660
|
+
/**
|
|
2661
|
+
* Bulk-imports bindings.
|
|
2662
|
+
*
|
|
2663
|
+
* @param bindings - Array of {@link CreateBindingInput}
|
|
2664
|
+
* @returns Array of created {@link Binding} objects
|
|
2665
|
+
*/
|
|
2578
2666
|
async import(bindings) {
|
|
2579
2667
|
const result = [];
|
|
2580
2668
|
for (const input of bindings) {
|
|
@@ -2582,14 +2670,104 @@ var InMemoryBindingStore = class {
|
|
|
2582
2670
|
}
|
|
2583
2671
|
return result;
|
|
2584
2672
|
}
|
|
2673
|
+
/**
|
|
2674
|
+
* Exports all bindings for a tenant.
|
|
2675
|
+
*
|
|
2676
|
+
* @param params - Filter by tenantId
|
|
2677
|
+
* @returns Array of {@link Binding} objects
|
|
2678
|
+
*/
|
|
2585
2679
|
async export(params) {
|
|
2586
2680
|
return this.list({ tenantId: params.tenantId, limit: 1e4 });
|
|
2587
2681
|
}
|
|
2682
|
+
/**
|
|
2683
|
+
* Clears all in-memory bindings. Primarily used in tests.
|
|
2684
|
+
*/
|
|
2588
2685
|
clear() {
|
|
2589
2686
|
this.bindings.clear();
|
|
2590
2687
|
}
|
|
2591
2688
|
};
|
|
2592
2689
|
|
|
2690
|
+
// src/store_lattice/InMemoryA2AApiKeyStore.ts
|
|
2691
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2692
|
+
function generateApiKey() {
|
|
2693
|
+
return `a2a_${randomUUID2().replace(/-/g, "")}`;
|
|
2694
|
+
}
|
|
2695
|
+
var InMemoryA2AApiKeyStore = class {
|
|
2696
|
+
constructor() {
|
|
2697
|
+
this.keys = /* @__PURE__ */ new Map();
|
|
2698
|
+
}
|
|
2699
|
+
async findByKey(key) {
|
|
2700
|
+
for (const record of this.keys.values()) {
|
|
2701
|
+
if (record.key === key && record.enabled) return record;
|
|
2702
|
+
}
|
|
2703
|
+
return null;
|
|
2704
|
+
}
|
|
2705
|
+
async list(params) {
|
|
2706
|
+
let records = Array.from(this.keys.values());
|
|
2707
|
+
if (params.tenantId) {
|
|
2708
|
+
records = records.filter((r) => r.tenantId === params.tenantId);
|
|
2709
|
+
}
|
|
2710
|
+
const offset = params.offset || 0;
|
|
2711
|
+
const limit = params.limit;
|
|
2712
|
+
records = records.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2713
|
+
return limit ? records.slice(offset, offset + limit) : records.slice(offset);
|
|
2714
|
+
}
|
|
2715
|
+
async create(input) {
|
|
2716
|
+
const now = /* @__PURE__ */ new Date();
|
|
2717
|
+
const record = {
|
|
2718
|
+
id: randomUUID2(),
|
|
2719
|
+
key: generateApiKey(),
|
|
2720
|
+
tenantId: input.tenantId,
|
|
2721
|
+
projectId: input.projectId,
|
|
2722
|
+
workspaceId: input.workspaceId,
|
|
2723
|
+
label: input.label,
|
|
2724
|
+
enabled: true,
|
|
2725
|
+
createdAt: now,
|
|
2726
|
+
updatedAt: now
|
|
2727
|
+
};
|
|
2728
|
+
this.keys.set(record.id, record);
|
|
2729
|
+
return record;
|
|
2730
|
+
}
|
|
2731
|
+
async disable(id) {
|
|
2732
|
+
const record = this.keys.get(id);
|
|
2733
|
+
if (!record) throw new Error(`A2A API key not found: ${id}`);
|
|
2734
|
+
const updated = { ...record, enabled: false, updatedAt: /* @__PURE__ */ new Date() };
|
|
2735
|
+
this.keys.set(id, updated);
|
|
2736
|
+
return updated;
|
|
2737
|
+
}
|
|
2738
|
+
async enable(id) {
|
|
2739
|
+
const record = this.keys.get(id);
|
|
2740
|
+
if (!record) throw new Error(`A2A API key not found: ${id}`);
|
|
2741
|
+
const updated = { ...record, enabled: true, updatedAt: /* @__PURE__ */ new Date() };
|
|
2742
|
+
this.keys.set(id, updated);
|
|
2743
|
+
return updated;
|
|
2744
|
+
}
|
|
2745
|
+
async rotate(id) {
|
|
2746
|
+
const record = this.keys.get(id);
|
|
2747
|
+
if (!record) throw new Error(`A2A API key not found: ${id}`);
|
|
2748
|
+
const updated = { ...record, key: generateApiKey(), updatedAt: /* @__PURE__ */ new Date() };
|
|
2749
|
+
this.keys.set(id, updated);
|
|
2750
|
+
return updated;
|
|
2751
|
+
}
|
|
2752
|
+
async delete(id) {
|
|
2753
|
+
this.keys.delete(id);
|
|
2754
|
+
}
|
|
2755
|
+
async loadIntoMap() {
|
|
2756
|
+
const map = /* @__PURE__ */ new Map();
|
|
2757
|
+
for (const record of this.keys.values()) {
|
|
2758
|
+
if (record.enabled) {
|
|
2759
|
+
map.set(record.key, {
|
|
2760
|
+
key: record.key,
|
|
2761
|
+
tenantId: record.tenantId,
|
|
2762
|
+
projectId: record.projectId,
|
|
2763
|
+
workspaceId: record.workspaceId
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
return map;
|
|
2768
|
+
}
|
|
2769
|
+
};
|
|
2770
|
+
|
|
2593
2771
|
// src/store_lattice/StoreLatticeManager.ts
|
|
2594
2772
|
var StoreLatticeManager = class _StoreLatticeManager extends BaseLatticeManager {
|
|
2595
2773
|
/**
|
|
@@ -2764,6 +2942,12 @@ storeLatticeManager.registerLattice(
|
|
|
2764
2942
|
"channelBinding",
|
|
2765
2943
|
defaultChannelBindingStore
|
|
2766
2944
|
);
|
|
2945
|
+
var defaultA2AApiKeyStore = new InMemoryA2AApiKeyStore();
|
|
2946
|
+
storeLatticeManager.registerLattice(
|
|
2947
|
+
"default",
|
|
2948
|
+
"a2aApiKey",
|
|
2949
|
+
defaultA2AApiKeyStore
|
|
2950
|
+
);
|
|
2767
2951
|
|
|
2768
2952
|
// src/tool_lattice/manage_binding/index.ts
|
|
2769
2953
|
function getInstallationStore() {
|
|
@@ -6860,6 +7044,7 @@ import {
|
|
|
6860
7044
|
isDeepAgentConfig,
|
|
6861
7045
|
isProcessingAgentConfig,
|
|
6862
7046
|
isTeamAgentConfig,
|
|
7047
|
+
isA2ARemoteAgentConfig,
|
|
6863
7048
|
getToolsFromConfig,
|
|
6864
7049
|
getSubAgentsFromConfig
|
|
6865
7050
|
} from "@axiom-lattice/protocols";
|
|
@@ -11906,6 +12091,14 @@ var ThreadStatus2 = /* @__PURE__ */ ((ThreadStatus3) => {
|
|
|
11906
12091
|
return ThreadStatus3;
|
|
11907
12092
|
})(ThreadStatus2 || {});
|
|
11908
12093
|
var Agent = class {
|
|
12094
|
+
/**
|
|
12095
|
+
* Constructs an Agent instance.
|
|
12096
|
+
*
|
|
12097
|
+
* Prefer {@link AgentInstanceManager.getAgent} over direct construction — it
|
|
12098
|
+
* ensures a single instance per thread.
|
|
12099
|
+
*
|
|
12100
|
+
* @param params - {@link AgentThreadInterface}
|
|
12101
|
+
*/
|
|
11909
12102
|
constructor({
|
|
11910
12103
|
assistant_id,
|
|
11911
12104
|
thread_id,
|
|
@@ -12118,6 +12311,7 @@ var Agent = class {
|
|
|
12118
12311
|
await this.queueStore?.removeMessage(p.id);
|
|
12119
12312
|
const runStatus = await this.getRunStatus();
|
|
12120
12313
|
const state = await this.getCurrentState();
|
|
12314
|
+
const customRunConfig = p.custom_run_config ?? queueMessageData.custom_run_config;
|
|
12121
12315
|
if (runStatus === "interrupted" /* INTERRUPTED */) {
|
|
12122
12316
|
this.publish("message:interrupted", {
|
|
12123
12317
|
type: "message:interrupted",
|
|
@@ -12142,6 +12336,12 @@ var Agent = class {
|
|
|
12142
12336
|
state
|
|
12143
12337
|
});
|
|
12144
12338
|
}
|
|
12339
|
+
this.publish("reply:ready", {
|
|
12340
|
+
type: "reply:ready",
|
|
12341
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
12342
|
+
state,
|
|
12343
|
+
customRunConfig
|
|
12344
|
+
});
|
|
12145
12345
|
} catch (error) {
|
|
12146
12346
|
console.error(`STEER/Command message ${p.id} execution failed:`, error);
|
|
12147
12347
|
this.addChunk({
|
|
@@ -12220,6 +12420,12 @@ var Agent = class {
|
|
|
12220
12420
|
});
|
|
12221
12421
|
}
|
|
12222
12422
|
}
|
|
12423
|
+
this.publish("reply:ready", {
|
|
12424
|
+
type: "reply:ready",
|
|
12425
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
12426
|
+
state,
|
|
12427
|
+
customRunConfig: remainingPendings[0]?.custom_run_config ?? firstQueueMessage?.custom_run_config
|
|
12428
|
+
});
|
|
12223
12429
|
} catch (error) {
|
|
12224
12430
|
console.error(`COLLECT mode execution failed:`, error);
|
|
12225
12431
|
for (const p of remainingPendings) {
|
|
@@ -12268,6 +12474,7 @@ var Agent = class {
|
|
|
12268
12474
|
await this.queueStore?.removeMessage(p.id);
|
|
12269
12475
|
const runStatus = await this.getRunStatus();
|
|
12270
12476
|
const state = await this.getCurrentState();
|
|
12477
|
+
const customRunConfig = p.custom_run_config ?? queueMessageData.custom_run_config;
|
|
12271
12478
|
if (runStatus === "interrupted" /* INTERRUPTED */) {
|
|
12272
12479
|
this.publish("message:interrupted", {
|
|
12273
12480
|
type: "message:interrupted",
|
|
@@ -12292,6 +12499,12 @@ var Agent = class {
|
|
|
12292
12499
|
state
|
|
12293
12500
|
});
|
|
12294
12501
|
}
|
|
12502
|
+
this.publish("reply:ready", {
|
|
12503
|
+
type: "reply:ready",
|
|
12504
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
12505
|
+
state,
|
|
12506
|
+
customRunConfig
|
|
12507
|
+
});
|
|
12295
12508
|
} catch (error) {
|
|
12296
12509
|
console.error(`FOLLOWUP mode message ${p.id} execution failed:`, error);
|
|
12297
12510
|
this.addChunk({
|
|
@@ -12334,9 +12547,25 @@ var Agent = class {
|
|
|
12334
12547
|
setQueueStore(store) {
|
|
12335
12548
|
this.queueStore = store;
|
|
12336
12549
|
}
|
|
12550
|
+
/**
|
|
12551
|
+
* Push a chunk into the streaming buffer for this thread.
|
|
12552
|
+
*
|
|
12553
|
+
* Consumers read chunks via {@link chunkStream}.
|
|
12554
|
+
*
|
|
12555
|
+
* @param content - The message chunk to buffer
|
|
12556
|
+
*/
|
|
12337
12557
|
addChunk(content) {
|
|
12338
12558
|
return this.chunkBuffer.addChunk(this.thread_id, content);
|
|
12339
12559
|
}
|
|
12560
|
+
/**
|
|
12561
|
+
* Returns an async iterator over new chunks since the given message ID.
|
|
12562
|
+
*
|
|
12563
|
+
* Used by SSE endpoints to stream agent output to clients in real time.
|
|
12564
|
+
*
|
|
12565
|
+
* @param message_id - The client message ID to start streaming from
|
|
12566
|
+
* @param stopTypes - Optional chunk types that terminate the stream
|
|
12567
|
+
* @returns An async iterable yielding {@link MessageChunk} objects
|
|
12568
|
+
*/
|
|
12340
12569
|
chunkStream(message_id, stopTypes) {
|
|
12341
12570
|
const stream = this.chunkBuffer.getNewChunksSinceContentIterator(
|
|
12342
12571
|
this.thread_id,
|
|
@@ -12425,13 +12654,21 @@ var Agent = class {
|
|
|
12425
12654
|
};
|
|
12426
12655
|
}
|
|
12427
12656
|
/**
|
|
12428
|
-
*
|
|
12657
|
+
* Override the thread's queue processing mode.
|
|
12658
|
+
*
|
|
12659
|
+
* @param config - Partial {@link ThreadQueueConfig} (e.g. `{ mode: QueueMode.FOLLOWUP }`)
|
|
12429
12660
|
*/
|
|
12430
12661
|
async setQueueConfig(config) {
|
|
12431
12662
|
this.queueMode = { ...this.queueMode, ...config };
|
|
12432
12663
|
}
|
|
12433
12664
|
/**
|
|
12434
|
-
*
|
|
12665
|
+
* Abort any ongoing queue processing without clearing the queue.
|
|
12666
|
+
*
|
|
12667
|
+
* Used internally by STEER mode to interrupt the current execution so the
|
|
12668
|
+
* steer message can be processed next. For a full abort that also clears
|
|
12669
|
+
* pending messages, use {@link abort}.
|
|
12670
|
+
*
|
|
12671
|
+
* @see {@link abort}
|
|
12435
12672
|
*/
|
|
12436
12673
|
stopQueueProcessor() {
|
|
12437
12674
|
if (this.abortController) {
|
|
@@ -12440,14 +12677,34 @@ var Agent = class {
|
|
|
12440
12677
|
}
|
|
12441
12678
|
}
|
|
12442
12679
|
/**
|
|
12443
|
-
*
|
|
12444
|
-
*
|
|
12445
|
-
*
|
|
12446
|
-
*
|
|
12447
|
-
*
|
|
12448
|
-
*
|
|
12449
|
-
* -
|
|
12450
|
-
* -
|
|
12680
|
+
* Enqueue a message for this thread.
|
|
12681
|
+
*
|
|
12682
|
+
* Messages are always queued; the queue processor starts automatically if idle.
|
|
12683
|
+
* Returns immediately with the message ID — execution is asynchronous.
|
|
12684
|
+
*
|
|
12685
|
+
* **Queue modes** (via the optional `mode` param):
|
|
12686
|
+
* - `COLLECT` (default) — Batch multiple pending messages into one agent call.
|
|
12687
|
+
* - `FOLLOWUP` — Process messages one at a time in order.
|
|
12688
|
+
* - `STEER` — High-priority, inserted at queue head, interrupts current processing.
|
|
12689
|
+
*
|
|
12690
|
+
* **Format**: Supports both `input.message` (legacy string) and `input.messages[]`
|
|
12691
|
+
* (array of `{ role, content }` objects). The array form takes precedence.
|
|
12692
|
+
*
|
|
12693
|
+
* The `custom_run_config` field is round-tripped through the queue and emitted
|
|
12694
|
+
* back in {@link ReplyReadyEvent}, enabling callers to attach routing metadata
|
|
12695
|
+
* (e.g. `_replyTarget` for channel reply).
|
|
12696
|
+
*
|
|
12697
|
+
* @param queueMessage - The message to enqueue
|
|
12698
|
+
* @param mode - Optional queue mode override (defaults to thread's current mode)
|
|
12699
|
+
* @returns `{ queued: true, executed: false, messageId }` — execution happens asynchronously
|
|
12700
|
+
*
|
|
12701
|
+
* @example
|
|
12702
|
+
* ```ts
|
|
12703
|
+
* await agent.addMessage({
|
|
12704
|
+
* input: { message: "Hello" },
|
|
12705
|
+
* custom_run_config: { _replyTarget: { adapterChannel: "lark", rawTarget: { chatId: "xxx" } } },
|
|
12706
|
+
* });
|
|
12707
|
+
* ```
|
|
12451
12708
|
*/
|
|
12452
12709
|
async addMessage(queueMessage, mode) {
|
|
12453
12710
|
const useMode = mode ?? this.queueMode.mode;
|
|
@@ -12541,8 +12798,13 @@ var Agent = class {
|
|
|
12541
12798
|
return { queued: true, executed: false, messageId };
|
|
12542
12799
|
}
|
|
12543
12800
|
/**
|
|
12544
|
-
* Start queue processor if not already running
|
|
12545
|
-
*
|
|
12801
|
+
* Start the queue processor if it is not already running.
|
|
12802
|
+
*
|
|
12803
|
+
* Called automatically by {@link addMessage} and {@link resumeTask}.
|
|
12804
|
+
* Safe to call externally — it is a no-op if processing is already active.
|
|
12805
|
+
*
|
|
12806
|
+
* Emits `thread:busy` before starting, and starts the {@link waitingForQueueEnd}
|
|
12807
|
+
* loop with a fresh {@link AbortController}.
|
|
12546
12808
|
*/
|
|
12547
12809
|
async startQueueProcessorIfNeeded() {
|
|
12548
12810
|
const store = this.getQueueStore();
|
|
@@ -12590,6 +12852,12 @@ var Agent = class {
|
|
|
12590
12852
|
type: "system"
|
|
12591
12853
|
});
|
|
12592
12854
|
}
|
|
12855
|
+
/**
|
|
12856
|
+
* Returns a LangGraph StateSnapshot for this thread.
|
|
12857
|
+
*
|
|
12858
|
+
* Includes `state.values.messages` (full conversation history) and
|
|
12859
|
+
* `state.tasks` / `state.next` (execution progress).
|
|
12860
|
+
*/
|
|
12593
12861
|
async getCurrentState() {
|
|
12594
12862
|
const { runnable_agent } = await this.getLatticeClientAndRuntimeConfig();
|
|
12595
12863
|
const state = await runnable_agent.getState({
|
|
@@ -12597,6 +12865,14 @@ var Agent = class {
|
|
|
12597
12865
|
});
|
|
12598
12866
|
return state;
|
|
12599
12867
|
}
|
|
12868
|
+
/**
|
|
12869
|
+
* Returns the conversation history as normalized message objects.
|
|
12870
|
+
*
|
|
12871
|
+
* Filters to `human`, `ai`, and `tool` message types. Each entry has
|
|
12872
|
+
* `{ id, role, content }` plus any LangChain kwargs.
|
|
12873
|
+
*
|
|
12874
|
+
* @returns Array of message objects with `id`, `role`, and `content`
|
|
12875
|
+
*/
|
|
12600
12876
|
async getCurrentMessages() {
|
|
12601
12877
|
const state = await this.getCurrentState();
|
|
12602
12878
|
const messages = state.values.messages || [];
|
|
@@ -12619,6 +12895,14 @@ var Agent = class {
|
|
|
12619
12895
|
const image = await drawableGraph.drawMermaid();
|
|
12620
12896
|
return image;
|
|
12621
12897
|
}
|
|
12898
|
+
/**
|
|
12899
|
+
* Determine the current thread execution status.
|
|
12900
|
+
*
|
|
12901
|
+
* Checks LangGraph's `state.tasks` for interrupts and `state.next` for
|
|
12902
|
+
* pending steps.
|
|
12903
|
+
*
|
|
12904
|
+
* @returns {@link ThreadStatus} — `IDLE`, `BUSY`, or `INTERRUPTED`
|
|
12905
|
+
*/
|
|
12622
12906
|
async getRunStatus() {
|
|
12623
12907
|
const state = await this.getCurrentState();
|
|
12624
12908
|
const isInterrupted = state.tasks?.some(
|
|
@@ -12633,9 +12917,14 @@ var Agent = class {
|
|
|
12633
12917
|
return "idle" /* IDLE */;
|
|
12634
12918
|
}
|
|
12635
12919
|
/**
|
|
12636
|
-
* Resume
|
|
12637
|
-
*
|
|
12638
|
-
*
|
|
12920
|
+
* Resume processing after a server restart.
|
|
12921
|
+
*
|
|
12922
|
+
* Resets any stuck "processing" messages back to "pending" and restarts the
|
|
12923
|
+
* queue processor. Skips threads that are in `INTERRUPTED` state (the
|
|
12924
|
+
* interruption was intentional).
|
|
12925
|
+
*
|
|
12926
|
+
* Called during gateway startup to recover threads that were mid-execution
|
|
12927
|
+
* when the server went down.
|
|
12639
12928
|
*/
|
|
12640
12929
|
async resumeTask() {
|
|
12641
12930
|
try {
|
|
@@ -12657,9 +12946,14 @@ var Agent = class {
|
|
|
12657
12946
|
await this.startQueueProcessorIfNeeded();
|
|
12658
12947
|
}
|
|
12659
12948
|
/**
|
|
12660
|
-
*
|
|
12661
|
-
*
|
|
12662
|
-
*
|
|
12949
|
+
* Fully abort all activity on this thread.
|
|
12950
|
+
*
|
|
12951
|
+
* Aborts any in-flight agent execution (via {@link AbortController}) and
|
|
12952
|
+
* clears all pending and processing messages from the queue. Also marks
|
|
12953
|
+
* the chunk buffer thread as aborted so streaming consumers can detect it.
|
|
12954
|
+
*
|
|
12955
|
+
* Unlike {@link stopQueueProcessor}, this is a destructive abort — the
|
|
12956
|
+
* queue is drained.
|
|
12663
12957
|
*/
|
|
12664
12958
|
async abort() {
|
|
12665
12959
|
if (this.abortController) {
|
|
@@ -12677,22 +12971,44 @@ var Agent = class {
|
|
|
12677
12971
|
return this.abortController?.signal.aborted ?? false;
|
|
12678
12972
|
}
|
|
12679
12973
|
/**
|
|
12680
|
-
* Subscribe to lifecycle
|
|
12681
|
-
*
|
|
12974
|
+
* Subscribe to a lifecycle event for this specific thread.
|
|
12975
|
+
*
|
|
12976
|
+
* Event names are namespaced as `{eventName}:{tenantId}:{threadId}` so
|
|
12977
|
+
* listeners only receive events for this agent instance.
|
|
12978
|
+
*
|
|
12979
|
+
* @param eventName - One of {@link AgentLifecycleEventName}
|
|
12980
|
+
* @param callback - Handler receiving the event data payload
|
|
12981
|
+
*
|
|
12982
|
+
* @example
|
|
12983
|
+
* ```ts
|
|
12984
|
+
* agent.subscribe("message:completed", (evt) => {
|
|
12985
|
+
* console.log("AI response:", evt.state?.values?.messages);
|
|
12986
|
+
* });
|
|
12987
|
+
* ```
|
|
12682
12988
|
*/
|
|
12683
12989
|
subscribe(eventName, callback) {
|
|
12684
12990
|
const namespacedEvent = `${eventName}:${this.tenant_id}:${this.thread_id}`;
|
|
12685
12991
|
event_bus_default.subscribe(namespacedEvent, callback);
|
|
12686
12992
|
}
|
|
12687
12993
|
/**
|
|
12688
|
-
*
|
|
12994
|
+
* Remove a previously registered event listener.
|
|
12995
|
+
*
|
|
12996
|
+
* @param eventName - The event that was subscribed to
|
|
12997
|
+
* @param callback - The same function reference used in {@link subscribe}
|
|
12689
12998
|
*/
|
|
12690
12999
|
unsubscribe(eventName, callback) {
|
|
12691
13000
|
const namespacedEvent = `${eventName}:${this.tenant_id}:${this.thread_id}`;
|
|
12692
13001
|
event_bus_default.unsubscribe(namespacedEvent, callback);
|
|
12693
13002
|
}
|
|
12694
13003
|
/**
|
|
12695
|
-
* Subscribe to lifecycle
|
|
13004
|
+
* Subscribe to a lifecycle event once — the listener is removed after the
|
|
13005
|
+
* first invocation.
|
|
13006
|
+
*
|
|
13007
|
+
* Ideal for one-shot async patterns (e.g. waiting for `reply:ready` before
|
|
13008
|
+
* sending a channel reply).
|
|
13009
|
+
*
|
|
13010
|
+
* @param eventName - One of {@link AgentLifecycleEventName}
|
|
13011
|
+
* @param callback - Handler receiving the event data payload
|
|
12696
13012
|
*/
|
|
12697
13013
|
subscribeOnce(eventName, callback) {
|
|
12698
13014
|
const namespacedEvent = `${eventName}:${this.tenant_id}:${this.thread_id}`;
|
|
@@ -12706,6 +13022,12 @@ var Agent = class {
|
|
|
12706
13022
|
console.log(namespacedEvent);
|
|
12707
13023
|
event_bus_default.publish(namespacedEvent, data);
|
|
12708
13024
|
}
|
|
13025
|
+
/**
|
|
13026
|
+
* Track a sub-agent async task spawned by this agent.
|
|
13027
|
+
*
|
|
13028
|
+
* Tasks are monitored by the agent task consumer in `packages/gateway`
|
|
13029
|
+
* and their status can be polled via {@link getAsyncTasks}.
|
|
13030
|
+
*/
|
|
12709
13031
|
addAsyncTask(task) {
|
|
12710
13032
|
this.asyncTasks.push(task);
|
|
12711
13033
|
}
|
|
@@ -12715,6 +13037,12 @@ var Agent = class {
|
|
|
12715
13037
|
getAsyncTask(taskId) {
|
|
12716
13038
|
return this.asyncTasks.find((t) => t.taskId === taskId);
|
|
12717
13039
|
}
|
|
13040
|
+
/**
|
|
13041
|
+
* Update the status of a tracked async task.
|
|
13042
|
+
*
|
|
13043
|
+
* Terminal states (`completed`, `failed`, `cancelled`) automatically
|
|
13044
|
+
* set `completedAt` to the current timestamp.
|
|
13045
|
+
*/
|
|
12718
13046
|
updateAsyncTaskStatus(taskId, status) {
|
|
12719
13047
|
const task = this.getAsyncTask(taskId);
|
|
12720
13048
|
if (!task) return;
|
|
@@ -13080,6 +13408,65 @@ function createSchedulerMiddleware(options = {}) {
|
|
|
13080
13408
|
});
|
|
13081
13409
|
}
|
|
13082
13410
|
|
|
13411
|
+
// src/agent_lattice/builders/CustomMiddlewareRegistry.ts
|
|
13412
|
+
var CustomMiddlewareRegistry = class {
|
|
13413
|
+
/**
|
|
13414
|
+
* Register a custom middleware factory under the given key.
|
|
13415
|
+
*
|
|
13416
|
+
* The key is referenced by `config.key` in the database middleware configuration.
|
|
13417
|
+
* When an agent is built, the framework looks up this key and calls the factory
|
|
13418
|
+
* with the remaining config fields.
|
|
13419
|
+
*
|
|
13420
|
+
* @param key - Unique identifier, referenced in database config as `config.key`
|
|
13421
|
+
* @param factory - Function that receives config (minus `key`) and returns an AgentMiddleware
|
|
13422
|
+
*
|
|
13423
|
+
* @example
|
|
13424
|
+
* ```ts
|
|
13425
|
+
* CustomMiddlewareRegistry.register("my-logger", (config) =>
|
|
13426
|
+
* createMiddleware({ name: "Logger", beforeAgent: async () => { ... } }),
|
|
13427
|
+
* );
|
|
13428
|
+
* ```
|
|
13429
|
+
*/
|
|
13430
|
+
static register(key, factory) {
|
|
13431
|
+
this.factories.set(key, factory);
|
|
13432
|
+
}
|
|
13433
|
+
/**
|
|
13434
|
+
* Remove a previously registered factory.
|
|
13435
|
+
*
|
|
13436
|
+
* @param key - The factory key to unregister
|
|
13437
|
+
* @returns `true` if a factory was removed, `false` if the key was not found
|
|
13438
|
+
*/
|
|
13439
|
+
static unregister(key) {
|
|
13440
|
+
return this.factories.delete(key);
|
|
13441
|
+
}
|
|
13442
|
+
/**
|
|
13443
|
+
* Look up a factory by key.
|
|
13444
|
+
*
|
|
13445
|
+
* @param key - The factory key
|
|
13446
|
+
* @returns The factory function, or `undefined` if not registered
|
|
13447
|
+
*/
|
|
13448
|
+
static get(key) {
|
|
13449
|
+
return this.factories.get(key);
|
|
13450
|
+
}
|
|
13451
|
+
/**
|
|
13452
|
+
* Check whether a factory is registered under the given key.
|
|
13453
|
+
*
|
|
13454
|
+
* @param key - The factory key to check
|
|
13455
|
+
*/
|
|
13456
|
+
static has(key) {
|
|
13457
|
+
return this.factories.has(key);
|
|
13458
|
+
}
|
|
13459
|
+
/**
|
|
13460
|
+
* Get all currently registered factory keys.
|
|
13461
|
+
*
|
|
13462
|
+
* @returns Array of registered key strings
|
|
13463
|
+
*/
|
|
13464
|
+
static list() {
|
|
13465
|
+
return Array.from(this.factories.keys());
|
|
13466
|
+
}
|
|
13467
|
+
};
|
|
13468
|
+
CustomMiddlewareRegistry.factories = /* @__PURE__ */ new Map();
|
|
13469
|
+
|
|
13083
13470
|
// src/agent_lattice/builders/commonMiddleware.ts
|
|
13084
13471
|
async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsIsExised) {
|
|
13085
13472
|
const middlewares = [];
|
|
@@ -13156,6 +13543,21 @@ async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsI
|
|
|
13156
13543
|
case "scheduler":
|
|
13157
13544
|
middlewares.push(createSchedulerMiddleware(config.config));
|
|
13158
13545
|
break;
|
|
13546
|
+
case "custom":
|
|
13547
|
+
{
|
|
13548
|
+
const customConfig = config.config;
|
|
13549
|
+
const { key, ...rest } = customConfig;
|
|
13550
|
+
const factory = CustomMiddlewareRegistry.get(key);
|
|
13551
|
+
if (factory) {
|
|
13552
|
+
const middleware = factory(rest);
|
|
13553
|
+
middlewares.push(middleware instanceof Promise ? await middleware : middleware);
|
|
13554
|
+
} else {
|
|
13555
|
+
console.warn(
|
|
13556
|
+
`[custom middleware] No factory registered for key "${key}". Use CustomMiddlewareRegistry.register("${key}", factory) before building the agent.`
|
|
13557
|
+
);
|
|
13558
|
+
}
|
|
13559
|
+
}
|
|
13560
|
+
break;
|
|
13159
13561
|
}
|
|
13160
13562
|
}
|
|
13161
13563
|
return middlewares;
|
|
@@ -17787,6 +18189,227 @@ var ProcessingAgentGraphBuilder = class {
|
|
|
17787
18189
|
}
|
|
17788
18190
|
};
|
|
17789
18191
|
|
|
18192
|
+
// src/agent_lattice/builders/RemoteAgentGraphBuilder.ts
|
|
18193
|
+
import { StateGraph as StateGraph2, MessagesAnnotation } from "@langchain/langgraph";
|
|
18194
|
+
import { AIMessage as AIMessage4 } from "@langchain/core/messages";
|
|
18195
|
+
|
|
18196
|
+
// src/services/a2a-client.ts
|
|
18197
|
+
import { v4 as v42 } from "uuid";
|
|
18198
|
+
var A2ARemoteError = class extends Error {
|
|
18199
|
+
constructor(message, statusCode, body) {
|
|
18200
|
+
super(message);
|
|
18201
|
+
this.statusCode = statusCode;
|
|
18202
|
+
this.body = body;
|
|
18203
|
+
this.name = "A2ARemoteError";
|
|
18204
|
+
}
|
|
18205
|
+
};
|
|
18206
|
+
var A2ARemoteClient = class {
|
|
18207
|
+
constructor(config) {
|
|
18208
|
+
this.config = config;
|
|
18209
|
+
this.jsonRpcUrl = null;
|
|
18210
|
+
this.card = null;
|
|
18211
|
+
}
|
|
18212
|
+
// ── Resolution ───────────────────────────────────────────────────────────
|
|
18213
|
+
/**
|
|
18214
|
+
* Fetch the agent card and resolve the JSON-RPC endpoint.
|
|
18215
|
+
*/
|
|
18216
|
+
async resolve() {
|
|
18217
|
+
if (this.card) return this.card;
|
|
18218
|
+
const resp = await fetch(this.config.agentCardUrl, {
|
|
18219
|
+
signal: AbortSignal.timeout(this.config.timeout ?? 3e4)
|
|
18220
|
+
});
|
|
18221
|
+
if (!resp.ok) {
|
|
18222
|
+
throw new A2ARemoteError(
|
|
18223
|
+
`Failed to fetch agent card: HTTP ${resp.status}`,
|
|
18224
|
+
resp.status
|
|
18225
|
+
);
|
|
18226
|
+
}
|
|
18227
|
+
this.card = await resp.json();
|
|
18228
|
+
const card = this.card;
|
|
18229
|
+
const jsonRpc = card.additionalInterfaces?.find(
|
|
18230
|
+
(i) => i.transport === "JSONRPC"
|
|
18231
|
+
);
|
|
18232
|
+
this.jsonRpcUrl = jsonRpc?.url ?? card.url;
|
|
18233
|
+
return this.card;
|
|
18234
|
+
}
|
|
18235
|
+
// ── Execute ──────────────────────────────────────────────────────────────
|
|
18236
|
+
/**
|
|
18237
|
+
* Send a text prompt to the remote A2A agent and return the response.
|
|
18238
|
+
*
|
|
18239
|
+
* Supports SSE streaming (parses status-update events) and falls back
|
|
18240
|
+
* to polling the task status for non-streaming agents.
|
|
18241
|
+
*/
|
|
18242
|
+
async sendMessage(text) {
|
|
18243
|
+
await this.resolve();
|
|
18244
|
+
const taskId = v42();
|
|
18245
|
+
const body = JSON.stringify({
|
|
18246
|
+
jsonrpc: "2.0",
|
|
18247
|
+
method: "tasks/send",
|
|
18248
|
+
params: {
|
|
18249
|
+
id: taskId,
|
|
18250
|
+
message: {
|
|
18251
|
+
role: "user",
|
|
18252
|
+
parts: [{ kind: "text", text }]
|
|
18253
|
+
}
|
|
18254
|
+
},
|
|
18255
|
+
id: taskId
|
|
18256
|
+
});
|
|
18257
|
+
const headers = {
|
|
18258
|
+
"Content-Type": "application/json",
|
|
18259
|
+
"Accept": "text/event-stream"
|
|
18260
|
+
};
|
|
18261
|
+
if (this.config.apiKey) {
|
|
18262
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
18263
|
+
}
|
|
18264
|
+
const resp = await fetch(this.jsonRpcUrl, {
|
|
18265
|
+
method: "POST",
|
|
18266
|
+
headers,
|
|
18267
|
+
body,
|
|
18268
|
+
signal: AbortSignal.timeout(this.config.timeout ?? 3e5)
|
|
18269
|
+
});
|
|
18270
|
+
if (!resp.ok) {
|
|
18271
|
+
const text2 = await resp.text().catch(() => "");
|
|
18272
|
+
throw new A2ARemoteError(
|
|
18273
|
+
`A2A request failed: HTTP ${resp.status}`,
|
|
18274
|
+
resp.status,
|
|
18275
|
+
text2
|
|
18276
|
+
);
|
|
18277
|
+
}
|
|
18278
|
+
const contentType = resp.headers.get("content-type") ?? "";
|
|
18279
|
+
if (contentType.includes("text/event-stream")) {
|
|
18280
|
+
return this.parseSSE(resp);
|
|
18281
|
+
}
|
|
18282
|
+
const json = await resp.json();
|
|
18283
|
+
return this.extractArtifactText(json);
|
|
18284
|
+
}
|
|
18285
|
+
// ── SSE Parsing ──────────────────────────────────────────────────────────
|
|
18286
|
+
async parseSSE(resp) {
|
|
18287
|
+
if (!resp.body) {
|
|
18288
|
+
throw new A2ARemoteError("Empty response body");
|
|
18289
|
+
}
|
|
18290
|
+
const reader = resp.body.getReader();
|
|
18291
|
+
const decoder = new TextDecoder();
|
|
18292
|
+
let buffer2 = "";
|
|
18293
|
+
let accumulatedText = "";
|
|
18294
|
+
try {
|
|
18295
|
+
while (true) {
|
|
18296
|
+
const { done, value } = await reader.read();
|
|
18297
|
+
if (done) break;
|
|
18298
|
+
buffer2 += decoder.decode(value, { stream: true });
|
|
18299
|
+
const lines = buffer2.split("\n");
|
|
18300
|
+
buffer2 = lines.pop() ?? "";
|
|
18301
|
+
for (const line of lines) {
|
|
18302
|
+
if (line.startsWith("data: ")) {
|
|
18303
|
+
const data = line.slice(6).trim();
|
|
18304
|
+
if (!data) continue;
|
|
18305
|
+
try {
|
|
18306
|
+
const event = JSON.parse(data);
|
|
18307
|
+
this.handleSSEEvent(event, (t) => {
|
|
18308
|
+
accumulatedText += t;
|
|
18309
|
+
});
|
|
18310
|
+
} catch {
|
|
18311
|
+
}
|
|
18312
|
+
}
|
|
18313
|
+
}
|
|
18314
|
+
}
|
|
18315
|
+
if (buffer2.startsWith("data: ")) {
|
|
18316
|
+
const data = buffer2.slice(6).trim();
|
|
18317
|
+
if (data) {
|
|
18318
|
+
try {
|
|
18319
|
+
const event = JSON.parse(data);
|
|
18320
|
+
this.handleSSEEvent(event, (t) => {
|
|
18321
|
+
accumulatedText += t;
|
|
18322
|
+
});
|
|
18323
|
+
} catch {
|
|
18324
|
+
}
|
|
18325
|
+
}
|
|
18326
|
+
}
|
|
18327
|
+
} finally {
|
|
18328
|
+
reader.releaseLock();
|
|
18329
|
+
}
|
|
18330
|
+
return accumulatedText;
|
|
18331
|
+
}
|
|
18332
|
+
handleSSEEvent(event, onText) {
|
|
18333
|
+
const e = event;
|
|
18334
|
+
const messageParts = e.status?.message?.parts;
|
|
18335
|
+
if (messageParts) {
|
|
18336
|
+
for (const part of messageParts) {
|
|
18337
|
+
if (part.text) onText(part.text);
|
|
18338
|
+
}
|
|
18339
|
+
}
|
|
18340
|
+
}
|
|
18341
|
+
// ── Artifact Extraction ──────────────────────────────────────────────────
|
|
18342
|
+
extractArtifactText(task) {
|
|
18343
|
+
const artifacts = task.artifacts ?? [];
|
|
18344
|
+
return artifacts.flatMap((a) => a.parts ?? []).filter((p) => p.text).map((p) => p.text).join("\n");
|
|
18345
|
+
}
|
|
18346
|
+
};
|
|
18347
|
+
|
|
18348
|
+
// src/agent_lattice/builders/RemoteAgentGraphBuilder.ts
|
|
18349
|
+
import { AgentType as AgentType2 } from "@axiom-lattice/protocols";
|
|
18350
|
+
var RemoteAgentGraphBuilder = class {
|
|
18351
|
+
async build(agentLattice, params) {
|
|
18352
|
+
if (agentLattice.config.type !== AgentType2.A2A_REMOTE) {
|
|
18353
|
+
throw new Error(
|
|
18354
|
+
`RemoteAgentGraphBuilder received wrong agent type: ${agentLattice.config.type}`
|
|
18355
|
+
);
|
|
18356
|
+
}
|
|
18357
|
+
const config = agentLattice.config;
|
|
18358
|
+
const client = new A2ARemoteClient({
|
|
18359
|
+
agentCardUrl: config.agentCardUrl,
|
|
18360
|
+
apiKey: config.apiKey,
|
|
18361
|
+
timeout: config.timeout
|
|
18362
|
+
});
|
|
18363
|
+
await client.resolve();
|
|
18364
|
+
const remoteCallNode = async (state) => {
|
|
18365
|
+
const messages = state.messages ?? [];
|
|
18366
|
+
const text = extractLastHumanMessage(messages);
|
|
18367
|
+
if (!text) {
|
|
18368
|
+
return {
|
|
18369
|
+
messages: [
|
|
18370
|
+
new AIMessage4("No text input provided to remote agent.")
|
|
18371
|
+
]
|
|
18372
|
+
};
|
|
18373
|
+
}
|
|
18374
|
+
try {
|
|
18375
|
+
const fullPrompt = params.prompt ? `${params.prompt}
|
|
18376
|
+
|
|
18377
|
+
User request:
|
|
18378
|
+
${text}` : text;
|
|
18379
|
+
const response = await client.sendMessage(fullPrompt);
|
|
18380
|
+
return {
|
|
18381
|
+
messages: [new AIMessage4(response)]
|
|
18382
|
+
};
|
|
18383
|
+
} catch (error) {
|
|
18384
|
+
const msg = error.message ?? String(error);
|
|
18385
|
+
return {
|
|
18386
|
+
messages: [
|
|
18387
|
+
new AIMessage4(`Remote A2A agent error: ${msg}`)
|
|
18388
|
+
]
|
|
18389
|
+
};
|
|
18390
|
+
}
|
|
18391
|
+
};
|
|
18392
|
+
const workflow = new StateGraph2(MessagesAnnotation).addNode("remote_call", remoteCallNode).addEdge("__start__", "remote_call").addEdge("remote_call", "__end__");
|
|
18393
|
+
return workflow.compile();
|
|
18394
|
+
}
|
|
18395
|
+
};
|
|
18396
|
+
function extractLastHumanMessage(messages) {
|
|
18397
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
18398
|
+
const msg = messages[i];
|
|
18399
|
+
if (msg._getType() === "human") {
|
|
18400
|
+
const content = msg.content;
|
|
18401
|
+
if (typeof content === "string") return content;
|
|
18402
|
+
if (Array.isArray(content)) {
|
|
18403
|
+
return content.filter(
|
|
18404
|
+
(c) => typeof c === "object" && c !== null && c.type === "text"
|
|
18405
|
+
).map((c) => c.text).join("\n");
|
|
18406
|
+
}
|
|
18407
|
+
return String(content);
|
|
18408
|
+
}
|
|
18409
|
+
}
|
|
18410
|
+
return "";
|
|
18411
|
+
}
|
|
18412
|
+
|
|
17790
18413
|
// src/agent_lattice/builders/AgentGraphBuilderFactory.ts
|
|
17791
18414
|
var AgentGraphBuilderFactory = class _AgentGraphBuilderFactory {
|
|
17792
18415
|
constructor() {
|
|
@@ -17810,6 +18433,7 @@ var AgentGraphBuilderFactory = class _AgentGraphBuilderFactory {
|
|
|
17810
18433
|
this.builders.set(AgentType.DEEP_AGENT, new DeepAgentGraphBuilder());
|
|
17811
18434
|
this.builders.set(AgentType.TEAM, new TeamAgentGraphBuilder());
|
|
17812
18435
|
this.builders.set(AgentType.PROCESSING, new ProcessingAgentGraphBuilder());
|
|
18436
|
+
this.builders.set(AgentType.A2A_REMOTE, new RemoteAgentGraphBuilder());
|
|
17813
18437
|
}
|
|
17814
18438
|
/**
|
|
17815
18439
|
* 注册自定义Builder
|
|
@@ -18486,8 +19110,8 @@ ${body}` : `${frontmatter}
|
|
|
18486
19110
|
|
|
18487
19111
|
// src/agent_lattice/agentArchitectTools.ts
|
|
18488
19112
|
import z55 from "zod";
|
|
18489
|
-
import { v4 as
|
|
18490
|
-
import { AgentType as
|
|
19113
|
+
import { v4 as v43 } from "uuid";
|
|
19114
|
+
import { AgentType as AgentType3 } from "@axiom-lattice/protocols";
|
|
18491
19115
|
function getTenantId(exeConfig) {
|
|
18492
19116
|
const runConfig = exeConfig?.configurable?.runConfig || {};
|
|
18493
19117
|
return runConfig.tenantId || "default";
|
|
@@ -18595,7 +19219,7 @@ registerToolLattice(
|
|
|
18595
19219
|
key: id,
|
|
18596
19220
|
name: input.name,
|
|
18597
19221
|
description: input.description || "",
|
|
18598
|
-
type: input.type === "deep_agent" ?
|
|
19222
|
+
type: input.type === "deep_agent" ? AgentType3.DEEP_AGENT : AgentType3.REACT,
|
|
18599
19223
|
prompt: input.prompt,
|
|
18600
19224
|
...input.tools && input.tools.length > 0 ? { tools: input.tools } : {},
|
|
18601
19225
|
...input.middleware && input.middleware.length > 0 ? { middleware: input.middleware } : {},
|
|
@@ -18684,7 +19308,7 @@ registerToolLattice(
|
|
|
18684
19308
|
key: id,
|
|
18685
19309
|
name: input.name,
|
|
18686
19310
|
description: input.description || "",
|
|
18687
|
-
type:
|
|
19311
|
+
type: AgentType3.PROCESSING,
|
|
18688
19312
|
prompt: input.prompt,
|
|
18689
19313
|
...input.tools && input.tools.length > 0 ? { tools: input.tools } : {},
|
|
18690
19314
|
middleware,
|
|
@@ -18752,7 +19376,7 @@ registerToolLattice(
|
|
|
18752
19376
|
return JSON.stringify({ error: `Agent '${input.id}' not found` });
|
|
18753
19377
|
}
|
|
18754
19378
|
const existingConfig = existing.graphDefinition || {};
|
|
18755
|
-
if (existingConfig.type !==
|
|
19379
|
+
if (existingConfig.type !== AgentType3.PROCESSING) {
|
|
18756
19380
|
return JSON.stringify({ error: `Agent '${input.id}' is not a PROCESSING agent (type: ${existingConfig.type})` });
|
|
18757
19381
|
}
|
|
18758
19382
|
const normalize = (s) => s.toLowerCase().replace(/[_\-\s]/g, "");
|
|
@@ -18960,7 +19584,7 @@ registerToolLattice(
|
|
|
18960
19584
|
if (!existing) {
|
|
18961
19585
|
return JSON.stringify({ error: `Agent '${id}' not found` });
|
|
18962
19586
|
}
|
|
18963
|
-
const threadId =
|
|
19587
|
+
const threadId = v43();
|
|
18964
19588
|
const agent = new Agent({
|
|
18965
19589
|
tenant_id: tenantId,
|
|
18966
19590
|
assistant_id: id,
|
|
@@ -18984,7 +19608,7 @@ registerToolLattice(
|
|
|
18984
19608
|
);
|
|
18985
19609
|
|
|
18986
19610
|
// src/agent_lattice/agentArchitectConfig.ts
|
|
18987
|
-
import { AgentType as
|
|
19611
|
+
import { AgentType as AgentType5 } from "@axiom-lattice/protocols";
|
|
18988
19612
|
|
|
18989
19613
|
// src/agent_lattice/agentArchitectPrompt.ts
|
|
18990
19614
|
var AGENT_ARCHITECT_PROMPT = `# Agent Architect
|
|
@@ -19624,7 +20248,7 @@ Updates a PROCESSING agent's topology edges, name, prompt, or sub-agents. Unlike
|
|
|
19624
20248
|
`;
|
|
19625
20249
|
|
|
19626
20250
|
// src/agent_lattice/agentReviewerConfig.ts
|
|
19627
|
-
import { AgentType as
|
|
20251
|
+
import { AgentType as AgentType4 } from "@axiom-lattice/protocols";
|
|
19628
20252
|
|
|
19629
20253
|
// src/agent_lattice/agentReviewerPrompt.ts
|
|
19630
20254
|
var AGENT_REVIEWER_PROMPT = `# Agent Reviewer
|
|
@@ -19687,7 +20311,7 @@ var agentReviewerConfig = {
|
|
|
19687
20311
|
key: AGENT_REVIEWER_KEY,
|
|
19688
20312
|
name: "Agent Reviewer",
|
|
19689
20313
|
description: "Review and test AI agents. Use this agent to check agent configurations for correctness and to test agents by invoking them with realistic messages.",
|
|
19690
|
-
type:
|
|
20314
|
+
type: AgentType4.REACT,
|
|
19691
20315
|
prompt: AGENT_REVIEWER_PROMPT,
|
|
19692
20316
|
tools: [
|
|
19693
20317
|
"invoke_agent",
|
|
@@ -19713,7 +20337,7 @@ var agentArchitectConfig = {
|
|
|
19713
20337
|
key: AGENT_ARCHITECT_KEY,
|
|
19714
20338
|
name: "Agent Architect",
|
|
19715
20339
|
description: "Design and manage AI agents through natural language conversation. Use this agent when you want to create a new agent, modify an existing agent, or manage your agent collection (list, view, update, delete).",
|
|
19716
|
-
type:
|
|
20340
|
+
type: AgentType5.DEEP_AGENT,
|
|
19717
20341
|
prompt: AGENT_ARCHITECT_PROMPT,
|
|
19718
20342
|
tools: [
|
|
19719
20343
|
"list_agents",
|
|
@@ -22532,6 +23156,7 @@ export {
|
|
|
22532
23156
|
CompositeBackend,
|
|
22533
23157
|
ConsoleLoggerClient,
|
|
22534
23158
|
CustomMetricsClient,
|
|
23159
|
+
CustomMiddlewareRegistry,
|
|
22535
23160
|
DaytonaInstance,
|
|
22536
23161
|
DaytonaProvider,
|
|
22537
23162
|
DefaultScheduleClient,
|
|
@@ -22542,6 +23167,7 @@ export {
|
|
|
22542
23167
|
FileSystemSkillStore,
|
|
22543
23168
|
FilesystemBackend,
|
|
22544
23169
|
HumanMessage3 as HumanMessage,
|
|
23170
|
+
InMemoryA2AApiKeyStore,
|
|
22545
23171
|
InMemoryAssistantStore,
|
|
22546
23172
|
InMemoryBindingStore,
|
|
22547
23173
|
InMemoryChannelInstallationStore,
|