@moltzap/client 2026.729.0 → 2026.730.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.
- package/dist/agent-client.d.ts +2 -0
- package/dist/agent-client.d.ts.map +1 -1
- package/dist/agent-client.js +1 -0
- package/dist/agent-client.js.map +1 -1
- package/dist/app-client.d.ts +2 -0
- package/dist/app-client.d.ts.map +1 -1
- package/dist/app-client.js +1 -0
- package/dist/app-client.js.map +1 -1
- package/dist/auth.d.ts +7 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +10 -5
- package/dist/auth.js.map +1 -1
- package/dist/channel-base/format-cross-conv.d.ts +8 -3
- package/dist/channel-base/format-cross-conv.d.ts.map +1 -1
- package/dist/channel-base/format-cross-conv.js +10 -9
- package/dist/channel-base/format-cross-conv.js.map +1 -1
- package/dist/channel-base/format-group-block.d.ts +9 -2
- package/dist/channel-base/format-group-block.d.ts.map +1 -1
- package/dist/channel-base/format-group-block.js +12 -7
- package/dist/channel-base/format-group-block.js.map +1 -1
- package/dist/channel-base/index.d.ts +8 -0
- package/dist/channel-base/index.d.ts.map +1 -1
- package/dist/channel-base/index.js +8 -0
- package/dist/channel-base/index.js.map +1 -1
- package/dist/channel-base/lease-guard.d.ts +6 -3
- package/dist/channel-base/lease-guard.d.ts.map +1 -1
- package/dist/channel-base/lease-guard.js +10 -4
- package/dist/channel-base/lease-guard.js.map +1 -1
- package/dist/channel-base/lease-store.d.ts +29 -10
- package/dist/channel-base/lease-store.d.ts.map +1 -1
- package/dist/channel-base/lease-store.js +33 -13
- package/dist/channel-base/lease-store.js.map +1 -1
- package/dist/channel-base/lease.d.ts +9 -1
- package/dist/channel-base/lease.d.ts.map +1 -1
- package/dist/channel-base/lease.js +16 -4
- package/dist/channel-base/lease.js.map +1 -1
- package/dist/channel-core-enrichment.d.ts +9 -2
- package/dist/channel-core-enrichment.d.ts.map +1 -1
- package/dist/channel-core-enrichment.js +17 -5
- package/dist/channel-core-enrichment.js.map +1 -1
- package/dist/channel-core-test-support.d.ts +46 -0
- package/dist/channel-core-test-support.d.ts.map +1 -1
- package/dist/channel-core-test-support.js +67 -13
- package/dist/channel-core-test-support.js.map +1 -1
- package/dist/channel-core.d.ts +37 -19
- package/dist/channel-core.d.ts.map +1 -1
- package/dist/channel-core.js +45 -30
- package/dist/channel-core.js.map +1 -1
- package/dist/cli/adapters.d.ts +4 -1
- package/dist/cli/adapters.d.ts.map +1 -1
- package/dist/cli/adapters.js +11 -4
- package/dist/cli/adapters.js.map +1 -1
- package/dist/cli/commands/agents.js +3 -3
- package/dist/cli/commands/contacts.d.ts +3 -3
- package/dist/cli/commands/contacts.d.ts.map +1 -1
- package/dist/cli/commands/contacts.js +13 -9
- package/dist/cli/commands/contacts.js.map +1 -1
- package/dist/cli/commands/conversations.d.ts +3 -2
- package/dist/cli/commands/conversations.d.ts.map +1 -1
- package/dist/cli/commands/conversations.js +7 -6
- package/dist/cli/commands/conversations.js.map +1 -1
- package/dist/cli/commands/messages.d.ts +6 -2
- package/dist/cli/commands/messages.d.ts.map +1 -1
- package/dist/cli/commands/messages.js +8 -5
- package/dist/cli/commands/messages.js.map +1 -1
- package/dist/cli/commands/register.d.ts +1 -1
- package/dist/cli/commands/register.js +4 -4
- package/dist/cli/commands/send.d.ts +7 -6
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +7 -6
- package/dist/cli/commands/send.js.map +1 -1
- package/dist/cli/commands/start.d.ts +15 -7
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +12 -5
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.js +2 -2
- package/dist/cli/commands/test-transport.d.ts +9 -1
- package/dist/cli/commands/test-transport.d.ts.map +1 -1
- package/dist/cli/commands/test-transport.js +5 -1
- package/dist/cli/commands/test-transport.js.map +1 -1
- package/dist/cli/index.js +8 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/output.d.ts +10 -0
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +10 -0
- package/dist/cli/output.js.map +1 -1
- package/dist/cli/socket-client.d.ts +13 -3
- package/dist/cli/socket-client.d.ts.map +1 -1
- package/dist/cli/socket-client.js +16 -5
- package/dist/cli/socket-client.js.map +1 -1
- package/dist/cli/transport.d.ts +16 -3
- package/dist/cli/transport.d.ts.map +1 -1
- package/dist/cli/transport.js +19 -6
- package/dist/cli/transport.js.map +1 -1
- package/dist/config.d.ts +9 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -3
- package/dist/config.js.map +1 -1
- package/dist/config.test-utils.d.ts +7 -2
- package/dist/config.test-utils.d.ts.map +1 -1
- package/dist/config.test-utils.js +8 -1
- package/dist/config.test-utils.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/local-daemon-rpc.d.ts +116 -93
- package/dist/local-daemon-rpc.d.ts.map +1 -1
- package/dist/local-daemon-rpc.js +128 -108
- package/dist/local-daemon-rpc.js.map +1 -1
- package/dist/local-history.d.ts +40 -20
- package/dist/local-history.d.ts.map +1 -1
- package/dist/local-history.js +45 -23
- package/dist/local-history.js.map +1 -1
- package/dist/local-paths.d.ts +17 -0
- package/dist/local-paths.d.ts.map +1 -1
- package/dist/local-paths.js +21 -4
- package/dist/local-paths.js.map +1 -1
- package/dist/local-socket-server.d.ts +13 -2
- package/dist/local-socket-server.d.ts.map +1 -1
- package/dist/local-socket-server.js +13 -2
- package/dist/local-socket-server.js.map +1 -1
- package/dist/message-rendering.d.ts +1 -0
- package/dist/message-rendering.d.ts.map +1 -1
- package/dist/message-rendering.js +1 -0
- package/dist/message-rendering.js.map +1 -1
- package/dist/notification/__tests__/subscribe-signatures.types-check.d.ts +50 -2
- package/dist/notification/__tests__/subscribe-signatures.types-check.d.ts.map +1 -1
- package/dist/notification/__tests__/subscribe-signatures.types-check.js +22 -48
- package/dist/notification/__tests__/subscribe-signatures.types-check.js.map +1 -1
- package/dist/notification/errors.d.ts +2 -0
- package/dist/notification/errors.d.ts.map +1 -1
- package/dist/notification/errors.js +3 -1
- package/dist/notification/errors.js.map +1 -1
- package/dist/notification/index.d.ts +1 -0
- package/dist/notification/index.d.ts.map +1 -1
- package/dist/notification/index.js +1 -0
- package/dist/notification/index.js.map +1 -1
- package/dist/notification/stream.d.ts +11 -4
- package/dist/notification/stream.d.ts.map +1 -1
- package/dist/notification/stream.js +5 -38
- package/dist/notification/stream.js.map +1 -1
- package/dist/pagination.d.ts +13 -16
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +9 -1
- package/dist/pagination.js.map +1 -1
- package/dist/profile.d.ts +21 -9
- package/dist/profile.d.ts.map +1 -1
- package/dist/profile.js +30 -16
- package/dist/profile.js.map +1 -1
- package/dist/refs.d.ts +9 -1
- package/dist/refs.d.ts.map +1 -1
- package/dist/refs.js +9 -1
- package/dist/refs.js.map +1 -1
- package/dist/service-archive-purge.d.ts +18 -0
- package/dist/service-archive-purge.d.ts.map +1 -1
- package/dist/service-archive-purge.js +18 -0
- package/dist/service-archive-purge.js.map +1 -1
- package/dist/service-event-trace.d.ts +15 -0
- package/dist/service-event-trace.d.ts.map +1 -0
- package/dist/service-event-trace.js +35 -0
- package/dist/service-event-trace.js.map +1 -0
- package/dist/service-helpers.d.ts +35 -6
- package/dist/service-helpers.d.ts.map +1 -1
- package/dist/service-helpers.js +38 -8
- package/dist/service-helpers.js.map +1 -1
- package/dist/service-local-daemon.d.ts +12 -2
- package/dist/service-local-daemon.d.ts.map +1 -1
- package/dist/service-local-daemon.js +38 -25
- package/dist/service-local-daemon.js.map +1 -1
- package/dist/service.d.ts +62 -22
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +135 -80
- package/dist/service.js.map +1 -1
- package/dist/test-utils/channel-service-fixture.d.ts +8 -0
- package/dist/test-utils/channel-service-fixture.d.ts.map +1 -1
- package/dist/test-utils/channel-service-fixture.js +31 -13
- package/dist/test-utils/channel-service-fixture.js.map +1 -1
- package/dist/test-utils/fake-service.d.ts +20 -7
- package/dist/test-utils/fake-service.d.ts.map +1 -1
- package/dist/test-utils/fake-service.js +18 -4
- package/dist/test-utils/fake-service.js.map +1 -1
- package/dist/test-utils/harness.d.ts +5 -1
- package/dist/test-utils/harness.d.ts.map +1 -1
- package/dist/test-utils/harness.js +3 -0
- package/dist/test-utils/harness.js.map +1 -1
- package/dist/test-utils/ids.d.ts +25 -0
- package/dist/test-utils/ids.d.ts.map +1 -1
- package/dist/test-utils/ids.js +27 -1
- package/dist/test-utils/ids.js.map +1 -1
- package/dist/test-utils/index.d.ts +7 -1
- package/dist/test-utils/index.d.ts.map +1 -1
- package/dist/test-utils/index.js +7 -1
- package/dist/test-utils/index.js.map +1 -1
- package/dist/test-utils/standalone-provisioning.d.ts +5 -0
- package/dist/test-utils/standalone-provisioning.d.ts.map +1 -1
- package/dist/test-utils/standalone-provisioning.js +3 -0
- package/dist/test-utils/standalone-provisioning.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
package/dist/service.js
CHANGED
|
@@ -1,26 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { ConversationArchivedError, } from "@moltzap/protocol/conversation";
|
|
8
|
-
import { MessageReceivedNotificationDefinition, MessagesList, MessagesSend, } from "@moltzap/protocol/message";
|
|
9
|
-
import { NotConnectedError, RpcTimeoutError, isNotificationDeliveryFor, } from "@moltzap/protocol/rpc";
|
|
10
|
-
import { AgentCallableGroup } from "@moltzap/protocol/socket/catalog";
|
|
1
|
+
import { agentId as AgentIdSchema, AgentNotFoundError, agentsList, } from "@moltzap/protocol/identity";
|
|
2
|
+
import { dispatchRequest, dispatchRelease, dispatchLeaseConsumed, dispatchLeaseExpired, } from "@moltzap/protocol/message/dispatch";
|
|
3
|
+
import { conversationCreatedNotificationDefinition, conversationArchivedNotificationDefinition, conversationUnarchivedNotificationDefinition, conversationList, ConversationArchivedError, } from "@moltzap/protocol/conversation";
|
|
4
|
+
import { DEFAULT_APP_ID, taskRequest, } from "@moltzap/protocol/task";
|
|
5
|
+
import { messageReceivedNotificationDefinition, messagesList, messagesSend, } from "@moltzap/protocol/message";
|
|
6
|
+
import { NotConnectedError, isNotificationDeliveryFor, } from "@moltzap/protocol/rpc";
|
|
11
7
|
import { BoundedMap } from "@moltzap/protocol/bounded-map";
|
|
12
|
-
import {
|
|
8
|
+
import { Effect, Exit, HashMap, Option, Ref, Schema, Scope, Stream, } from "effect";
|
|
13
9
|
import { MoltZapAgentClient } from "./agent-client.js";
|
|
14
10
|
import { loadServiceConfig, } from "./config.js";
|
|
15
11
|
import { getOr, snapshot } from "./refs.js";
|
|
16
12
|
import { getMoltZapAgentServiceSocketPath, getMoltZapServiceSocketPath, } from "./local-paths.js";
|
|
17
|
-
import {} from "./local-daemon-rpc.js";
|
|
18
13
|
import { makeLocalDaemonHandlers } from "./service-local-daemon.js";
|
|
19
14
|
import { startLocalSocketServer, stopLocalSocketServer, } from "./local-socket-server.js";
|
|
20
15
|
import { buildContextEntries, makeContextCandidate, newMessagesForConversation, notificationTraceRecord, } from "./service-helpers.js";
|
|
21
16
|
import { renderPart } from "./message-rendering.js";
|
|
22
17
|
import { formatHistoryMessage, lastReadIdsForSession, } from "./local-history.js";
|
|
23
18
|
import { purgeAgentCacheEntries, purgeLastNotified, purgeLastRead, } from "./service-archive-purge.js";
|
|
19
|
+
import { appendClientEventTrace } from "./service-event-trace.js";
|
|
24
20
|
const CROSS_CONTEXT_TEXT_LIMIT = 120;
|
|
25
21
|
const DEFAULT_MAX_CONTEXT_CONVERSATIONS = 5;
|
|
26
22
|
const DEFAULT_MAX_MESSAGES_PER_CONVERSATION = 3;
|
|
@@ -30,25 +26,15 @@ const AGENT_LOOKUP_MAX_PAGES = 20;
|
|
|
30
26
|
const REUSABLE_CONVERSATION_LOOKUP_PAGE_SIZE = 50;
|
|
31
27
|
const REUSABLE_CONVERSATION_LOOKUP_MAX_PAGES = 20;
|
|
32
28
|
const decodeAgentId = Schema.decodeUnknownOption(AgentIdSchema);
|
|
33
|
-
const ClientEventLogDir = Config.option(Config.string("MOLTZAP_CLIENT_EVENT_LOG_DIR"));
|
|
34
|
-
const getClientEventLogDir = () => Option.getOrUndefined(Effect.runSync(ClientEventLogDir.pipe(Effect.withConfigProvider(ConfigProvider.fromEnv()))));
|
|
35
|
-
function appendClientEventTrace(record) {
|
|
36
|
-
const dir = getClientEventLogDir();
|
|
37
|
-
if (!dir)
|
|
38
|
-
return Effect.void;
|
|
39
|
-
return Effect.gen(function* () {
|
|
40
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
41
|
-
const path = yield* Path.Path;
|
|
42
|
-
const agentId = typeof record["agentId"] === "string" ? record["agentId"] : "unknown";
|
|
43
|
-
const safeAgentId = /^[A-Za-z0-9_-]+$/.test(agentId) ? agentId : "unknown";
|
|
44
|
-
yield* Effect.zipRight(fileSystem.makeDirectory(dir, { recursive: true }), fileSystem.writeFileString(path.join(dir, `client-events-${safeAgentId}.jsonl`), JSON.stringify(record) + "\n", { flag: "a" }));
|
|
45
|
-
}).pipe(Effect.withSpan("appendClientEventTrace"), Effect.provide(NodeContext.layer), Effect.catchAll((err) => Effect.logWarning("moltzap client event trace write failed", err)));
|
|
46
|
-
}
|
|
47
29
|
const agentNotFound = (agentName) => new AgentNotFoundError({
|
|
48
30
|
message: `Agent not found: ${agentName}`,
|
|
49
31
|
data: { agentName },
|
|
50
32
|
});
|
|
51
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Escape `<`, `>`, `&` so sender content can't escape a `<system-reminder>` block.
|
|
35
|
+
* @param s Value supplied to the operation.
|
|
36
|
+
* @returns The sanitize for system reminder result.
|
|
37
|
+
*/
|
|
52
38
|
export function sanitizeForSystemReminder(s) {
|
|
53
39
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
54
40
|
}
|
|
@@ -56,10 +42,15 @@ export function sanitizeForSystemReminder(s) {
|
|
|
56
42
|
* Format CrossConversationEntry[] as a `<system-reminder>` block. Adapters
|
|
57
43
|
* that inline context into prompt text (nanoclaw) and `MoltZapService.getContext`
|
|
58
44
|
* share this formatter so sanitization and line shape stay in one place.
|
|
45
|
+
* @param entries Value supplied to the operation.
|
|
46
|
+
* @param opts Value supplied to the operation.
|
|
47
|
+
* @param opts.header Value supplied to the operation.
|
|
48
|
+
* @returns The format cross conversation block result.
|
|
59
49
|
*/
|
|
60
50
|
function formatCrossConversationBlock(entries, opts) {
|
|
61
|
-
if (entries.length === 0)
|
|
51
|
+
if (entries.length === 0) {
|
|
62
52
|
return null;
|
|
53
|
+
}
|
|
63
54
|
const lines = entries.map((e) => {
|
|
64
55
|
const safeSender = sanitizeForSystemReminder(e.senderName);
|
|
65
56
|
const safeText = sanitizeForSystemReminder(e.text.slice(0, CROSS_CONTEXT_TEXT_LIMIT));
|
|
@@ -88,6 +79,8 @@ const DEDUP_WINDOW_PER_CONV = 1000;
|
|
|
88
79
|
/**
|
|
89
80
|
* Invoke every handler with `arg`, isolating throws so one bad handler
|
|
90
81
|
* doesn't abort the remaining fanout.
|
|
82
|
+
* @param handlers Value supplied to the operation.
|
|
83
|
+
* @param arg Value supplied to the operation.
|
|
91
84
|
*/
|
|
92
85
|
function fanout(handlers, arg) {
|
|
93
86
|
for (const h of handlers) {
|
|
@@ -109,9 +102,8 @@ function fanout(handlers, arg) {
|
|
|
109
102
|
* callers compose failures and cancellation explicitly.
|
|
110
103
|
*/
|
|
111
104
|
export class MoltZapService {
|
|
112
|
-
opts;
|
|
113
105
|
client = null;
|
|
114
|
-
|
|
106
|
+
connectedValue = false;
|
|
115
107
|
/**
|
|
116
108
|
* Service-owned scope. Opened in `connect()`, owns the
|
|
117
109
|
* `subscribeAll → Stream.runForEach` fan-out fiber. Closed in `close()` so
|
|
@@ -143,18 +135,19 @@ export class MoltZapService {
|
|
|
143
135
|
dispatchLeaseConsumed: [],
|
|
144
136
|
dispatchLeaseExpired: [],
|
|
145
137
|
};
|
|
146
|
-
|
|
138
|
+
ownAgentIdValue;
|
|
139
|
+
opts;
|
|
147
140
|
constructor(opts) {
|
|
148
141
|
this.opts = opts;
|
|
149
142
|
// The empty HelloOk carries no identity; `ownAgentId` is the client's
|
|
150
143
|
// registered/stored id, available before the handshake.
|
|
151
|
-
this.
|
|
144
|
+
this.ownAgentIdValue = opts.agentId;
|
|
152
145
|
}
|
|
153
146
|
static fromConfig(config) {
|
|
154
147
|
return new MoltZapService(config);
|
|
155
148
|
}
|
|
156
149
|
static make(profileName) {
|
|
157
|
-
return loadServiceConfig(profileName).pipe(Effect.map(MoltZapService.fromConfig));
|
|
150
|
+
return loadServiceConfig(profileName).pipe(Effect.map((config) => MoltZapService.fromConfig(config)));
|
|
158
151
|
}
|
|
159
152
|
static startDaemon(profileName) {
|
|
160
153
|
return Effect.gen(function* () {
|
|
@@ -165,12 +158,15 @@ export class MoltZapService {
|
|
|
165
158
|
}).pipe(Effect.withSpan("MoltZapService.startDaemon"));
|
|
166
159
|
}
|
|
167
160
|
get connected() {
|
|
168
|
-
return this.
|
|
161
|
+
return this.connectedValue;
|
|
169
162
|
}
|
|
170
163
|
get ownAgentId() {
|
|
171
|
-
return this.
|
|
164
|
+
return this.ownAgentIdValue;
|
|
172
165
|
}
|
|
173
|
-
/**
|
|
166
|
+
/**
|
|
167
|
+
* Effect-native: compose via `yield*` or bridge at the edge via `Effect.runPromise`.
|
|
168
|
+
* @returns The client result.
|
|
169
|
+
*/
|
|
174
170
|
connect() {
|
|
175
171
|
return Effect.gen(this, function* () {
|
|
176
172
|
const client = new MoltZapAgentClient({
|
|
@@ -180,7 +176,7 @@ export class MoltZapService {
|
|
|
180
176
|
// kept explicit so a future disconnect-handler chain can plumb
|
|
181
177
|
// code/reason through.
|
|
182
178
|
onDisconnect: () => {
|
|
183
|
-
this.
|
|
179
|
+
this.connectedValue = false;
|
|
184
180
|
fanout(this.handlers.disconnect, undefined);
|
|
185
181
|
},
|
|
186
182
|
});
|
|
@@ -197,10 +193,12 @@ export class MoltZapService {
|
|
|
197
193
|
// before the fiber exits.
|
|
198
194
|
const serviceScope = yield* Scope.make();
|
|
199
195
|
this.serviceScope = serviceScope;
|
|
200
|
-
const fanoutEffect = client.subscribeAll().pipe(Stream.runForEach((notification) => Effect.sync(() =>
|
|
196
|
+
const fanoutEffect = client.subscribeAll().pipe(Stream.runForEach((notification) => Effect.sync(() => {
|
|
197
|
+
this.handleNotification(notification);
|
|
198
|
+
})), Effect.catchAll((cause) => Effect.logWarning("MoltZapService notification fan-out terminated", cause)), Effect.asVoid);
|
|
201
199
|
yield* Effect.forkIn(fanoutEffect, serviceScope);
|
|
202
200
|
const helloOk = yield* client.connect();
|
|
203
|
-
this.
|
|
201
|
+
this.connectedValue = true;
|
|
204
202
|
return helloOk;
|
|
205
203
|
});
|
|
206
204
|
}
|
|
@@ -211,7 +209,7 @@ export class MoltZapService {
|
|
|
211
209
|
* shutdown.
|
|
212
210
|
*/
|
|
213
211
|
close() {
|
|
214
|
-
this.
|
|
212
|
+
this.connectedValue = false;
|
|
215
213
|
Effect.runFork(this.stopSocketServer());
|
|
216
214
|
const scopeToClose = this.serviceScope;
|
|
217
215
|
const clientToClose = this.client;
|
|
@@ -246,11 +244,16 @@ export class MoltZapService {
|
|
|
246
244
|
* compromised or malicious server returns an id containing `..` or a
|
|
247
245
|
* path separator, a naive `path.join(... , agentId)` escapes `~/.moltzap`.
|
|
248
246
|
* Reject anything that isn't a safe identifier.
|
|
247
|
+
* @param id Value supplied to the operation.
|
|
248
|
+
* @returns The id result.
|
|
249
249
|
*/
|
|
250
250
|
static safeAgentIdSegment(id) {
|
|
251
251
|
return /^[A-Za-z0-9_-]+$/.test(id) ? id : "default";
|
|
252
252
|
}
|
|
253
|
-
/**
|
|
253
|
+
/**
|
|
254
|
+
* Per-instance socket path based on connected agentId.
|
|
255
|
+
* @returns The id result.
|
|
256
|
+
*/
|
|
254
257
|
get socketPath() {
|
|
255
258
|
const id = MoltZapService.safeAgentIdSegment(this.ownAgentId ?? "default");
|
|
256
259
|
return getMoltZapAgentServiceSocketPath(id);
|
|
@@ -289,8 +292,8 @@ export class MoltZapService {
|
|
|
289
292
|
}
|
|
290
293
|
localDaemonHandlers() {
|
|
291
294
|
return makeLocalDaemonHandlers({
|
|
292
|
-
ownAgentId: this.
|
|
293
|
-
connected: this.
|
|
295
|
+
ownAgentId: this.ownAgentIdValue,
|
|
296
|
+
connected: this.connectedValue,
|
|
294
297
|
conversationCount: () => this.getConversations().length,
|
|
295
298
|
call: this.call.bind(this),
|
|
296
299
|
handleHistoryRequest: (request) => this.handleHistoryRequest(request),
|
|
@@ -298,7 +301,7 @@ export class MoltZapService {
|
|
|
298
301
|
}
|
|
299
302
|
handleHistoryRequest(request) {
|
|
300
303
|
return Effect.gen(this, function* () {
|
|
301
|
-
const result = yield* this.call(
|
|
304
|
+
const result = yield* this.call(messagesList.name, {
|
|
302
305
|
taskId: request.taskId,
|
|
303
306
|
conversationId: request.conversationId,
|
|
304
307
|
limit: request.limit,
|
|
@@ -338,15 +341,16 @@ export class MoltZapService {
|
|
|
338
341
|
const unknownAgentIds = [
|
|
339
342
|
...new Set(messages.map((message) => message.senderId)),
|
|
340
343
|
].filter((id) => !HashMap.has(knownNames, id));
|
|
341
|
-
if (unknownAgentIds.length === 0)
|
|
344
|
+
if (unknownAgentIds.length === 0) {
|
|
342
345
|
return;
|
|
346
|
+
}
|
|
343
347
|
yield* this.cacheVisibleAgentNamesForIds(new Set(unknownAgentIds)).pipe(Effect.asVoid, Effect.catchAll(() => Effect.void));
|
|
344
348
|
});
|
|
345
349
|
}
|
|
346
350
|
fetchHistoryConversationMeta(convId) {
|
|
347
351
|
// The client filters `ConversationList` output for the matching
|
|
348
352
|
// conversation id (there is no per-conversation get RPC).
|
|
349
|
-
return this.call(
|
|
353
|
+
return this.call(conversationList.name, {}).pipe(Effect.map((result) => {
|
|
350
354
|
const hit = result.items.find((item) => item.conversation.id === convId);
|
|
351
355
|
return hit?.conversation;
|
|
352
356
|
}), Effect.catchAll(() => Effect.succeed(undefined)));
|
|
@@ -358,9 +362,11 @@ export class MoltZapService {
|
|
|
358
362
|
const { conversationId, sessionKey } = request;
|
|
359
363
|
return Ref.update(this.lastReadRef, (outer) => {
|
|
360
364
|
const perSession = getOr(outer, sessionKey, () => HashMap.empty());
|
|
361
|
-
const existing = getOr(perSession, conversationId, () =>
|
|
362
|
-
|
|
365
|
+
const existing = getOr(perSession, conversationId, () =>
|
|
366
|
+
/* Safe because the surrounding invariant establishes this asserted shape. */ new Set());
|
|
367
|
+
if (messages.every((message) => existing.has(message.id))) {
|
|
363
368
|
return outer;
|
|
369
|
+
}
|
|
364
370
|
const nextSet = new Set(existing);
|
|
365
371
|
for (const message of messages) {
|
|
366
372
|
nextSet.add(message.id);
|
|
@@ -392,15 +398,19 @@ export class MoltZapService {
|
|
|
392
398
|
* when the RPC errors or the server has no record. The error path logs
|
|
393
399
|
* so ops can see repeated lookup failures; the empty-response path is
|
|
394
400
|
* silent (a cold agent is an expected transient state).
|
|
401
|
+
* @param agentId Identifier of the agent targeted by the operation.
|
|
402
|
+
* @returns The decoded d agent id.
|
|
395
403
|
*/
|
|
396
404
|
resolveAgentName(agentId) {
|
|
397
405
|
return Effect.gen(this, function* () {
|
|
398
406
|
const decodedAgentId = Option.getOrUndefined(decodeAgentId(agentId));
|
|
399
|
-
if (decodedAgentId === undefined)
|
|
407
|
+
if (decodedAgentId === undefined) {
|
|
400
408
|
return agentId;
|
|
409
|
+
}
|
|
401
410
|
const cached = Option.getOrUndefined(HashMap.get(snapshot(this.agentNamesRef), agentId));
|
|
402
|
-
if (cached !== undefined)
|
|
411
|
+
if (cached !== undefined) {
|
|
403
412
|
return cached;
|
|
413
|
+
}
|
|
404
414
|
return yield* this.cacheVisibleAgentNamesForIds(new Set([decodedAgentId])).pipe(Effect.map(() => {
|
|
405
415
|
const resolved = Option.getOrUndefined(HashMap.get(snapshot(this.agentNamesRef), agentId));
|
|
406
416
|
return resolved ?? agentId;
|
|
@@ -437,12 +447,19 @@ export class MoltZapService {
|
|
|
437
447
|
* params frame. The server marks the lease consumed, blocking the
|
|
438
448
|
* app authorization timeout sweep. `MoltZapChannelCore.sendReply` forwards
|
|
439
449
|
* `leaseIdInFlight` automatically when the caller omits it.
|
|
450
|
+
* @param taskId Value supplied to the operation.
|
|
451
|
+
* @param conversationId Value supplied to the operation.
|
|
452
|
+
* @param text Text to process.
|
|
453
|
+
* @param opts Value supplied to the operation.
|
|
454
|
+
* @param opts.replyTo Value supplied to the operation.
|
|
455
|
+
* @param opts.dispatchLeaseId Value supplied to the operation.
|
|
456
|
+
* @returns The send result.
|
|
440
457
|
*/
|
|
441
458
|
send(taskId, conversationId, text, opts) {
|
|
442
459
|
if (this.isConversationArchived(conversationId)) {
|
|
443
460
|
return Effect.fail(new ConversationArchivedError({ message: "Conversation is archived" }));
|
|
444
461
|
}
|
|
445
|
-
return Effect.asVoid(this.call(
|
|
462
|
+
return Effect.asVoid(this.call(messagesSend.name, {
|
|
446
463
|
taskId,
|
|
447
464
|
conversationId,
|
|
448
465
|
parts: [{ type: "text", text }],
|
|
@@ -456,9 +473,11 @@ export class MoltZapService {
|
|
|
456
473
|
* Issue `agent/dispatch/request`. The server returns the ack
|
|
457
474
|
* `{leaseId, dispatchId}` immediately; the recipient observes the
|
|
458
475
|
* verdict asynchronously via the `dispatchRelease` event.
|
|
476
|
+
* @param params Request payload to process.
|
|
477
|
+
* @returns The cache result.
|
|
459
478
|
*/
|
|
460
479
|
requestDispatch(params) {
|
|
461
|
-
return this.call(
|
|
480
|
+
return this.call(dispatchRequest.name, params);
|
|
462
481
|
}
|
|
463
482
|
sendToAgent(agentName, text, opts) {
|
|
464
483
|
return Effect.gen(this, function* () {
|
|
@@ -469,7 +488,7 @@ export class MoltZapService {
|
|
|
469
488
|
if (!agent) {
|
|
470
489
|
return yield* Effect.fail(agentNotFound(agentName));
|
|
471
490
|
}
|
|
472
|
-
const createResult = yield* this.call(
|
|
491
|
+
const createResult = yield* this.call(taskRequest.name, {
|
|
473
492
|
appId: DEFAULT_APP_ID,
|
|
474
493
|
invitedAgentIds: [agent.id],
|
|
475
494
|
initialConversation: { participants: [agent.id] },
|
|
@@ -495,23 +514,27 @@ export class MoltZapService {
|
|
|
495
514
|
const params = {
|
|
496
515
|
limit: REUSABLE_CONVERSATION_LOOKUP_PAGE_SIZE,
|
|
497
516
|
};
|
|
498
|
-
if (cursor !== undefined)
|
|
517
|
+
if (cursor !== undefined) {
|
|
499
518
|
params.cursor = cursor;
|
|
500
|
-
|
|
519
|
+
}
|
|
520
|
+
const result = yield* this.call(conversationList.name, params);
|
|
501
521
|
const hit = result.items.find((item) => item.taskId === taskId &&
|
|
502
522
|
item.conversation.archivedAt === undefined);
|
|
503
|
-
if (hit !== undefined)
|
|
523
|
+
if (hit !== undefined) {
|
|
504
524
|
return hit.conversation.id;
|
|
505
|
-
|
|
525
|
+
}
|
|
526
|
+
if (result.nextCursor === undefined) {
|
|
506
527
|
break;
|
|
528
|
+
}
|
|
507
529
|
cursor = result.nextCursor;
|
|
508
530
|
}
|
|
509
531
|
return yield* Effect.fail(agentNotFound(agentName));
|
|
510
532
|
});
|
|
511
533
|
}
|
|
512
534
|
cacheAgentNames(agents) {
|
|
513
|
-
if (agents.length === 0)
|
|
535
|
+
if (agents.length === 0) {
|
|
514
536
|
return Effect.void;
|
|
537
|
+
}
|
|
515
538
|
return Ref.update(this.agentNamesRef, (names) => {
|
|
516
539
|
let next = names;
|
|
517
540
|
for (const agent of agents) {
|
|
@@ -531,12 +554,14 @@ export class MoltZapService {
|
|
|
531
554
|
let cursor = undefined;
|
|
532
555
|
for (let page = 0; page < AGENT_LOOKUP_MAX_PAGES; page++) {
|
|
533
556
|
const params = this.agentListParams(cursor);
|
|
534
|
-
const result = yield* this.call(
|
|
557
|
+
const result = yield* this.call(agentsList.name, params);
|
|
535
558
|
yield* this.cacheAgentNames(result.agents);
|
|
536
|
-
for (const agent of result.agents)
|
|
559
|
+
for (const agent of result.agents) {
|
|
537
560
|
missing.delete(agent.id);
|
|
538
|
-
|
|
561
|
+
}
|
|
562
|
+
if (missing.size === 0 || result.nextCursor === undefined) {
|
|
539
563
|
return;
|
|
564
|
+
}
|
|
540
565
|
cursor = result.nextCursor;
|
|
541
566
|
}
|
|
542
567
|
});
|
|
@@ -546,11 +571,12 @@ export class MoltZapService {
|
|
|
546
571
|
let cursor = undefined;
|
|
547
572
|
for (let page = 0; page < AGENT_LOOKUP_MAX_PAGES; page++) {
|
|
548
573
|
const params = this.agentListParams(cursor);
|
|
549
|
-
const result = yield* this.call(
|
|
574
|
+
const result = yield* this.call(agentsList.name, params);
|
|
550
575
|
yield* this.cacheAgentNames(result.agents);
|
|
551
576
|
const hit = result.agents.find((agent) => agent.name === agentName);
|
|
552
|
-
if (hit !== undefined || result.nextCursor === undefined)
|
|
577
|
+
if (hit !== undefined || result.nextCursor === undefined) {
|
|
553
578
|
return hit;
|
|
579
|
+
}
|
|
554
580
|
cursor = result.nextCursor;
|
|
555
581
|
}
|
|
556
582
|
return undefined;
|
|
@@ -561,12 +587,16 @@ export class MoltZapService {
|
|
|
561
587
|
* Generate a system reminder with updates from other conversations.
|
|
562
588
|
* Each conversation has its own view of what's "new" — markers are tracked
|
|
563
589
|
* per viewing conversation and advanced after notification.
|
|
590
|
+
* @param currentConvId Value supplied to the operation.
|
|
591
|
+
* @param opts Value supplied to the operation.
|
|
592
|
+
* @returns The context options result.
|
|
564
593
|
*/
|
|
565
594
|
getContext(currentConvId, opts) {
|
|
566
595
|
const contextOptions = opts ?? {};
|
|
567
596
|
const { entries, commit } = this.peekContextEntries(currentConvId, contextOptions);
|
|
568
|
-
if (entries.length === 0)
|
|
597
|
+
if (entries.length === 0) {
|
|
569
598
|
return null;
|
|
599
|
+
}
|
|
570
600
|
commit();
|
|
571
601
|
return formatCrossConversationBlock(entries, {
|
|
572
602
|
header: `Recent updates (you are in conv:${currentConvId}):`,
|
|
@@ -577,6 +607,11 @@ export class MoltZapService {
|
|
|
577
607
|
* Call `commit()` on the result to mark the returned messages as seen so
|
|
578
608
|
* subsequent peeks return only what's new. A caller that reads without
|
|
579
609
|
* committing can re-peek idempotently.
|
|
610
|
+
* @param currentConvId Value supplied to the operation.
|
|
611
|
+
* @param opts Value supplied to the operation.
|
|
612
|
+
* @param opts.maxConversations Value supplied to the operation.
|
|
613
|
+
* @param opts.maxMessagesPerConv Value supplied to the operation.
|
|
614
|
+
* @returns The max convs result.
|
|
580
615
|
*/
|
|
581
616
|
peekContextEntries(currentConvId, opts) {
|
|
582
617
|
const maxConvs = opts?.maxConversations ?? DEFAULT_MAX_CONTEXT_CONVERSATIONS;
|
|
@@ -586,13 +621,17 @@ export class MoltZapService {
|
|
|
586
621
|
const { entries, pendingAdvances } = buildContextEntries(candidates.slice(0, maxConvs), state, maxMsgsPerConv);
|
|
587
622
|
return {
|
|
588
623
|
entries,
|
|
589
|
-
commit: () =>
|
|
624
|
+
commit: () => {
|
|
625
|
+
this.advanceLastNotified(currentConvId, pendingAdvances);
|
|
626
|
+
},
|
|
590
627
|
};
|
|
591
628
|
}
|
|
592
629
|
/**
|
|
593
630
|
* Return all new messages from all other conversations as full transcripts,
|
|
594
631
|
* sorted chronologically. Uses the same lastNotified markers as
|
|
595
632
|
* peekContextEntries. Call commit() to advance markers.
|
|
633
|
+
* @param currentConvId Value supplied to the operation.
|
|
634
|
+
* @returns The all messages result.
|
|
596
635
|
*/
|
|
597
636
|
peekFullMessages(currentConvId) {
|
|
598
637
|
const { messagesMap, conversationsMap, agentNamesMap, viewMarkers } = this.readCrossConvState(currentConvId);
|
|
@@ -610,12 +649,17 @@ export class MoltZapService {
|
|
|
610
649
|
timestamp: m.createdAt,
|
|
611
650
|
});
|
|
612
651
|
}
|
|
613
|
-
pendingAdvances.push([
|
|
652
|
+
pendingAdvances.push([
|
|
653
|
+
convId,
|
|
654
|
+
/* Safe because the surrounding invariant establishes this asserted shape. */ newMsgs[newMsgs.length - 1].id,
|
|
655
|
+
]);
|
|
614
656
|
}
|
|
615
657
|
allMessages.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
616
658
|
return {
|
|
617
659
|
messages: allMessages,
|
|
618
|
-
commit: () =>
|
|
660
|
+
commit: () => {
|
|
661
|
+
this.advanceLastNotified(currentConvId, pendingAdvances);
|
|
662
|
+
},
|
|
619
663
|
};
|
|
620
664
|
}
|
|
621
665
|
readCrossConvState(currentConvId) {
|
|
@@ -638,13 +682,15 @@ export class MoltZapService {
|
|
|
638
682
|
*iterNewMessagesByConv(messagesMap, viewMarkers, currentConvId) {
|
|
639
683
|
for (const [convId, msgs] of messagesMap) {
|
|
640
684
|
const newMsgs = newMessagesForConversation(convId, msgs, viewMarkers, currentConvId);
|
|
641
|
-
if (newMsgs.length > 0)
|
|
685
|
+
if (newMsgs.length > 0) {
|
|
642
686
|
yield [convId, newMsgs];
|
|
687
|
+
}
|
|
643
688
|
}
|
|
644
689
|
}
|
|
645
690
|
advanceLastNotified(currentConvId, pendingAdvances) {
|
|
646
|
-
if (pendingAdvances.length === 0)
|
|
691
|
+
if (pendingAdvances.length === 0) {
|
|
647
692
|
return;
|
|
693
|
+
}
|
|
648
694
|
Effect.runSync(Ref.update(this.lastNotifiedRef, (outer) => {
|
|
649
695
|
let markers = getOr(outer, currentConvId, () => HashMap.empty());
|
|
650
696
|
for (const [convId, msgId] of pendingAdvances) {
|
|
@@ -662,6 +708,10 @@ export class MoltZapService {
|
|
|
662
708
|
* Outbound RPC, typed per method — delegates to the agent client's typed
|
|
663
709
|
* `call`. `call("agent/identity/agents/list", payload)` recovers the result and that
|
|
664
710
|
* method's tagged-error union; an app-only method does not typecheck.
|
|
711
|
+
* @param tag Value supplied to the operation.
|
|
712
|
+
* @param payload Value supplied to the operation.
|
|
713
|
+
* @param opts Value supplied to the operation.
|
|
714
|
+
* @returns The client result.
|
|
665
715
|
*/
|
|
666
716
|
call(tag, payload, opts) {
|
|
667
717
|
return Effect.suspend(() => {
|
|
@@ -688,47 +738,49 @@ export class MoltZapService {
|
|
|
688
738
|
this.dispatchTypedNotification(notification);
|
|
689
739
|
}
|
|
690
740
|
recordNotificationTrace(notification) {
|
|
691
|
-
Effect.runFork(appendClientEventTrace(notificationTraceRecord(notification, this.
|
|
741
|
+
Effect.runFork(appendClientEventTrace(notificationTraceRecord(notification, this.ownAgentIdValue)));
|
|
692
742
|
}
|
|
693
743
|
dispatchTypedNotification(notification) {
|
|
694
|
-
if (this.dispatchMessageNotification(notification))
|
|
744
|
+
if (this.dispatchMessageNotification(notification)) {
|
|
695
745
|
return;
|
|
696
|
-
|
|
746
|
+
}
|
|
747
|
+
if (this.dispatchConversationNotification(notification)) {
|
|
697
748
|
return;
|
|
749
|
+
}
|
|
698
750
|
this.dispatchAppNotification(notification);
|
|
699
751
|
}
|
|
700
752
|
dispatchMessageNotification(notification) {
|
|
701
|
-
if (isNotificationDeliveryFor(notification,
|
|
753
|
+
if (isNotificationDeliveryFor(notification, messageReceivedNotificationDefinition)) {
|
|
702
754
|
this.handleMessageReceivedNotification(notification.params);
|
|
703
755
|
return true;
|
|
704
756
|
}
|
|
705
757
|
return false;
|
|
706
758
|
}
|
|
707
759
|
dispatchConversationNotification(notification) {
|
|
708
|
-
if (isNotificationDeliveryFor(notification,
|
|
760
|
+
if (isNotificationDeliveryFor(notification, conversationCreatedNotificationDefinition)) {
|
|
709
761
|
this.handleConversationCreatedNotification(notification.params);
|
|
710
762
|
return true;
|
|
711
763
|
}
|
|
712
|
-
if (isNotificationDeliveryFor(notification,
|
|
764
|
+
if (isNotificationDeliveryFor(notification, conversationArchivedNotificationDefinition)) {
|
|
713
765
|
this.handleConversationArchivedNotification(notification.params);
|
|
714
766
|
return true;
|
|
715
767
|
}
|
|
716
|
-
if (isNotificationDeliveryFor(notification,
|
|
768
|
+
if (isNotificationDeliveryFor(notification, conversationUnarchivedNotificationDefinition)) {
|
|
717
769
|
this.handleConversationUnarchivedNotification(notification.params);
|
|
718
770
|
return true;
|
|
719
771
|
}
|
|
720
772
|
return false;
|
|
721
773
|
}
|
|
722
774
|
dispatchAppNotification(notification) {
|
|
723
|
-
if (isNotificationDeliveryFor(notification,
|
|
775
|
+
if (isNotificationDeliveryFor(notification, dispatchRelease)) {
|
|
724
776
|
fanout(this.handlers.dispatchRelease, notification.params);
|
|
725
777
|
return;
|
|
726
778
|
}
|
|
727
|
-
if (isNotificationDeliveryFor(notification,
|
|
779
|
+
if (isNotificationDeliveryFor(notification, dispatchLeaseConsumed)) {
|
|
728
780
|
fanout(this.handlers.dispatchLeaseConsumed, notification.params);
|
|
729
781
|
return;
|
|
730
782
|
}
|
|
731
|
-
if (isNotificationDeliveryFor(notification,
|
|
783
|
+
if (isNotificationDeliveryFor(notification, dispatchLeaseExpired)) {
|
|
732
784
|
fanout(this.handlers.dispatchLeaseExpired, notification.params);
|
|
733
785
|
}
|
|
734
786
|
}
|
|
@@ -743,6 +795,9 @@ export class MoltZapService {
|
|
|
743
795
|
* replay, dual subscription) is suppressed to one
|
|
744
796
|
* `on("message", ...)` event. `agent/message/list` returns raw history
|
|
745
797
|
* unfiltered; consumers that combine both feeds dedup themselves.
|
|
798
|
+
* @param convId Value supplied to the operation.
|
|
799
|
+
* @param msgId Value supplied to the operation.
|
|
800
|
+
* @returns The dedup window result.
|
|
746
801
|
*/
|
|
747
802
|
recordMessageIdIfNew(convId, msgId) {
|
|
748
803
|
let dedupWindow = this.seenMessageIds.get(convId);
|
|
@@ -768,7 +823,7 @@ export class MoltZapService {
|
|
|
768
823
|
// Name resolution is driven lazily by channel-core's serialized consumer
|
|
769
824
|
// via resolveAgentName(), which populates agentNamesRef on first miss and
|
|
770
825
|
// hits the cache on every subsequent message.
|
|
771
|
-
if (msg.senderId !== this.
|
|
826
|
+
if (msg.senderId !== this.ownAgentIdValue) {
|
|
772
827
|
fanout(this.handlers.message, {
|
|
773
828
|
taskId: notification.taskId,
|
|
774
829
|
message: msg,
|