@otto-code/client 0.8.10 → 0.8.13
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/README.md +34 -7
- package/dist/compat/normalize-provider-models.js +4 -1
- package/dist/daemon-client-websocket-transport.d.ts +4 -0
- package/dist/daemon-client-websocket-transport.js +14 -2
- package/dist/daemon-client.d.ts +241 -127
- package/dist/daemon-client.js +525 -209
- package/dist/index.d.ts +73 -55
- package/dist/index.js +208 -48
- package/dist/terminal-stream-router.js +1 -0
- package/package.json +7 -3
package/dist/daemon-client.js
CHANGED
|
@@ -117,6 +117,7 @@ function toTimeoutError(error, label, timeoutMs) {
|
|
|
117
117
|
const DEFAULT_RECONNECT_BASE_DELAY_MS = 1500;
|
|
118
118
|
const DEFAULT_RECONNECT_MAX_DELAY_MS = 30000;
|
|
119
119
|
const DEFAULT_SESSION_RPC_TIMEOUT_MS = 60000;
|
|
120
|
+
const PUSH_TOKEN_REVOCATION_TIMEOUT_MS = 2000;
|
|
120
121
|
const DEFAULT_CONNECT_TIMEOUT_MS = 15000;
|
|
121
122
|
const DEFAULT_LIVENESS_TIMEOUT_MS = 5000;
|
|
122
123
|
const LIVENESS_HEARTBEAT_INTERVAL_MS = 10000;
|
|
@@ -185,6 +186,21 @@ function concatByteChunks(chunks, size) {
|
|
|
185
186
|
}
|
|
186
187
|
return bytes;
|
|
187
188
|
}
|
|
189
|
+
function getTransportFrameSize(frame) {
|
|
190
|
+
if (typeof frame === "string") {
|
|
191
|
+
return frame.length;
|
|
192
|
+
}
|
|
193
|
+
return frame.byteLength;
|
|
194
|
+
}
|
|
195
|
+
function describeInboundTransportFrame(frame, rawBytes) {
|
|
196
|
+
if (typeof frame === "string") {
|
|
197
|
+
return { kind: "text", size: String(frame.length) };
|
|
198
|
+
}
|
|
199
|
+
if (rawBytes) {
|
|
200
|
+
return { kind: "binary", size: String(rawBytes.byteLength) };
|
|
201
|
+
}
|
|
202
|
+
return { kind: "unknown", size: "0" };
|
|
203
|
+
}
|
|
188
204
|
function hashForLog(value) {
|
|
189
205
|
let hash = 0;
|
|
190
206
|
for (let index = 0; index < value.length; index += 1) {
|
|
@@ -595,6 +611,45 @@ export class DaemonClient {
|
|
|
595
611
|
// ============================================================================
|
|
596
612
|
// Core Send Helpers
|
|
597
613
|
// ============================================================================
|
|
614
|
+
beginTraceSection(name, args) {
|
|
615
|
+
const trace = this.config.trace;
|
|
616
|
+
if (!trace?.isEnabled()) {
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
trace.beginSection(name, args);
|
|
620
|
+
return true;
|
|
621
|
+
}
|
|
622
|
+
endTraceSection(isOpen) {
|
|
623
|
+
if (isOpen) {
|
|
624
|
+
this.config.trace?.endSection();
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
traceInstant(name, args) {
|
|
628
|
+
const isOpen = this.beginTraceSection(name, args);
|
|
629
|
+
this.endTraceSection(isOpen);
|
|
630
|
+
}
|
|
631
|
+
sendJsonMessage(envelopeType, messageType, message) {
|
|
632
|
+
this.traceInstant("otto.ws.message.outbound", {
|
|
633
|
+
envelopeType,
|
|
634
|
+
messageType,
|
|
635
|
+
});
|
|
636
|
+
this.sendTransportFrame(JSON.stringify(message));
|
|
637
|
+
}
|
|
638
|
+
sendTransportFrame(frame) {
|
|
639
|
+
if (!this.transport) {
|
|
640
|
+
throw new Error("Transport not connected");
|
|
641
|
+
}
|
|
642
|
+
const isOpen = this.beginTraceSection("otto.ws.frame.outbound", {
|
|
643
|
+
kind: typeof frame === "string" ? "text" : "binary",
|
|
644
|
+
size: String(getTransportFrameSize(frame)),
|
|
645
|
+
});
|
|
646
|
+
try {
|
|
647
|
+
this.transport.send(frame);
|
|
648
|
+
}
|
|
649
|
+
finally {
|
|
650
|
+
this.endTraceSection(isOpen);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
598
653
|
/**
|
|
599
654
|
* Send a session message. For fire-and-forget messages (heartbeats, etc.),
|
|
600
655
|
* failures are suppressed if `suppressSendErrors` is configured.
|
|
@@ -609,7 +664,7 @@ export class DaemonClient {
|
|
|
609
664
|
}
|
|
610
665
|
const payload = SessionInboundMessageSchema.parse(message);
|
|
611
666
|
try {
|
|
612
|
-
this.
|
|
667
|
+
this.sendJsonMessage("session", payload.type, { type: "session", message: payload });
|
|
613
668
|
}
|
|
614
669
|
catch (error) {
|
|
615
670
|
if (this.config.suppressSendErrors) {
|
|
@@ -626,7 +681,11 @@ export class DaemonClient {
|
|
|
626
681
|
throw new Error(`Transport not connected (status: ${this.connectionState.status})`);
|
|
627
682
|
}
|
|
628
683
|
try {
|
|
629
|
-
this.
|
|
684
|
+
this.traceInstant("otto.ws.message.outbound", {
|
|
685
|
+
envelopeType: "binary",
|
|
686
|
+
messageType: "binary",
|
|
687
|
+
});
|
|
688
|
+
this.sendTransportFrame(frame);
|
|
630
689
|
}
|
|
631
690
|
catch (error) {
|
|
632
691
|
if (this.config.suppressSendErrors) {
|
|
@@ -646,7 +705,7 @@ export class DaemonClient {
|
|
|
646
705
|
// If connected, send immediately
|
|
647
706
|
if (this.transport && status === "connected") {
|
|
648
707
|
const payload = SessionInboundMessageSchema.parse(message);
|
|
649
|
-
this.
|
|
708
|
+
this.sendJsonMessage("session", payload.type, { type: "session", message: payload });
|
|
650
709
|
return Promise.resolve();
|
|
651
710
|
}
|
|
652
711
|
// If connecting, queue the message to be sent once connected
|
|
@@ -677,7 +736,7 @@ export class DaemonClient {
|
|
|
677
736
|
try {
|
|
678
737
|
if (this.transport && this.connectionState.status === "connected") {
|
|
679
738
|
const payload = SessionInboundMessageSchema.parse(pending.message);
|
|
680
|
-
this.
|
|
739
|
+
this.sendJsonMessage("session", payload.type, { type: "session", message: payload });
|
|
681
740
|
pending.resolve();
|
|
682
741
|
}
|
|
683
742
|
else {
|
|
@@ -785,7 +844,7 @@ export class DaemonClient {
|
|
|
785
844
|
}
|
|
786
845
|
const payload = SessionInboundMessageSchema.parse(message);
|
|
787
846
|
try {
|
|
788
|
-
this.
|
|
847
|
+
this.sendJsonMessage("session", payload.type, { type: "session", message: payload });
|
|
789
848
|
}
|
|
790
849
|
catch (error) {
|
|
791
850
|
throw error instanceof Error ? error : new Error(String(error));
|
|
@@ -855,6 +914,15 @@ export class DaemonClient {
|
|
|
855
914
|
token,
|
|
856
915
|
});
|
|
857
916
|
}
|
|
917
|
+
async unregisterPushToken(token) {
|
|
918
|
+
const requestId = this.createRequestId();
|
|
919
|
+
await this.sendCorrelatedSessionRequest({
|
|
920
|
+
requestId,
|
|
921
|
+
message: { type: "push.unregister.request", token, requestId },
|
|
922
|
+
responseType: "push.unregister.response",
|
|
923
|
+
timeout: PUSH_TOKEN_REVOCATION_TIMEOUT_MS,
|
|
924
|
+
});
|
|
925
|
+
}
|
|
858
926
|
async ping(params) {
|
|
859
927
|
const requestId = params?.requestId ?? `ping-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
860
928
|
const clientSentAt = Date.now();
|
|
@@ -934,7 +1002,7 @@ export class DaemonClient {
|
|
|
934
1002
|
};
|
|
935
1003
|
this.pingProbe = probe;
|
|
936
1004
|
try {
|
|
937
|
-
this.
|
|
1005
|
+
this.sendJsonMessage("ping", "ping", { type: "ping" });
|
|
938
1006
|
}
|
|
939
1007
|
catch (error) {
|
|
940
1008
|
this.clearPingProbe();
|
|
@@ -1007,6 +1075,7 @@ export class DaemonClient {
|
|
|
1007
1075
|
type: "fetch_agent_history_request",
|
|
1008
1076
|
requestId: resolvedRequestId,
|
|
1009
1077
|
...(options?.filter ? { filter: options.filter } : {}),
|
|
1078
|
+
...(options?.search ? { search: options.search } : {}),
|
|
1010
1079
|
...(options?.sort ? { sort: options.sort } : {}),
|
|
1011
1080
|
...(options?.page ? { page: options.page } : {}),
|
|
1012
1081
|
});
|
|
@@ -1757,6 +1826,35 @@ export class DaemonClient {
|
|
|
1757
1826
|
}
|
|
1758
1827
|
return { customName: payload.customName };
|
|
1759
1828
|
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Sets (or with a null target, clears) which tracking board a project shows
|
|
1831
|
+
* on the Kanban screen. The daemon normalizes what it stores - a pasted board
|
|
1832
|
+
* URL comes back as the parsed id - so the caller should render the returned
|
|
1833
|
+
* target rather than its own draft.
|
|
1834
|
+
*/
|
|
1835
|
+
async setKanbanProjectTarget(input, requestId) {
|
|
1836
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
1837
|
+
requestId,
|
|
1838
|
+
message: {
|
|
1839
|
+
type: "kanban.project.target.set.request",
|
|
1840
|
+
projectId: input.projectId,
|
|
1841
|
+
target: input.target,
|
|
1842
|
+
},
|
|
1843
|
+
responseType: "kanban.project.target.set.response",
|
|
1844
|
+
});
|
|
1845
|
+
if (!payload.accepted) {
|
|
1846
|
+
throw new Error(payload.error ?? "setKanbanProjectTarget rejected");
|
|
1847
|
+
}
|
|
1848
|
+
return { target: payload.target };
|
|
1849
|
+
}
|
|
1850
|
+
async setProjectIcon(projectId, source, requestId) {
|
|
1851
|
+
const payload = await this.sendNamespacedCorrelatedSessionRequest({
|
|
1852
|
+
requestId,
|
|
1853
|
+
message: { type: "project.icon.set.request", projectId, source },
|
|
1854
|
+
});
|
|
1855
|
+
if (!payload.accepted)
|
|
1856
|
+
throw new Error(payload.error ?? "setProjectIcon rejected");
|
|
1857
|
+
}
|
|
1760
1858
|
async removeProject(projectId, requestId) {
|
|
1761
1859
|
const payload = await this.sendNamespacedCorrelatedSessionRequest({
|
|
1762
1860
|
requestId,
|
|
@@ -2004,6 +2102,58 @@ export class DaemonClient {
|
|
|
2004
2102
|
},
|
|
2005
2103
|
});
|
|
2006
2104
|
}
|
|
2105
|
+
/**
|
|
2106
|
+
* The provider-agnostic Kanban board surface. Each call names its provider
|
|
2107
|
+
* ("memory", "github", ...) - the daemon dispatches to the registered
|
|
2108
|
+
* KanbanProvider implementation and the wire never carries provider-native
|
|
2109
|
+
* identifiers beyond the opaque board/card/column ids.
|
|
2110
|
+
*
|
|
2111
|
+
* A project-scoped request is authoritative: the daemon resolves the
|
|
2112
|
+
* project's configured board target and overrides providerId from it. The
|
|
2113
|
+
* wire still carries providerId so older clients keep working; pass an inert
|
|
2114
|
+
* value (e.g. "github") when a project is supplied.
|
|
2115
|
+
*/
|
|
2116
|
+
async kanbanListBoards(input, requestId) {
|
|
2117
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
2118
|
+
requestId,
|
|
2119
|
+
message: {
|
|
2120
|
+
type: "kanban.boards.list.request",
|
|
2121
|
+
providerId: input.providerId,
|
|
2122
|
+
...(input.projectId ? { projectId: input.projectId } : {}),
|
|
2123
|
+
...(input.projectKey ? { projectKey: input.projectKey } : {}),
|
|
2124
|
+
requestId: "",
|
|
2125
|
+
},
|
|
2126
|
+
timeout: 60000,
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
async kanbanGetBoard(providerId, boardId, requestId) {
|
|
2130
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
2131
|
+
requestId,
|
|
2132
|
+
message: { type: "kanban.board.get.request", providerId, boardId, requestId: "" },
|
|
2133
|
+
timeout: 60000,
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
async kanbanMoveCard(input, requestId) {
|
|
2137
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
2138
|
+
requestId,
|
|
2139
|
+
message: { ...input, type: "kanban.card.move.request", requestId: "" },
|
|
2140
|
+
timeout: 60000,
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
async kanbanCreateCard(input, requestId) {
|
|
2144
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
2145
|
+
requestId,
|
|
2146
|
+
message: { ...input, type: "kanban.card.create.request", requestId: "" },
|
|
2147
|
+
timeout: 60000,
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
async kanbanLinkTask(input, requestId) {
|
|
2151
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
2152
|
+
requestId,
|
|
2153
|
+
message: { ...input, type: "kanban.task.link.request", requestId: "" },
|
|
2154
|
+
timeout: 60000,
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2007
2157
|
async getCommitFileDiff(cwd, sha, path, requestId) {
|
|
2008
2158
|
const payload = await this.sendNamespacedCorrelatedSessionRequest({
|
|
2009
2159
|
requestId,
|
|
@@ -2189,6 +2339,7 @@ export class DaemonClient {
|
|
|
2189
2339
|
...(options.cursor ? { cursor: options.cursor } : {}),
|
|
2190
2340
|
...(typeof options.limit === "number" ? { limit: options.limit } : {}),
|
|
2191
2341
|
...(options.projection ? { projection: options.projection } : {}),
|
|
2342
|
+
...(options.mergeWindow === true ? { mergeWindow: true } : {}),
|
|
2192
2343
|
});
|
|
2193
2344
|
const payload = await this.sendRequest({
|
|
2194
2345
|
requestId: resolvedRequestId,
|
|
@@ -2210,6 +2361,28 @@ export class DaemonClient {
|
|
|
2210
2361
|
}
|
|
2211
2362
|
return payload;
|
|
2212
2363
|
}
|
|
2364
|
+
async listAgentTimelinePrompts(agentId, options = {}) {
|
|
2365
|
+
const requestId = this.createRequestId(options.requestId);
|
|
2366
|
+
const message = SessionInboundMessageSchema.parse({
|
|
2367
|
+
type: "agent.timeline.list_prompts.request",
|
|
2368
|
+
agentId,
|
|
2369
|
+
requestId,
|
|
2370
|
+
});
|
|
2371
|
+
const payload = await this.sendRequest({
|
|
2372
|
+
requestId,
|
|
2373
|
+
message,
|
|
2374
|
+
timeout: options.timeout,
|
|
2375
|
+
options: { skipQueue: true },
|
|
2376
|
+
select: (response) => response.type === "agent.timeline.list_prompts.response" &&
|
|
2377
|
+
response.payload.requestId === requestId
|
|
2378
|
+
? response.payload
|
|
2379
|
+
: null,
|
|
2380
|
+
});
|
|
2381
|
+
if (payload.error) {
|
|
2382
|
+
throw new Error(payload.error);
|
|
2383
|
+
}
|
|
2384
|
+
return payload;
|
|
2385
|
+
}
|
|
2213
2386
|
async buildAgentForkContext(agentId, options = {}) {
|
|
2214
2387
|
const resolvedRequestId = this.createRequestId(options.requestId);
|
|
2215
2388
|
const message = SessionInboundMessageSchema.parse({
|
|
@@ -2574,6 +2747,40 @@ export class DaemonClient {
|
|
|
2574
2747
|
}
|
|
2575
2748
|
return payload.notice ?? null;
|
|
2576
2749
|
}
|
|
2750
|
+
/**
|
|
2751
|
+
* Applies a whole agent-config bundle in one request. Use this instead of
|
|
2752
|
+
* chaining the single-field setters when the values belong together so client
|
|
2753
|
+
* interruption and other mutations cannot interleave between steps. A
|
|
2754
|
+
* provider rejection can still leave earlier steps applied.
|
|
2755
|
+
* Gated on `server_info.features.agentConfigApply`.
|
|
2756
|
+
*/
|
|
2757
|
+
async applyAgentConfig(agentId, config) {
|
|
2758
|
+
const requestId = this.createRequestId();
|
|
2759
|
+
const message = SessionInboundMessageSchema.parse({
|
|
2760
|
+
type: "agent.config.apply.request",
|
|
2761
|
+
agentId,
|
|
2762
|
+
config,
|
|
2763
|
+
requestId,
|
|
2764
|
+
});
|
|
2765
|
+
const payload = await this.sendRequest({
|
|
2766
|
+
requestId,
|
|
2767
|
+
message,
|
|
2768
|
+
options: { skipQueue: true },
|
|
2769
|
+
select: (msg) => {
|
|
2770
|
+
if (msg.type !== "agent.config.apply.response") {
|
|
2771
|
+
return null;
|
|
2772
|
+
}
|
|
2773
|
+
if (msg.payload.requestId !== requestId) {
|
|
2774
|
+
return null;
|
|
2775
|
+
}
|
|
2776
|
+
return msg.payload;
|
|
2777
|
+
},
|
|
2778
|
+
});
|
|
2779
|
+
if (!payload.accepted) {
|
|
2780
|
+
throw new Error(payload.error ?? "applyAgentConfig rejected");
|
|
2781
|
+
}
|
|
2782
|
+
return payload.notice ?? null;
|
|
2783
|
+
}
|
|
2577
2784
|
async restartServer(reason, requestId) {
|
|
2578
2785
|
const resolvedRequestId = this.createRequestId(requestId);
|
|
2579
2786
|
const message = SessionInboundMessageSchema.parse({
|
|
@@ -3140,6 +3347,16 @@ export class DaemonClient {
|
|
|
3140
3347
|
responseType: "checkout.refresh.response",
|
|
3141
3348
|
});
|
|
3142
3349
|
}
|
|
3350
|
+
async checkoutGitFetch(cwd, requestId) {
|
|
3351
|
+
return this.sendCorrelatedSessionRequest({
|
|
3352
|
+
requestId,
|
|
3353
|
+
message: {
|
|
3354
|
+
type: "checkout.git.fetch.request",
|
|
3355
|
+
cwd,
|
|
3356
|
+
},
|
|
3357
|
+
responseType: "checkout.git.fetch.response",
|
|
3358
|
+
});
|
|
3359
|
+
}
|
|
3143
3360
|
async checkoutPrCreate(cwd, input, requestId) {
|
|
3144
3361
|
return this.sendCorrelatedSessionRequest({
|
|
3145
3362
|
requestId,
|
|
@@ -3629,48 +3846,6 @@ export class DaemonClient {
|
|
|
3629
3846
|
requestId: input.requestId,
|
|
3630
3847
|
}));
|
|
3631
3848
|
}
|
|
3632
|
-
/** Create an empty file or a directory. Never overwrites - see FileCreateResultSchema. */
|
|
3633
|
-
async createFileEntry(options) {
|
|
3634
|
-
const payload = await this.sendCorrelatedSessionRequest({
|
|
3635
|
-
requestId: options.requestId,
|
|
3636
|
-
message: {
|
|
3637
|
-
type: "file.create.request",
|
|
3638
|
-
cwd: options.cwd,
|
|
3639
|
-
path: options.path,
|
|
3640
|
-
kind: options.kind,
|
|
3641
|
-
},
|
|
3642
|
-
responseType: "file.create.response",
|
|
3643
|
-
});
|
|
3644
|
-
return payload.result;
|
|
3645
|
-
}
|
|
3646
|
-
/** Permanent delete - an unlink, not a move to any trash. */
|
|
3647
|
-
async deleteFileEntry(options) {
|
|
3648
|
-
const payload = await this.sendCorrelatedSessionRequest({
|
|
3649
|
-
requestId: options.requestId,
|
|
3650
|
-
message: {
|
|
3651
|
-
type: "file.delete.request",
|
|
3652
|
-
cwd: options.cwd,
|
|
3653
|
-
path: options.path,
|
|
3654
|
-
recursive: options.recursive,
|
|
3655
|
-
},
|
|
3656
|
-
responseType: "file.delete.response",
|
|
3657
|
-
});
|
|
3658
|
-
return payload.result;
|
|
3659
|
-
}
|
|
3660
|
-
/** Rename, which is also move. Never clobbers an occupied destination. */
|
|
3661
|
-
async renameFileEntry(options) {
|
|
3662
|
-
const payload = await this.sendCorrelatedSessionRequest({
|
|
3663
|
-
requestId: options.requestId,
|
|
3664
|
-
message: {
|
|
3665
|
-
type: "file.rename.request",
|
|
3666
|
-
cwd: options.cwd,
|
|
3667
|
-
path: options.path,
|
|
3668
|
-
newPath: options.newPath,
|
|
3669
|
-
},
|
|
3670
|
-
responseType: "file.rename.response",
|
|
3671
|
-
});
|
|
3672
|
-
return payload.result;
|
|
3673
|
-
}
|
|
3674
3849
|
async refineFile(options) {
|
|
3675
3850
|
const payload = await this.sendCorrelatedSessionRequest({
|
|
3676
3851
|
requestId: options.requestId,
|
|
@@ -4363,6 +4538,7 @@ export class DaemonClient {
|
|
|
4363
4538
|
message: {
|
|
4364
4539
|
type: "get_providers_snapshot_request",
|
|
4365
4540
|
cwd: options?.cwd,
|
|
4541
|
+
ifNoneMatch: options?.ifNoneMatch,
|
|
4366
4542
|
},
|
|
4367
4543
|
responseType: "get_providers_snapshot_response",
|
|
4368
4544
|
});
|
|
@@ -4456,6 +4632,208 @@ export class DaemonClient {
|
|
|
4456
4632
|
responseType: "connectors.oauth.disconnect.response",
|
|
4457
4633
|
});
|
|
4458
4634
|
}
|
|
4635
|
+
/**
|
|
4636
|
+
* Read the daemon-owned, provider-neutral communications inbox projection.
|
|
4637
|
+
* Requires server_info.features.communications; callers own that one gate.
|
|
4638
|
+
*/
|
|
4639
|
+
async communicationsGetOverview(requestId) {
|
|
4640
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4641
|
+
requestId,
|
|
4642
|
+
message: { type: "communications.get_overview.request" },
|
|
4643
|
+
responseType: "communications.get_overview.response",
|
|
4644
|
+
});
|
|
4645
|
+
return payload.overview;
|
|
4646
|
+
}
|
|
4647
|
+
async communicationsInboxGetHome(providerId, requestId) {
|
|
4648
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4649
|
+
requestId,
|
|
4650
|
+
message: { type: "communications.inbox.get_home.request", providerId },
|
|
4651
|
+
responseType: "communications.inbox.get_home.response",
|
|
4652
|
+
});
|
|
4653
|
+
return payload.home;
|
|
4654
|
+
}
|
|
4655
|
+
async communicationsInboxAcknowledgeNotifications(input, requestId) {
|
|
4656
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4657
|
+
requestId,
|
|
4658
|
+
message: { type: "communications.inbox.notifications.acknowledge.request", ...input },
|
|
4659
|
+
responseType: "communications.inbox.notifications.acknowledge.response",
|
|
4660
|
+
});
|
|
4661
|
+
return payload.home;
|
|
4662
|
+
}
|
|
4663
|
+
async communicationsInboxSearch(input, requestId) {
|
|
4664
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4665
|
+
requestId,
|
|
4666
|
+
message: { type: "communications.inbox.search.request", ...input },
|
|
4667
|
+
responseType: "communications.inbox.search.response",
|
|
4668
|
+
});
|
|
4669
|
+
return payload.results;
|
|
4670
|
+
}
|
|
4671
|
+
async communicationsInboxSetFavorite(input, requestId) {
|
|
4672
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4673
|
+
requestId,
|
|
4674
|
+
message: { type: "communications.inbox.set_favorite.request", ...input },
|
|
4675
|
+
responseType: "communications.inbox.set_favorite.response",
|
|
4676
|
+
});
|
|
4677
|
+
return payload.home;
|
|
4678
|
+
}
|
|
4679
|
+
async communicationsInboxGetPresence(providerId, requestId) {
|
|
4680
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4681
|
+
requestId,
|
|
4682
|
+
message: { type: "communications.inbox.get_presence.request", providerId },
|
|
4683
|
+
responseType: "communications.inbox.get_presence.response",
|
|
4684
|
+
});
|
|
4685
|
+
return payload.presence;
|
|
4686
|
+
}
|
|
4687
|
+
async communicationsInboxSetPresence(input, requestId) {
|
|
4688
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4689
|
+
requestId,
|
|
4690
|
+
message: { type: "communications.inbox.set_presence.request", ...input },
|
|
4691
|
+
responseType: "communications.inbox.set_presence.response",
|
|
4692
|
+
});
|
|
4693
|
+
return payload.presence;
|
|
4694
|
+
}
|
|
4695
|
+
async communicationsInboxSetEnabled(input, requestId) {
|
|
4696
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4697
|
+
requestId,
|
|
4698
|
+
message: { type: "communications.inbox.set_enabled.request", ...input },
|
|
4699
|
+
responseType: "communications.inbox.set_enabled.response",
|
|
4700
|
+
});
|
|
4701
|
+
return payload.presence;
|
|
4702
|
+
}
|
|
4703
|
+
async communicationsInboxGetMessages(input, requestId) {
|
|
4704
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4705
|
+
requestId,
|
|
4706
|
+
message: { type: "communications.inbox.get_messages.request", ...input },
|
|
4707
|
+
responseType: "communications.inbox.get_messages.response",
|
|
4708
|
+
});
|
|
4709
|
+
return payload.messages;
|
|
4710
|
+
}
|
|
4711
|
+
async communicationsInboxSendMessage(input, requestId) {
|
|
4712
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4713
|
+
requestId,
|
|
4714
|
+
message: { type: "communications.inbox.send_message.request", ...input },
|
|
4715
|
+
responseType: "communications.inbox.send_message.response",
|
|
4716
|
+
});
|
|
4717
|
+
return payload.message;
|
|
4718
|
+
}
|
|
4719
|
+
/** Requires server_info.features.communicationsRooms. */
|
|
4720
|
+
async communicationsRoomGet(input, requestId) {
|
|
4721
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4722
|
+
requestId,
|
|
4723
|
+
message: { type: "communications.room.get.request", ...input },
|
|
4724
|
+
responseType: "communications.room.get.response",
|
|
4725
|
+
});
|
|
4726
|
+
return payload.room;
|
|
4727
|
+
}
|
|
4728
|
+
async communicationsRoomThreadGet(input, requestId) {
|
|
4729
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4730
|
+
requestId,
|
|
4731
|
+
message: { type: "communications.room.thread.get.request", ...input },
|
|
4732
|
+
responseType: "communications.room.thread.get.response",
|
|
4733
|
+
});
|
|
4734
|
+
return payload.messages;
|
|
4735
|
+
}
|
|
4736
|
+
async communicationsRoomMessageSend(input, requestId) {
|
|
4737
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4738
|
+
requestId,
|
|
4739
|
+
message: { type: "communications.room.message.send.request", ...input },
|
|
4740
|
+
responseType: "communications.room.message.send.response",
|
|
4741
|
+
});
|
|
4742
|
+
return payload.message;
|
|
4743
|
+
}
|
|
4744
|
+
async communicationsRoomReactionSet(input, requestId) {
|
|
4745
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4746
|
+
requestId,
|
|
4747
|
+
message: { type: "communications.room.reaction.set.request", ...input },
|
|
4748
|
+
responseType: "communications.room.reaction.set.response",
|
|
4749
|
+
});
|
|
4750
|
+
return payload.message;
|
|
4751
|
+
}
|
|
4752
|
+
/**
|
|
4753
|
+
* Daemon-owned meeting transcript library. Requires
|
|
4754
|
+
* `server_info.features.meetingTranscripts`; callers own that capability gate.
|
|
4755
|
+
*/
|
|
4756
|
+
async meetingsTranscriptsList(requestId) {
|
|
4757
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4758
|
+
requestId,
|
|
4759
|
+
message: { type: "meetings.transcripts.list.request" },
|
|
4760
|
+
responseType: "meetings.transcripts.list.response",
|
|
4761
|
+
});
|
|
4762
|
+
return payload.records;
|
|
4763
|
+
}
|
|
4764
|
+
async meetingsTranscriptsCreate(input, requestId) {
|
|
4765
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4766
|
+
requestId,
|
|
4767
|
+
message: { type: "meetings.transcripts.create.request", ...input },
|
|
4768
|
+
responseType: "meetings.transcripts.create.response",
|
|
4769
|
+
});
|
|
4770
|
+
return payload.record;
|
|
4771
|
+
}
|
|
4772
|
+
async meetingsTranscriptsUpdate(input, requestId) {
|
|
4773
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4774
|
+
requestId,
|
|
4775
|
+
message: { type: "meetings.transcripts.update.request", ...input },
|
|
4776
|
+
responseType: "meetings.transcripts.update.response",
|
|
4777
|
+
});
|
|
4778
|
+
return payload.record;
|
|
4779
|
+
}
|
|
4780
|
+
async meetingsTranscriptsDelete(id, requestId) {
|
|
4781
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4782
|
+
requestId,
|
|
4783
|
+
message: { type: "meetings.transcripts.delete.request", id },
|
|
4784
|
+
responseType: "meetings.transcripts.delete.response",
|
|
4785
|
+
});
|
|
4786
|
+
return payload.deleted;
|
|
4787
|
+
}
|
|
4788
|
+
/**
|
|
4789
|
+
* Read daemon-owned connection metadata for reusable integration settings.
|
|
4790
|
+
* Requires server_info.features.integrationAuthorization; callers own that
|
|
4791
|
+
* one capability gate.
|
|
4792
|
+
*/
|
|
4793
|
+
async integrationsAuthorizationGetOverview(requestId) {
|
|
4794
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4795
|
+
requestId,
|
|
4796
|
+
message: { type: "integrations.authorization.get_overview.request" },
|
|
4797
|
+
responseType: "integrations.authorization.get_overview.response",
|
|
4798
|
+
});
|
|
4799
|
+
return payload.overview;
|
|
4800
|
+
}
|
|
4801
|
+
/**
|
|
4802
|
+
* List the daemon's nonsecret authorization choices for an integration.
|
|
4803
|
+
* Requires server_info.features.integrationAuthorization; callers own that
|
|
4804
|
+
* one capability gate.
|
|
4805
|
+
*/
|
|
4806
|
+
async integrationsAuthorizationGetMethods(integrationId, requestId) {
|
|
4807
|
+
const payload = await this.sendCorrelatedSessionRequest({
|
|
4808
|
+
requestId,
|
|
4809
|
+
message: {
|
|
4810
|
+
type: "integrations.authorization.get_methods.request",
|
|
4811
|
+
...(integrationId ? { integrationId } : {}),
|
|
4812
|
+
},
|
|
4813
|
+
responseType: "integrations.authorization.get_methods.response",
|
|
4814
|
+
});
|
|
4815
|
+
return payload.methods;
|
|
4816
|
+
}
|
|
4817
|
+
/**
|
|
4818
|
+
* Starts a daemon-owned browser sign-in through a registered integration
|
|
4819
|
+
* driver. Requires server_info.features.integrationAuthorizationBrowserFlow;
|
|
4820
|
+
* callers own that one capability gate.
|
|
4821
|
+
*/
|
|
4822
|
+
async integrationsAuthorizationStartBrowser(input, requestId) {
|
|
4823
|
+
return this.sendCorrelatedSessionRequest({
|
|
4824
|
+
requestId,
|
|
4825
|
+
message: { type: "integrations.authorization.start_browser.request", ...input },
|
|
4826
|
+
responseType: "integrations.authorization.start_browser.response",
|
|
4827
|
+
});
|
|
4828
|
+
}
|
|
4829
|
+
/** Starts the daemon-owned Zoom Team Chat browser sign-in. */
|
|
4830
|
+
async integrationsZoomStartAuthorization(requestId) {
|
|
4831
|
+
return this.sendCorrelatedSessionRequest({
|
|
4832
|
+
requestId,
|
|
4833
|
+
message: { type: "integrations.zoom.start_authorization.request" },
|
|
4834
|
+
responseType: "integrations.zoom.start_authorization.response",
|
|
4835
|
+
});
|
|
4836
|
+
}
|
|
4459
4837
|
/**
|
|
4460
4838
|
* The brain's status. Pass `resources` only from a surface that renders the
|
|
4461
4839
|
* live CPU/RAM/GPU numbers: it costs an `nvidia-smi` spawn on the brain, and
|
|
@@ -4600,7 +4978,7 @@ export class DaemonClient {
|
|
|
4600
4978
|
}
|
|
4601
4979
|
return payload.runtimes;
|
|
4602
4980
|
}
|
|
4603
|
-
async brainModelsPull(model, componentsOrRequestId, quantOrRequestId) {
|
|
4981
|
+
async brainModelsPull(model, componentsOrRequestId, quantOrRequestId, expectedBytes) {
|
|
4604
4982
|
const components = Array.isArray(componentsOrRequestId) ? componentsOrRequestId : undefined;
|
|
4605
4983
|
const quant = Array.isArray(componentsOrRequestId) ? quantOrRequestId : undefined;
|
|
4606
4984
|
const correlationId = typeof componentsOrRequestId === "string" ? componentsOrRequestId : undefined;
|
|
@@ -4611,6 +4989,7 @@ export class DaemonClient {
|
|
|
4611
4989
|
model,
|
|
4612
4990
|
...(components ? { components } : {}),
|
|
4613
4991
|
...(quant ? { quant } : {}),
|
|
4992
|
+
...(expectedBytes !== undefined ? { expectedBytes } : {}),
|
|
4614
4993
|
},
|
|
4615
4994
|
responseType: "brain.models.pull.response",
|
|
4616
4995
|
});
|
|
@@ -4638,10 +5017,16 @@ export class DaemonClient {
|
|
|
4638
5017
|
}
|
|
4639
5018
|
return payload.quants;
|
|
4640
5019
|
}
|
|
4641
|
-
async brainModelsAdd(repo, quant, components, requestId) {
|
|
5020
|
+
async brainModelsAdd(repo, quant, components, requestId, expectedBytes) {
|
|
4642
5021
|
const payload = await this.sendCorrelatedSessionRequest({
|
|
4643
5022
|
requestId,
|
|
4644
|
-
message: {
|
|
5023
|
+
message: {
|
|
5024
|
+
type: "brain.models.add.request",
|
|
5025
|
+
repo,
|
|
5026
|
+
quant,
|
|
5027
|
+
components,
|
|
5028
|
+
...(expectedBytes !== undefined ? { expectedBytes } : {}),
|
|
5029
|
+
},
|
|
4645
5030
|
responseType: "brain.models.add.response",
|
|
4646
5031
|
});
|
|
4647
5032
|
return unwrapBrainJob(payload);
|
|
@@ -4686,6 +5071,31 @@ export class DaemonClient {
|
|
|
4686
5071
|
});
|
|
4687
5072
|
return unwrapBrainJob(payload);
|
|
4688
5073
|
}
|
|
5074
|
+
async createFileEntry(input) {
|
|
5075
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
5076
|
+
message: { type: "fs.entry.create.request", ...input },
|
|
5077
|
+
});
|
|
5078
|
+
}
|
|
5079
|
+
async renameFileEntry(input) {
|
|
5080
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
5081
|
+
message: { type: "fs.entry.rename.request", ...input },
|
|
5082
|
+
});
|
|
5083
|
+
}
|
|
5084
|
+
async duplicateFileEntry(input) {
|
|
5085
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
5086
|
+
message: { type: "fs.entry.duplicate.request", ...input },
|
|
5087
|
+
});
|
|
5088
|
+
}
|
|
5089
|
+
async deleteFileEntry(input) {
|
|
5090
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
5091
|
+
message: { type: "fs.entry.delete.request", ...input },
|
|
5092
|
+
});
|
|
5093
|
+
}
|
|
5094
|
+
async checkoutDiscardChanges(cwd, input) {
|
|
5095
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
5096
|
+
message: { type: "checkout.discard_changes.request", cwd, paths: input.paths },
|
|
5097
|
+
});
|
|
5098
|
+
}
|
|
4689
5099
|
async brainJobsList(requestId) {
|
|
4690
5100
|
const payload = await this.sendCorrelatedSessionRequest({
|
|
4691
5101
|
requestId,
|
|
@@ -4801,6 +5211,15 @@ export class DaemonClient {
|
|
|
4801
5211
|
}
|
|
4802
5212
|
return payload.status;
|
|
4803
5213
|
}
|
|
5214
|
+
async getProjectIcon(projectId, requestId) {
|
|
5215
|
+
return this.sendNamespacedCorrelatedSessionRequest({
|
|
5216
|
+
requestId,
|
|
5217
|
+
message: { type: "project.icon.get.request", projectId },
|
|
5218
|
+
});
|
|
5219
|
+
}
|
|
5220
|
+
// ============================================================================
|
|
5221
|
+
// Provider Models / Commands
|
|
5222
|
+
// ============================================================================
|
|
4804
5223
|
/** Delete a model's files. The brain refuses while that model is loaded. */
|
|
4805
5224
|
async brainModelDelete(modelId, requestId) {
|
|
4806
5225
|
const payload = await this.sendCorrelatedSessionRequest({
|
|
@@ -4862,6 +5281,21 @@ export class DaemonClient {
|
|
|
4862
5281
|
}
|
|
4863
5282
|
return payload;
|
|
4864
5283
|
}
|
|
5284
|
+
/**
|
|
5285
|
+
* Turn the live Brain log feed on or off for this socket.
|
|
5286
|
+
*
|
|
5287
|
+
* Only meaningful against a daemon advertising `features.brainLogWatch`; older
|
|
5288
|
+
* daemons push every line regardless, and the request would go unrouted.
|
|
5289
|
+
* Watching is per socket, so this does not affect the same account's other
|
|
5290
|
+
* connected clients.
|
|
5291
|
+
*/
|
|
5292
|
+
async brainLogsWatch(watching, requestId) {
|
|
5293
|
+
return this.sendCorrelatedSessionRequest({
|
|
5294
|
+
requestId,
|
|
5295
|
+
message: { type: "brain.logs.watch.request", watching },
|
|
5296
|
+
responseType: "brain.logs.watch.response",
|
|
5297
|
+
});
|
|
5298
|
+
}
|
|
4865
5299
|
async getSpeechSettingsOptions(requestId) {
|
|
4866
5300
|
return this.sendNamespacedCorrelatedSessionRequest({
|
|
4867
5301
|
requestId,
|
|
@@ -5382,86 +5816,6 @@ export class DaemonClient {
|
|
|
5382
5816
|
responseType: "terminal.compatibility.diagnostic.response",
|
|
5383
5817
|
});
|
|
5384
5818
|
}
|
|
5385
|
-
async createChatRoom(options) {
|
|
5386
|
-
return this.sendCorrelatedSessionRequest({
|
|
5387
|
-
requestId: options.requestId,
|
|
5388
|
-
message: {
|
|
5389
|
-
type: "chat/create",
|
|
5390
|
-
name: options.name,
|
|
5391
|
-
...(options.purpose ? { purpose: options.purpose } : {}),
|
|
5392
|
-
},
|
|
5393
|
-
responseType: "chat/create/response",
|
|
5394
|
-
});
|
|
5395
|
-
}
|
|
5396
|
-
async listChatRooms(requestId) {
|
|
5397
|
-
return this.sendCorrelatedSessionRequest({
|
|
5398
|
-
requestId,
|
|
5399
|
-
message: {
|
|
5400
|
-
type: "chat/list",
|
|
5401
|
-
},
|
|
5402
|
-
responseType: "chat/list/response",
|
|
5403
|
-
});
|
|
5404
|
-
}
|
|
5405
|
-
async inspectChatRoom(options) {
|
|
5406
|
-
return this.sendCorrelatedSessionRequest({
|
|
5407
|
-
requestId: options.requestId,
|
|
5408
|
-
message: {
|
|
5409
|
-
type: "chat/inspect",
|
|
5410
|
-
room: options.room,
|
|
5411
|
-
},
|
|
5412
|
-
responseType: "chat/inspect/response",
|
|
5413
|
-
});
|
|
5414
|
-
}
|
|
5415
|
-
async deleteChatRoom(options) {
|
|
5416
|
-
return this.sendCorrelatedSessionRequest({
|
|
5417
|
-
requestId: options.requestId,
|
|
5418
|
-
message: {
|
|
5419
|
-
type: "chat/delete",
|
|
5420
|
-
room: options.room,
|
|
5421
|
-
},
|
|
5422
|
-
responseType: "chat/delete/response",
|
|
5423
|
-
});
|
|
5424
|
-
}
|
|
5425
|
-
async postChatMessage(options) {
|
|
5426
|
-
return this.sendCorrelatedSessionRequest({
|
|
5427
|
-
requestId: options.requestId,
|
|
5428
|
-
message: {
|
|
5429
|
-
type: "chat/post",
|
|
5430
|
-
room: options.room,
|
|
5431
|
-
body: options.body,
|
|
5432
|
-
...(options.authorAgentId ? { authorAgentId: options.authorAgentId } : {}),
|
|
5433
|
-
...(options.replyToMessageId ? { replyToMessageId: options.replyToMessageId } : {}),
|
|
5434
|
-
},
|
|
5435
|
-
responseType: "chat/post/response",
|
|
5436
|
-
});
|
|
5437
|
-
}
|
|
5438
|
-
async readChatMessages(options) {
|
|
5439
|
-
return this.sendCorrelatedSessionRequest({
|
|
5440
|
-
requestId: options.requestId,
|
|
5441
|
-
message: {
|
|
5442
|
-
type: "chat/read",
|
|
5443
|
-
room: options.room,
|
|
5444
|
-
...(typeof options.limit === "number" ? { limit: options.limit } : {}),
|
|
5445
|
-
...(options.since ? { since: options.since } : {}),
|
|
5446
|
-
...(options.authorAgentId ? { authorAgentId: options.authorAgentId } : {}),
|
|
5447
|
-
},
|
|
5448
|
-
responseType: "chat/read/response",
|
|
5449
|
-
timeout: options.timeout,
|
|
5450
|
-
});
|
|
5451
|
-
}
|
|
5452
|
-
async waitForChatMessages(options) {
|
|
5453
|
-
return this.sendCorrelatedSessionRequest({
|
|
5454
|
-
requestId: options.requestId,
|
|
5455
|
-
message: {
|
|
5456
|
-
type: "chat/wait",
|
|
5457
|
-
room: options.room,
|
|
5458
|
-
...(options.afterMessageId ? { afterMessageId: options.afterMessageId } : {}),
|
|
5459
|
-
...(typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {}),
|
|
5460
|
-
},
|
|
5461
|
-
responseType: "chat/wait/response",
|
|
5462
|
-
timeout: (options.timeoutMs ?? 0) + 10000,
|
|
5463
|
-
});
|
|
5464
|
-
}
|
|
5465
5819
|
async scheduleCreate(options) {
|
|
5466
5820
|
return this.sendCorrelatedSessionRequest({
|
|
5467
5821
|
requestId: options.requestId,
|
|
@@ -5661,76 +6015,6 @@ export class DaemonClient {
|
|
|
5661
6015
|
responseType: "artifact.get-content.response",
|
|
5662
6016
|
});
|
|
5663
6017
|
}
|
|
5664
|
-
async loopRun(options) {
|
|
5665
|
-
return this.sendCorrelatedSessionRequest({
|
|
5666
|
-
requestId: options.requestId,
|
|
5667
|
-
message: {
|
|
5668
|
-
type: "loop/run",
|
|
5669
|
-
prompt: options.prompt,
|
|
5670
|
-
cwd: options.cwd,
|
|
5671
|
-
...(options.provider ? { provider: options.provider } : {}),
|
|
5672
|
-
...(options.model ? { model: options.model } : {}),
|
|
5673
|
-
...(options.modeId ? { modeId: options.modeId } : {}),
|
|
5674
|
-
...(options.verifierProvider ? { verifierProvider: options.verifierProvider } : {}),
|
|
5675
|
-
...(options.verifierModel ? { verifierModel: options.verifierModel } : {}),
|
|
5676
|
-
...(options.verifierModeId ? { verifierModeId: options.verifierModeId } : {}),
|
|
5677
|
-
...(options.verifyPrompt ? { verifyPrompt: options.verifyPrompt } : {}),
|
|
5678
|
-
...(options.verifyChecks && options.verifyChecks.length > 0
|
|
5679
|
-
? { verifyChecks: options.verifyChecks }
|
|
5680
|
-
: {}),
|
|
5681
|
-
...(options.name ? { name: options.name } : {}),
|
|
5682
|
-
...(typeof options.sleepMs === "number" ? { sleepMs: options.sleepMs } : {}),
|
|
5683
|
-
...(typeof options.maxIterations === "number"
|
|
5684
|
-
? { maxIterations: options.maxIterations }
|
|
5685
|
-
: {}),
|
|
5686
|
-
...(typeof options.maxTimeMs === "number" ? { maxTimeMs: options.maxTimeMs } : {}),
|
|
5687
|
-
},
|
|
5688
|
-
responseType: "loop/run/response",
|
|
5689
|
-
});
|
|
5690
|
-
}
|
|
5691
|
-
async loopList(requestId) {
|
|
5692
|
-
return this.sendCorrelatedSessionRequest({
|
|
5693
|
-
requestId,
|
|
5694
|
-
message: {
|
|
5695
|
-
type: "loop/list",
|
|
5696
|
-
},
|
|
5697
|
-
responseType: "loop/list/response",
|
|
5698
|
-
});
|
|
5699
|
-
}
|
|
5700
|
-
async loopInspect(options) {
|
|
5701
|
-
const normalized = typeof options === "string" ? { id: options } : options;
|
|
5702
|
-
return this.sendCorrelatedSessionRequest({
|
|
5703
|
-
requestId: normalized.requestId,
|
|
5704
|
-
message: {
|
|
5705
|
-
type: "loop/inspect",
|
|
5706
|
-
id: normalized.id,
|
|
5707
|
-
},
|
|
5708
|
-
responseType: "loop/inspect/response",
|
|
5709
|
-
});
|
|
5710
|
-
}
|
|
5711
|
-
async loopLogs(options, afterSeq) {
|
|
5712
|
-
const normalized = typeof options === "string" ? { id: options, afterSeq } : options;
|
|
5713
|
-
return this.sendCorrelatedSessionRequest({
|
|
5714
|
-
requestId: normalized.requestId,
|
|
5715
|
-
message: {
|
|
5716
|
-
type: "loop/logs",
|
|
5717
|
-
id: normalized.id,
|
|
5718
|
-
...(typeof normalized.afterSeq === "number" ? { afterSeq: normalized.afterSeq } : {}),
|
|
5719
|
-
},
|
|
5720
|
-
responseType: "loop/logs/response",
|
|
5721
|
-
});
|
|
5722
|
-
}
|
|
5723
|
-
async loopStop(options) {
|
|
5724
|
-
const normalized = typeof options === "string" ? { id: options } : options;
|
|
5725
|
-
return this.sendCorrelatedSessionRequest({
|
|
5726
|
-
requestId: normalized.requestId,
|
|
5727
|
-
message: {
|
|
5728
|
-
type: "loop/stop",
|
|
5729
|
-
id: normalized.id,
|
|
5730
|
-
},
|
|
5731
|
-
responseType: "loop/stop/response",
|
|
5732
|
-
});
|
|
5733
|
-
}
|
|
5734
6018
|
onTerminalStreamEvent(handler) {
|
|
5735
6019
|
return this.terminalStreams.onEvent(handler);
|
|
5736
6020
|
}
|
|
@@ -5784,7 +6068,7 @@ export class DaemonClient {
|
|
|
5784
6068
|
return;
|
|
5785
6069
|
}
|
|
5786
6070
|
try {
|
|
5787
|
-
this.
|
|
6071
|
+
this.sendJsonMessage("hello", "hello", {
|
|
5788
6072
|
type: "hello",
|
|
5789
6073
|
clientId: this.config.clientId,
|
|
5790
6074
|
clientType: this.config.clientType ?? "cli",
|
|
@@ -5797,10 +6081,12 @@ export class DaemonClient {
|
|
|
5797
6081
|
// The daemon gates project.updated.notification on this (session.ts),
|
|
5798
6082
|
// so dropping it silently kills cross-session project renames.
|
|
5799
6083
|
[CLIENT_CAPS.projectUpdates]: true,
|
|
6084
|
+
[CLIENT_CAPS.communicationsPresenceUpdates]: true,
|
|
6085
|
+
[CLIENT_CAPS.compactProviderSnapshots]: true,
|
|
5800
6086
|
...this.config.capabilities,
|
|
5801
6087
|
},
|
|
5802
6088
|
...(this.config.appVersion ? { appVersion: this.config.appVersion } : {}),
|
|
5803
|
-
})
|
|
6089
|
+
});
|
|
5804
6090
|
}
|
|
5805
6091
|
catch (error) {
|
|
5806
6092
|
const message = error instanceof Error ? error.message : "Failed to send hello message";
|
|
@@ -5865,25 +6151,37 @@ export class DaemonClient {
|
|
|
5865
6151
|
return;
|
|
5866
6152
|
}
|
|
5867
6153
|
const rawBytes = asUint8Array(rawData);
|
|
5868
|
-
|
|
5869
|
-
|
|
6154
|
+
const isOpen = this.beginTraceSection("otto.ws.frame.inbound", describeInboundTransportFrame(rawData, rawBytes));
|
|
6155
|
+
try {
|
|
6156
|
+
if (rawBytes && this.tryHandleBinaryFrame(rawBytes)) {
|
|
6157
|
+
return;
|
|
6158
|
+
}
|
|
6159
|
+
const payload = decodeMessageData(rawData);
|
|
6160
|
+
if (!payload) {
|
|
6161
|
+
return;
|
|
6162
|
+
}
|
|
6163
|
+
this.handleJsonPayload(payload, rawBytes?.byteLength);
|
|
5870
6164
|
}
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
return;
|
|
6165
|
+
finally {
|
|
6166
|
+
this.endTraceSection(isOpen);
|
|
5874
6167
|
}
|
|
5875
|
-
this.handleJsonPayload(payload, rawBytes?.byteLength);
|
|
5876
6168
|
}
|
|
5877
6169
|
handleJsonPayload(payload, rawBytesLength) {
|
|
5878
6170
|
const bytes = rawBytesLength ?? payload.length;
|
|
5879
6171
|
const startMs = perfNow();
|
|
5880
6172
|
let parsedJson;
|
|
6173
|
+
const parseTraceOpen = this.beginTraceSection("otto.ws.json.parse", {
|
|
6174
|
+
size: String(bytes),
|
|
6175
|
+
});
|
|
5881
6176
|
try {
|
|
5882
6177
|
parsedJson = JSON.parse(payload);
|
|
5883
6178
|
}
|
|
5884
6179
|
catch {
|
|
5885
6180
|
return;
|
|
5886
6181
|
}
|
|
6182
|
+
finally {
|
|
6183
|
+
this.endTraceSection(parseTraceOpen);
|
|
6184
|
+
}
|
|
5887
6185
|
const parsed = validateWSOutboundMessage(parsedJson);
|
|
5888
6186
|
if (!parsed.success) {
|
|
5889
6187
|
const responseIdentity = extractCorrelatedResponseIdentity(parsedJson);
|
|
@@ -5902,10 +6200,18 @@ export class DaemonClient {
|
|
|
5902
6200
|
}
|
|
5903
6201
|
this.consecutiveLivenessFailures = 0;
|
|
5904
6202
|
if (parsed.data.type === "pong") {
|
|
6203
|
+
this.traceInstant("otto.ws.message.inbound", {
|
|
6204
|
+
envelopeType: "pong",
|
|
6205
|
+
messageType: "pong",
|
|
6206
|
+
});
|
|
5905
6207
|
this.resolvePingProbe();
|
|
5906
6208
|
this.runtimeMetrics?.recordMessage("pong", bytes, perfNow() - startMs);
|
|
5907
6209
|
return;
|
|
5908
6210
|
}
|
|
6211
|
+
this.traceInstant("otto.ws.message.inbound", {
|
|
6212
|
+
envelopeType: "session",
|
|
6213
|
+
messageType: parsed.data.message.type,
|
|
6214
|
+
});
|
|
5909
6215
|
this.handleSessionMessage(parsed.data.message);
|
|
5910
6216
|
const msgType = parsed.data.message.type;
|
|
5911
6217
|
this.runtimeMetrics?.recordMessage(msgType, bytes, perfNow() - startMs);
|
|
@@ -5916,6 +6222,11 @@ export class DaemonClient {
|
|
|
5916
6222
|
tryHandleBinaryFrame(rawBytes) {
|
|
5917
6223
|
const fileFrame = decodeFileTransferFrame(rawBytes);
|
|
5918
6224
|
if (fileFrame) {
|
|
6225
|
+
this.traceInstant("otto.ws.message.inbound", {
|
|
6226
|
+
envelopeType: "binary",
|
|
6227
|
+
messageType: "file",
|
|
6228
|
+
opcode: String(fileFrame.opcode),
|
|
6229
|
+
});
|
|
5919
6230
|
this.consecutiveLivenessFailures = 0;
|
|
5920
6231
|
this.handleFileTransferFrame(fileFrame);
|
|
5921
6232
|
this.runtimeMetrics?.recordBinaryFrame("other", rawBytes.byteLength, 0);
|
|
@@ -5925,6 +6236,11 @@ export class DaemonClient {
|
|
|
5925
6236
|
if (!frame) {
|
|
5926
6237
|
return false;
|
|
5927
6238
|
}
|
|
6239
|
+
this.traceInstant("otto.ws.message.inbound", {
|
|
6240
|
+
envelopeType: "binary",
|
|
6241
|
+
messageType: "terminal",
|
|
6242
|
+
opcode: String(frame.opcode),
|
|
6243
|
+
});
|
|
5928
6244
|
this.consecutiveLivenessFailures = 0;
|
|
5929
6245
|
const binaryStartMs = perfNow();
|
|
5930
6246
|
this.terminalStreams.handleFrame(frame);
|