@harness-gw/sdk 0.5.1 → 0.6.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/README.md +2 -2
- package/dist/chunks/index.js +5 -3
- package/dist/client/acp-workspace-client-sessions.d.ts +6 -1
- package/dist/client/index.js +113 -44
- package/dist/client/log.d.ts +11 -0
- package/dist/client/types.d.ts +4 -1
- package/dist/protocol/acp.d.ts +10 -0
- package/dist/protocol/sdk.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ You need a running harness. Hermes is the primary one; see
|
|
|
63
63
|
docker run --rm \
|
|
64
64
|
-v "$PWD/config.yaml:/run/harness-gw/config.yaml:ro" \
|
|
65
65
|
-v "$PWD/hermes-token:/run/secrets/hermes-token:ro" \
|
|
66
|
-
ghcr.io/almogbaku/harness-gw:0.
|
|
66
|
+
ghcr.io/almogbaku/harness-gw:0.6.0 \
|
|
67
67
|
config check --config /run/harness-gw/config.yaml
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -73,7 +73,7 @@ You need a running harness. Hermes is the primary one; see
|
|
|
73
73
|
docker run --rm -p 127.0.0.1:4100:4100 \
|
|
74
74
|
-v "$PWD/config.yaml:/run/harness-gw/config.yaml:ro" \
|
|
75
75
|
-v "$PWD/hermes-token:/run/secrets/hermes-token:ro" \
|
|
76
|
-
ghcr.io/almogbaku/harness-gw:0.
|
|
76
|
+
ghcr.io/almogbaku/harness-gw:0.6.0 \
|
|
77
77
|
serve --config /run/harness-gw/config.yaml
|
|
78
78
|
```
|
|
79
79
|
|
package/dist/chunks/index.js
CHANGED
|
@@ -208,7 +208,7 @@ var SessionMessagePartSchema = z.discriminatedUnion("type", [
|
|
|
208
208
|
}),
|
|
209
209
|
z.strictObject({
|
|
210
210
|
type: z.literal("image"),
|
|
211
|
-
image: z.string().min(1).max(
|
|
211
|
+
image: z.string().min(1).max(35000000),
|
|
212
212
|
filename: z.string().min(1).max(4096).optional()
|
|
213
213
|
}),
|
|
214
214
|
z.strictObject({
|
|
@@ -727,8 +727,10 @@ var HGW_JSONRPC_ERRORS2 = {
|
|
|
727
727
|
revisionConflict: -31012,
|
|
728
728
|
temporarilyUnavailable: -31013,
|
|
729
729
|
uncertainMutation: -31014,
|
|
730
|
-
unsupported: -31015
|
|
730
|
+
unsupported: -31015,
|
|
731
|
+
requestTooLarge: -31016
|
|
731
732
|
};
|
|
733
|
+
var HGW_MAX_FRAME_BYTES2 = 1100000;
|
|
732
734
|
var IdentifierSchema2 = z2.string().min(1).max(256).refine((value) => [...value].every((character) => {
|
|
733
735
|
const code = character.charCodeAt(0);
|
|
734
736
|
return code >= 32 && code !== 127;
|
|
@@ -1180,4 +1182,4 @@ var MCP_APP_SANDBOX_CSP2 = [
|
|
|
1180
1182
|
"frame-ancestors 'self'"
|
|
1181
1183
|
].join("; ");
|
|
1182
1184
|
|
|
1183
|
-
export { SESSION_CATALOG_MAX_WINDOW2, RuntimeAuthStateSchema2, OperationCapabilitySchema2, RuntimeInfoSchema2, AgentAvatarSchema2, AgentCatalogEntrySchema2, AgentCatalogResponseSchema2, AgentUpdateFields2, writesAgentField2, AgentUpdateRequestSchema2, AgentUpdateResponseSchema2, SessionStatusSchema2, SessionPlatformSchema2, SessionSchema2, newestSessionFirst2, SessionCatalogResponseSchema2, StopReason2, ToolKind2, ToolLocationSchema2, DiffOperation2, DiffChangeSchema2, ToolDiffSchema2, SessionMessageErrorStatusSchema2, SessionMessageSchema2, SessionPlanActivityMessageSchema2, SessionHistoryResponseSchema2, SessionCreateRequestSchema2, SessionCreateResponseSchema2, SessionPatchRequestSchema2, TurnStopResponseSchema2, TurnSteerRequestSchema2, TurnSteerResponseSchema2, SlashCommandSchema2, MAX_SLASH_COMMANDS2, INTERACTION_PROTOCOL2, SessionWorkspaceCapabilitiesResponseSchema2, GuestRuntimeCapabilitiesResponseSchema2, SessionModelsResponseSchema2, SessionModelUpdateRequestSchema2, SessionModelUpdateResponseSchema2, SessionContextResponseSchema2, ArtifactDescriptorSchema2, SessionTodosResponseSchema2, SessionActivityResponseSchema2, SessionAttachmentStageRequestSchema2, SessionAttachmentStageResponseSchema2, SessionAudioResponseSchema2, SessionTranscriptionRequestSchema2, SessionTranscriptionResponseSchema2, SessionSpeechRequestSchema2, ErrorResponseSchema2, ACP_PROTOCOL_VERSION2, HGW_API_PREFIX2, HGW_ACP_PATH2, HGW_ACP_AGENTS_PATH2, hgwAcpAgentPath2, HGW_META_KEY2, HGW_EXTENSION_VERSION2, HGW_AUTH_METHOD_INVITE2, HGW_ATTACHMENT_URI_SCHEME2, HGW_ARTIFACT_URI_SCHEME2, HGW_METHODS2, HGW_STOP_REASONS2, HGW_PERMISSION_KIND_SESSION2, HGW_PLAN_ID2, HGW_JSONRPC_ERRORS2, IdentifierSchema2, HgwExtensionsSchema2, HgwClientCapabilitiesMetaSchema2, HgwInitializeMetaSchema2, HgwLoginMetaSchema2, HgwSessionNewMetaSchema2, HgwSessionListMetaSchema2, HgwSessionSearchMetaSchema2, HgwSessionSearchResponseSchema2, HgwSessionInfoMetaSchema2, HgwSessionResumeMetaSchema2, HGW_REPLAY_BEFORE2, HgwReplayBeforeSchema2, HgwHistoryCursorSchema2, HgwHistoryPageResponseMetaSchema2, HgwHistoryPageTagSchema2, HgwSessionResumeResponseMetaSchema2, HgwPromptMetaSchema2, HgwSessionUpdateRequestSchema2, HgwSessionPartRequestSchema2, HgwSteerRequestSchema2, HgwSteerResponseSchema2, HgwFocusRequestSchema2, HgwAgentsListResponseSchema2, HgwAgentUpdateRequestSchema2, HgwAgentUpdateResponseSchema2, HgwTurnMetaSchema2, HgwNoticeMetaSchema2, HgwMessageMetaSchema2, HgwCostSchema2, HGW_SUBAGENT_STATUSES2, HgwSubagentSchema2, HgwStateMetaSchema2, HgwChunkMetaSchema2, HgwToolCallMetaSchema2, HgwPlanMetaSchema2, HgwUsageMetaSchema2, HgwAvailableCommandsMetaSchema2, HgwPermissionMetaSchema2, HgwQuestionOptionSchema2, HgwQuestionSchema2, HgwElicitationMetaSchema2, HgwArtifactDescriptorSchema2, formatArtifactUri2, parseArtifactUri2, HgwComposerPrefillNotificationSchema2, HgwErrorNotificationSchema2, HGW_REQUEST_TEXT_MAX2, HgwActivityNotificationSchema2, PushCategorySchema2, categoryOf2, PushLocaleSchema2, PRESENCE_IDLE_MS2, PRESENCE_HEARTBEAT_MS2, PRESENCE_GRACE_MS2, PRESENCE_CLOSED_GRACE_MS2, COALESCE_WINDOW_MS2, PUSH_TEXT_LIMITS2, PushMessageSchema2, PushCategoriesSchema2, PushSubscriptionJsonSchema2, PushInfoSchema2, PushRegistrationSchema2, PushUnregistrationSchema2, CallToolResultSchema2, ReadResourceResultSchema2, McpUiCspSchema2, isMcpAppCspDomain2, McpUiPermissionsSchema2, McpAppFilesSchema2, McpAppViewSchema2, viewerFileResult2, McpAppToolCallRequestSchema2, McpAppResourceReadRequestSchema2, MCP_APP_SANDBOX_PATH2, MCP_APP_SANDBOX_CSP2 };
|
|
1185
|
+
export { SESSION_CATALOG_MAX_WINDOW2, RuntimeAuthStateSchema2, OperationCapabilitySchema2, RuntimeInfoSchema2, AgentAvatarSchema2, AgentCatalogEntrySchema2, AgentCatalogResponseSchema2, AgentUpdateFields2, writesAgentField2, AgentUpdateRequestSchema2, AgentUpdateResponseSchema2, SessionStatusSchema2, SessionPlatformSchema2, SessionSchema2, newestSessionFirst2, SessionCatalogResponseSchema2, StopReason2, ToolKind2, ToolLocationSchema2, DiffOperation2, DiffChangeSchema2, ToolDiffSchema2, SessionMessageErrorStatusSchema2, SessionMessageSchema2, SessionPlanActivityMessageSchema2, SessionHistoryResponseSchema2, SessionCreateRequestSchema2, SessionCreateResponseSchema2, SessionPatchRequestSchema2, TurnStopResponseSchema2, TurnSteerRequestSchema2, TurnSteerResponseSchema2, SlashCommandSchema2, MAX_SLASH_COMMANDS2, INTERACTION_PROTOCOL2, SessionWorkspaceCapabilitiesResponseSchema2, GuestRuntimeCapabilitiesResponseSchema2, SessionModelsResponseSchema2, SessionModelUpdateRequestSchema2, SessionModelUpdateResponseSchema2, SessionContextResponseSchema2, ArtifactDescriptorSchema2, SessionTodosResponseSchema2, SessionActivityResponseSchema2, SessionAttachmentStageRequestSchema2, SessionAttachmentStageResponseSchema2, SessionAudioResponseSchema2, SessionTranscriptionRequestSchema2, SessionTranscriptionResponseSchema2, SessionSpeechRequestSchema2, ErrorResponseSchema2, ACP_PROTOCOL_VERSION2, HGW_API_PREFIX2, HGW_ACP_PATH2, HGW_ACP_AGENTS_PATH2, hgwAcpAgentPath2, HGW_META_KEY2, HGW_EXTENSION_VERSION2, HGW_AUTH_METHOD_INVITE2, HGW_ATTACHMENT_URI_SCHEME2, HGW_ARTIFACT_URI_SCHEME2, HGW_METHODS2, HGW_STOP_REASONS2, HGW_PERMISSION_KIND_SESSION2, HGW_PLAN_ID2, HGW_JSONRPC_ERRORS2, HGW_MAX_FRAME_BYTES2, IdentifierSchema2, HgwExtensionsSchema2, HgwClientCapabilitiesMetaSchema2, HgwInitializeMetaSchema2, HgwLoginMetaSchema2, HgwSessionNewMetaSchema2, HgwSessionListMetaSchema2, HgwSessionSearchMetaSchema2, HgwSessionSearchResponseSchema2, HgwSessionInfoMetaSchema2, HgwSessionResumeMetaSchema2, HGW_REPLAY_BEFORE2, HgwReplayBeforeSchema2, HgwHistoryCursorSchema2, HgwHistoryPageResponseMetaSchema2, HgwHistoryPageTagSchema2, HgwSessionResumeResponseMetaSchema2, HgwPromptMetaSchema2, HgwSessionUpdateRequestSchema2, HgwSessionPartRequestSchema2, HgwSteerRequestSchema2, HgwSteerResponseSchema2, HgwFocusRequestSchema2, HgwAgentsListResponseSchema2, HgwAgentUpdateRequestSchema2, HgwAgentUpdateResponseSchema2, HgwTurnMetaSchema2, HgwNoticeMetaSchema2, HgwMessageMetaSchema2, HgwCostSchema2, HGW_SUBAGENT_STATUSES2, HgwSubagentSchema2, HgwStateMetaSchema2, HgwChunkMetaSchema2, HgwToolCallMetaSchema2, HgwPlanMetaSchema2, HgwUsageMetaSchema2, HgwAvailableCommandsMetaSchema2, HgwPermissionMetaSchema2, HgwQuestionOptionSchema2, HgwQuestionSchema2, HgwElicitationMetaSchema2, HgwArtifactDescriptorSchema2, formatArtifactUri2, parseArtifactUri2, HgwComposerPrefillNotificationSchema2, HgwErrorNotificationSchema2, HGW_REQUEST_TEXT_MAX2, HgwActivityNotificationSchema2, PushCategorySchema2, categoryOf2, PushLocaleSchema2, PRESENCE_IDLE_MS2, PRESENCE_HEARTBEAT_MS2, PRESENCE_GRACE_MS2, PRESENCE_CLOSED_GRACE_MS2, COALESCE_WINDOW_MS2, PUSH_TEXT_LIMITS2, PushMessageSchema2, PushCategoriesSchema2, PushSubscriptionJsonSchema2, PushInfoSchema2, PushRegistrationSchema2, PushUnregistrationSchema2, CallToolResultSchema2, ReadResourceResultSchema2, McpUiCspSchema2, isMcpAppCspDomain2, McpUiPermissionsSchema2, McpAppFilesSchema2, McpAppViewSchema2, viewerFileResult2, McpAppToolCallRequestSchema2, McpAppResourceReadRequestSchema2, MCP_APP_SANDBOX_PATH2, MCP_APP_SANDBOX_CSP2 };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type SessionInfo } from "@agentclientprotocol/sdk/experimental/v2";
|
|
2
2
|
import { type HgwSessionInfoMeta } from "../protocol/acp.js";
|
|
3
3
|
import type { SessionMetadata, SessionStatus, TodoItem, WorkspaceActivityEvent } from "./workspace.js";
|
|
4
|
+
import type { Logger } from "../lifecycle/index.js";
|
|
4
5
|
import type { AcpConnection } from "./types.js";
|
|
5
6
|
/**
|
|
6
7
|
* Everything the workspace learns by subscribing to the connection: the Session row
|
|
@@ -13,8 +14,10 @@ export type AcpSessionStoreOptions = {
|
|
|
13
14
|
now?: () => number;
|
|
14
15
|
/** A live turn of a subscribed Session just stopped, however it ended. */
|
|
15
16
|
onTurnFinished?: (sessionId: string) => void;
|
|
17
|
+
/** Where a listener that throws is told; the others still hear. */
|
|
18
|
+
logger?: Logger;
|
|
16
19
|
};
|
|
17
|
-
export declare function createAcpSessionStore({ connection, now, onTurnFinished, }: AcpSessionStoreOptions): {
|
|
20
|
+
export declare function createAcpSessionStore({ connection, now, onTurnFinished, logger, }: AcpSessionStoreOptions): {
|
|
18
21
|
subscribe: (sessionId: string, agentId?: string) => () => void;
|
|
19
22
|
dispose(): void;
|
|
20
23
|
/**
|
|
@@ -36,6 +39,8 @@ export declare function createAcpSessionStore({ connection, now, onTurnFinished,
|
|
|
36
39
|
createdAt?: string;
|
|
37
40
|
platform?: import("./workspace.js").SessionPlatform;
|
|
38
41
|
}[];
|
|
42
|
+
/** Marks Sessions a read has answered for, so a missing one gates no snapshot. */
|
|
43
|
+
answer(sessionIds: readonly string[]): void;
|
|
39
44
|
/** The provider's title, as a list page or a subscribed Session reports it. */
|
|
40
45
|
setTitle: (sessionId: string, title: string) => Map<string, string>;
|
|
41
46
|
/** The proxy's read state, or the operator's own optimistic ack. */
|
package/dist/client/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
HGW_STOP_REASONS2,
|
|
15
15
|
HGW_PERMISSION_KIND_SESSION2,
|
|
16
16
|
HGW_JSONRPC_ERRORS2,
|
|
17
|
+
HGW_MAX_FRAME_BYTES2,
|
|
17
18
|
HgwInitializeMetaSchema2,
|
|
18
19
|
HgwSessionSearchMetaSchema2,
|
|
19
20
|
HgwSessionSearchResponseSchema2,
|
|
@@ -370,6 +371,21 @@ function loggedStream(stream, logger) {
|
|
|
370
371
|
})
|
|
371
372
|
};
|
|
372
373
|
}
|
|
374
|
+
var LOGGED_ID_LENGTH = 256;
|
|
375
|
+
function loggedId(row, ...path) {
|
|
376
|
+
let value = row;
|
|
377
|
+
for (const key of path)
|
|
378
|
+
value = typeof value === "object" && value !== null ? value[key] : undefined;
|
|
379
|
+
return typeof value === "string" && value.length > 0 ? value.slice(0, LOGGED_ID_LENGTH) : undefined;
|
|
380
|
+
}
|
|
381
|
+
function callEach(listeners, logger, event, ...args) {
|
|
382
|
+
for (const listener of [...listeners])
|
|
383
|
+
try {
|
|
384
|
+
listener(...args);
|
|
385
|
+
} catch (err) {
|
|
386
|
+
logger.warn({ err }, event);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
373
389
|
|
|
374
390
|
// client/connection.ts
|
|
375
391
|
var TRY_AGAIN_LATER = 1013;
|
|
@@ -637,6 +653,35 @@ function sessionMachine({
|
|
|
637
653
|
}
|
|
638
654
|
});
|
|
639
655
|
}
|
|
656
|
+
function readEach(entries, schema, skipped) {
|
|
657
|
+
const kept = [];
|
|
658
|
+
let unreadable;
|
|
659
|
+
for (const entry of entries) {
|
|
660
|
+
const read = schema.safeParse(entry);
|
|
661
|
+
if (read.success)
|
|
662
|
+
kept.push(read.data);
|
|
663
|
+
else {
|
|
664
|
+
unreadable = read.error;
|
|
665
|
+
skipped(entry);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (entries.length > 0 && kept.length === 0)
|
|
669
|
+
throw unreadable;
|
|
670
|
+
return kept;
|
|
671
|
+
}
|
|
672
|
+
var AgentsListEntriesSchema = HgwAgentsListResponseSchema2.extend({
|
|
673
|
+
agents: z.array(z.unknown()).max(1000)
|
|
674
|
+
});
|
|
675
|
+
var SessionSearchEntriesSchema = HgwSessionSearchResponseSchema2.extend({
|
|
676
|
+
sessions: z.array(z.unknown())
|
|
677
|
+
});
|
|
678
|
+
var FRAME_ENVELOPE_BYTES = 1024;
|
|
679
|
+
var frameEncoder = new TextEncoder;
|
|
680
|
+
function assertFrameFits(params) {
|
|
681
|
+
const bytes = frameEncoder.encode(JSON.stringify(params)).byteLength;
|
|
682
|
+
if (bytes > HGW_MAX_FRAME_BYTES2 - FRAME_ENVELOPE_BYTES)
|
|
683
|
+
throw new RequestError(HGW_JSONRPC_ERRORS2.requestTooLarge, "Request too large to send");
|
|
684
|
+
}
|
|
640
685
|
function createAcpConnection(options) {
|
|
641
686
|
const {
|
|
642
687
|
clientInfo,
|
|
@@ -674,23 +719,13 @@ function createAcpConnection(options) {
|
|
|
674
719
|
if (status === next)
|
|
675
720
|
return;
|
|
676
721
|
status = next;
|
|
677
|
-
|
|
678
|
-
listener(next);
|
|
722
|
+
callEach(statusListeners, logger, "acp.listener.failed", next);
|
|
679
723
|
}
|
|
680
724
|
function setOutage(next) {
|
|
681
725
|
if (outage === next)
|
|
682
726
|
return;
|
|
683
727
|
outage = next;
|
|
684
|
-
|
|
685
|
-
listener(next);
|
|
686
|
-
}
|
|
687
|
-
function settleOutage() {
|
|
688
|
-
if (status !== "ready")
|
|
689
|
-
return;
|
|
690
|
-
for (const open of sessions.values())
|
|
691
|
-
if (open.state === "joining")
|
|
692
|
-
return;
|
|
693
|
-
setOutage(undefined);
|
|
728
|
+
callEach(outageListeners, logger, "acp.listener.failed", next);
|
|
694
729
|
}
|
|
695
730
|
function emitPending(request) {
|
|
696
731
|
for (const listener of pendingListeners)
|
|
@@ -708,7 +743,9 @@ function createAcpConnection(options) {
|
|
|
708
743
|
sendLivenessProbeAsync();
|
|
709
744
|
}, LIVENESS_SILENCE_MS);
|
|
710
745
|
}
|
|
746
|
+
let lastInboundAt = Number.NEGATIVE_INFINITY;
|
|
711
747
|
function onInbound() {
|
|
748
|
+
lastInboundAt = clock.now();
|
|
712
749
|
armSilenceTimer();
|
|
713
750
|
}
|
|
714
751
|
function sendLivenessProbeAsync() {
|
|
@@ -766,14 +803,13 @@ function createAcpConnection(options) {
|
|
|
766
803
|
if (open.replaying)
|
|
767
804
|
open.replaying.delivered = true;
|
|
768
805
|
for (const { listener } of open.listeners)
|
|
769
|
-
listener.update
|
|
806
|
+
callEach(listener.update ? [listener.update] : [], logger, "acp.listener.failed", params.update, meta);
|
|
770
807
|
}).onRequest(methods2.client.session.requestPermission, ({ params, signal }) => permissionRequest(params, signal)).onRequest(methods2.client.elicitation.create, ({ params, signal }) => elicitationRequest(params, signal));
|
|
771
808
|
for (const [method, parser] of Object.entries(NOTIFICATION_PARSERS))
|
|
772
809
|
app.onNotification(method, parser, ({ params }) => {
|
|
773
810
|
if (method === HGW_METHODS2.notify.error)
|
|
774
811
|
noticeGone(params);
|
|
775
|
-
|
|
776
|
-
listener(params);
|
|
812
|
+
callEach(notificationListeners.get(method) ?? [], logger, "acp.listener.failed", params);
|
|
777
813
|
});
|
|
778
814
|
function noticeGone(params) {
|
|
779
815
|
const notice = HgwErrorNotificationSchema2.safeParse(params).data;
|
|
@@ -796,14 +832,15 @@ function createAcpConnection(options) {
|
|
|
796
832
|
}
|
|
797
833
|
async function requestOn({ connection }, tier, send, parent) {
|
|
798
834
|
const deadline = new Deadline(REQUEST_DEADLINE_MS[tier], clock, parent);
|
|
835
|
+
const sentAt = clock.now();
|
|
799
836
|
try {
|
|
800
837
|
return await deadline.run((cancellationSignal) => send(connection.agent, { cancellationSignal }));
|
|
801
838
|
} catch (error) {
|
|
802
839
|
if (deadline.signal.aborted && !parent?.aborted) {
|
|
803
|
-
if (tier
|
|
804
|
-
connection.close(error);
|
|
805
|
-
else
|
|
840
|
+
if (tier !== "probe")
|
|
806
841
|
sendLivenessProbeAsync();
|
|
842
|
+
else if (lastInboundAt <= sentAt)
|
|
843
|
+
connection.close(error);
|
|
807
844
|
}
|
|
808
845
|
throw error;
|
|
809
846
|
}
|
|
@@ -921,7 +958,11 @@ function createAcpConnection(options) {
|
|
|
921
958
|
}
|
|
922
959
|
}
|
|
923
960
|
async function listAgents() {
|
|
924
|
-
|
|
961
|
+
const response = AgentsListEntriesSchema.parse(await request("short", (agent, options) => agent.request(HGW_METHODS2.agents.list, undefined, options)));
|
|
962
|
+
return {
|
|
963
|
+
...response,
|
|
964
|
+
agents: readEach(response.agents, HgwAgentsListResponseSchema2.shape.agents.element, (entry) => logger.warn({ id: loggedId(entry, "summary", "id") }, "agents.entry_skipped"))
|
|
965
|
+
};
|
|
925
966
|
}
|
|
926
967
|
function settleOf({ listener }) {
|
|
927
968
|
const settle = listener.replay?.();
|
|
@@ -976,8 +1017,7 @@ function createAcpConnection(options) {
|
|
|
976
1017
|
if (next === "joined" || failure !== undefined)
|
|
977
1018
|
open.waiters = undefined;
|
|
978
1019
|
for (const { listener } of open.listeners)
|
|
979
|
-
listener.state
|
|
980
|
-
settleOutage();
|
|
1020
|
+
callEach(listener.state ? [listener.state] : [], logger, "acp.listener.failed", next);
|
|
981
1021
|
}
|
|
982
1022
|
function subscribe(sessionId, listener) {
|
|
983
1023
|
if (ended)
|
|
@@ -1028,7 +1068,6 @@ function createAcpConnection(options) {
|
|
|
1028
1068
|
open.owner?.dispose();
|
|
1029
1069
|
open.waiters?.reject(notOpen());
|
|
1030
1070
|
open.waiters = undefined;
|
|
1031
|
-
settleOutage();
|
|
1032
1071
|
if (open.state === "gone")
|
|
1033
1072
|
return;
|
|
1034
1073
|
request("short", (agent, options) => agent.request(HGW_METHODS2.session.part, { sessionId }, options)).catch((err) => {
|
|
@@ -1063,6 +1102,7 @@ function createAcpConnection(options) {
|
|
|
1063
1102
|
sendLivenessProbeAsync();
|
|
1064
1103
|
}
|
|
1065
1104
|
joinable = true;
|
|
1105
|
+
setOutage(undefined);
|
|
1066
1106
|
const opened = [...sessions.keys()];
|
|
1067
1107
|
for (const open of sessions.values())
|
|
1068
1108
|
open.owner?.actor.send({ type: "ready" });
|
|
@@ -1102,7 +1142,6 @@ function createAcpConnection(options) {
|
|
|
1102
1142
|
closeTransport: () => live?.connection.close(),
|
|
1103
1143
|
markReady() {
|
|
1104
1144
|
setStatus("ready");
|
|
1105
|
-
settleOutage();
|
|
1106
1145
|
},
|
|
1107
1146
|
lose(next) {
|
|
1108
1147
|
recovering = true;
|
|
@@ -1215,9 +1254,13 @@ function createAcpConnection(options) {
|
|
|
1215
1254
|
};
|
|
1216
1255
|
},
|
|
1217
1256
|
async searchSessions(meta) {
|
|
1218
|
-
|
|
1257
|
+
const response = SessionSearchEntriesSchema.parse(await request("short", (agent, options) => agent.request(HGW_METHODS2.sessions.search, {
|
|
1219
1258
|
_meta: { [HGW_META_KEY2]: HgwSessionSearchMetaSchema2.parse(meta) }
|
|
1220
1259
|
}, options)));
|
|
1260
|
+
return {
|
|
1261
|
+
...response,
|
|
1262
|
+
sessions: readEach(response.sessions, HgwSessionSearchResponseSchema2.shape.sessions.element, (entry) => logger.warn({ id: loggedId(entry, "sessionId") }, "sessions.result_skipped"))
|
|
1263
|
+
};
|
|
1221
1264
|
},
|
|
1222
1265
|
subscribe,
|
|
1223
1266
|
joined,
|
|
@@ -1226,7 +1269,13 @@ function createAcpConnection(options) {
|
|
|
1226
1269
|
resumePage,
|
|
1227
1270
|
history: (sessionId) => sessions.get(sessionId)?.history,
|
|
1228
1271
|
async prompt(sessionId, blocks, meta) {
|
|
1229
|
-
const
|
|
1272
|
+
const params = {
|
|
1273
|
+
sessionId,
|
|
1274
|
+
prompt: blocks,
|
|
1275
|
+
_meta: { [HGW_META_KEY2]: meta }
|
|
1276
|
+
};
|
|
1277
|
+
assertFrameFits(params);
|
|
1278
|
+
const response = await write(sessionId, "long", (agent, options) => agent.request(methods2.agent.session.prompt, params, options));
|
|
1230
1279
|
return { messageId: response.messageId };
|
|
1231
1280
|
},
|
|
1232
1281
|
cancel(sessionId) {
|
|
@@ -1243,6 +1292,7 @@ function createAcpConnection(options) {
|
|
|
1243
1292
|
await request("short", (agent, options) => agent.request(HGW_METHODS2.session.update, update, options));
|
|
1244
1293
|
},
|
|
1245
1294
|
async steer(steer) {
|
|
1295
|
+
assertFrameFits(steer);
|
|
1246
1296
|
return HgwSteerResponseSchema2.parse(await write(steer.sessionId, "medium", (agent, options) => agent.request(HGW_METHODS2.session.steer, steer, options)));
|
|
1247
1297
|
},
|
|
1248
1298
|
focus(sessionId, presence) {
|
|
@@ -1558,7 +1608,8 @@ function sameRow(left, right) {
|
|
|
1558
1608
|
function createAcpSessionStore({
|
|
1559
1609
|
connection,
|
|
1560
1610
|
now = Date.now,
|
|
1561
|
-
onTurnFinished
|
|
1611
|
+
onTurnFinished,
|
|
1612
|
+
logger = SILENT_LOGGER
|
|
1562
1613
|
}) {
|
|
1563
1614
|
const rows = new Map;
|
|
1564
1615
|
const titles = new Map;
|
|
@@ -1577,11 +1628,12 @@ function createAcpSessionStore({
|
|
|
1577
1628
|
return row ? [{ ...row }] : [];
|
|
1578
1629
|
});
|
|
1579
1630
|
}
|
|
1631
|
+
const answered = new Set;
|
|
1580
1632
|
function notify(subscription) {
|
|
1581
1633
|
for (const sessionId of subscription.sessionIds)
|
|
1582
|
-
if (!rows.has(sessionId))
|
|
1634
|
+
if (!rows.has(sessionId) && !answered.has(sessionId))
|
|
1583
1635
|
return;
|
|
1584
|
-
subscription.listener
|
|
1636
|
+
callEach([subscription.listener], logger, "sessions.listener.failed", rowsFor(subscription.sessionIds));
|
|
1585
1637
|
}
|
|
1586
1638
|
function publish(sessionId) {
|
|
1587
1639
|
for (const subscription of subscriptions)
|
|
@@ -1623,15 +1675,13 @@ function createAcpSessionStore({
|
|
|
1623
1675
|
function setTodos(sessionId, next) {
|
|
1624
1676
|
todos.set(sessionId, next);
|
|
1625
1677
|
for (const listener of todoListeners.get(sessionId) ?? [])
|
|
1626
|
-
listener
|
|
1678
|
+
callEach([listener], logger, "sessions.listener.failed", next.map((todo) => ({ ...todo })));
|
|
1627
1679
|
}
|
|
1628
1680
|
function emitActivity(event) {
|
|
1629
|
-
|
|
1630
|
-
listener(event);
|
|
1681
|
+
callEach(activityListeners, logger, "sessions.listener.failed", event);
|
|
1631
1682
|
}
|
|
1632
1683
|
function invalidate(sessionId) {
|
|
1633
|
-
|
|
1634
|
-
listener();
|
|
1684
|
+
callEach(invalidationListeners.get(sessionId) ?? [], logger, "sessions.listener.failed");
|
|
1635
1685
|
}
|
|
1636
1686
|
function acceptUpdate(sessionId, update, meta) {
|
|
1637
1687
|
if (SessionUpdate2.isStateUpdate(update)) {
|
|
@@ -1726,6 +1776,13 @@ function createAcpSessionStore({
|
|
|
1726
1776
|
},
|
|
1727
1777
|
put,
|
|
1728
1778
|
rowsFor,
|
|
1779
|
+
answer(sessionIds) {
|
|
1780
|
+
for (const sessionId of sessionIds)
|
|
1781
|
+
answered.add(sessionId);
|
|
1782
|
+
for (const subscription of subscriptions)
|
|
1783
|
+
if (sessionIds.some((id) => subscription.sessionIds.has(id)))
|
|
1784
|
+
notify(subscription);
|
|
1785
|
+
},
|
|
1729
1786
|
setTitle: (sessionId, title) => titles.set(sessionId, title),
|
|
1730
1787
|
setUnread: (sessionId, unread) => patch(sessionId, { unread }),
|
|
1731
1788
|
setPinned: (sessionId, pinned) => patch(sessionId, { pinned }),
|
|
@@ -1807,6 +1864,7 @@ function createAcpWorkspaceClient({
|
|
|
1807
1864
|
}) {
|
|
1808
1865
|
const store = createAcpSessionStore({
|
|
1809
1866
|
connection,
|
|
1867
|
+
logger: clientLog,
|
|
1810
1868
|
...now ? { now } : {},
|
|
1811
1869
|
onTurnFinished: (sessionId) => {
|
|
1812
1870
|
reportCreatedAgents(sessionId).catch((err) => clientLog.warn({ err }, "agent.report_created_failed"));
|
|
@@ -1853,12 +1911,25 @@ function createAcpWorkspaceClient({
|
|
|
1853
1911
|
return response;
|
|
1854
1912
|
}
|
|
1855
1913
|
function rememberRows(sessions) {
|
|
1914
|
+
const kept = [];
|
|
1915
|
+
let unreadable;
|
|
1856
1916
|
for (const session of sessions) {
|
|
1857
|
-
|
|
1917
|
+
let row;
|
|
1918
|
+
try {
|
|
1919
|
+
row = rowOf(session);
|
|
1920
|
+
} catch (error) {
|
|
1921
|
+
unreadable = error;
|
|
1922
|
+
clientLog.warn({ id: loggedId(session, "sessionId") }, "sessions.row_skipped");
|
|
1923
|
+
continue;
|
|
1924
|
+
}
|
|
1858
1925
|
remember(row.sessionId, row.info, row.updatedAt);
|
|
1859
1926
|
if (row.title)
|
|
1860
1927
|
store.setTitle(row.sessionId, row.title);
|
|
1928
|
+
kept.push(session);
|
|
1861
1929
|
}
|
|
1930
|
+
if (sessions.length > 0 && kept.length === 0)
|
|
1931
|
+
throw unreadable;
|
|
1932
|
+
return kept;
|
|
1862
1933
|
}
|
|
1863
1934
|
const pageReads = new Map;
|
|
1864
1935
|
let catalogScope;
|
|
@@ -1867,10 +1938,7 @@ function createAcpWorkspaceClient({
|
|
|
1867
1938
|
const inFlight = pageReads.get(key);
|
|
1868
1939
|
if (inFlight)
|
|
1869
1940
|
return inFlight;
|
|
1870
|
-
const read = connection.listSessions(meta, cursor).then((page) => {
|
|
1871
|
-
rememberRows(page.sessions);
|
|
1872
|
-
return page;
|
|
1873
|
-
}).finally(() => pageReads.delete(key));
|
|
1941
|
+
const read = connection.listSessions(meta, cursor).then((page) => ({ ...page, sessions: rememberRows(page.sessions) })).finally(() => pageReads.delete(key));
|
|
1874
1942
|
pageReads.set(key, read);
|
|
1875
1943
|
return read;
|
|
1876
1944
|
}
|
|
@@ -1938,16 +2006,16 @@ function createAcpWorkspaceClient({
|
|
|
1938
2006
|
let page;
|
|
1939
2007
|
try {
|
|
1940
2008
|
page = await listSessions();
|
|
1941
|
-
} catch {
|
|
2009
|
+
} catch (err) {
|
|
1942
2010
|
if (stale(generation))
|
|
1943
2011
|
return;
|
|
2012
|
+
clientLog.warn({ err, attempt }, "sessions.relist_retry_failed");
|
|
1944
2013
|
attempt += 1;
|
|
1945
2014
|
await backOff(backoffDelay(attempt, RELIST_BACKOFF));
|
|
1946
2015
|
continue;
|
|
1947
2016
|
}
|
|
1948
2017
|
const sessionIds = page.sessions.map(({ sessionId }) => sessionId);
|
|
1949
|
-
|
|
1950
|
-
listener(sessionIds);
|
|
2018
|
+
callEach(catalogListeners, clientLog, "sessions.catalog_listener.failed", sessionIds);
|
|
1951
2019
|
return;
|
|
1952
2020
|
}
|
|
1953
2021
|
}
|
|
@@ -2048,9 +2116,9 @@ function createAcpWorkspaceClient({
|
|
|
2048
2116
|
query,
|
|
2049
2117
|
...agentId === undefined ? {} : { agentId }
|
|
2050
2118
|
});
|
|
2051
|
-
rememberRows(sessions);
|
|
2052
|
-
const titles = new Map(
|
|
2053
|
-
return store.rowsFor(
|
|
2119
|
+
const kept = rememberRows(sessions);
|
|
2120
|
+
const titles = new Map(kept.map(({ sessionId, title }) => [sessionId, title ?? ""]));
|
|
2121
|
+
return store.rowsFor(kept.map(({ sessionId }) => sessionId)).map(({ sessionId, agentId, updatedAt }) => ({
|
|
2054
2122
|
sessionId,
|
|
2055
2123
|
agentId,
|
|
2056
2124
|
title: titles.get(sessionId) ?? "",
|
|
@@ -2063,6 +2131,7 @@ function createAcpWorkspaceClient({
|
|
|
2063
2131
|
},
|
|
2064
2132
|
async getSessionMetadata(sessionIds) {
|
|
2065
2133
|
await readRows(sessionIds);
|
|
2134
|
+
store.answer(sessionIds);
|
|
2066
2135
|
return store.rowsFor(sessionIds);
|
|
2067
2136
|
},
|
|
2068
2137
|
subscribeSessionMetadata: store.subscribeMetadata,
|
package/dist/client/log.d.ts
CHANGED
|
@@ -28,4 +28,15 @@ export declare function frameFields(direction: "in" | "out", message: unknown):
|
|
|
28
28
|
};
|
|
29
29
|
/** `stream` with one `acp.frame` debug line per message, each way. */
|
|
30
30
|
export declare function loggedStream(stream: WireStream, logger: Logger): WireStream;
|
|
31
|
+
/**
|
|
32
|
+
* The id at `path` in a row not yet checked, bounded for a warning. A warning
|
|
33
|
+
* about a rejected row names it by this alone, never by its content.
|
|
34
|
+
*/
|
|
35
|
+
export declare function loggedId(row: unknown, ...path: string[]): string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Calls every listener with `args`, each on its own: one that throws is
|
|
38
|
+
* logged as `event` and costs only itself, never the listeners after it nor
|
|
39
|
+
* the caller that fanned out to them.
|
|
40
|
+
*/
|
|
41
|
+
export declare function callEach<Args extends readonly unknown[]>(listeners: Iterable<(...args: Args) => void>, logger: Logger, event: string, ...args: Args): void;
|
|
31
42
|
export {};
|
package/dist/client/types.d.ts
CHANGED
|
@@ -75,7 +75,10 @@ export interface AcpConnection {
|
|
|
75
75
|
/** Resolves with `InitializeResponse._meta.hgw` once the handshake settles. */
|
|
76
76
|
readonly initialized: Promise<z.infer<typeof HgwInitializeMetaSchema>>;
|
|
77
77
|
subscribeStatus(listener: (status: AcpConnectionStatus) => void): () => void;
|
|
78
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Set from a close until the transport is ready again. Each Session's
|
|
80
|
+
* rejoin is its own state (`sessionState`), not the connection's.
|
|
81
|
+
*/
|
|
79
82
|
readonly outage: AcpConnectionOutage | undefined;
|
|
80
83
|
subscribeOutage(listener: (outage: AcpConnectionOutage | undefined) => void): () => void;
|
|
81
84
|
/**
|
package/dist/protocol/acp.d.ts
CHANGED
|
@@ -90,7 +90,17 @@ export declare const HGW_JSONRPC_ERRORS: {
|
|
|
90
90
|
readonly temporarilyUnavailable: -31013;
|
|
91
91
|
readonly uncertainMutation: -31014;
|
|
92
92
|
readonly unsupported: -31015;
|
|
93
|
+
/**
|
|
94
|
+
* Params too large for one frame. The client raises it before sending and
|
|
95
|
+
* the gateway never sends it, so it has no machine name on the wire.
|
|
96
|
+
*/
|
|
97
|
+
readonly requestTooLarge: -31016;
|
|
93
98
|
};
|
|
99
|
+
/**
|
|
100
|
+
* The largest inbound WebSocket frame the gateway takes, in bytes. A larger
|
|
101
|
+
* one closes the socket, and with it every Session on the tab.
|
|
102
|
+
*/
|
|
103
|
+
export declare const HGW_MAX_FRAME_BYTES = 1100000;
|
|
94
104
|
export type HgwJsonRpcErrorCode = (typeof HGW_JSONRPC_ERRORS)[keyof typeof HGW_JSONRPC_ERRORS];
|
|
95
105
|
export declare const IdentifierSchema: z.ZodString;
|
|
96
106
|
export declare const HgwExtensionsSchema: z.ZodObject<{
|
package/dist/protocol/sdk.js
CHANGED
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
HGW_PERMISSION_KIND_SESSION2,
|
|
66
66
|
HGW_PLAN_ID2,
|
|
67
67
|
HGW_JSONRPC_ERRORS2,
|
|
68
|
+
HGW_MAX_FRAME_BYTES2,
|
|
68
69
|
IdentifierSchema2,
|
|
69
70
|
HgwExtensionsSchema2,
|
|
70
71
|
HgwClientCapabilitiesMetaSchema2,
|
|
@@ -215,6 +216,7 @@ export {
|
|
|
215
216
|
HGW_AUTH_METHOD_INVITE2 as HGW_AUTH_METHOD_INVITE,
|
|
216
217
|
HGW_EXTENSION_VERSION2 as HGW_EXTENSION_VERSION,
|
|
217
218
|
HGW_JSONRPC_ERRORS2 as HGW_JSONRPC_ERRORS,
|
|
219
|
+
HGW_MAX_FRAME_BYTES2 as HGW_MAX_FRAME_BYTES,
|
|
218
220
|
HGW_META_KEY2 as HGW_META_KEY,
|
|
219
221
|
HGW_METHODS2 as HGW_METHODS,
|
|
220
222
|
HGW_PERMISSION_KIND_SESSION2 as HGW_PERMISSION_KIND_SESSION,
|
package/package.json
CHANGED