@moltzap/client 2026.503.4 → 2026.504.1
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/channel-core.d.ts +1 -4
- package/dist/channel-core.d.ts.map +1 -1
- package/dist/channel-core.js +0 -9
- package/dist/channel-core.js.map +1 -1
- package/dist/channel-core.test.js +90 -71
- package/dist/channel-core.test.js.map +1 -1
- package/dist/cli/commands/agents.js +2 -2
- package/dist/cli/commands/agents.js.map +1 -1
- package/dist/cli/commands/apps.d.ts +2 -24
- package/dist/cli/commands/apps.d.ts.map +1 -1
- package/dist/cli/commands/apps.js +22 -47
- package/dist/cli/commands/apps.js.map +1 -1
- package/dist/cli/commands/apps.test.js +59 -72
- package/dist/cli/commands/apps.test.js.map +1 -1
- package/dist/cli/commands/contacts.d.ts +1 -5
- package/dist/cli/commands/contacts.d.ts.map +1 -1
- package/dist/cli/commands/contacts.js +11 -26
- package/dist/cli/commands/contacts.js.map +1 -1
- package/dist/cli/commands/conversations-v2.test.js +30 -26
- package/dist/cli/commands/conversations-v2.test.js.map +1 -1
- package/dist/cli/commands/conversations.d.ts.map +1 -1
- package/dist/cli/commands/conversations.js +48 -33
- package/dist/cli/commands/conversations.js.map +1 -1
- package/dist/cli/commands/messages.d.ts.map +1 -1
- package/dist/cli/commands/messages.js +11 -8
- package/dist/cli/commands/messages.js.map +1 -1
- package/dist/cli/commands/messages.test.js +28 -30
- package/dist/cli/commands/messages.test.js.map +1 -1
- package/dist/cli/commands/presence.d.ts.map +1 -1
- package/dist/cli/commands/presence.js +5 -4
- package/dist/cli/commands/presence.js.map +1 -1
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +20 -11
- package/dist/cli/commands/send.js.map +1 -1
- package/dist/cli/commands/send.test.js +11 -9
- package/dist/cli/commands/send.test.js.map +1 -1
- package/dist/cli/commands/status.js +2 -2
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/test-transport.d.ts +10 -0
- package/dist/cli/commands/test-transport.d.ts.map +1 -0
- package/dist/cli/commands/test-transport.js +29 -0
- package/dist/cli/commands/test-transport.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +0 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/socket-client.d.ts +9 -4
- package/dist/cli/socket-client.d.ts.map +1 -1
- package/dist/cli/socket-client.js +27 -12
- package/dist/cli/socket-client.js.map +1 -1
- package/dist/cli/transport.d.ts +3 -2
- package/dist/cli/transport.d.ts.map +1 -1
- package/dist/cli/transport.js +6 -6
- package/dist/cli/transport.js.map +1 -1
- package/dist/cli/transport.test.js +4 -4
- package/dist/cli/transport.test.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/__tests__/app-callback-partition-key.test.d.ts +2 -0
- package/dist/internal/__tests__/app-callback-partition-key.test.d.ts.map +1 -0
- package/dist/internal/__tests__/app-callback-partition-key.test.js +33 -0
- package/dist/internal/__tests__/app-callback-partition-key.test.js.map +1 -0
- package/dist/internal/__tests__/app-callback-partition-worker.test.d.ts +2 -0
- package/dist/internal/__tests__/app-callback-partition-worker.test.d.ts.map +1 -0
- package/dist/internal/__tests__/{s2c-partition-worker.test.js → app-callback-partition-worker.test.js} +6 -9
- package/dist/internal/__tests__/app-callback-partition-worker.test.js.map +1 -0
- package/dist/internal/__tests__/app-callback-partitioned-dispatcher-real-ws.test.d.ts +2 -0
- package/dist/internal/__tests__/app-callback-partitioned-dispatcher-real-ws.test.d.ts.map +1 -0
- package/dist/internal/__tests__/{s2c-partitioned-dispatcher-real-ws.test.js → app-callback-partitioned-dispatcher-real-ws.test.js} +69 -94
- package/dist/internal/__tests__/app-callback-partitioned-dispatcher-real-ws.test.js.map +1 -0
- package/dist/internal/__tests__/app-callback-partitioned-dispatcher.test.d.ts +2 -0
- package/dist/internal/__tests__/app-callback-partitioned-dispatcher.test.d.ts.map +1 -0
- package/dist/internal/__tests__/{s2c-partitioned-dispatcher.test.js → app-callback-partitioned-dispatcher.test.js} +35 -69
- package/dist/internal/__tests__/app-callback-partitioned-dispatcher.test.js.map +1 -0
- package/dist/internal/__tests__/app-callback-test-requests.d.ts +43 -0
- package/dist/internal/__tests__/app-callback-test-requests.d.ts.map +1 -0
- package/dist/internal/__tests__/app-callback-test-requests.js +101 -0
- package/dist/internal/__tests__/app-callback-test-requests.js.map +1 -0
- package/dist/internal/{s2c-dispatcher-errors.d.ts → app-callback-dispatcher-errors.d.ts} +5 -21
- package/dist/internal/app-callback-dispatcher-errors.d.ts.map +1 -0
- package/dist/internal/{s2c-dispatcher-errors.js → app-callback-dispatcher-errors.js} +3 -13
- package/dist/internal/app-callback-dispatcher-errors.js.map +1 -0
- package/dist/internal/app-callback-partition-key.d.ts +68 -0
- package/dist/internal/app-callback-partition-key.d.ts.map +1 -0
- package/dist/internal/app-callback-partition-key.js +90 -0
- package/dist/internal/app-callback-partition-key.js.map +1 -0
- package/dist/internal/{s2c-partition-worker.d.ts → app-callback-partition-worker.d.ts} +5 -5
- package/dist/internal/app-callback-partition-worker.d.ts.map +1 -0
- package/dist/internal/{s2c-partition-worker.js → app-callback-partition-worker.js} +5 -5
- package/dist/internal/app-callback-partition-worker.js.map +1 -0
- package/dist/internal/{s2c-partitioned-dispatcher.d.ts → app-callback-partitioned-dispatcher.d.ts} +9 -10
- package/dist/internal/app-callback-partitioned-dispatcher.d.ts.map +1 -0
- package/dist/internal/{s2c-partitioned-dispatcher.js → app-callback-partitioned-dispatcher.js} +12 -19
- package/dist/internal/app-callback-partitioned-dispatcher.js.map +1 -0
- package/dist/runtime/errors.d.ts +2 -28
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/errors.js +0 -9
- package/dist/runtime/errors.js.map +1 -1
- package/dist/runtime/errors.test.js +2 -2
- package/dist/runtime/errors.test.js.map +1 -1
- package/dist/runtime/frame.d.ts +17 -18
- package/dist/runtime/frame.d.ts.map +1 -1
- package/dist/runtime/frame.js +83 -31
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/frame.test.js +15 -11
- package/dist/runtime/frame.test.js.map +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/local-service-commands.d.ts +7 -0
- package/dist/runtime/local-service-commands.d.ts.map +1 -0
- package/dist/runtime/local-service-commands.js +6 -0
- package/dist/runtime/local-service-commands.js.map +1 -0
- package/dist/runtime/runtime.test.js +1 -2
- package/dist/runtime/runtime.test.js.map +1 -1
- package/dist/runtime/subscribers.d.ts +34 -29
- package/dist/runtime/subscribers.d.ts.map +1 -1
- package/dist/runtime/subscribers.js +19 -19
- package/dist/runtime/subscribers.js.map +1 -1
- package/dist/runtime/subscribers.test.js +50 -31
- package/dist/runtime/subscribers.test.js.map +1 -1
- package/dist/service.d.ts +24 -39
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +165 -161
- package/dist/service.js.map +1 -1
- package/dist/service.test.js +111 -170
- package/dist/service.test.js.map +1 -1
- package/dist/test-utils/channel-service-fixture.d.ts +1 -2
- package/dist/test-utils/channel-service-fixture.d.ts.map +1 -1
- package/dist/test-utils/channel-service-fixture.js +28 -20
- package/dist/test-utils/channel-service-fixture.js.map +1 -1
- package/dist/test-utils/conformance-adapter.d.ts +2 -2
- package/dist/test-utils/conformance-adapter.d.ts.map +1 -1
- package/dist/test-utils/conformance-adapter.js +33 -26
- package/dist/test-utils/conformance-adapter.js.map +1 -1
- package/dist/test-utils/fake-service.d.ts +14 -39
- package/dist/test-utils/fake-service.d.ts.map +1 -1
- package/dist/test-utils/fake-service.js +33 -39
- package/dist/test-utils/fake-service.js.map +1 -1
- package/dist/test-utils/ids.d.ts +9 -0
- package/dist/test-utils/ids.d.ts.map +1 -0
- package/dist/test-utils/ids.js +52 -0
- package/dist/test-utils/ids.js.map +1 -0
- package/dist/test-utils/index.d.ts +10 -2
- package/dist/test-utils/index.d.ts.map +1 -1
- package/dist/test-utils/index.js +12 -5
- package/dist/test-utils/index.js.map +1 -1
- package/dist/ws-client.d.ts +58 -64
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +151 -133
- package/dist/ws-client.js.map +1 -1
- package/dist/ws-client.test.js +157 -167
- package/dist/ws-client.test.js.map +1 -1
- package/package.json +3 -3
- package/dist/cli/commands/permissions.d.ts +0 -70
- package/dist/cli/commands/permissions.d.ts.map +0 -1
- package/dist/cli/commands/permissions.js +0 -100
- package/dist/cli/commands/permissions.js.map +0 -1
- package/dist/cli/commands/permissions.test.d.ts +0 -2
- package/dist/cli/commands/permissions.test.d.ts.map +0 -1
- package/dist/cli/commands/permissions.test.js +0 -127
- package/dist/cli/commands/permissions.test.js.map +0 -1
- package/dist/internal/__tests__/s2c-partition-key.test.d.ts +0 -2
- package/dist/internal/__tests__/s2c-partition-key.test.d.ts.map +0 -1
- package/dist/internal/__tests__/s2c-partition-key.test.js +0 -231
- package/dist/internal/__tests__/s2c-partition-key.test.js.map +0 -1
- package/dist/internal/__tests__/s2c-partition-worker.test.d.ts +0 -2
- package/dist/internal/__tests__/s2c-partition-worker.test.d.ts.map +0 -1
- package/dist/internal/__tests__/s2c-partition-worker.test.js.map +0 -1
- package/dist/internal/__tests__/s2c-partitioned-dispatcher-real-ws.test.d.ts +0 -2
- package/dist/internal/__tests__/s2c-partitioned-dispatcher-real-ws.test.d.ts.map +0 -1
- package/dist/internal/__tests__/s2c-partitioned-dispatcher-real-ws.test.js.map +0 -1
- package/dist/internal/__tests__/s2c-partitioned-dispatcher.test.d.ts +0 -2
- package/dist/internal/__tests__/s2c-partitioned-dispatcher.test.d.ts.map +0 -1
- package/dist/internal/__tests__/s2c-partitioned-dispatcher.test.js.map +0 -1
- package/dist/internal/s2c-dispatcher-errors.d.ts.map +0 -1
- package/dist/internal/s2c-dispatcher-errors.js.map +0 -1
- package/dist/internal/s2c-partition-key.d.ts +0 -79
- package/dist/internal/s2c-partition-key.d.ts.map +0 -1
- package/dist/internal/s2c-partition-key.js +0 -146
- package/dist/internal/s2c-partition-key.js.map +0 -1
- package/dist/internal/s2c-partition-worker.d.ts.map +0 -1
- package/dist/internal/s2c-partition-worker.js.map +0 -1
- package/dist/internal/s2c-partitioned-dispatcher.d.ts.map +0 -1
- package/dist/internal/s2c-partitioned-dispatcher.js.map +0 -1
package/dist/service.js
CHANGED
|
@@ -2,11 +2,12 @@ import * as net from "node:net";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import * as os from "node:os";
|
|
5
|
-
import { AgentsLookup, AgentsLookupByName, AppsAuthorizeDispatch, ConversationsCreate, ConversationsGet, ErrorCodes,
|
|
5
|
+
import { AgentsLookup, AgentsLookupByName, agentId as toAgentId, AppsAuthorizeDispatch, bindNotificationHandler, ConversationsCreate, ConversationsGet, conversationId as toConversationId, defineEffectNotificationHandlers, defineNotificationGroup, MessageReceivedNotificationDefinition, ConversationCreatedNotificationDefinition, ConversationUpdatedNotificationDefinition, ConversationArchivedNotificationDefinition, ConversationUnarchivedNotificationDefinition, ErrorCodes, isDecodedNotificationInGroup, messageId as toMessageId, MessagesList, MessagesSend, NotConnectedError, rpcMethods, RpcServerError, RpcTimeoutError, } from "@moltzap/protocol";
|
|
6
6
|
import { Config, ConfigProvider, Data, Effect, HashMap, Match, Option, Ref, } from "effect";
|
|
7
7
|
import { MoltZapWsClient, } from "./ws-client.js";
|
|
8
|
-
import { AgentNotFoundError
|
|
8
|
+
import { AgentNotFoundError } from "./runtime/errors.js";
|
|
9
9
|
import { getOr, snapshot } from "./runtime/refs.js";
|
|
10
|
+
import { LocalServiceCommands } from "./runtime/local-service-commands.js";
|
|
10
11
|
const isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
12
|
const CROSS_CONTEXT_TEXT_LIMIT = 120;
|
|
12
13
|
const DEFAULT_HISTORY_LIMIT = 10;
|
|
@@ -74,6 +75,14 @@ const renderPart = Match.type().pipe(Match.discriminatorsExhaustive("type")({
|
|
|
74
75
|
* display windows — `conversations get` shows at most a few hundred.
|
|
75
76
|
*/
|
|
76
77
|
const MAX_MESSAGES_PER_CONV = 1000;
|
|
78
|
+
const serviceNotificationDefinitions = [
|
|
79
|
+
MessageReceivedNotificationDefinition,
|
|
80
|
+
ConversationCreatedNotificationDefinition,
|
|
81
|
+
ConversationUpdatedNotificationDefinition,
|
|
82
|
+
ConversationArchivedNotificationDefinition,
|
|
83
|
+
ConversationUnarchivedNotificationDefinition,
|
|
84
|
+
];
|
|
85
|
+
const serviceNotificationGroup = defineNotificationGroup("clientService", serviceNotificationDefinitions);
|
|
77
86
|
/**
|
|
78
87
|
* Invoke every handler with `arg`, isolating throws so one bad handler
|
|
79
88
|
* doesn't abort the remaining fanout. Logs via the optional client logger.
|
|
@@ -84,7 +93,7 @@ function fanout(handlers, arg, logger) {
|
|
|
84
93
|
h(arg);
|
|
85
94
|
}
|
|
86
95
|
catch (err) {
|
|
87
|
-
logger?.error("
|
|
96
|
+
logger?.error("notification handler threw", err);
|
|
88
97
|
}
|
|
89
98
|
}
|
|
90
99
|
}
|
|
@@ -110,13 +119,15 @@ export class MoltZapService {
|
|
|
110
119
|
lastNotifiedRef = Effect.runSync(Ref.make(HashMap.empty()));
|
|
111
120
|
lastReadRef = Effect.runSync(Ref.make(HashMap.empty()));
|
|
112
121
|
archivedConversationIds = new Set();
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
handlers = {
|
|
123
|
+
message: [],
|
|
124
|
+
rawNotification: [],
|
|
125
|
+
disconnect: [],
|
|
126
|
+
reconnect: [],
|
|
127
|
+
conversationArchived: [],
|
|
128
|
+
conversationUnarchived: [],
|
|
129
|
+
};
|
|
130
|
+
notificationHandlers = this.createNotificationHandlers();
|
|
120
131
|
_ownAgentId;
|
|
121
132
|
constructor(opts) {
|
|
122
133
|
this.opts = opts;
|
|
@@ -137,25 +148,24 @@ export class MoltZapService {
|
|
|
137
148
|
// Spec #222 OQ-6: arg required. The body doesn't branch on
|
|
138
149
|
// close metadata today; signature kept explicit so a future
|
|
139
150
|
// disconnect-handler chain can plumb code/reason through.
|
|
140
|
-
onDisconnect: (
|
|
141
|
-
void
|
|
151
|
+
onDisconnect: (close) => {
|
|
152
|
+
void close;
|
|
142
153
|
this._connected = false;
|
|
143
|
-
fanout(this.
|
|
154
|
+
fanout(this.handlers.disconnect, undefined, this.opts.logger);
|
|
144
155
|
},
|
|
145
156
|
onReconnect: (helloOk) => {
|
|
146
157
|
this._connected = true;
|
|
147
|
-
|
|
148
|
-
this.
|
|
149
|
-
fanout(this.reconnectHandlers, hello, this.opts.logger);
|
|
158
|
+
this.populateFromHello(helloOk);
|
|
159
|
+
fanout(this.handlers.reconnect, helloOk, this.opts.logger);
|
|
150
160
|
},
|
|
151
161
|
});
|
|
152
|
-
// Spec #222 OQ-4
|
|
162
|
+
// Spec #222 OQ-4: per-notification callback is replaced by subscriptions.
|
|
153
163
|
// Replacement: register a `{}` filter subscription before
|
|
154
|
-
// `connect()` so every inbound
|
|
155
|
-
// `
|
|
164
|
+
// `connect()` so every inbound notification still fans out to
|
|
165
|
+
// `handleNotification`. Pre-connect registration is supported by the
|
|
156
166
|
// registry.
|
|
157
|
-
yield* this.client.subscribe({}, (
|
|
158
|
-
const helloOk =
|
|
167
|
+
yield* this.client.subscribe({}, (notification) => Effect.sync(() => this.handleNotification(notification)));
|
|
168
|
+
const helloOk = yield* this.client.connect();
|
|
159
169
|
this._connected = true;
|
|
160
170
|
this._ownAgentId = helloOk.agentId;
|
|
161
171
|
this.populateFromHello(helloOk);
|
|
@@ -331,19 +341,25 @@ export class MoltZapService {
|
|
|
331
341
|
handleSocketRequestEffect(method, params) {
|
|
332
342
|
return Effect.suspend(() => {
|
|
333
343
|
switch (method) {
|
|
334
|
-
case
|
|
344
|
+
case LocalServiceCommands.Ping:
|
|
335
345
|
return Effect.succeed({ ok: true, agentId: this.ownAgentId });
|
|
336
|
-
case
|
|
346
|
+
case LocalServiceCommands.Status:
|
|
337
347
|
return Effect.succeed({
|
|
338
348
|
agentId: this.ownAgentId,
|
|
339
349
|
connected: this._connected,
|
|
340
350
|
conversations: this.getConversations().length,
|
|
341
351
|
});
|
|
342
|
-
case
|
|
352
|
+
case LocalServiceCommands.History:
|
|
343
353
|
return this.handleHistoryRequest(params);
|
|
344
|
-
default:
|
|
345
|
-
|
|
346
|
-
|
|
354
|
+
default: {
|
|
355
|
+
const definition = rpcMethods.find((def) => def.name === method);
|
|
356
|
+
if (definition === undefined || !definition.validateParams(params)) {
|
|
357
|
+
return Effect.fail(new ServiceInputError({
|
|
358
|
+
message: `unknown or invalid RPC method: ${method}`,
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
return this.sendRpc(definition, params);
|
|
362
|
+
}
|
|
347
363
|
}
|
|
348
364
|
});
|
|
349
365
|
}
|
|
@@ -360,31 +376,29 @@ export class MoltZapService {
|
|
|
360
376
|
const convId = params.conversationId;
|
|
361
377
|
const limit = params.limit ?? DEFAULT_HISTORY_LIMIT;
|
|
362
378
|
const sessionKey = params.sessionKey;
|
|
363
|
-
const result =
|
|
364
|
-
conversationId: convId,
|
|
379
|
+
const result = yield* this.sendRpc(MessagesList, {
|
|
380
|
+
conversationId: toConversationId(convId),
|
|
365
381
|
limit,
|
|
366
|
-
})
|
|
382
|
+
});
|
|
367
383
|
// Resolve agent names (batch) + fetch conversation metadata (concurrent)
|
|
368
384
|
const knownNames = yield* Ref.get(this.agentNamesRef);
|
|
369
385
|
const unknownAgentIds = [
|
|
370
386
|
...new Set(result.messages.map((m) => m.senderId)),
|
|
371
387
|
].filter((id) => !HashMap.has(knownNames, id));
|
|
372
388
|
const lookupEff = unknownAgentIds.length > 0
|
|
373
|
-
? this.sendRpc(AgentsLookup
|
|
374
|
-
const agents = res.agents;
|
|
389
|
+
? this.sendRpc(AgentsLookup, { agentIds: unknownAgentIds }).pipe(Effect.tap((res) => {
|
|
375
390
|
return Ref.update(this.agentNamesRef, (names) => {
|
|
376
391
|
let next = names;
|
|
377
|
-
for (const a of agents) {
|
|
392
|
+
for (const a of res.agents) {
|
|
378
393
|
next = HashMap.set(next, a.id, a.name);
|
|
379
394
|
}
|
|
380
395
|
return next;
|
|
381
396
|
});
|
|
382
397
|
}), Effect.asVoid, Effect.catchAll(() => Effect.void))
|
|
383
398
|
: Effect.void;
|
|
384
|
-
const metaEff = this.sendRpc(ConversationsGet
|
|
385
|
-
conversationId: convId,
|
|
386
|
-
}).pipe(Effect.map((res) => res
|
|
387
|
-
.conversation), Effect.catchAll(() => Effect.succeed(undefined)));
|
|
399
|
+
const metaEff = this.sendRpc(ConversationsGet, {
|
|
400
|
+
conversationId: toConversationId(convId),
|
|
401
|
+
}).pipe(Effect.map((res) => res.conversation), Effect.catchAll(() => Effect.succeed(undefined)));
|
|
388
402
|
const [, convMeta] = yield* Effect.all([lookupEff, metaEff], {
|
|
389
403
|
concurrency: HISTORY_LOOKUP_CONCURRENCY,
|
|
390
404
|
});
|
|
@@ -460,7 +474,7 @@ export class MoltZapService {
|
|
|
460
474
|
const cached = Option.getOrUndefined(HashMap.get(snapshot(this.agentNamesRef), agentId));
|
|
461
475
|
if (cached !== undefined)
|
|
462
476
|
return cached;
|
|
463
|
-
return yield* this.sendRpc(AgentsLookup
|
|
477
|
+
return yield* this.sendRpc(AgentsLookup, {
|
|
464
478
|
agentIds: [agentId],
|
|
465
479
|
}).pipe(Effect.flatMap((result) => {
|
|
466
480
|
const agent = result.agents[0];
|
|
@@ -478,33 +492,43 @@ export class MoltZapService {
|
|
|
478
492
|
message: "Conversation is archived",
|
|
479
493
|
}));
|
|
480
494
|
}
|
|
481
|
-
return Effect.asVoid(this.sendRpc(MessagesSend
|
|
482
|
-
conversationId: convId,
|
|
495
|
+
return Effect.asVoid(this.sendRpc(MessagesSend, {
|
|
496
|
+
conversationId: toConversationId(convId),
|
|
483
497
|
parts: [{ type: "text", text }],
|
|
484
|
-
...(opts?.replyTo ? { replyToId: opts.replyTo } : {}),
|
|
498
|
+
...(opts?.replyTo ? { replyToId: toMessageId(opts.replyTo) } : {}),
|
|
485
499
|
...(opts?.dispatchLeaseId
|
|
486
500
|
? { dispatchLeaseId: opts.dispatchLeaseId }
|
|
487
501
|
: {}),
|
|
488
502
|
}));
|
|
489
503
|
}
|
|
490
504
|
authorizeDispatch(request) {
|
|
491
|
-
return this.sendRpc(AppsAuthorizeDispatch
|
|
492
|
-
conversationId: request.conversationId,
|
|
493
|
-
messageId: request.message.id,
|
|
494
|
-
senderAgentId: request.senderAgentId,
|
|
495
|
-
parts: request.message.parts,
|
|
505
|
+
return this.sendRpc(AppsAuthorizeDispatch, {
|
|
506
|
+
conversationId: toConversationId(request.conversationId),
|
|
507
|
+
messageId: toMessageId(request.message.id),
|
|
508
|
+
senderAgentId: toAgentId(request.senderAgentId),
|
|
509
|
+
parts: [...request.message.parts],
|
|
496
510
|
receivedAt: request.receivedAt,
|
|
497
511
|
clock: request.clock,
|
|
498
|
-
pending: request.pending
|
|
512
|
+
pending: request.pending.map((pending) => ({
|
|
513
|
+
...pending,
|
|
514
|
+
messageId: toMessageId(pending.messageId),
|
|
515
|
+
conversationId: toConversationId(pending.conversationId),
|
|
516
|
+
senderAgentId: toAgentId(pending.senderAgentId),
|
|
517
|
+
...(pending.parts !== undefined ? { parts: [...pending.parts] } : {}),
|
|
518
|
+
})),
|
|
499
519
|
attempt: request.attempt,
|
|
500
520
|
}, { timeoutMs: DISPATCH_AUTHORIZATION_RPC_TIMEOUT_MS }).pipe(Effect.map((result) => {
|
|
501
|
-
const admission = result
|
|
521
|
+
const { admission } = result;
|
|
502
522
|
switch (admission.decision) {
|
|
503
523
|
case "grant":
|
|
504
524
|
return {
|
|
505
525
|
_tag: "grant",
|
|
506
|
-
|
|
507
|
-
|
|
526
|
+
...(admission.leaseId !== undefined
|
|
527
|
+
? { leaseId: admission.leaseId }
|
|
528
|
+
: {}),
|
|
529
|
+
...(admission.leaseTimeoutMs !== undefined
|
|
530
|
+
? { leaseTimeoutMs: admission.leaseTimeoutMs }
|
|
531
|
+
: {}),
|
|
508
532
|
...(admission.dispatchMessageId
|
|
509
533
|
? { dispatchMessageId: admission.dispatchMessageId }
|
|
510
534
|
: {}),
|
|
@@ -512,12 +536,16 @@ export class MoltZapService {
|
|
|
512
536
|
case "deny":
|
|
513
537
|
return {
|
|
514
538
|
_tag: "deny",
|
|
515
|
-
|
|
539
|
+
...(admission.reason !== undefined
|
|
540
|
+
? { reason: admission.reason }
|
|
541
|
+
: {}),
|
|
516
542
|
};
|
|
517
543
|
case "hold":
|
|
518
544
|
return {
|
|
519
545
|
_tag: "hold",
|
|
520
|
-
|
|
546
|
+
...(admission.reason !== undefined
|
|
547
|
+
? { reason: admission.reason }
|
|
548
|
+
: {}),
|
|
521
549
|
};
|
|
522
550
|
}
|
|
523
551
|
}));
|
|
@@ -527,17 +555,17 @@ export class MoltZapService {
|
|
|
527
555
|
const cache = yield* Ref.get(this.agentConversationCacheRef);
|
|
528
556
|
let conversationId = Option.getOrUndefined(HashMap.get(cache, agentName));
|
|
529
557
|
if (!conversationId) {
|
|
530
|
-
const lookupResult =
|
|
558
|
+
const lookupResult = yield* this.sendRpc(AgentsLookupByName, {
|
|
531
559
|
names: [agentName],
|
|
532
|
-
})
|
|
560
|
+
});
|
|
533
561
|
const agent = lookupResult.agents[0];
|
|
534
562
|
if (!agent) {
|
|
535
563
|
return yield* Effect.fail(new AgentNotFoundError({ agentName }));
|
|
536
564
|
}
|
|
537
|
-
const createResult =
|
|
565
|
+
const createResult = yield* this.sendRpc(ConversationsCreate, {
|
|
538
566
|
type: "dm",
|
|
539
|
-
participants: [{ type: "agent", id: agent.id }],
|
|
540
|
-
})
|
|
567
|
+
participants: [{ type: "agent", id: toAgentId(agent.id) }],
|
|
568
|
+
});
|
|
541
569
|
conversationId = createResult.conversation.id;
|
|
542
570
|
const newId = conversationId;
|
|
543
571
|
yield* Ref.update(this.agentConversationCacheRef, (m) => HashMap.set(m, agentName, newId));
|
|
@@ -671,57 +699,37 @@ export class MoltZapService {
|
|
|
671
699
|
return HashMap.set(outer, currentConvId, markers);
|
|
672
700
|
}));
|
|
673
701
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
this.messageHandlers.push(handler);
|
|
678
|
-
break;
|
|
679
|
-
case "rawEvent":
|
|
680
|
-
this.rawEventHandlers.push(handler);
|
|
681
|
-
break;
|
|
682
|
-
case "disconnect":
|
|
683
|
-
this.disconnectHandlers.push(handler);
|
|
684
|
-
break;
|
|
685
|
-
case "reconnect":
|
|
686
|
-
this.reconnectHandlers.push(handler);
|
|
687
|
-
break;
|
|
688
|
-
case "permissionRequired":
|
|
689
|
-
this.permissionRequiredHandlers.push(handler);
|
|
690
|
-
break;
|
|
691
|
-
case "conversationArchived":
|
|
692
|
-
this.conversationArchivedHandlers.push(handler);
|
|
693
|
-
break;
|
|
694
|
-
case "conversationUnarchived":
|
|
695
|
-
this.conversationUnarchivedHandlers.push(handler);
|
|
696
|
-
break;
|
|
697
|
-
}
|
|
702
|
+
// --- Notifications ---
|
|
703
|
+
on(notification, handler) {
|
|
704
|
+
this.handlers[notification].push(handler);
|
|
698
705
|
}
|
|
699
706
|
// --- RPC passthrough ---
|
|
700
|
-
sendRpc(
|
|
707
|
+
sendRpc(definition, params, opts) {
|
|
701
708
|
return Effect.suspend(() => {
|
|
702
709
|
if (!this.client) {
|
|
703
710
|
return Effect.fail(new NotConnectedError({ message: "Not connected" }));
|
|
704
711
|
}
|
|
705
|
-
return this.client.sendRpc(
|
|
712
|
+
return this.client.sendRpc(definition, params, opts);
|
|
706
713
|
});
|
|
707
714
|
}
|
|
708
|
-
grantPermission(params) {
|
|
709
|
-
return Effect.asVoid(this.sendRpc(PermissionsGrant.name, params));
|
|
710
|
-
}
|
|
711
715
|
// --- Internals ---
|
|
712
716
|
/**
|
|
713
717
|
* Fetch full conversation details (including participants) via RPC and merge
|
|
714
|
-
* into the local cache. Called on ConversationCreated
|
|
715
|
-
*
|
|
718
|
+
* into the local cache. Called on ConversationCreated notifications because
|
|
719
|
+
* the notification schema omits the participants list — see
|
|
720
|
+
* protocol/notifications.ts.
|
|
716
721
|
*/
|
|
717
722
|
refreshConversationParticipants(conversationId) {
|
|
718
|
-
return this.sendRpc(ConversationsGet
|
|
719
|
-
|
|
723
|
+
return this.sendRpc(ConversationsGet, {
|
|
724
|
+
conversationId: toConversationId(conversationId),
|
|
725
|
+
}).pipe(Effect.tap((res) => {
|
|
720
726
|
const meta = {
|
|
721
|
-
id:
|
|
722
|
-
type:
|
|
723
|
-
|
|
724
|
-
|
|
727
|
+
id: res.conversation.id,
|
|
728
|
+
type: res.conversation.type,
|
|
729
|
+
participants: res.participants.map((p) => `${p.participant.type}:${p.participant.id}`),
|
|
730
|
+
...(res.conversation.name !== undefined
|
|
731
|
+
? { name: res.conversation.name }
|
|
732
|
+
: {}),
|
|
725
733
|
};
|
|
726
734
|
if (this.archivedConversationIds.has(conversationId)) {
|
|
727
735
|
return Effect.void;
|
|
@@ -742,92 +750,88 @@ export class MoltZapService {
|
|
|
742
750
|
next = HashMap.set(next, conv.id, {
|
|
743
751
|
id: conv.id,
|
|
744
752
|
type: conv.type,
|
|
745
|
-
name: conv.name,
|
|
746
753
|
participants: (conv.participants ?? []).map((p) => `${p.type}:${p.id}`),
|
|
754
|
+
...(conv.name !== undefined ? { name: conv.name } : {}),
|
|
747
755
|
});
|
|
748
756
|
}
|
|
749
757
|
return next;
|
|
750
758
|
}));
|
|
751
759
|
}
|
|
752
|
-
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
|
|
760
|
+
handleNotification(notification) {
|
|
761
|
+
const params = isPlainRecord(notification.params)
|
|
762
|
+
? notification.params
|
|
763
|
+
: {};
|
|
764
|
+
const message = isPlainRecord(params["message"])
|
|
765
|
+
? params["message"]
|
|
756
766
|
: undefined;
|
|
757
|
-
const conversation = isPlainRecord(
|
|
758
|
-
?
|
|
767
|
+
const conversation = isPlainRecord(params["conversation"])
|
|
768
|
+
? params["conversation"]
|
|
759
769
|
: undefined;
|
|
760
|
-
const
|
|
761
|
-
?
|
|
770
|
+
const notificationConversationId = typeof params["conversationId"] === "string"
|
|
771
|
+
? params["conversationId"]
|
|
762
772
|
: undefined;
|
|
763
773
|
appendClientEventTrace({
|
|
764
774
|
ts: new Date().toISOString(),
|
|
765
775
|
agentId: this._ownAgentId ?? "unknown",
|
|
766
|
-
|
|
776
|
+
notification: notification.method,
|
|
767
777
|
messageId: message?.["id"],
|
|
768
778
|
messageConversationId: message?.["conversationId"],
|
|
769
779
|
messageSenderId: message?.["senderId"],
|
|
770
|
-
conversationId: conversation?.["id"] ??
|
|
780
|
+
conversationId: conversation?.["id"] ?? notificationConversationId,
|
|
771
781
|
conversationName: conversation?.["name"],
|
|
772
782
|
});
|
|
773
|
-
fanout(this.
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
// specific event.
|
|
777
|
-
switch (event.event) {
|
|
778
|
-
case EventNames.MessageReceived: {
|
|
779
|
-
const msg = event.data.message;
|
|
780
|
-
this.storeMessage(msg);
|
|
781
|
-
// Name resolution is driven lazily by channel-core's serialized
|
|
782
|
-
// consumer via resolveAgentName(), which populates agentNamesRef on
|
|
783
|
-
// first miss and hits the cache on every subsequent message.
|
|
784
|
-
if (msg.senderId !== this._ownAgentId) {
|
|
785
|
-
fanout(this.messageHandlers, msg, this.opts.logger);
|
|
786
|
-
}
|
|
787
|
-
break;
|
|
788
|
-
}
|
|
789
|
-
case EventNames.PermissionsRequired: {
|
|
790
|
-
fanout(this.permissionRequiredHandlers, event.data, this.opts.logger);
|
|
791
|
-
break;
|
|
792
|
-
}
|
|
793
|
-
case EventNames.ConversationCreated:
|
|
794
|
-
case EventNames.ConversationUpdated: {
|
|
795
|
-
const { conversation } = event.data;
|
|
796
|
-
this.archivedConversationIds.delete(conversation.id);
|
|
797
|
-
Effect.runSync(Ref.update(this.conversationsRef, (m) => {
|
|
798
|
-
const existing = Option.getOrUndefined(HashMap.get(m, conversation.id));
|
|
799
|
-
return HashMap.set(m, conversation.id, {
|
|
800
|
-
id: conversation.id,
|
|
801
|
-
type: conversation.type,
|
|
802
|
-
name: conversation.name,
|
|
803
|
-
participants: existing?.participants ?? [],
|
|
804
|
-
});
|
|
805
|
-
}));
|
|
806
|
-
// The ConversationCreated event schema doesn't carry participants
|
|
807
|
-
// (protocol/schema/events.ts: ConversationSchema is id/type/name/
|
|
808
|
-
// createdBy/timestamps only). Fetch full details asynchronously so
|
|
809
|
-
// downstream code that reads getConversation(id).participants sees
|
|
810
|
-
// a populated list within a round-trip of the event.
|
|
811
|
-
if (event.event === EventNames.ConversationCreated) {
|
|
812
|
-
// Fire-and-forget: refreshConversationParticipants never fails.
|
|
813
|
-
Effect.runFork(this.refreshConversationParticipants(conversation.id));
|
|
814
|
-
}
|
|
815
|
-
break;
|
|
816
|
-
}
|
|
817
|
-
case EventNames.ConversationArchived: {
|
|
818
|
-
const data = event.data;
|
|
819
|
-
this.markConversationArchived(data.conversationId);
|
|
820
|
-
fanout(this.conversationArchivedHandlers, data, this.opts.logger);
|
|
821
|
-
break;
|
|
822
|
-
}
|
|
823
|
-
case EventNames.ConversationUnarchived: {
|
|
824
|
-
const data = event.data;
|
|
825
|
-
this.archivedConversationIds.delete(data.conversationId);
|
|
826
|
-
fanout(this.conversationUnarchivedHandlers, data, this.opts.logger);
|
|
827
|
-
break;
|
|
828
|
-
}
|
|
783
|
+
fanout(this.handlers.rawNotification, notification, this.opts.logger);
|
|
784
|
+
if (isDecodedNotificationInGroup(serviceNotificationGroup, notification)) {
|
|
785
|
+
Effect.runSync(this.notificationHandlers.dispatch(notification));
|
|
829
786
|
}
|
|
830
787
|
}
|
|
788
|
+
createNotificationHandlers() {
|
|
789
|
+
return defineEffectNotificationHandlers(serviceNotificationGroup, [
|
|
790
|
+
bindNotificationHandler(MessageReceivedNotificationDefinition, (params) => Effect.sync(() => this.handleMessageReceivedNotification(params))),
|
|
791
|
+
bindNotificationHandler(ConversationCreatedNotificationDefinition, (params) => Effect.sync(() => this.handleConversationCreatedNotification(params))),
|
|
792
|
+
bindNotificationHandler(ConversationUpdatedNotificationDefinition, (params) => Effect.sync(() => this.handleConversationUpdatedNotification(params))),
|
|
793
|
+
bindNotificationHandler(ConversationArchivedNotificationDefinition, (params) => Effect.sync(() => {
|
|
794
|
+
this.markConversationArchived(params.conversationId);
|
|
795
|
+
fanout(this.handlers.conversationArchived, params, this.opts.logger);
|
|
796
|
+
})),
|
|
797
|
+
bindNotificationHandler(ConversationUnarchivedNotificationDefinition, (params) => Effect.sync(() => {
|
|
798
|
+
this.archivedConversationIds.delete(params.conversationId);
|
|
799
|
+
fanout(this.handlers.conversationUnarchived, params, this.opts.logger);
|
|
800
|
+
})),
|
|
801
|
+
]);
|
|
802
|
+
}
|
|
803
|
+
handleMessageReceivedNotification(notification) {
|
|
804
|
+
const msg = notification.message;
|
|
805
|
+
this.storeMessage(msg);
|
|
806
|
+
// Name resolution is driven lazily by channel-core's serialized consumer
|
|
807
|
+
// via resolveAgentName(), which populates agentNamesRef on first miss and
|
|
808
|
+
// hits the cache on every subsequent message.
|
|
809
|
+
if (msg.senderId !== this._ownAgentId) {
|
|
810
|
+
fanout(this.handlers.message, msg, this.opts.logger);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
handleConversationCreatedNotification(notification) {
|
|
814
|
+
this.upsertConversationNotification(notification);
|
|
815
|
+
Effect.runFork(this.refreshConversationParticipants(notification.conversation.id));
|
|
816
|
+
}
|
|
817
|
+
handleConversationUpdatedNotification(notification) {
|
|
818
|
+
this.upsertConversationNotification(notification);
|
|
819
|
+
}
|
|
820
|
+
upsertConversationNotification(notification) {
|
|
821
|
+
const { conversation } = notification;
|
|
822
|
+
this.archivedConversationIds.delete(conversation.id);
|
|
823
|
+
Effect.runSync(Ref.update(this.conversationsRef, (m) => {
|
|
824
|
+
const existing = Option.getOrUndefined(HashMap.get(m, conversation.id));
|
|
825
|
+
return HashMap.set(m, conversation.id, {
|
|
826
|
+
id: conversation.id,
|
|
827
|
+
type: conversation.type,
|
|
828
|
+
participants: existing?.participants ?? [],
|
|
829
|
+
...(conversation.name !== undefined
|
|
830
|
+
? { name: conversation.name }
|
|
831
|
+
: {}),
|
|
832
|
+
});
|
|
833
|
+
}));
|
|
834
|
+
}
|
|
831
835
|
markConversationArchived(conversationId) {
|
|
832
836
|
this.archivedConversationIds.add(conversationId);
|
|
833
837
|
Effect.runSync(Effect.all([
|