quorum-eliza-plugin 0.2.0 → 0.3.0

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.
@@ -1,13 +1,2 @@
1
- export declare const createMultisigAction: {
2
- name: string;
3
- description: string;
4
- similes: string[];
5
- examples: {
6
- user: string;
7
- content: {
8
- text: string;
9
- };
10
- }[][];
11
- validate: (runtime: any, message: any) => Promise<any>;
12
- handler: (runtime: any, message: any, state: any, options: Record<string, unknown>, callback?: any) => Promise<any>;
13
- };
1
+ import type { Action } from '../types.js';
2
+ export declare const createMultisigAction: Action;
@@ -11,11 +11,11 @@ export const createMultisigAction = {
11
11
  ],
12
12
  examples: [
13
13
  [
14
- { user: '{{user1}}', content: { text: 'Create a 2-of-3 Bitcoin multisig called "Team Treasury"' } },
15
- { user: '{{agent}}', content: { text: 'Created multisig "Team Treasury" (2-of-3). Invite code: abc123. Share this with other signers to join.' } },
14
+ { name: '{{user1}}', content: { text: 'Create a 2-of-3 Bitcoin multisig called "Team Treasury"' } },
15
+ { name: '{{agent}}', content: { text: 'Created multisig "Team Treasury" (2-of-3). Invite code: abc123. Share this with other signers to join.' } },
16
16
  ],
17
17
  ],
18
- validate: async (runtime, message) => {
18
+ validate: async (runtime, message, state) => {
19
19
  const text = message.content?.text?.toLowerCase() || '';
20
20
  return text.includes('create') && (text.includes('multisig') || text.includes('wallet') || text.includes('treasury'));
21
21
  },
@@ -50,11 +50,11 @@ export const createMultisigAction = {
50
50
  **Join Link:** https://quorumclaw.com/join/${result.inviteCode}
51
51
 
52
52
  Share this with other signers to join the wallet.`;
53
- callback?.({ text: response });
53
+ await callback?.({ text: response });
54
54
  return true;
55
55
  }
56
56
  catch (err) {
57
- callback?.({ text: `❌ Failed to create multisig: ${err.message}` });
57
+ await callback?.({ text: `❌ Failed to create multisig: ${err.message}` });
58
58
  return false;
59
59
  }
60
60
  },
@@ -1,13 +1,2 @@
1
- export declare const createProposalAction: {
2
- name: string;
3
- description: string;
4
- similes: string[];
5
- examples: {
6
- user: string;
7
- content: {
8
- text: string;
9
- };
10
- }[][];
11
- validate: (runtime: any, message: any) => Promise<any>;
12
- handler: (runtime: any, message: any, state: any, options: Record<string, unknown>, callback?: any) => Promise<any>;
13
- };
1
+ import type { Action } from '../types.js';
2
+ export declare const createProposalAction: Action;
@@ -11,11 +11,11 @@ export const createProposalAction = {
11
11
  ],
12
12
  examples: [
13
13
  [
14
- { user: '{{user1}}', content: { text: 'Send 5000 sats to bc1q... from our treasury' } },
15
- { user: '{{agent}}', content: { text: 'Created proposal! ID: abc123. Waiting for 2 more signatures.' } },
14
+ { name: '{{user1}}', content: { text: 'Send 5000 sats to bc1q... from our treasury' } },
15
+ { name: '{{agent}}', content: { text: 'Created proposal! ID: abc123. Waiting for 2 more signatures.' } },
16
16
  ],
17
17
  ],
18
- validate: async (runtime, message) => {
18
+ validate: async (runtime, message, state) => {
19
19
  const text = message.content?.text?.toLowerCase() || '';
20
20
  return (text.includes('send') || text.includes('propose') || text.includes('transfer')) &&
21
21
  (text.includes('multisig') || text.includes('treasury') || text.includes('proposal') || text.includes('sats'));
@@ -26,7 +26,7 @@ export const createProposalAction = {
26
26
  // Parse amount
27
27
  const amountMatch = text.match(/(\d+(?:,\d+)?)\s*(?:sats?|satoshis?)/i);
28
28
  if (!amountMatch) {
29
- callback?.({ text: '❌ Please specify an amount in sats. Example: "Send 5000 sats to bc1q..."' });
29
+ await callback?.({ text: '❌ Please specify an amount in sats. Example: "Send 5000 sats to bc1q..."' });
30
30
  return false;
31
31
  }
32
32
  const amount = parseInt(amountMatch[1].replace(/,/g, ''));
@@ -35,14 +35,14 @@ export const createProposalAction = {
35
35
  text.match(/(tb1[a-z0-9]{39,87})/i) || // Testnet
36
36
  text.match(/(0x[a-fA-F0-9]{40})/i); // EVM
37
37
  if (!addressMatch) {
38
- callback?.({ text: '❌ Please provide a recipient address. Example: "Send 5000 sats to bc1q..."' });
38
+ await callback?.({ text: '❌ Please provide a recipient address. Example: "Send 5000 sats to bc1q..."' });
39
39
  return false;
40
40
  }
41
41
  const recipient = addressMatch[1];
42
42
  // Get multisigs
43
43
  const multisigs = await quorumService.listMultisigs();
44
44
  if (multisigs.length === 0) {
45
- callback?.({ text: '❌ You are not part of any multisigs. Create or join one first.' });
45
+ await callback?.({ text: '❌ You are not part of any multisigs. Create or join one first.' });
46
46
  return false;
47
47
  }
48
48
  // Use first multisig or parse from message
@@ -57,7 +57,7 @@ export const createProposalAction = {
57
57
  amount,
58
58
  note,
59
59
  });
60
- callback?.({
60
+ await callback?.({
61
61
  text: `✅ **Proposal Created**
62
62
 
63
63
  **ID:** \`${proposal.id}\`
@@ -72,7 +72,7 @@ https://quorumclaw.com/p/${proposal.id}`
72
72
  return true;
73
73
  }
74
74
  catch (err) {
75
- callback?.({ text: `❌ Failed to create proposal: ${err.message}` });
75
+ await callback?.({ text: `❌ Failed to create proposal: ${err.message}` });
76
76
  return false;
77
77
  }
78
78
  },
@@ -1,13 +1,2 @@
1
- export declare const joinMultisigAction: {
2
- name: string;
3
- description: string;
4
- similes: string[];
5
- examples: {
6
- user: string;
7
- content: {
8
- text: string;
9
- };
10
- }[][];
11
- validate: (runtime: any, message: any) => Promise<any>;
12
- handler: (runtime: any, message: any, state: any, options: Record<string, unknown>, callback?: any) => Promise<any>;
13
- };
1
+ import type { Action } from '../types.js';
2
+ export declare const joinMultisigAction: Action;
@@ -10,11 +10,11 @@ export const joinMultisigAction = {
10
10
  ],
11
11
  examples: [
12
12
  [
13
- { user: '{{user1}}', content: { text: 'Join multisig with code abc123' } },
14
- { user: '{{agent}}', content: { text: 'Joined multisig "Team Treasury"! Address: bc1p...' } },
13
+ { name: '{{user1}}', content: { text: 'Join multisig with code abc123' } },
14
+ { name: '{{agent}}', content: { text: 'Joined multisig "Team Treasury"! Address: bc1p...' } },
15
15
  ],
16
16
  ],
17
- validate: async (runtime, message) => {
17
+ validate: async (runtime, message, state) => {
18
18
  const text = message.content?.text?.toLowerCase() || '';
19
19
  return text.includes('join') && (text.includes('multisig') || text.includes('wallet') || text.includes('code'));
20
20
  },
@@ -25,7 +25,7 @@ export const joinMultisigAction = {
25
25
  const codeMatch = text.match(/(?:code|invite)?\s*([a-f0-9]{8})/i) ||
26
26
  text.match(/join\/([a-f0-9]{8})/i);
27
27
  if (!codeMatch) {
28
- callback?.({ text: '❌ Please provide an invite code. Example: "Join multisig with code abc12345"' });
28
+ await callback?.({ text: '❌ Please provide an invite code. Example: "Join multisig with code abc12345"' });
29
29
  return false;
30
30
  }
31
31
  const inviteCode = codeMatch[1];
@@ -38,11 +38,11 @@ export const joinMultisigAction = {
38
38
  **Signers:** ${multisig.agents.map(a => a.name).join(', ')}
39
39
 
40
40
  The wallet is ready to receive funds.`;
41
- callback?.({ text: response });
41
+ await callback?.({ text: response });
42
42
  return true;
43
43
  }
44
44
  catch (err) {
45
- callback?.({ text: `❌ Failed to join multisig: ${err.message}` });
45
+ await callback?.({ text: `❌ Failed to join multisig: ${err.message}` });
46
46
  return false;
47
47
  }
48
48
  },
@@ -1,13 +1,2 @@
1
- export declare const listProposalsAction: {
2
- name: string;
3
- description: string;
4
- similes: string[];
5
- examples: {
6
- user: string;
7
- content: {
8
- text: string;
9
- };
10
- }[][];
11
- validate: (runtime: any, message: any) => Promise<any>;
12
- handler: (runtime: any, message: any, state: any, options: Record<string, unknown>, callback?: any) => Promise<any>;
13
- };
1
+ import type { Action } from '../types.js';
2
+ export declare const listProposalsAction: Action;
@@ -11,11 +11,11 @@ export const listProposalsAction = {
11
11
  ],
12
12
  examples: [
13
13
  [
14
- { user: '{{user1}}', content: { text: 'Show pending proposals' } },
15
- { user: '{{agent}}', content: { text: 'You have 2 pending proposals:\n- 5000 sats to bc1q... (1/2 sigs)\n- 10000 sats to bc1p... (0/3 sigs)' } },
14
+ { name: '{{user1}}', content: { text: 'Show pending proposals' } },
15
+ { name: '{{agent}}', content: { text: 'You have 2 pending proposals:\n- 5000 sats to bc1q... (1/2 sigs)\n- 10000 sats to bc1p... (0/3 sigs)' } },
16
16
  ],
17
17
  ],
18
- validate: async (runtime, message) => {
18
+ validate: async (runtime, message, state) => {
19
19
  const text = message.content?.text?.toLowerCase() || '';
20
20
  return (text.includes('list') || text.includes('show') || text.includes('pending') || text.includes('check')) &&
21
21
  (text.includes('proposal') || text.includes('transaction') || text.includes('signing'));
@@ -24,7 +24,7 @@ export const listProposalsAction = {
24
24
  try {
25
25
  const proposals = await quorumService.listPendingProposals();
26
26
  if (proposals.length === 0) {
27
- callback?.({ text: '✅ No pending proposals. All caught up!' });
27
+ await callback?.({ text: '✅ No pending proposals. All caught up!' });
28
28
  return true;
29
29
  }
30
30
  const list = proposals.map(p => {
@@ -34,13 +34,13 @@ export const listProposalsAction = {
34
34
  return `• **${amount.toLocaleString()} sats** → ${shortRecipient}
35
35
  ID: \`${p.id.slice(0, 8)}...\` | Sigs: ${p.signatures.length}/? | ${p.note || 'No note'}`;
36
36
  }).join('\n\n');
37
- callback?.({
37
+ await callback?.({
38
38
  text: `📋 **Pending Proposals (${proposals.length})**\n\n${list}\n\nSay "sign proposal <id>" to approve.`
39
39
  });
40
40
  return true;
41
41
  }
42
42
  catch (err) {
43
- callback?.({ text: `❌ Failed to list proposals: ${err.message}` });
43
+ await callback?.({ text: `❌ Failed to list proposals: ${err.message}` });
44
44
  return false;
45
45
  }
46
46
  },
@@ -1,13 +1,2 @@
1
- export declare const signProposalAction: {
2
- name: string;
3
- description: string;
4
- similes: string[];
5
- examples: {
6
- user: string;
7
- content: {
8
- text: string;
9
- };
10
- }[][];
11
- validate: (runtime: any, message: any) => Promise<any>;
12
- handler: (runtime: any, message: any, state: any, options: Record<string, unknown>, callback?: any) => Promise<any>;
13
- };
1
+ import type { Action } from '../types.js';
2
+ export declare const signProposalAction: Action;
@@ -11,11 +11,11 @@ export const signProposalAction = {
11
11
  ],
12
12
  examples: [
13
13
  [
14
- { user: '{{user1}}', content: { text: 'Sign proposal abc123' } },
15
- { user: '{{agent}}', content: { text: 'Signed! 2/3 signatures collected. Waiting for one more signer.' } },
14
+ { name: '{{user1}}', content: { text: 'Sign proposal abc123' } },
15
+ { name: '{{agent}}', content: { text: 'Signed! 2/3 signatures collected. Waiting for one more signer.' } },
16
16
  ],
17
17
  ],
18
- validate: async (runtime, message) => {
18
+ validate: async (runtime, message, state) => {
19
19
  const text = message.content?.text?.toLowerCase() || '';
20
20
  return (text.includes('sign') || text.includes('approve')) &&
21
21
  (text.includes('proposal') || text.includes('transaction') || text.includes('tx'));
@@ -30,42 +30,42 @@ export const signProposalAction = {
30
30
  // Try to find pending proposals
31
31
  const pending = await quorumService.listPendingProposals();
32
32
  if (pending.length === 0) {
33
- callback?.({ text: '❌ No pending proposals found. Provide a proposal ID or create a new proposal.' });
33
+ await callback?.({ text: '❌ No pending proposals found. Provide a proposal ID or create a new proposal.' });
34
34
  return false;
35
35
  }
36
36
  if (pending.length === 1) {
37
37
  // Auto-sign the only pending proposal
38
38
  const result = await quorumService.signProposal(pending[0].id);
39
39
  if (result.txid) {
40
- callback?.({ text: `✅ Signed and broadcast! txid: ${result.txid}\n\nhttps://mempool.space/tx/${result.txid}` });
40
+ await callback?.({ text: `✅ Signed and broadcast! txid: ${result.txid}\n\nhttps://mempool.space/tx/${result.txid}` });
41
41
  }
42
42
  else {
43
- callback?.({ text: `✅ Signed! Proposal status: ${result.status}. Waiting for more signatures.` });
43
+ await callback?.({ text: `✅ Signed! Proposal status: ${result.status}. Waiting for more signatures.` });
44
44
  }
45
45
  return true;
46
46
  }
47
47
  // List pending proposals
48
48
  const list = pending.map(p => `- \`${p.id.slice(0, 8)}...\`: ${p.outputs.map(o => `${o.amount} sats`).join(', ')} (${p.signatures.length} sigs)`).join('\n');
49
- callback?.({ text: `Multiple pending proposals. Please specify which one:\n\n${list}` });
49
+ await callback?.({ text: `Multiple pending proposals. Please specify which one:\n\n${list}` });
50
50
  return false;
51
51
  }
52
52
  const proposalId = idMatch[1];
53
53
  const result = await quorumService.signProposal(proposalId);
54
54
  if (result.txid) {
55
- callback?.({
55
+ await callback?.({
56
56
  text: `✅ **Threshold met! Transaction broadcast.**\n\n**txid:** \`${result.txid}\`\n\nhttps://mempool.space/tx/${result.txid}`
57
57
  });
58
58
  }
59
59
  else {
60
60
  const proposal = await quorumService.getProposal(proposalId);
61
- callback?.({
61
+ await callback?.({
62
62
  text: `✅ Signed! ${proposal.signatures.length}/${proposal.sighashes.length + 1} signatures collected. Waiting for more signers.`
63
63
  });
64
64
  }
65
65
  return true;
66
66
  }
67
67
  catch (err) {
68
- callback?.({ text: `❌ Failed to sign: ${err.message}` });
68
+ await callback?.({ text: `❌ Failed to sign: ${err.message}` });
69
69
  return false;
70
70
  }
71
71
  },
package/dist/index.d.ts CHANGED
@@ -3,33 +3,15 @@
3
3
  *
4
4
  * Enables any Eliza agent to participate in multi-agent wallets via Quorum.
5
5
  */
6
- export declare const quorumPlugin: {
7
- name: string;
8
- description: string;
9
- init(runtime: any): Promise<void>;
10
- actions: {
11
- name: string;
12
- description: string;
13
- similes: string[];
14
- examples: {
15
- user: string;
16
- content: {
17
- text: string;
18
- };
19
- }[][];
20
- validate: (runtime: any, message: any) => Promise<any>;
21
- handler: (runtime: any, message: any, state: any, options: Record<string, unknown>, callback?: any) => Promise<any>;
22
- }[];
23
- providers: {
24
- name: string;
25
- description: string;
26
- get: (runtime: any, message: any, state?: any) => Promise<any>;
27
- }[];
28
- };
6
+ import type { Plugin } from './types.js';
7
+ export declare const quorumPlugin: Plugin;
29
8
  export default quorumPlugin;
9
+ export type { IAgentRuntime, Plugin, Action, Provider, Memory, State, Content, Handler, Validator, HandlerCallback, } from './types.js';
30
10
  export { quorumService } from './services/quorum.js';
31
- export * from './actions/createMultisig.js';
32
- export * from './actions/joinMultisig.js';
33
- export * from './actions/signProposal.js';
34
- export * from './actions/listProposals.js';
35
- export * from './actions/createProposal.js';
11
+ export type { QuorumAgent, QuorumMultisig, QuorumProposal, QuorumConfig } from './services/quorum.js';
12
+ export { createMultisigAction } from './actions/createMultisig.js';
13
+ export { joinMultisigAction } from './actions/joinMultisig.js';
14
+ export { signProposalAction } from './actions/signProposal.js';
15
+ export { listProposalsAction } from './actions/listProposals.js';
16
+ export { createProposalAction } from './actions/createProposal.js';
17
+ export { multisigProvider } from './providers/multisigInfo.js';
package/dist/index.js CHANGED
@@ -27,10 +27,11 @@ export const quorumPlugin = {
27
27
  providers: [multisigProvider],
28
28
  };
29
29
  export default quorumPlugin;
30
- // Re-export for convenience
30
+ // Re-export service and actions
31
31
  export { quorumService } from './services/quorum.js';
32
- export * from './actions/createMultisig.js';
33
- export * from './actions/joinMultisig.js';
34
- export * from './actions/signProposal.js';
35
- export * from './actions/listProposals.js';
36
- export * from './actions/createProposal.js';
32
+ export { createMultisigAction } from './actions/createMultisig.js';
33
+ export { joinMultisigAction } from './actions/joinMultisig.js';
34
+ export { signProposalAction } from './actions/signProposal.js';
35
+ export { listProposalsAction } from './actions/listProposals.js';
36
+ export { createProposalAction } from './actions/createProposal.js';
37
+ export { multisigProvider } from './providers/multisigInfo.js';
@@ -1,5 +1,2 @@
1
- export declare const multisigProvider: {
2
- name: string;
3
- description: string;
4
- get: (runtime: any, message: any, state?: any) => Promise<any>;
5
- };
1
+ import type { Provider } from '../types.js';
2
+ export declare const multisigProvider: Provider;
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Manages connection to Quorum API and handles signing operations.
5
5
  */
6
+ import type { IAgentRuntime } from '../types.js';
6
7
  export interface QuorumAgent {
7
8
  id: string;
8
9
  name: string;
@@ -35,12 +36,25 @@ export interface QuorumProposal {
35
36
  note?: string;
36
37
  createdAt: string;
37
38
  }
39
+ export interface QuorumConfig {
40
+ privateKey?: string;
41
+ apiUrl?: string;
42
+ agentName?: string;
43
+ }
38
44
  declare class QuorumService {
39
45
  private agentId;
46
+ private agentName;
40
47
  private publicKey;
41
48
  private privateKey;
49
+ private apiUrl;
50
+ private initialized;
42
51
  get capabilityDescription(): string;
43
- initialize(runtime: any): Promise<void>;
52
+ isInitialized(): boolean;
53
+ initialize(runtime: IAgentRuntime): Promise<void>;
54
+ /**
55
+ * Initialize with explicit config (useful for testing)
56
+ */
57
+ initializeWithConfig(config: QuorumConfig): Promise<void>;
44
58
  register(name: string): Promise<QuorumAgent>;
45
59
  createMultisig(params: {
46
60
  name: string;
@@ -51,7 +65,7 @@ declare class QuorumService {
51
65
  multisig: QuorumMultisig;
52
66
  inviteCode: string;
53
67
  }>;
54
- joinMultisig(inviteCode: string): Promise<QuorumMultisig>;
68
+ joinMultisig(inviteCode: string, agentName?: string): Promise<QuorumMultisig>;
55
69
  listMultisigs(): Promise<QuorumMultisig[]>;
56
70
  listPendingProposals(): Promise<QuorumProposal[]>;
57
71
  getProposal(proposalId: string): Promise<QuorumProposal>;
@@ -68,6 +82,11 @@ declare class QuorumService {
68
82
  }): Promise<QuorumProposal>;
69
83
  getAgentId(): string | null;
70
84
  getPublicKey(): string | null;
85
+ getApiUrl(): string;
86
+ /**
87
+ * Reset service state (useful for testing)
88
+ */
89
+ reset(): void;
71
90
  }
72
91
  export declare const quorumService: QuorumService;
73
92
  export default quorumService;
@@ -8,11 +8,17 @@ import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
8
8
  const QUORUM_API = process.env.QUORUM_API_URL || 'https://quorumclaw.com';
9
9
  class QuorumService {
10
10
  agentId = null;
11
+ agentName = null;
11
12
  publicKey = null;
12
13
  privateKey = null;
14
+ apiUrl = QUORUM_API;
15
+ initialized = false;
13
16
  get capabilityDescription() {
14
17
  return 'Multi-agent wallet coordination via Quorum';
15
18
  }
19
+ isInitialized() {
20
+ return this.initialized && this.agentId !== null;
21
+ }
16
22
  async initialize(runtime) {
17
23
  // Try to get private key from runtime settings
18
24
  const privateKeyHex = runtime.getSetting?.('QUORUM_PRIVATE_KEY') ||
@@ -22,75 +28,116 @@ class QuorumService {
22
28
  console.warn('[Quorum] No private key configured. Set QUORUM_PRIVATE_KEY or WALLET_PRIVATE_KEY.');
23
29
  return;
24
30
  }
31
+ // Get API URL override if set
32
+ const apiUrlOverride = runtime.getSetting?.('QUORUM_API_URL') || process.env.QUORUM_API_URL;
33
+ if (apiUrlOverride) {
34
+ this.apiUrl = apiUrlOverride;
35
+ }
25
36
  try {
26
37
  const keyHex = String(privateKeyHex).replace('0x', '');
27
38
  this.privateKey = hexToBytes(keyHex);
28
39
  this.publicKey = bytesToHex(schnorr.getPublicKey(this.privateKey));
29
40
  // Register with Quorum
30
- await this.register(runtime.character?.name || 'Eliza Agent');
31
- console.log(`[Quorum] Initialized. Agent ID: ${this.agentId}`);
41
+ const agentName = runtime.character?.name || 'Eliza Agent';
42
+ await this.register(agentName);
43
+ this.initialized = true;
44
+ console.log(`[Quorum] Initialized. Agent ID: ${this.agentId}, Public Key: ${this.publicKey?.slice(0, 16)}...`);
32
45
  }
33
46
  catch (err) {
34
47
  console.error('[Quorum] Failed to initialize:', err);
48
+ throw err;
49
+ }
50
+ }
51
+ /**
52
+ * Initialize with explicit config (useful for testing)
53
+ */
54
+ async initializeWithConfig(config) {
55
+ if (!config.privateKey) {
56
+ throw new Error('Private key is required');
35
57
  }
58
+ if (config.apiUrl) {
59
+ this.apiUrl = config.apiUrl;
60
+ }
61
+ const keyHex = config.privateKey.replace('0x', '');
62
+ this.privateKey = hexToBytes(keyHex);
63
+ this.publicKey = bytesToHex(schnorr.getPublicKey(this.privateKey));
64
+ await this.register(config.agentName || 'Test Agent');
65
+ this.initialized = true;
36
66
  }
37
67
  async register(name) {
38
68
  if (!this.publicKey)
39
69
  throw new Error('No public key available');
40
- const res = await fetch(`${QUORUM_API}/v1/agents`, {
70
+ const res = await fetch(`${this.apiUrl}/v1/agents`, {
41
71
  method: 'POST',
42
72
  headers: { 'Content-Type': 'application/json' },
43
73
  body: JSON.stringify({
44
74
  name,
45
75
  publicKey: this.publicKey,
46
- provider: 'eliza',
76
+ provider: 'custom', // 'eliza' not in allowed enum, use 'custom'
77
+ metadata: { framework: 'eliza' }, // Store framework info in metadata
47
78
  }),
48
79
  });
49
80
  const json = await res.json();
50
81
  if (!json.success)
51
82
  throw new Error(json.error?.message || 'Registration failed');
52
83
  this.agentId = json.data.id;
84
+ this.agentName = name;
53
85
  return json.data;
54
86
  }
55
87
  async createMultisig(params) {
56
88
  if (!this.agentId)
57
89
  throw new Error('Not registered with Quorum');
58
- const res = await fetch(`${QUORUM_API}/v1/invites`, {
90
+ const res = await fetch(`${this.apiUrl}/v1/invites`, {
59
91
  method: 'POST',
60
92
  headers: { 'Content-Type': 'application/json' },
61
93
  body: JSON.stringify({
62
94
  name: params.name,
63
95
  chainId: params.chainId,
64
96
  threshold: params.threshold,
65
- slots: params.totalSigners,
66
- creatorAgentId: this.agentId,
97
+ totalSigners: params.totalSigners,
67
98
  }),
68
99
  });
69
100
  const json = await res.json();
70
101
  if (!json.success)
71
102
  throw new Error(json.error?.message || 'Create failed');
72
103
  return {
73
- multisig: json.data.multisig,
74
- inviteCode: json.data.code,
104
+ multisig: json.data, // Invite data until multisig is finalized
105
+ inviteCode: json.data.inviteId || json.data.id,
75
106
  };
76
107
  }
77
- async joinMultisig(inviteCode) {
78
- if (!this.agentId)
79
- throw new Error('Not registered with Quorum');
80
- const res = await fetch(`${QUORUM_API}/v1/invites/${inviteCode}/join`, {
108
+ async joinMultisig(inviteCode, agentName) {
109
+ if (!this.publicKey)
110
+ throw new Error('Not initialized');
111
+ const res = await fetch(`${this.apiUrl}/v1/invites/${inviteCode}/join`, {
81
112
  method: 'POST',
82
113
  headers: { 'Content-Type': 'application/json' },
83
- body: JSON.stringify({ agentId: this.agentId }),
114
+ body: JSON.stringify({
115
+ name: agentName || this.agentName || 'Agent',
116
+ publicKey: this.publicKey,
117
+ }),
84
118
  });
85
119
  const json = await res.json();
86
120
  if (!json.success)
87
121
  throw new Error(json.error?.message || 'Join failed');
88
- return json.data.multisig;
122
+ // API returns invite data directly; convert to multisig format
123
+ const data = json.data;
124
+ return {
125
+ id: data.multisigId || data.id,
126
+ name: data.name,
127
+ address: data.address || 'pending',
128
+ chainId: data.chainId,
129
+ threshold: data.threshold,
130
+ agents: data.slots.filter((s) => s.publicKey).map((s) => ({
131
+ id: s.sessionId,
132
+ name: s.name,
133
+ publicKey: s.publicKey,
134
+ })),
135
+ };
89
136
  }
90
137
  async listMultisigs() {
91
138
  if (!this.agentId)
92
139
  return [];
93
- const res = await fetch(`${QUORUM_API}/v1/agents/${this.agentId}/multisigs`);
140
+ const res = await fetch(`${this.apiUrl}/v1/agents/${this.agentId}/multisigs`);
94
141
  const json = await res.json();
95
142
  return json.success ? json.data : [];
96
143
  }
@@ -98,7 +145,7 @@ class QuorumService {
98
145
  const multisigs = await this.listMultisigs();
99
146
  const proposals = [];
100
147
  for (const ms of multisigs) {
101
- const res = await fetch(`${QUORUM_API}/v1/proposals?multisigId=${ms.id}&status=pending`);
148
+ const res = await fetch(`${this.apiUrl}/v1/proposals?multisigId=${ms.id}&status=pending`);
102
149
  const json = await res.json();
103
150
  if (json.success) {
104
151
  proposals.push(...json.data);
@@ -107,7 +154,7 @@ class QuorumService {
107
154
  return proposals;
108
155
  }
109
156
  async getProposal(proposalId) {
110
- const res = await fetch(`${QUORUM_API}/v1/proposals/${proposalId}`);
157
+ const res = await fetch(`${this.apiUrl}/v1/proposals/${proposalId}`);
111
158
  const json = await res.json();
112
159
  if (!json.success)
113
160
  throw new Error(json.error?.message || 'Proposal not found');
@@ -130,7 +177,7 @@ class QuorumService {
130
177
  for (const sh of proposal.sighashes) {
131
178
  const sighashBytes = hexToBytes(sh.sighash);
132
179
  const signature = schnorr.sign(sighashBytes, this.privateKey);
133
- const res = await fetch(`${QUORUM_API}/v1/proposals/${proposalId}/sign`, {
180
+ const res = await fetch(`${this.apiUrl}/v1/proposals/${proposalId}/sign`, {
134
181
  method: 'POST',
135
182
  headers: { 'Content-Type': 'application/json' },
136
183
  body: JSON.stringify({
@@ -153,7 +200,7 @@ class QuorumService {
153
200
  return { success: true, status: 'pending' };
154
201
  }
155
202
  async createProposal(params) {
156
- const res = await fetch(`${QUORUM_API}/v1/proposals`, {
203
+ const res = await fetch(`${this.apiUrl}/v1/proposals`, {
157
204
  method: 'POST',
158
205
  headers: { 'Content-Type': 'application/json' },
159
206
  body: JSON.stringify({
@@ -174,6 +221,20 @@ class QuorumService {
174
221
  getPublicKey() {
175
222
  return this.publicKey;
176
223
  }
224
+ getApiUrl() {
225
+ return this.apiUrl;
226
+ }
227
+ /**
228
+ * Reset service state (useful for testing)
229
+ */
230
+ reset() {
231
+ this.agentId = null;
232
+ this.agentName = null;
233
+ this.publicKey = null;
234
+ this.privateKey = null;
235
+ this.initialized = false;
236
+ this.apiUrl = QUORUM_API;
237
+ }
177
238
  }
178
239
  export const quorumService = new QuorumService();
179
240
  export default quorumService;