@harness-gw/sdk 0.6.0 → 0.7.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/client/index.js +73 -41
- package/dist/client/types.d.ts +7 -0
- package/dist/client/workspace.d.ts +9 -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.7.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.7.0 \
|
|
77
77
|
serve --config /run/harness-gw/config.yaml
|
|
78
78
|
```
|
|
79
79
|
|
package/dist/client/index.js
CHANGED
|
@@ -278,6 +278,13 @@ class AgentUpdateError extends Error {
|
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
class AcpConnectionClosedError extends Error {
|
|
282
|
+
constructor(message = "The ACP connection closed", options) {
|
|
283
|
+
super(message, options);
|
|
284
|
+
this.name = "AcpConnectionClosedError";
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
281
288
|
// client/limits.ts
|
|
282
289
|
var HANDSHAKE_DEADLINE_MS = 1e4;
|
|
283
290
|
var REQUEST_DEADLINE_MS = {
|
|
@@ -436,9 +443,6 @@ var SESSION_STATES = new Map([
|
|
|
436
443
|
["refused", "unavailable"],
|
|
437
444
|
["gone", "gone"]
|
|
438
445
|
]);
|
|
439
|
-
function isTimeout(error) {
|
|
440
|
-
return error instanceof DOMException && error.name === "TimeoutError";
|
|
441
|
-
}
|
|
442
446
|
function agentUpdateError(error) {
|
|
443
447
|
if (codeOf(error) === HGW_JSONRPC_ERRORS2.unsupported)
|
|
444
448
|
return new AgentUpdateError("unsupported", "This runtime cannot store that Agent field");
|
|
@@ -482,6 +486,9 @@ function trackedInbound(stream, onReceived) {
|
|
|
482
486
|
writable: stream.writable
|
|
483
487
|
};
|
|
484
488
|
}
|
|
489
|
+
function closedUnder({ connection }, error) {
|
|
490
|
+
return connection.signal.aborted && error === connection.signal.reason;
|
|
491
|
+
}
|
|
485
492
|
function observedSocket(Socket, onOpen, onClose) {
|
|
486
493
|
return class extends Socket {
|
|
487
494
|
constructor(...args) {
|
|
@@ -705,6 +712,7 @@ function createAcpConnection(options) {
|
|
|
705
712
|
let live;
|
|
706
713
|
let recovering = false;
|
|
707
714
|
let joinable = false;
|
|
715
|
+
let recovery;
|
|
708
716
|
let invitation;
|
|
709
717
|
let lastFocus;
|
|
710
718
|
let silenceTimer = undefined;
|
|
@@ -817,18 +825,48 @@ function createAcpConnection(options) {
|
|
|
817
825
|
return;
|
|
818
826
|
sessions.get(notice.sessionId)?.owner?.actor.send({ type: "gone" });
|
|
819
827
|
}
|
|
820
|
-
async function
|
|
828
|
+
async function liveTransport() {
|
|
821
829
|
start();
|
|
822
830
|
if (inspecting)
|
|
823
831
|
await inspecting;
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
832
|
+
if (ended || !live)
|
|
833
|
+
throw new AcpConnectionClosedError("The ACP connection is not open");
|
|
834
|
+
return live;
|
|
835
|
+
}
|
|
836
|
+
async function readyTransport() {
|
|
837
|
+
const current = await liveTransport();
|
|
827
838
|
await current.ready;
|
|
828
839
|
return current;
|
|
829
840
|
}
|
|
830
|
-
|
|
831
|
-
|
|
841
|
+
function recovered() {
|
|
842
|
+
start();
|
|
843
|
+
if (ended)
|
|
844
|
+
return Promise.reject(new AcpConnectionClosedError);
|
|
845
|
+
if (joinable)
|
|
846
|
+
return Promise.resolve();
|
|
847
|
+
recovery ??= Promise.withResolvers();
|
|
848
|
+
return recovery.promise;
|
|
849
|
+
}
|
|
850
|
+
async function request(tier, send, resend) {
|
|
851
|
+
const sessionId = resend === "read" ? undefined : resend?.sessionId;
|
|
852
|
+
for (;; ) {
|
|
853
|
+
if (sessionId !== undefined && sessions.has(sessionId))
|
|
854
|
+
await joined(sessionId);
|
|
855
|
+
else if (resend)
|
|
856
|
+
await recovered();
|
|
857
|
+
const transport = await liveTransport();
|
|
858
|
+
try {
|
|
859
|
+
await transport.ready;
|
|
860
|
+
return await requestOn(transport, tier, send);
|
|
861
|
+
} catch (error) {
|
|
862
|
+
if (!closedUnder(transport, error))
|
|
863
|
+
throw error;
|
|
864
|
+
const again = resend === "read" || sessionId !== undefined && sessions.has(sessionId);
|
|
865
|
+
if (!again)
|
|
866
|
+
throw new AcpConnectionClosedError(undefined, { cause: error });
|
|
867
|
+
await transport.connection.closed.catch((err) => logger.warn({ err }, "acp.connection.closed_with_error"));
|
|
868
|
+
}
|
|
869
|
+
}
|
|
832
870
|
}
|
|
833
871
|
async function requestOn({ connection }, tier, send, parent) {
|
|
834
872
|
const deadline = new Deadline(REQUEST_DEADLINE_MS[tier], clock, parent);
|
|
@@ -845,21 +883,6 @@ function createAcpConnection(options) {
|
|
|
845
883
|
throw error;
|
|
846
884
|
}
|
|
847
885
|
}
|
|
848
|
-
async function write(sessionId, tier, send) {
|
|
849
|
-
for (;; ) {
|
|
850
|
-
if (sessions.has(sessionId))
|
|
851
|
-
await joined(sessionId);
|
|
852
|
-
const transport = await readyTransport();
|
|
853
|
-
try {
|
|
854
|
-
return await requestOn(transport, tier, send);
|
|
855
|
-
} catch (error) {
|
|
856
|
-
const closedUnder = transport.connection.signal.aborted && !isTimeout(error) && sessions.has(sessionId);
|
|
857
|
-
if (!closedUnder)
|
|
858
|
-
throw error;
|
|
859
|
-
await transport.connection.closed.catch((err) => logger.warn({ err }, "acp.connection.closed_with_error"));
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
886
|
function notifyAgent(send, sessionId) {
|
|
864
887
|
(async () => {
|
|
865
888
|
if (sessionId !== undefined && sessions.has(sessionId))
|
|
@@ -958,7 +981,7 @@ function createAcpConnection(options) {
|
|
|
958
981
|
}
|
|
959
982
|
}
|
|
960
983
|
async function listAgents() {
|
|
961
|
-
const response = AgentsListEntriesSchema.parse(await request("short", (agent, options) => agent.request(HGW_METHODS2.agents.list, undefined, options)));
|
|
984
|
+
const response = AgentsListEntriesSchema.parse(await request("short", (agent, options) => agent.request(HGW_METHODS2.agents.list, undefined, options), "read"));
|
|
962
985
|
return {
|
|
963
986
|
...response,
|
|
964
987
|
agents: readEach(response.agents, HgwAgentsListResponseSchema2.shape.agents.element, (entry) => logger.warn({ id: loggedId(entry, "summary", "id") }, "agents.entry_skipped"))
|
|
@@ -1075,18 +1098,20 @@ function createAcpConnection(options) {
|
|
|
1075
1098
|
});
|
|
1076
1099
|
}
|
|
1077
1100
|
async function resumePage(sessionId, cursor) {
|
|
1078
|
-
await joined(sessionId);
|
|
1079
1101
|
const open = sessions.get(sessionId);
|
|
1080
1102
|
if (!open)
|
|
1081
1103
|
throw notOpen();
|
|
1082
|
-
|
|
1083
|
-
open.page = updates;
|
|
1104
|
+
let updates = [];
|
|
1084
1105
|
try {
|
|
1085
|
-
const response = await request("medium", (agent, options) =>
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1106
|
+
const response = await request("medium", (agent, options) => {
|
|
1107
|
+
updates = [];
|
|
1108
|
+
open.page = updates;
|
|
1109
|
+
return agent.request(methods2.agent.session.resume, {
|
|
1110
|
+
sessionId,
|
|
1111
|
+
cwd: AGENT_CWD,
|
|
1112
|
+
replayFrom: { type: HGW_REPLAY_BEFORE2, cursor }
|
|
1113
|
+
}, options);
|
|
1114
|
+
}, { sessionId });
|
|
1090
1115
|
const { history } = HgwHistoryPageResponseMetaSchema2.parse(hgwMetaOf(response._meta));
|
|
1091
1116
|
return { updates, history };
|
|
1092
1117
|
} finally {
|
|
@@ -1103,6 +1128,8 @@ function createAcpConnection(options) {
|
|
|
1103
1128
|
}
|
|
1104
1129
|
joinable = true;
|
|
1105
1130
|
setOutage(undefined);
|
|
1131
|
+
recovery?.resolve();
|
|
1132
|
+
recovery = undefined;
|
|
1106
1133
|
const opened = [...sessions.keys()];
|
|
1107
1134
|
for (const open of sessions.values())
|
|
1108
1135
|
open.owner?.actor.send({ type: "ready" });
|
|
@@ -1204,13 +1231,15 @@ function createAcpConnection(options) {
|
|
|
1204
1231
|
ended = true;
|
|
1205
1232
|
setStatus("closed");
|
|
1206
1233
|
setOutage(undefined);
|
|
1207
|
-
failInitialized?.(new
|
|
1234
|
+
failInitialized?.(new AcpConnectionClosedError);
|
|
1208
1235
|
failInitialized = undefined;
|
|
1209
1236
|
settleInitialized = undefined;
|
|
1237
|
+
recovery?.reject(new AcpConnectionClosedError);
|
|
1238
|
+
recovery = undefined;
|
|
1210
1239
|
for (const open of sessions.values()) {
|
|
1211
1240
|
clock.clearTimeout(open.grace);
|
|
1212
1241
|
open.owner?.dispose();
|
|
1213
|
-
open.waiters?.reject(new
|
|
1242
|
+
open.waiters?.reject(new AcpConnectionClosedError);
|
|
1214
1243
|
}
|
|
1215
1244
|
sessions.clear();
|
|
1216
1245
|
live?.connection.close();
|
|
@@ -1247,7 +1276,7 @@ function createAcpConnection(options) {
|
|
|
1247
1276
|
const response = await request("short", (agent, options) => agent.request(methods2.agent.session.list, {
|
|
1248
1277
|
...cursor === undefined ? {} : { cursor },
|
|
1249
1278
|
_meta: { [HGW_META_KEY2]: meta }
|
|
1250
|
-
}, options));
|
|
1279
|
+
}, options), "read");
|
|
1251
1280
|
return {
|
|
1252
1281
|
sessions: response.sessions,
|
|
1253
1282
|
...response.nextCursor ? { nextCursor: response.nextCursor } : {}
|
|
@@ -1255,8 +1284,10 @@ function createAcpConnection(options) {
|
|
|
1255
1284
|
},
|
|
1256
1285
|
async searchSessions(meta) {
|
|
1257
1286
|
const response = SessionSearchEntriesSchema.parse(await request("short", (agent, options) => agent.request(HGW_METHODS2.sessions.search, {
|
|
1258
|
-
_meta: {
|
|
1259
|
-
|
|
1287
|
+
_meta: {
|
|
1288
|
+
[HGW_META_KEY2]: HgwSessionSearchMetaSchema2.parse(meta)
|
|
1289
|
+
}
|
|
1290
|
+
}, options), "read"));
|
|
1260
1291
|
return {
|
|
1261
1292
|
...response,
|
|
1262
1293
|
sessions: readEach(response.sessions, HgwSessionSearchResponseSchema2.shape.sessions.element, (entry) => logger.warn({ id: loggedId(entry, "sessionId") }, "sessions.result_skipped"))
|
|
@@ -1275,14 +1306,14 @@ function createAcpConnection(options) {
|
|
|
1275
1306
|
_meta: { [HGW_META_KEY2]: meta }
|
|
1276
1307
|
};
|
|
1277
1308
|
assertFrameFits(params);
|
|
1278
|
-
const response = await
|
|
1309
|
+
const response = await request("long", (agent, options) => agent.request(methods2.agent.session.prompt, params, options), { sessionId });
|
|
1279
1310
|
return { messageId: response.messageId };
|
|
1280
1311
|
},
|
|
1281
1312
|
cancel(sessionId) {
|
|
1282
1313
|
notifyAgent((agent) => agent.notify(methods2.agent.session.cancel, { sessionId }), sessionId);
|
|
1283
1314
|
},
|
|
1284
1315
|
async setConfigOption(sessionId, configId, value) {
|
|
1285
|
-
const response = await
|
|
1316
|
+
const response = await request("medium", (agent, options) => agent.request(methods2.agent.session.setConfigOption, { sessionId, configId, type: "id", value }, options), { sessionId });
|
|
1286
1317
|
return response.configOptions;
|
|
1287
1318
|
},
|
|
1288
1319
|
async deleteSession(sessionId) {
|
|
@@ -1293,7 +1324,7 @@ function createAcpConnection(options) {
|
|
|
1293
1324
|
},
|
|
1294
1325
|
async steer(steer) {
|
|
1295
1326
|
assertFrameFits(steer);
|
|
1296
|
-
return HgwSteerResponseSchema2.parse(await
|
|
1327
|
+
return HgwSteerResponseSchema2.parse(await request("medium", (agent, options) => agent.request(HGW_METHODS2.session.steer, steer, options), { sessionId: steer.sessionId }));
|
|
1297
1328
|
},
|
|
1298
1329
|
focus(sessionId, presence) {
|
|
1299
1330
|
const report = {
|
|
@@ -2682,6 +2713,7 @@ class HgwRemoteClient {
|
|
|
2682
2713
|
}
|
|
2683
2714
|
}
|
|
2684
2715
|
export {
|
|
2716
|
+
AcpConnectionClosedError,
|
|
2685
2717
|
AgentUpdateError,
|
|
2686
2718
|
HgwClientError,
|
|
2687
2719
|
HgwRemoteClient,
|
package/dist/client/types.d.ts
CHANGED
|
@@ -63,6 +63,13 @@ export type AcpSessionListener = {
|
|
|
63
63
|
replay?: AcpSessionReplayListener;
|
|
64
64
|
state?: (state: AcpSessionState) => void;
|
|
65
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* A read (`listAgents`, `listSessions`, `searchSessions`, `resumePage`) waits
|
|
68
|
+
* out a reconnect and goes out again on the transport that recovers, and a
|
|
69
|
+
* write for an opened Session once it has rejoined. Any other call a
|
|
70
|
+
* transport closes under, and every call once the connection has ended,
|
|
71
|
+
* rejects as `AcpConnectionClosedError`.
|
|
72
|
+
*/
|
|
66
73
|
export interface AcpConnection {
|
|
67
74
|
readonly status: AcpConnectionStatus;
|
|
68
75
|
/**
|
|
@@ -17,6 +17,15 @@ export declare class AgentUpdateError extends Error {
|
|
|
17
17
|
readonly code: "unsupported" | "conflict";
|
|
18
18
|
constructor(code: "unsupported" | "conflict", message: string);
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* The ACP connection is gone for a request: it has ended, no transport is open
|
|
22
|
+
* to send on, or a transport closed under a request it does not send again.
|
|
23
|
+
* `cause` holds what that transport closed with, which a failed socket leaves
|
|
24
|
+
* a DOM `Event`. Gateway refusals and timeouts reject as they come.
|
|
25
|
+
*/
|
|
26
|
+
export declare class AcpConnectionClosedError extends Error {
|
|
27
|
+
constructor(message?: string, options?: ErrorOptions);
|
|
28
|
+
}
|
|
20
29
|
export type SessionStatus = "idle" | "running" | "waiting-for-input" | "failed" | "unknown";
|
|
21
30
|
export type ActivityBase = {
|
|
22
31
|
id: string;
|
package/package.json
CHANGED