@holoscript/holoscript-agent 2.0.0 → 2.0.2

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/dist/provision.js CHANGED
@@ -14,9 +14,14 @@ async function provisionAgent(req, opts = { execute: false }) {
14
14
  throw new Error(`handle "${req.handle}" must match ${HANDLE_PATTERN}`);
15
15
  }
16
16
  if (!req.founderBearer || req.founderBearer.trim().length === 0) {
17
- throw new Error("founderBearer is required (HOLOMESH_API_KEY of an agent that can call /register)");
17
+ throw new Error(
18
+ "founderBearer is required (HOLOMESH_API_KEY of an agent that can call /register)"
19
+ );
18
20
  }
19
- const meshApiBase = (req.meshApiBase ?? "https://mcp.holoscript.net/api/holomesh").replace(/\/$/, "");
21
+ const meshApiBase = (req.meshApiBase ?? "https://mcp.holoscript.net/api/holomesh").replace(
22
+ /\/$/,
23
+ ""
24
+ );
20
25
  const seatsRoot = req.seatsRoot ?? defaultSeatsRoot();
21
26
  const surface = req.handle;
22
27
  const seatId = makeSeatId(surface);
@@ -31,10 +36,7 @@ async function provisionAgent(req, opts = { execute: false }) {
31
36
  seatId,
32
37
  seatDir,
33
38
  willGenerateWallet: !existsSync(walletPath),
34
- willCallEndpoints: [
35
- `POST ${meshApiBase}/register/challenge`,
36
- `POST ${meshApiBase}/register`
37
- ]
39
+ willCallEndpoints: [`POST ${meshApiBase}/register/challenge`, `POST ${meshApiBase}/register`]
38
40
  };
39
41
  }
40
42
  if (existsSync(walletPath) && !opts.force) {
@@ -77,30 +79,40 @@ async function provisionAgent(req, opts = { execute: false }) {
77
79
  if (!challenge.nonce) {
78
80
  throw new Error(`/register/challenge returned no nonce: ${JSON.stringify(challenge)}`);
79
81
  }
80
- const signature = await wallet.signTypedData(EIP712_DOMAIN, EIP712_TYPES, { nonce: challenge.nonce });
81
- const registration = await postJson(
82
- fetchImpl,
83
- `${meshApiBase}/register`,
84
- req.founderBearer,
85
- {
86
- name: req.handle,
87
- wallet_address: wallet.address,
88
- nonce: challenge.nonce,
89
- signature
90
- }
91
- );
82
+ const signature = await wallet.signTypedData(EIP712_DOMAIN, EIP712_TYPES, {
83
+ nonce: challenge.nonce
84
+ });
85
+ const registration = await postJson(fetchImpl, `${meshApiBase}/register`, req.founderBearer, {
86
+ name: req.handle,
87
+ wallet_address: wallet.address,
88
+ nonce: challenge.nonce,
89
+ signature
90
+ });
92
91
  writeFileSync(
93
92
  regPath,
94
- JSON.stringify({ status: 201, response: registration, registered_at: (/* @__PURE__ */ new Date()).toISOString(), flow: "x402" }, null, 2),
93
+ JSON.stringify(
94
+ {
95
+ status: 201,
96
+ response: registration,
97
+ registered_at: (/* @__PURE__ */ new Date()).toISOString(),
98
+ flow: "x402"
99
+ },
100
+ null,
101
+ 2
102
+ ),
95
103
  "utf8"
96
104
  );
97
105
  const agentId = registration.agent?.id;
98
106
  const bearer = registration.agent?.api_key;
99
107
  if (!agentId || !bearer) {
100
- throw new Error(`/register did not return agent.id + agent.api_key: ${JSON.stringify(registration).slice(0, 400)}`);
108
+ throw new Error(
109
+ `/register did not return agent.id + agent.api_key: ${JSON.stringify(registration).slice(0, 400)}`
110
+ );
101
111
  }
102
112
  if (registration.wallet?.private_key) {
103
- console.warn("[provision] WARN \u2014 server returned private_key despite x402 flow; ignoring (using local key).");
113
+ console.warn(
114
+ "[provision] WARN \u2014 server returned private_key despite x402 flow; ignoring (using local key)."
115
+ );
104
116
  }
105
117
  let joinedTeam;
106
118
  if (req.autoJoinTeamId) {
@@ -160,7 +172,12 @@ function encryptPrivateKey(privKey, masterKey) {
160
172
  const iv = randomBytes(12);
161
173
  const cipher = createCipheriv("aes-256-gcm", masterKey, iv);
162
174
  const ct = Buffer.concat([cipher.update(privKey, "utf8"), cipher.final()]);
163
- return { iv: iv.toString("base64"), ct: ct.toString("base64"), tag: cipher.getAuthTag().toString("base64"), alg: "aes-256-gcm" };
175
+ return {
176
+ iv: iv.toString("base64"),
177
+ ct: ct.toString("base64"),
178
+ tag: cipher.getAuthTag().toString("base64"),
179
+ alg: "aes-256-gcm"
180
+ };
164
181
  }
165
182
  async function postJson(fetchImpl, url, bearer, body) {
166
183
  const res = await fetchImpl(url, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provision.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync, existsSync, chmodSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir, hostname } from 'node:os';\nimport { randomBytes, createCipheriv, createHash } from 'node:crypto';\nimport { Wallet } from 'ethers';\n\nconst HANDLE_PATTERN = /^[a-z0-9_-]{1,64}$/i;\n\nexport interface ProvisionRequest {\n handle: string;\n meshApiBase?: string;\n founderBearer: string;\n seatsRoot?: string;\n fetchImpl?: typeof fetch;\n autoJoinTeamId?: string;\n}\n\nexport interface ProvisionDryRun {\n status: 'dry-run';\n handle: string;\n surface: string;\n seatId: string;\n seatDir: string;\n willGenerateWallet: boolean;\n willCallEndpoints: string[];\n}\n\nexport interface ProvisionExecuted {\n status: 'executed' | 'reused';\n handle: string;\n surface: string;\n seatId: string;\n seatDir: string;\n walletAddress: string;\n bearer?: string;\n agentId?: string;\n envVarLines: string[];\n joinedTeam?: { teamId: string; role: string; members: number } | { teamId: string; error: string };\n}\n\nexport type ProvisionResult = ProvisionDryRun | ProvisionExecuted;\n\nconst EIP712_DOMAIN = { name: 'HoloMesh', version: '1' };\nconst EIP712_TYPES: Record<string, Array<{ name: string; type: string }>> = {\n Registration: [{ name: 'nonce', type: 'string' }],\n};\n\nexport async function provisionAgent(\n req: ProvisionRequest,\n opts: { execute: boolean; force?: boolean } = { execute: false }\n): Promise<ProvisionResult> {\n if (!HANDLE_PATTERN.test(req.handle)) {\n throw new Error(`handle \"${req.handle}\" must match ${HANDLE_PATTERN}`);\n }\n if (!req.founderBearer || req.founderBearer.trim().length === 0) {\n throw new Error('founderBearer is required (HOLOMESH_API_KEY of an agent that can call /register)');\n }\n\n const meshApiBase = (req.meshApiBase ?? 'https://mcp.holoscript.net/api/holomesh').replace(/\\/$/, '');\n const seatsRoot = req.seatsRoot ?? defaultSeatsRoot();\n const surface = req.handle;\n const seatId = makeSeatId(surface);\n const seatDir = join(seatsRoot, seatId);\n const walletPath = join(seatDir, 'wallet.enc');\n const regPath = join(seatDir, 'registration.json');\n\n if (!opts.execute) {\n return {\n status: 'dry-run',\n handle: req.handle,\n surface,\n seatId,\n seatDir,\n willGenerateWallet: !existsSync(walletPath),\n willCallEndpoints: [\n `POST ${meshApiBase}/register/challenge`,\n `POST ${meshApiBase}/register`,\n ],\n };\n }\n\n if (existsSync(walletPath) && !opts.force) {\n const blob = JSON.parse(readFileSync(walletPath, 'utf8')) as { address: string };\n const reused: ProvisionExecuted = {\n status: 'reused',\n handle: req.handle,\n surface,\n seatId,\n seatDir,\n walletAddress: blob.address,\n envVarLines: envVarLinesFor(req.handle, blob.address, undefined),\n };\n return reused;\n }\n\n const wallet = Wallet.createRandom();\n mkdirSync(seatDir, { recursive: true });\n\n const masterKey = ensureMasterKey(seatsRoot);\n const encryptedBlob = {\n seat_id: seatId,\n surface,\n handle: req.handle,\n address: wallet.address,\n encrypted_privkey: encryptPrivateKey(wallet.privateKey, masterKey),\n created_at: new Date().toISOString(),\n source: 'holoscript-agent.provision',\n };\n writeFileSync(walletPath, JSON.stringify(encryptedBlob, null, 2), 'utf8');\n try { chmodSync(walletPath, 0o600); } catch {}\n\n const fetchImpl = req.fetchImpl ?? fetch;\n\n const challenge = await postJson<{ nonce: string }>(\n fetchImpl,\n `${meshApiBase}/register/challenge`,\n req.founderBearer,\n { wallet_address: wallet.address }\n );\n if (!challenge.nonce) {\n throw new Error(`/register/challenge returned no nonce: ${JSON.stringify(challenge)}`);\n }\n\n const signature = await wallet.signTypedData(EIP712_DOMAIN, EIP712_TYPES, { nonce: challenge.nonce });\n\n const registration = await postJson<{\n agent?: { id: string; api_key: string };\n wallet?: { private_key?: string };\n }>(\n fetchImpl,\n `${meshApiBase}/register`,\n req.founderBearer,\n {\n name: req.handle,\n wallet_address: wallet.address,\n nonce: challenge.nonce,\n signature,\n }\n );\n writeFileSync(\n regPath,\n JSON.stringify({ status: 201, response: registration, registered_at: new Date().toISOString(), flow: 'x402' }, null, 2),\n 'utf8'\n );\n\n const agentId = registration.agent?.id;\n const bearer = registration.agent?.api_key;\n if (!agentId || !bearer) {\n throw new Error(`/register did not return agent.id + agent.api_key: ${JSON.stringify(registration).slice(0, 400)}`);\n }\n if (registration.wallet?.private_key) {\n console.warn('[provision] WARN — server returned private_key despite x402 flow; ignoring (using local key).');\n }\n\n let joinedTeam: ProvisionExecuted['joinedTeam'];\n if (req.autoJoinTeamId) {\n try {\n const joinRes = await postJson<{ success?: boolean; role?: string; members?: number }>(\n fetchImpl,\n `${meshApiBase}/team/${req.autoJoinTeamId}/join`,\n bearer,\n {}\n );\n joinedTeam = {\n teamId: req.autoJoinTeamId,\n role: joinRes.role ?? 'member',\n members: joinRes.members ?? 0,\n };\n } catch (err) {\n joinedTeam = {\n teamId: req.autoJoinTeamId,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n return {\n status: 'executed',\n handle: req.handle,\n surface,\n seatId,\n seatDir,\n walletAddress: wallet.address,\n bearer,\n agentId,\n envVarLines: envVarLinesFor(req.handle, wallet.address, bearer),\n joinedTeam,\n };\n}\n\nfunction defaultSeatsRoot(): string {\n return process.env.HOLOSCRIPT_AGENT_SEATS_ROOT\n ?? join(homedir(), '.holoscript-agent', 'seats');\n}\n\nfunction makeSeatId(surface: string): string {\n const fp = createHash('sha256').update(hostname() + homedir()).digest('hex').slice(0, 8);\n return `holoscript-${surface}-${fp}-x402`;\n}\n\nfunction ensureMasterKey(seatsRoot: string): Buffer {\n const keyPath = join(seatsRoot, '.master-key');\n if (!existsSync(seatsRoot)) mkdirSync(seatsRoot, { recursive: true });\n if (!existsSync(keyPath)) {\n const k = randomBytes(32);\n writeFileSync(keyPath, k);\n try { chmodSync(keyPath, 0o600); } catch {}\n }\n return readFileSync(keyPath);\n}\n\nfunction encryptPrivateKey(\n privKey: string,\n masterKey: Buffer\n): { iv: string; ct: string; tag: string; alg: 'aes-256-gcm' } {\n const iv = randomBytes(12);\n const cipher = createCipheriv('aes-256-gcm', masterKey, iv);\n const ct = Buffer.concat([cipher.update(privKey, 'utf8'), cipher.final()]);\n return { iv: iv.toString('base64'), ct: ct.toString('base64'), tag: cipher.getAuthTag().toString('base64'), alg: 'aes-256-gcm' };\n}\n\nasync function postJson<T>(\n fetchImpl: typeof fetch,\n url: string,\n bearer: string,\n body: unknown\n): Promise<T> {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${bearer}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`POST ${url} ${res.status}: ${text.slice(0, 400)}`);\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new Error(`POST ${url} returned non-JSON: ${text.slice(0, 200)}`);\n }\n}\n\nfunction envVarLinesFor(handle: string, walletAddress: string, bearer?: string): string[] {\n const suffix = handle.toUpperCase().replace(/-/g, '_');\n const lines = [`HOLOSCRIPT_AGENT_WALLET_${suffix}=${walletAddress}`];\n if (bearer) {\n lines.push(`HOLOMESH_API_KEY_${suffix}_X402=${bearer}`);\n }\n return lines;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/provision.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync, existsSync, chmodSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir, hostname } from 'node:os';\nimport { randomBytes, createCipheriv, createHash } from 'node:crypto';\nimport { Wallet } from 'ethers';\n\nconst HANDLE_PATTERN = /^[a-z0-9_-]{1,64}$/i;\n\nexport interface ProvisionRequest {\n handle: string;\n meshApiBase?: string;\n founderBearer: string;\n seatsRoot?: string;\n fetchImpl?: typeof fetch;\n autoJoinTeamId?: string;\n}\n\nexport interface ProvisionDryRun {\n status: 'dry-run';\n handle: string;\n surface: string;\n seatId: string;\n seatDir: string;\n willGenerateWallet: boolean;\n willCallEndpoints: string[];\n}\n\nexport interface ProvisionExecuted {\n status: 'executed' | 'reused';\n handle: string;\n surface: string;\n seatId: string;\n seatDir: string;\n walletAddress: string;\n bearer?: string;\n agentId?: string;\n envVarLines: string[];\n joinedTeam?:\n | { teamId: string; role: string; members: number }\n | { teamId: string; error: string };\n}\n\nexport type ProvisionResult = ProvisionDryRun | ProvisionExecuted;\n\nconst EIP712_DOMAIN = { name: 'HoloMesh', version: '1' };\nconst EIP712_TYPES: Record<string, Array<{ name: string; type: string }>> = {\n Registration: [{ name: 'nonce', type: 'string' }],\n};\n\nexport async function provisionAgent(\n req: ProvisionRequest,\n opts: { execute: boolean; force?: boolean } = { execute: false }\n): Promise<ProvisionResult> {\n if (!HANDLE_PATTERN.test(req.handle)) {\n throw new Error(`handle \"${req.handle}\" must match ${HANDLE_PATTERN}`);\n }\n if (!req.founderBearer || req.founderBearer.trim().length === 0) {\n throw new Error(\n 'founderBearer is required (HOLOMESH_API_KEY of an agent that can call /register)'\n );\n }\n\n const meshApiBase = (req.meshApiBase ?? 'https://mcp.holoscript.net/api/holomesh').replace(\n /\\/$/,\n ''\n );\n const seatsRoot = req.seatsRoot ?? defaultSeatsRoot();\n const surface = req.handle;\n const seatId = makeSeatId(surface);\n const seatDir = join(seatsRoot, seatId);\n const walletPath = join(seatDir, 'wallet.enc');\n const regPath = join(seatDir, 'registration.json');\n\n if (!opts.execute) {\n return {\n status: 'dry-run',\n handle: req.handle,\n surface,\n seatId,\n seatDir,\n willGenerateWallet: !existsSync(walletPath),\n willCallEndpoints: [`POST ${meshApiBase}/register/challenge`, `POST ${meshApiBase}/register`],\n };\n }\n\n if (existsSync(walletPath) && !opts.force) {\n const blob = JSON.parse(readFileSync(walletPath, 'utf8')) as { address: string };\n const reused: ProvisionExecuted = {\n status: 'reused',\n handle: req.handle,\n surface,\n seatId,\n seatDir,\n walletAddress: blob.address,\n envVarLines: envVarLinesFor(req.handle, blob.address, undefined),\n };\n return reused;\n }\n\n const wallet = Wallet.createRandom();\n mkdirSync(seatDir, { recursive: true });\n\n const masterKey = ensureMasterKey(seatsRoot);\n const encryptedBlob = {\n seat_id: seatId,\n surface,\n handle: req.handle,\n address: wallet.address,\n encrypted_privkey: encryptPrivateKey(wallet.privateKey, masterKey),\n created_at: new Date().toISOString(),\n source: 'holoscript-agent.provision',\n };\n writeFileSync(walletPath, JSON.stringify(encryptedBlob, null, 2), 'utf8');\n try {\n chmodSync(walletPath, 0o600);\n } catch {}\n\n const fetchImpl = req.fetchImpl ?? fetch;\n\n const challenge = await postJson<{ nonce: string }>(\n fetchImpl,\n `${meshApiBase}/register/challenge`,\n req.founderBearer,\n { wallet_address: wallet.address }\n );\n if (!challenge.nonce) {\n throw new Error(`/register/challenge returned no nonce: ${JSON.stringify(challenge)}`);\n }\n\n const signature = await wallet.signTypedData(EIP712_DOMAIN, EIP712_TYPES, {\n nonce: challenge.nonce,\n });\n\n const registration = await postJson<{\n agent?: { id: string; api_key: string };\n wallet?: { private_key?: string };\n }>(fetchImpl, `${meshApiBase}/register`, req.founderBearer, {\n name: req.handle,\n wallet_address: wallet.address,\n nonce: challenge.nonce,\n signature,\n });\n writeFileSync(\n regPath,\n JSON.stringify(\n {\n status: 201,\n response: registration,\n registered_at: new Date().toISOString(),\n flow: 'x402',\n },\n null,\n 2\n ),\n 'utf8'\n );\n\n const agentId = registration.agent?.id;\n const bearer = registration.agent?.api_key;\n if (!agentId || !bearer) {\n throw new Error(\n `/register did not return agent.id + agent.api_key: ${JSON.stringify(registration).slice(0, 400)}`\n );\n }\n if (registration.wallet?.private_key) {\n console.warn(\n '[provision] WARN — server returned private_key despite x402 flow; ignoring (using local key).'\n );\n }\n\n let joinedTeam: ProvisionExecuted['joinedTeam'];\n if (req.autoJoinTeamId) {\n try {\n const joinRes = await postJson<{ success?: boolean; role?: string; members?: number }>(\n fetchImpl,\n `${meshApiBase}/team/${req.autoJoinTeamId}/join`,\n bearer,\n {}\n );\n joinedTeam = {\n teamId: req.autoJoinTeamId,\n role: joinRes.role ?? 'member',\n members: joinRes.members ?? 0,\n };\n } catch (err) {\n joinedTeam = {\n teamId: req.autoJoinTeamId,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n return {\n status: 'executed',\n handle: req.handle,\n surface,\n seatId,\n seatDir,\n walletAddress: wallet.address,\n bearer,\n agentId,\n envVarLines: envVarLinesFor(req.handle, wallet.address, bearer),\n joinedTeam,\n };\n}\n\nfunction defaultSeatsRoot(): string {\n return process.env.HOLOSCRIPT_AGENT_SEATS_ROOT ?? join(homedir(), '.holoscript-agent', 'seats');\n}\n\nfunction makeSeatId(surface: string): string {\n const fp = createHash('sha256')\n .update(hostname() + homedir())\n .digest('hex')\n .slice(0, 8);\n return `holoscript-${surface}-${fp}-x402`;\n}\n\nfunction ensureMasterKey(seatsRoot: string): Buffer {\n const keyPath = join(seatsRoot, '.master-key');\n if (!existsSync(seatsRoot)) mkdirSync(seatsRoot, { recursive: true });\n if (!existsSync(keyPath)) {\n const k = randomBytes(32);\n writeFileSync(keyPath, k);\n try {\n chmodSync(keyPath, 0o600);\n } catch {}\n }\n return readFileSync(keyPath);\n}\n\nfunction encryptPrivateKey(\n privKey: string,\n masterKey: Buffer\n): { iv: string; ct: string; tag: string; alg: 'aes-256-gcm' } {\n const iv = randomBytes(12);\n const cipher = createCipheriv('aes-256-gcm', masterKey, iv);\n const ct = Buffer.concat([cipher.update(privKey, 'utf8'), cipher.final()]);\n return {\n iv: iv.toString('base64'),\n ct: ct.toString('base64'),\n tag: cipher.getAuthTag().toString('base64'),\n alg: 'aes-256-gcm',\n };\n}\n\nasync function postJson<T>(\n fetchImpl: typeof fetch,\n url: string,\n bearer: string,\n body: unknown\n): Promise<T> {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${bearer}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`POST ${url} ${res.status}: ${text.slice(0, 400)}`);\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new Error(`POST ${url} returned non-JSON: ${text.slice(0, 200)}`);\n }\n}\n\nfunction envVarLinesFor(handle: string, walletAddress: string, bearer?: string): string[] {\n const suffix = handle.toUpperCase().replace(/-/g, '_');\n const lines = [`HOLOSCRIPT_AGENT_WALLET_${suffix}=${walletAddress}`];\n if (bearer) {\n lines.push(`HOLOMESH_API_KEY_${suffix}_X402=${bearer}`);\n }\n return lines;\n}\n"],"mappings":"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","names":[]}
package/dist/runner.d.ts CHANGED
@@ -4,6 +4,61 @@ import { HolomeshClient } from './holomesh-client.js';
4
4
  import { AuditLog } from './audit-log.js';
5
5
  import { AgentIdentity, RuntimeBrainConfig, ExecutionResult, BoardTask, TickResult } from './types.js';
6
6
 
7
+ interface TeamMessage {
8
+ id: string;
9
+ fromAgentId: string;
10
+ fromAgentName: string;
11
+ content: string;
12
+ messageType: string;
13
+ createdAt: string;
14
+ }
15
+ type AuthorityRequestType = 'owner-op' | 'founder-gated';
16
+ interface AuthorityRequest {
17
+ messageId: string;
18
+ fromAgentId: string;
19
+ fromAgentName: string;
20
+ requestType: AuthorityRequestType;
21
+ action: string;
22
+ payload: Record<string, unknown>;
23
+ rawContent: string;
24
+ }
25
+ interface AuthorityReceipt {
26
+ requestMessageId: string;
27
+ status: 'executed' | 'ruled' | 'rejected' | 'escalated' | 'deferred';
28
+ action: string;
29
+ result?: unknown;
30
+ ruling?: string;
31
+ reason: string;
32
+ timestamp: string;
33
+ }
34
+ interface DelegatedAuthorityOptions {
35
+ mesh: HolomeshClient;
36
+ /** Required for founder-gated rulings. Optional if handler only does owner-ops. */
37
+ provider?: ILLMProvider;
38
+ /** System prompt / founder-engine corpus. Injected into the LLM for rulings. */
39
+ systemPrompt?: string;
40
+ /** Agents whose requests are accepted. Empty = accept all team members. */
41
+ allowList?: Set<string>;
42
+ /** Actions this handler is permitted to execute. Empty = all owner-ops. */
43
+ permittedActions?: Set<string>;
44
+ /** Message IDs already processed (persisted across ticks). */
45
+ processedMessageIds?: Set<string>;
46
+ }
47
+ declare class DelegatedAuthorityHandler {
48
+ private readonly mesh;
49
+ private readonly provider?;
50
+ private readonly systemPrompt?;
51
+ private readonly allowList?;
52
+ private readonly permittedActions?;
53
+ private readonly processed;
54
+ constructor(opts: DelegatedAuthorityOptions);
55
+ processMessages(): Promise<AuthorityReceipt[]>;
56
+ parseRequest(msg: TeamMessage): AuthorityRequest | null;
57
+ handleRequest(req: AuthorityRequest): Promise<AuthorityReceipt>;
58
+ private executeOwnerOp;
59
+ private ruleFounderGated;
60
+ }
61
+
7
62
  interface AgentRunnerOptions {
8
63
  identity: AgentIdentity;
9
64
  brain: RuntimeBrainConfig;
@@ -13,6 +68,8 @@ interface AgentRunnerOptions {
13
68
  logger?: (event: Record<string, unknown>) => void;
14
69
  onTaskExecuted?: (result: ExecutionResult, task: BoardTask) => Promise<void>;
15
70
  auditLog?: AuditLog;
71
+ /** Optional delegated-authority handler for governance message processing (E4). */
72
+ messageHandler?: DelegatedAuthorityHandler;
16
73
  }
17
74
  declare class AgentRunner {
18
75
  private readonly opts;