feat(hub): add singleton pattern and subagent spawning
Add global Hub singleton for cross-module access by subagent tools. Add createSubagent() method to Hub for spawning ephemeral child agents with isSubagent flag and custom system prompts. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
85f71e0084
commit
5b6a1c6953
2 changed files with 54 additions and 0 deletions
28
src/hub/hub-singleton.ts
Normal file
28
src/hub/hub-singleton.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Global Hub singleton for cross-module access.
|
||||
*
|
||||
* Used by subagent tools and announce flow to interact with the Hub
|
||||
* without threading references through the entire call chain.
|
||||
*/
|
||||
|
||||
import type { Hub } from "./hub.js";
|
||||
|
||||
let _hub: Hub | undefined;
|
||||
|
||||
/** Set the global Hub instance. Called once during Hub construction. */
|
||||
export function setHub(hub: Hub): void {
|
||||
_hub = hub;
|
||||
}
|
||||
|
||||
/** Get the global Hub instance. Throws if not yet initialized. */
|
||||
export function getHub(): Hub {
|
||||
if (!_hub) {
|
||||
throw new Error("[Hub] Hub singleton not initialized. Ensure Hub is constructed before accessing.");
|
||||
}
|
||||
return _hub;
|
||||
}
|
||||
|
||||
/** Check if the Hub singleton has been initialized. */
|
||||
export function isHubInitialized(): boolean {
|
||||
return _hub !== undefined;
|
||||
}
|
||||
|
|
@ -9,7 +9,9 @@ import {
|
|||
type ResponseErrorPayload,
|
||||
} from "@multica/sdk";
|
||||
import { AsyncAgent } from "../agent/async-agent.js";
|
||||
import type { AgentOptions } from "../agent/types.js";
|
||||
import { getHubId } from "./hub-identity.js";
|
||||
import { setHub } from "./hub-singleton.js";
|
||||
import { loadAgentRecords, addAgentRecord, removeAgentRecord } from "./agent-store.js";
|
||||
import { RpcDispatcher, RpcError } from "./rpc/dispatcher.js";
|
||||
import { createGetAgentMessagesHandler } from "./rpc/handlers/get-agent-messages.js";
|
||||
|
|
@ -48,6 +50,9 @@ export class Hub {
|
|||
this.rpc.register("deleteAgent", createDeleteAgentHandler(this));
|
||||
this.rpc.register("updateGateway", createUpdateGatewayHandler(this));
|
||||
|
||||
// Register as global singleton for cross-module access (subagent tools, announce flow)
|
||||
setHub(this);
|
||||
|
||||
this.client = this.createClient(this.url);
|
||||
this.client.connect();
|
||||
this.restoreAgents();
|
||||
|
|
@ -243,6 +248,27 @@ export class Hub {
|
|||
}
|
||||
}
|
||||
|
||||
/** Create a subagent with specific options (isSubagent, systemPrompt, model) */
|
||||
createSubagent(sessionId: string, options: Omit<AgentOptions, "sessionId"> = {}): AsyncAgent {
|
||||
const existing = this.agents.get(sessionId);
|
||||
if (existing && !existing.closed) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
const agent = new AsyncAgent({
|
||||
...options,
|
||||
sessionId,
|
||||
isSubagent: true,
|
||||
});
|
||||
this.agents.set(agent.sessionId, agent);
|
||||
|
||||
// Subagents are ephemeral — don't persist to agent store
|
||||
void this.consumeAgent(agent);
|
||||
|
||||
console.log(`[Hub] Subagent created: ${agent.sessionId}`);
|
||||
return agent;
|
||||
}
|
||||
|
||||
getAgent(id: string): AsyncAgent | undefined {
|
||||
return this.agents.get(id);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue