@ericsanchezok/meta-synergy 1.1.5 → 1.1.7

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/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@ericsanchezok/meta-synergy",
4
- "version": "1.1.5",
4
+ "version": "1.1.7",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
+ "bin": {
8
+ "meta-synergy": "./src/cli.ts"
9
+ },
7
10
  "exports": {
8
11
  ".": {
9
12
  "import": "./dist/index.js",
package/script/publish.ts CHANGED
@@ -21,12 +21,18 @@ for (const [key, value] of Object.entries(pkg.exports)) {
21
21
  await Bun.write("package.json", JSON.stringify(pkg, null, 2))
22
22
 
23
23
  try {
24
- const NPM_REGISTRY = "https://registry.npmjs.org"
25
- await $`rm -f *.tgz`.nothrow()
26
- await $`bun pm pack`
27
- const tgz = (await $`ls *.tgz`.text()).trim()
28
- const authFlag = process.env.NPM_TOKEN ? `--//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}` : ""
29
- await $`npm publish ${tgz} --tag ${Script.channel} --registry ${NPM_REGISTRY} --access public ${authFlag}`
24
+ if (await Script.npmVersionExists(pkg.name, Script.version)) {
25
+ console.log(`${pkg.name}@${Script.version} already published, skipping`)
26
+ } else {
27
+ const NPM_REGISTRY = "https://registry.npmjs.org"
28
+ await $`rm -f *.tgz`.nothrow()
29
+ await $`bun pm pack`
30
+ const tgz = (await $`ls *.tgz`.text()).trim()
31
+ const authFlag = process.env.NPM_TOKEN ? `--//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}` : ""
32
+ await Script.retry(
33
+ () => $`npm publish ${tgz} --tag ${Script.channel} --registry ${NPM_REGISTRY} --access public ${authFlag}`,
34
+ )
35
+ }
30
36
  } finally {
31
37
  await Bun.write("package.json", JSON.stringify(original, null, 2))
32
38
  }
package/src/cli.ts ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env bun
2
+ import { spawn } from "node:child_process"
3
+ import { MetaSynergyRuntime } from "./runtime"
4
+
5
+ async function main() {
6
+ const command = process.argv[2] ?? "start"
7
+ const runtime = await MetaSynergyRuntime.create()
8
+
9
+ switch (command) {
10
+ case "start":
11
+ await runtime.start()
12
+ return
13
+ case "login": {
14
+ const result = await runtime.login()
15
+ console.log(`Logged in as ${result.agentID}`)
16
+ return
17
+ }
18
+ case "status": {
19
+ const status = await runtime.status()
20
+ console.log(JSON.stringify(status, null, 2))
21
+ return
22
+ }
23
+ case "logout":
24
+ await runtime.logout()
25
+ console.log("Logged out from Holos.")
26
+ return
27
+ case "enable":
28
+ await runtime.setCollaborationEnabled(true)
29
+ console.log("Collaboration enabled.")
30
+ return
31
+ case "disable":
32
+ await runtime.setCollaborationEnabled(false)
33
+ console.log("Collaboration disabled.")
34
+ return
35
+ case "kick": {
36
+ await runtime.requestKick(false)
37
+ console.log("Requested current collaboration session to close.")
38
+ return
39
+ }
40
+ case "block": {
41
+ await runtime.requestKick(true)
42
+ console.log("Requested current collaboration session to close and block the collaborator.")
43
+ return
44
+ }
45
+ case "reconnect":
46
+ await runtime.requestReconnect()
47
+ console.log("Requested Holos reconnect.")
48
+ return
49
+ case "menu":
50
+ await new Promise<void>((resolve, reject) => {
51
+ const child = spawn("swift", ["src/menu.swift"], { stdio: "inherit" })
52
+ child.once("exit", (code) => {
53
+ if ((code ?? 0) === 0) resolve()
54
+ else reject(new Error(`Menu exited with code ${code ?? 0}`))
55
+ })
56
+ child.once("error", reject)
57
+ })
58
+ return
59
+ default:
60
+ console.error(`Unknown command: ${command}`)
61
+ console.error("Usage: bun meta-synergy [start|login|status|logout|enable|disable|kick|block|reconnect|menu]")
62
+ process.exit(1)
63
+ }
64
+ }
65
+
66
+ await main()
@@ -0,0 +1,146 @@
1
+ import { MetaProtocolBridge } from "@ericsanchezok/meta-protocol"
2
+ import type { RPCResult } from "../rpc/schema"
3
+ import { MetaSynergyHolosEnvelope } from "./envelope"
4
+ import { MetaSynergyHolosProtocol } from "./protocol"
5
+ import type { MetaSynergyInboundHandler } from "../inbound/handler"
6
+ import { MetaSynergyLog } from "../log"
7
+
8
+ const HOLOS_HOST = "www.holosai.io"
9
+ const HOLOS_URL = `https://${HOLOS_HOST}`
10
+ const HOLOS_WS_URL = `wss://${HOLOS_HOST}`
11
+
12
+ export class MetaSynergyHolosClient {
13
+ #ws: WebSocket | null = null
14
+ #heartbeat: ReturnType<typeof setInterval> | null = null
15
+
16
+ constructor(
17
+ readonly auth: { agentID: string; agentSecret: string },
18
+ readonly inbound: MetaSynergyInboundHandler,
19
+ ) {}
20
+
21
+ async connect() {
22
+ const token = await fetchWsToken(this.auth.agentSecret)
23
+ const endpoint = `${HOLOS_WS_URL}/api/v1/holos/agent_tunnel/ws?token=${token}`
24
+ MetaSynergyLog.info("holos.connect.begin", {
25
+ agentID: this.auth.agentID,
26
+ endpoint,
27
+ })
28
+ const ws = new WebSocket(endpoint)
29
+ this.#ws = ws
30
+
31
+ await new Promise<void>((resolve, reject) => {
32
+ let opened = false
33
+ ws.addEventListener("open", () => {
34
+ opened = true
35
+ MetaSynergyLog.info("holos.connect.open", {
36
+ agentID: this.auth.agentID,
37
+ })
38
+ resolve()
39
+ })
40
+ ws.addEventListener("error", (error) => {
41
+ MetaSynergyLog.error("holos.connect.error", {
42
+ agentID: this.auth.agentID,
43
+ error: String(error),
44
+ })
45
+ if (!opened) reject(new Error("Failed to connect to Holos websocket."))
46
+ })
47
+ ws.addEventListener("close", () => {
48
+ MetaSynergyLog.warn("holos.connect.closed_before_open", {
49
+ agentID: this.auth.agentID,
50
+ })
51
+ if (!opened) reject(new Error("Holos websocket closed before opening."))
52
+ })
53
+ })
54
+
55
+ ws.addEventListener("message", (event) => {
56
+ void this.#handleMessage(String(event.data))
57
+ })
58
+ ws.addEventListener("close", () => {
59
+ MetaSynergyLog.warn("holos.socket.closed", {
60
+ agentID: this.auth.agentID,
61
+ })
62
+ if (this.#heartbeat) clearInterval(this.#heartbeat)
63
+ this.#heartbeat = null
64
+ this.#ws = null
65
+ })
66
+
67
+ this.#heartbeat = setInterval(() => {
68
+ if (ws.readyState === WebSocket.OPEN) {
69
+ ws.send(MetaSynergyHolosEnvelope.ping())
70
+ }
71
+ }, 60_000)
72
+ this.#heartbeat.unref?.()
73
+ }
74
+
75
+ async disconnect() {
76
+ MetaSynergyLog.info("holos.disconnect", {
77
+ agentID: this.auth.agentID,
78
+ })
79
+ if (this.#heartbeat) clearInterval(this.#heartbeat)
80
+ this.#heartbeat = null
81
+ this.#ws?.close()
82
+ this.#ws = null
83
+ }
84
+
85
+ connected() {
86
+ return this.#ws?.readyState === WebSocket.OPEN
87
+ }
88
+
89
+ async #handleMessage(raw: string) {
90
+ MetaSynergyLog.info("holos.message.received.raw", {
91
+ raw,
92
+ })
93
+ const parsed = MetaSynergyHolosEnvelope.parse(raw)
94
+ if (!parsed) {
95
+ MetaSynergyLog.warn("holos.message.ignored.unparsed")
96
+ return
97
+ }
98
+
99
+ MetaSynergyLog.info("holos.message.received.parsed", {
100
+ event: parsed.event,
101
+ callerAgentID: parsed.caller.agentID,
102
+ callerOwnerUserID: parsed.caller.ownerUserID,
103
+ payload: parsed.payload,
104
+ })
105
+
106
+ if (parsed.event !== MetaProtocolBridge.RequestEvent) {
107
+ MetaSynergyLog.info("holos.message.ignored.non_request_event", {
108
+ event: parsed.event,
109
+ callerAgentID: parsed.caller.agentID,
110
+ })
111
+ return
112
+ }
113
+
114
+ const result = await this.inbound.handle({ caller: parsed.caller, body: parsed.payload })
115
+ this.#sendResult(parsed.caller.agentID, result)
116
+ }
117
+
118
+ #sendResult(targetAgentID: string, result: RPCResult) {
119
+ if (this.#ws?.readyState !== WebSocket.OPEN) {
120
+ MetaSynergyLog.warn("holos.response.dropped.socket_not_open", {
121
+ targetAgentID,
122
+ result,
123
+ })
124
+ return
125
+ }
126
+ MetaSynergyLog.info("holos.response.sending", {
127
+ targetAgentID,
128
+ result,
129
+ })
130
+ this.#ws.send(MetaSynergyHolosEnvelope.response(targetAgentID, result))
131
+ }
132
+ }
133
+
134
+ async function fetchWsToken(agentSecret: string): Promise<string> {
135
+ const response = await fetch(`${HOLOS_URL}/api/v1/holos/agent_tunnel/ws_token`, {
136
+ headers: { Authorization: `Bearer ${agentSecret}` },
137
+ })
138
+ if (!response.ok) {
139
+ throw new Error(`Failed to get Holos ws token: ${response.status} ${response.statusText}`)
140
+ }
141
+ const body = MetaSynergyHolosProtocol.WsTokenResponse.parse(await response.json())
142
+ if (body.code !== 0) {
143
+ throw new Error(body.message ?? "Failed to get Holos ws token.")
144
+ }
145
+ return body.data.ws_token
146
+ }
@@ -0,0 +1,68 @@
1
+ import { MetaProtocolBridge } from "@ericsanchezok/meta-protocol"
2
+ import type { HolosCaller } from "../types"
3
+ import { MetaSynergyHolosProtocol } from "./protocol"
4
+
5
+ export namespace MetaSynergyHolosEnvelope {
6
+ export function parse(raw: string): { event: string; payload: unknown; caller: HolosCaller } | null {
7
+ let data: unknown
8
+ try {
9
+ data = JSON.parse(raw)
10
+ } catch {
11
+ return null
12
+ }
13
+
14
+ const parsed = MetaSynergyHolosProtocol.Envelope.safeParse(data)
15
+ if (!parsed.success || parsed.data.type !== "ws_send" || !parsed.data.caller) {
16
+ return null
17
+ }
18
+
19
+ return {
20
+ event: String(parsed.data.meta.event ?? ""),
21
+ payload: parsed.data.payload,
22
+ caller: {
23
+ type: parsed.data.caller.type,
24
+ agentID: parsed.data.caller.agent_id,
25
+ ownerUserID: parsed.data.caller.owner_user_id,
26
+ profile: parsed.data.caller.profile,
27
+ },
28
+ }
29
+ }
30
+
31
+ export function request(targetAgentID: string, payload: unknown, requestID = crypto.randomUUID()): string {
32
+ return JSON.stringify({
33
+ type: "ws_send",
34
+ request_id: requestID,
35
+ meta: {
36
+ target_agent_id: targetAgentID,
37
+ event: MetaProtocolBridge.RequestEvent,
38
+ content_type: "application/json",
39
+ },
40
+ payload,
41
+ caller: null,
42
+ })
43
+ }
44
+
45
+ export function response(targetAgentID: string, payload: unknown, requestID = crypto.randomUUID()): string {
46
+ return JSON.stringify({
47
+ type: "ws_send",
48
+ request_id: requestID,
49
+ meta: {
50
+ target_agent_id: targetAgentID,
51
+ event: MetaProtocolBridge.ResponseEvent,
52
+ content_type: "application/json",
53
+ },
54
+ payload,
55
+ caller: null,
56
+ })
57
+ }
58
+
59
+ export function ping(): string {
60
+ return JSON.stringify({
61
+ type: "ping",
62
+ request_id: null,
63
+ meta: { timestamp: Date.now() },
64
+ payload: null,
65
+ caller: null,
66
+ })
67
+ }
68
+ }
@@ -0,0 +1,128 @@
1
+ import process from "node:process"
2
+ import { createServer, type IncomingMessage } from "node:http"
3
+ import { spawn } from "node:child_process"
4
+ import { MetaSynergyStore } from "../state/store"
5
+ import { MetaSynergyHolosProtocol } from "./protocol"
6
+
7
+ const HOLOS_HOST = "www.holosai.io"
8
+ const HOLOS_URL = `https://${HOLOS_HOST}`
9
+
10
+ export namespace MetaSynergyHolosLogin {
11
+ export function createBindURL(input: { callbackURL: string; state: string }) {
12
+ return (
13
+ `${HOLOS_URL}/api/v1/holos/agent_tunnel/bind/start` +
14
+ `?local_callback=${encodeURIComponent(input.callbackURL)}` +
15
+ `&state=${encodeURIComponent(input.state)}`
16
+ )
17
+ }
18
+
19
+ export async function verifySecret(agentSecret: string): Promise<{ valid: true } | { valid: false; reason: string }> {
20
+ const response = await fetch(`${HOLOS_URL}/api/v1/holos/agent_tunnel/ws_token`, {
21
+ headers: { Authorization: `Bearer ${agentSecret}` },
22
+ })
23
+ const body = MetaSynergyHolosProtocol.WsTokenResponse.safeParse(await response.json())
24
+ if (!body.success || !response.ok || body.data.code !== 0) {
25
+ return { valid: false, reason: body.success ? body.data.message ?? "Invalid response" : "Invalid response" }
26
+ }
27
+ return { valid: true }
28
+ }
29
+
30
+ export async function login(): Promise<{ agentID: string }> {
31
+ await MetaSynergyStore.ensureRoot()
32
+ const state = crypto.randomUUID()
33
+ const port = 19836 + Math.floor(Math.random() * 1000)
34
+ const callbackURL = `http://127.0.0.1:${port}/holos/login`
35
+ const bindURL = createBindURL({ callbackURL, state })
36
+
37
+ const callback = new Promise<{ code: string; state: string }>((resolve, reject) => {
38
+ const server = createServer((request, response) => {
39
+ const params = parseRequest(request)
40
+ if (!params || params.pathname !== "/holos/login") {
41
+ response.statusCode = 404
42
+ response.end("Not found")
43
+ return
44
+ }
45
+
46
+ server.close()
47
+ if (!params.code || !params.state) {
48
+ reject(new Error("Missing login callback parameters."))
49
+ response.statusCode = 400
50
+ response.end("Login failed.")
51
+ return
52
+ }
53
+
54
+ resolve({ code: params.code, state: params.state })
55
+ response.end("Login successful. You can close this window.")
56
+ })
57
+ server.listen(port, "127.0.0.1")
58
+
59
+ const timer = setTimeout(() => {
60
+ server.close()
61
+ reject(new Error("Login timed out."))
62
+ }, 5 * 60_000)
63
+ timer.unref?.()
64
+ })
65
+
66
+ try {
67
+ await launchBrowser(bindURL)
68
+ } catch {
69
+ console.log(`Open this URL to continue login:\n${bindURL}`)
70
+ }
71
+
72
+ const params = await callback
73
+ if (params.state !== state) {
74
+ throw new Error("State mismatch during Holos login.")
75
+ }
76
+
77
+ const exchangeResponse = await fetch(`${HOLOS_URL}/api/v1/holos/agent_tunnel/bind/exchange`, {
78
+ method: "POST",
79
+ headers: { "Content-Type": "application/json" },
80
+ body: JSON.stringify({
81
+ code: params.code,
82
+ state: params.state,
83
+ profile: { name: "Meta Synergy Host" },
84
+ }),
85
+ })
86
+ if (!exchangeResponse.ok) {
87
+ throw new Error(`Exchange failed: ${exchangeResponse.status} ${exchangeResponse.statusText}`)
88
+ }
89
+
90
+ const exchangeBody = MetaSynergyHolosProtocol.BindExchangeResponse.parse(await exchangeResponse.json())
91
+ if (exchangeBody.code !== 0) {
92
+ throw new Error(exchangeBody.message ?? exchangeBody.msg ?? "Holos exchange failed.")
93
+ }
94
+
95
+ const agentSecret = exchangeBody.data.agent_secret ?? exchangeBody.data.secret
96
+ if (!agentSecret) {
97
+ throw new Error("Holos exchange did not return an agent secret.")
98
+ }
99
+
100
+ await MetaSynergyStore.saveAuth({
101
+ agentID: exchangeBody.data.agent_id,
102
+ agentSecret,
103
+ })
104
+
105
+ return { agentID: exchangeBody.data.agent_id }
106
+ }
107
+ }
108
+
109
+ function parseRequest(request: IncomingMessage) {
110
+ if (!request.url) return
111
+ const url = new URL(request.url, "http://127.0.0.1")
112
+ return {
113
+ pathname: url.pathname,
114
+ code: url.searchParams.get("code") ?? undefined,
115
+ state: url.searchParams.get("state") ?? undefined,
116
+ }
117
+ }
118
+
119
+ async function launchBrowser(url: string): Promise<void> {
120
+ const command =
121
+ process.platform === "darwin"
122
+ ? ["open", url]
123
+ : process.platform === "win32"
124
+ ? ["cmd", "/c", "start", "", url]
125
+ : ["xdg-open", url]
126
+ const child = spawn(command[0], command.slice(1), { stdio: "ignore", detached: true })
127
+ child.unref()
128
+ }
@@ -0,0 +1,38 @@
1
+ import z from "zod"
2
+
3
+ export namespace MetaSynergyHolosProtocol {
4
+ export const Caller = z.object({
5
+ type: z.string(),
6
+ agent_id: z.string(),
7
+ owner_user_id: z.number(),
8
+ profile: z.record(z.string(), z.unknown()).optional(),
9
+ })
10
+
11
+ export const Envelope = z.object({
12
+ type: z.string(),
13
+ request_id: z.string().nullable(),
14
+ meta: z.record(z.string(), z.unknown()),
15
+ payload: z.unknown().nullable(),
16
+ caller: Caller.nullable().optional(),
17
+ })
18
+
19
+ export const WsTokenResponse = z.object({
20
+ code: z.number(),
21
+ message: z.string().optional(),
22
+ data: z.object({
23
+ ws_token: z.string(),
24
+ expires_in: z.number(),
25
+ }),
26
+ })
27
+
28
+ export const BindExchangeResponse = z.object({
29
+ code: z.number(),
30
+ msg: z.string().optional(),
31
+ message: z.string().optional(),
32
+ data: z.object({
33
+ agent_id: z.string(),
34
+ agent_secret: z.string().optional(),
35
+ secret: z.string().optional(),
36
+ }),
37
+ })
38
+ }
@@ -0,0 +1,206 @@
1
+ import {
2
+ MetaProtocolBash,
3
+ MetaProtocolEnvelope,
4
+ MetaProtocolError,
5
+ MetaProtocolProcess,
6
+ MetaProtocolSession,
7
+ } from "@ericsanchezok/meta-protocol"
8
+ import type { HolosCaller } from "../types"
9
+ import { RPCHandler } from "../rpc/handler"
10
+ import { RPCRequestSchema, type RPCResult } from "../rpc/schema"
11
+ import { SessionManager } from "../session/manager"
12
+ import { MetaSynergyLog } from "../log"
13
+
14
+ export class MetaSynergyInboundHandler {
15
+ constructor(
16
+ readonly rpc: RPCHandler,
17
+ readonly sessions: SessionManager,
18
+ readonly collaborationEnabled: () => boolean,
19
+ readonly confirmOpen?: (input: { caller: HolosCaller; label?: string }) => Promise<boolean>,
20
+ ) {}
21
+
22
+ async handle(input: { caller: HolosCaller; body: unknown }): Promise<RPCResult> {
23
+ try {
24
+ const request = RPCRequestSchema.parse(input.body)
25
+ MetaSynergyLog.info("inbound.request.accepted", {
26
+ callerAgentID: input.caller.agentID,
27
+ callerOwnerUserID: input.caller.ownerUserID,
28
+ tool: request.tool,
29
+ action: request.action,
30
+ requestID: request.requestID,
31
+ envID: request.envID,
32
+ sessionID: "sessionID" in request ? request.sessionID : undefined,
33
+ payload: request.payload,
34
+ })
35
+
36
+ if (request.tool === "session") {
37
+ return this.#handleSession(input.caller, request)
38
+ }
39
+
40
+ if (!this.collaborationEnabled()) {
41
+ MetaSynergyLog.warn("inbound.request.refused.collaboration_disabled", {
42
+ callerAgentID: input.caller.agentID,
43
+ tool: request.tool,
44
+ action: request.action,
45
+ requestID: request.requestID,
46
+ })
47
+ return errorResult(
48
+ { requestID: request.requestID, tool: request.tool, action: request.action },
49
+ "session_refused",
50
+ "Collaboration is currently disabled.",
51
+ )
52
+ }
53
+
54
+ this.sessions.validateCaller(input.caller, request.sessionID)
55
+ const result = await this.rpc.handle(request)
56
+ MetaSynergyLog.info("inbound.request.completed", {
57
+ callerAgentID: input.caller.agentID,
58
+ tool: request.tool,
59
+ action: request.action,
60
+ requestID: request.requestID,
61
+ result,
62
+ })
63
+ return result
64
+ } catch (error) {
65
+ if (isEnvelopeError(error)) {
66
+ MetaSynergyLog.warn("inbound.request.failed.envelope", {
67
+ callerAgentID: input.caller.agentID,
68
+ code: error.code,
69
+ message: error.message,
70
+ details: error.details,
71
+ })
72
+ return errorResult(
73
+ {
74
+ requestID: error.requestID,
75
+ tool: error.tool,
76
+ action: error.action,
77
+ },
78
+ error.code,
79
+ error.message,
80
+ error.details,
81
+ )
82
+ }
83
+
84
+ MetaSynergyLog.error("inbound.request.failed.unexpected", {
85
+ callerAgentID: input.caller.agentID,
86
+ error: error instanceof Error ? error.message : String(error),
87
+ })
88
+ return errorResult(
89
+ undefined,
90
+ "host_internal_error",
91
+ error instanceof Error ? error.message : String(error),
92
+ )
93
+ }
94
+ }
95
+
96
+ async #handleSession(
97
+ caller: HolosCaller,
98
+ request: MetaProtocolSession.ExecuteRequest,
99
+ ): Promise<MetaProtocolSession.ExecuteResult | MetaProtocolEnvelope.ErrorResult> {
100
+ MetaSynergyLog.info("session.request.received", {
101
+ callerAgentID: caller.agentID,
102
+ callerOwnerUserID: caller.ownerUserID,
103
+ action: request.payload.action,
104
+ requestID: request.requestID,
105
+ payload: request.payload,
106
+ })
107
+
108
+ if (!this.collaborationEnabled() && request.payload.action === "open") {
109
+ MetaSynergyLog.warn("session.request.refused.collaboration_disabled", {
110
+ callerAgentID: caller.agentID,
111
+ requestID: request.requestID,
112
+ })
113
+ return errorResult(
114
+ { requestID: request.requestID, tool: request.tool, action: request.action },
115
+ "session_refused",
116
+ "Collaboration is currently disabled.",
117
+ )
118
+ }
119
+
120
+ if (request.payload.action === "open" && !this.sessions.current()) {
121
+ const approved = await (this.confirmOpen?.({
122
+ caller,
123
+ label: request.payload.label,
124
+ }) ?? Promise.resolve(true))
125
+ if (!approved) {
126
+ MetaSynergyLog.warn("session.request.refused.user_denied", {
127
+ callerAgentID: caller.agentID,
128
+ requestID: request.requestID,
129
+ })
130
+ return errorResult(
131
+ { requestID: request.requestID, tool: request.tool, action: request.action },
132
+ "session_refused",
133
+ "User denied this collaboration request.",
134
+ )
135
+ }
136
+ }
137
+
138
+ let result: MetaProtocolSession.Result
139
+ switch (request.payload.action) {
140
+ case "open":
141
+ result = await this.sessions.open(caller, request.payload.label)
142
+ break
143
+ case "close":
144
+ result = await this.sessions.close(caller, request.payload.sessionID)
145
+ break
146
+ case "heartbeat":
147
+ result = await this.sessions.heartbeat(caller, request.payload.sessionID)
148
+ break
149
+ }
150
+
151
+ const response = {
152
+ version: 1,
153
+ requestID: request.requestID,
154
+ ok: true,
155
+ tool: request.tool,
156
+ action: request.action,
157
+ result,
158
+ } as const
159
+
160
+ MetaSynergyLog.info("session.request.completed", {
161
+ callerAgentID: caller.agentID,
162
+ requestID: request.requestID,
163
+ action: request.payload.action,
164
+ status: result.metadata.status,
165
+ sessionID: result.metadata.sessionID,
166
+ result,
167
+ })
168
+
169
+ return response
170
+ }
171
+ }
172
+
173
+ function errorResult(
174
+ request:
175
+ | {
176
+ requestID?: string
177
+ tool?: MetaProtocolEnvelope.Tool
178
+ action?: string
179
+ }
180
+ | undefined,
181
+ code: MetaProtocolError.Code,
182
+ message: string,
183
+ details?: unknown,
184
+ ): MetaProtocolEnvelope.ErrorResult {
185
+ return {
186
+ version: 1,
187
+ requestID: request?.requestID || crypto.randomUUID(),
188
+ ok: false,
189
+ tool: request?.tool,
190
+ action: request?.action,
191
+ error: { code, message, details },
192
+ }
193
+ }
194
+
195
+ function isEnvelopeError(
196
+ error: unknown,
197
+ ): error is {
198
+ requestID?: string
199
+ tool?: MetaProtocolEnvelope.Tool
200
+ action?: string
201
+ code: MetaProtocolError.Code
202
+ message: string
203
+ details?: unknown
204
+ } {
205
+ return typeof error === "object" && error !== null && "code" in error && "message" in error
206
+ }