@harness-gw/sdk 0.6.1 → 0.8.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/acp-approvals.d.ts +1 -1
- package/dist/client/acp-workspace-client-sessions.d.ts +5 -2
- package/dist/client/acp-workspace-client.d.ts +2 -0
- package/dist/client/connection.d.ts +1 -1
- package/dist/client/hgw-client.d.ts +5 -5
- package/dist/client/hgw-notification.d.ts +1 -1
- package/dist/client/index.js +31 -20
- package/dist/client/limits.d.ts +1 -1
- package/dist/client/types.d.ts +1 -1
- package/dist/client/workspace.d.ts +1 -0
- package/dist/protocol/mcp-apps.d.ts +1 -1
- 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.8.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.8.0 \
|
|
77
77
|
serve --config /run/harness-gw/config.yaml
|
|
78
78
|
```
|
|
79
79
|
|
|
@@ -21,7 +21,7 @@ export type AcpApprovalOption = {
|
|
|
21
21
|
};
|
|
22
22
|
export type AcpApproval = {
|
|
23
23
|
readonly id: string;
|
|
24
|
-
/** The tool call the request guards, when the
|
|
24
|
+
/** The tool call the request guards, when the gateway knows it. */
|
|
25
25
|
readonly toolCallId?: string;
|
|
26
26
|
/** The operation asked for, which is the request's title. */
|
|
27
27
|
readonly action: string;
|
|
@@ -43,8 +43,11 @@ export declare function createAcpSessionStore({ connection, now, onTurnFinished,
|
|
|
43
43
|
answer(sessionIds: readonly string[]): void;
|
|
44
44
|
/** The provider's title, as a list page or a subscribed Session reports it. */
|
|
45
45
|
setTitle: (sessionId: string, title: string) => Map<string, string>;
|
|
46
|
-
/**
|
|
47
|
-
|
|
46
|
+
/**
|
|
47
|
+
* The gateway's read state, or the operator's own optimistic write;
|
|
48
|
+
* `undefined` restores a refused one.
|
|
49
|
+
*/
|
|
50
|
+
setUnread: (sessionId: string, unread: boolean | undefined) => void;
|
|
48
51
|
/** The operator's own optimistic pin; `undefined` restores a refused one. */
|
|
49
52
|
setPinned: (sessionId: string, pinned: boolean | undefined) => void;
|
|
50
53
|
setStatus: (sessionId: string, status: SessionStatus) => void;
|
|
@@ -109,6 +109,8 @@ export declare function createAcpWorkspaceClient({ connection, rest, agentIdFor,
|
|
|
109
109
|
markSessionRead(sessionId: string): Promise<void>;
|
|
110
110
|
/** The row leads the write, so a refused pin has to be taken back. */
|
|
111
111
|
setSessionPinned(sessionId: string, pinned: boolean): Promise<void>;
|
|
112
|
+
/** As a pin does, the row leads the write and a refusal takes it back. */
|
|
113
|
+
markSessionUnread(sessionId: string): Promise<void>;
|
|
112
114
|
/**
|
|
113
115
|
* The runtime's own report, read once. A failed read is not an answer, so
|
|
114
116
|
* it is not remembered.
|
|
@@ -48,7 +48,7 @@ export type AcpConnectionOptions = {
|
|
|
48
48
|
url?: never;
|
|
49
49
|
socketConstructor?: never;
|
|
50
50
|
});
|
|
51
|
-
/** Whether the
|
|
51
|
+
/** Whether the gateway refused an invitation it cannot redeem. */
|
|
52
52
|
export declare function isAuthenticationRequired(error: unknown): boolean;
|
|
53
53
|
/** One of the gateway's ACP listener paths as a WebSocket URL from `base`. */
|
|
54
54
|
export declare function acpSocketUrl(path: string, base: string): string;
|
|
@@ -6,7 +6,7 @@ import type { StagedAttachment } from "./workspace.js";
|
|
|
6
6
|
export type HgwWorkspaceCapabilities = z.infer<typeof SessionWorkspaceCapabilitiesResponseSchema>;
|
|
7
7
|
export type HgwModelChoices = z.infer<typeof SessionModelsResponseSchema>;
|
|
8
8
|
export type HgwContext = z.infer<typeof SessionContextResponseSchema>;
|
|
9
|
-
export type HgwClientFailure = "connection-interrupted" | "provider-unavailable" | "
|
|
9
|
+
export type HgwClientFailure = "connection-interrupted" | "provider-unavailable" | "gateway-failure"
|
|
10
10
|
/** The provider no longer holds the bytes a published receipt points at. */
|
|
11
11
|
| "artifact-missing";
|
|
12
12
|
/** What an MCP App view is opened for: a tool call, or a published Artifact. */
|
|
@@ -18,10 +18,10 @@ export type McpAppSubject = {
|
|
|
18
18
|
export declare class HgwClientError extends Error {
|
|
19
19
|
readonly kind: HgwClientFailure;
|
|
20
20
|
readonly code?: string | undefined;
|
|
21
|
-
/** The
|
|
21
|
+
/** The gateway's HTTP status, when it answered. */
|
|
22
22
|
readonly status?: number | undefined;
|
|
23
23
|
constructor(kind: HgwClientFailure, message?: string, code?: string | undefined,
|
|
24
|
-
/** The
|
|
24
|
+
/** The gateway's HTTP status, when it answered. */
|
|
25
25
|
status?: number | undefined);
|
|
26
26
|
}
|
|
27
27
|
/** The `fetch` the client sends every request with. */
|
|
@@ -302,7 +302,7 @@ export declare class HgwRemoteClient {
|
|
|
302
302
|
addresses: Record<string, string>;
|
|
303
303
|
expiresAt?: string | undefined;
|
|
304
304
|
}>;
|
|
305
|
-
/** A tool call the App view makes, answered by the
|
|
305
|
+
/** A tool call the App view makes, answered by the gateway's MCP server. */
|
|
306
306
|
callMcpAppTool(sessionId: string, toolCallId: string, request: McpAppToolCallRequest): Promise<{
|
|
307
307
|
[x: string]: unknown;
|
|
308
308
|
_meta?: {
|
|
@@ -395,7 +395,7 @@ export declare class HgwRemoteClient {
|
|
|
395
395
|
structuredContent?: unknown;
|
|
396
396
|
isError?: boolean | undefined;
|
|
397
397
|
}>;
|
|
398
|
-
/** A resource read the App view makes, answered by the
|
|
398
|
+
/** A resource read the App view makes, answered by the gateway's MCP server. */
|
|
399
399
|
readMcpAppResource(sessionId: string, subject: McpAppSubject, request: McpAppResourceReadRequest): Promise<{
|
|
400
400
|
[x: string]: unknown;
|
|
401
401
|
_meta?: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
2
|
import type { AcpConnection } from "./types.js";
|
|
3
|
-
/** Runs `handler` for every notification of `method` the
|
|
3
|
+
/** Runs `handler` for every notification of `method` the gateway sends. */
|
|
4
4
|
export declare function subscribeHgwNotification<Value>(connection: Pick<AcpConnection, "subscribeNotification">, method: string, schema: Pick<z.ZodType<Value>, "safeParse">, handler: (value: Value) => void): () => void;
|
package/dist/client/index.js
CHANGED
|
@@ -2191,12 +2191,23 @@ function createAcpWorkspaceClient({
|
|
|
2191
2191
|
throw reason;
|
|
2192
2192
|
}
|
|
2193
2193
|
},
|
|
2194
|
+
async markSessionUnread(sessionId) {
|
|
2195
|
+
const [previous] = store.rowsFor([sessionId]);
|
|
2196
|
+
store.setUnread(sessionId, true);
|
|
2197
|
+
try {
|
|
2198
|
+
await connection.updateSession({ sessionId, unread: true });
|
|
2199
|
+
} catch (reason) {
|
|
2200
|
+
store.setUnread(sessionId, previous?.unread);
|
|
2201
|
+
throw reason;
|
|
2202
|
+
}
|
|
2203
|
+
},
|
|
2194
2204
|
sessionActionCapabilities() {
|
|
2195
2205
|
sessionActions ??= rest.runtimeInfo().then(({ capabilities }) => ({
|
|
2196
2206
|
rename: offers(capabilities.sessionTitle),
|
|
2197
2207
|
archive: offers(capabilities.sessionArchival),
|
|
2198
2208
|
delete: offers(capabilities.sessionDeletion),
|
|
2199
|
-
pin: offers(capabilities.sessionPin)
|
|
2209
|
+
pin: offers(capabilities.sessionPin),
|
|
2210
|
+
unread: offers(capabilities.sessionReadState)
|
|
2200
2211
|
})).catch((reason) => {
|
|
2201
2212
|
sessionActions = undefined;
|
|
2202
2213
|
throw reason;
|
|
@@ -2470,7 +2481,7 @@ class HgwClientError extends Error {
|
|
|
2470
2481
|
kind;
|
|
2471
2482
|
code;
|
|
2472
2483
|
status;
|
|
2473
|
-
constructor(kind, message = "
|
|
2484
|
+
constructor(kind, message = "Gateway request failed", code, status) {
|
|
2474
2485
|
super(message);
|
|
2475
2486
|
this.kind = kind;
|
|
2476
2487
|
this.code = code;
|
|
@@ -2504,7 +2515,7 @@ class HgwRemoteClient {
|
|
|
2504
2515
|
this.#authorization = options.authorization;
|
|
2505
2516
|
this.#origin = options.origin;
|
|
2506
2517
|
}
|
|
2507
|
-
async#read(path, schema, init, notFound = "
|
|
2518
|
+
async#read(path, schema, init, notFound = "gateway-failure") {
|
|
2508
2519
|
let response;
|
|
2509
2520
|
try {
|
|
2510
2521
|
const headers = new Headers(init?.headers);
|
|
@@ -2521,17 +2532,17 @@ class HgwRemoteClient {
|
|
|
2521
2532
|
}
|
|
2522
2533
|
if (!response.ok) {
|
|
2523
2534
|
const error = await normalizedError(response);
|
|
2524
|
-
throw new HgwClientError(response.status === 503 ? "provider-unavailable" : response.status === 404 ? notFound : "
|
|
2535
|
+
throw new HgwClientError(response.status === 503 ? "provider-unavailable" : response.status === 404 ? notFound : "gateway-failure", error?.description, error?.code, response.status);
|
|
2525
2536
|
}
|
|
2526
2537
|
let payload;
|
|
2527
2538
|
try {
|
|
2528
2539
|
payload = await response.json();
|
|
2529
2540
|
} catch {
|
|
2530
|
-
throw new HgwClientError("
|
|
2541
|
+
throw new HgwClientError("gateway-failure", "Invalid gateway response");
|
|
2531
2542
|
}
|
|
2532
2543
|
const parsed = schema.safeParse(payload);
|
|
2533
2544
|
if (!parsed.success)
|
|
2534
|
-
throw new HgwClientError("
|
|
2545
|
+
throw new HgwClientError("gateway-failure", "Invalid gateway response");
|
|
2535
2546
|
return parsed.data;
|
|
2536
2547
|
}
|
|
2537
2548
|
runtimeInfo(signal) {
|
|
@@ -2546,7 +2557,7 @@ class HgwRemoteClient {
|
|
|
2546
2557
|
async deletePushSubscription(endpoint) {
|
|
2547
2558
|
const request = PushUnregistrationSchema2.safeParse({ endpoint });
|
|
2548
2559
|
if (!request.success || !endpoint.trim())
|
|
2549
|
-
throw new HgwClientError("
|
|
2560
|
+
throw new HgwClientError("gateway-failure", "Invalid push subscription");
|
|
2550
2561
|
await this.#write("/push/subscriptions", "DELETE", request.data);
|
|
2551
2562
|
}
|
|
2552
2563
|
adoptSessionOwnership(sessionId, agentId) {
|
|
@@ -2567,7 +2578,7 @@ class HgwRemoteClient {
|
|
|
2567
2578
|
})
|
|
2568
2579
|
});
|
|
2569
2580
|
if (!request.success)
|
|
2570
|
-
throw new HgwClientError("
|
|
2581
|
+
throw new HgwClientError("gateway-failure", "Invalid attachment staging request");
|
|
2571
2582
|
return this.#read(this.#sessionPath(sessionId, "/attachments/stage"), SessionAttachmentStageResponseSchema2, {
|
|
2572
2583
|
method: "POST",
|
|
2573
2584
|
headers: { "content-type": "application/json" },
|
|
@@ -2576,11 +2587,11 @@ class HgwRemoteClient {
|
|
|
2576
2587
|
}
|
|
2577
2588
|
async readArtifact(sessionId, artifactId, signal) {
|
|
2578
2589
|
if (!artifactId.trim() || artifactId.length > 512)
|
|
2579
|
-
throw new HgwClientError("
|
|
2590
|
+
throw new HgwClientError("gateway-failure", "Invalid artifact reference");
|
|
2580
2591
|
return this.#readBlob(this.#sessionPath(sessionId, `/artifacts/${encodeURIComponent(artifactId)}`), { signal }, "artifact-missing");
|
|
2581
2592
|
}
|
|
2582
2593
|
async openMcpApp(sessionId, subject, signal) {
|
|
2583
|
-
const view = await this.#read(this.#mcpAppPath(sessionId, subject), McpAppViewSchema2, { signal }, "artifactId" in subject ? "artifact-missing" : "
|
|
2594
|
+
const view = await this.#read(this.#mcpAppPath(sessionId, subject), McpAppViewSchema2, { signal }, "artifactId" in subject ? "artifact-missing" : "gateway-failure");
|
|
2584
2595
|
return view.files ? { ...view, files: this.#absoluteFiles(view.files) } : view;
|
|
2585
2596
|
}
|
|
2586
2597
|
async renewMcpAppFiles(sessionId, subject) {
|
|
@@ -2607,13 +2618,13 @@ class HgwRemoteClient {
|
|
|
2607
2618
|
}
|
|
2608
2619
|
async transcribeForAgent(agentId, audio, signal) {
|
|
2609
2620
|
if (!audio.size || !audio.type)
|
|
2610
|
-
throw new HgwClientError("
|
|
2621
|
+
throw new HgwClientError("gateway-failure", "Invalid audio recording");
|
|
2611
2622
|
const request = SessionTranscriptionRequestSchema2.safeParse({
|
|
2612
2623
|
dataUrl: await dataUrl(audio),
|
|
2613
2624
|
mimeType: audio.type
|
|
2614
2625
|
});
|
|
2615
2626
|
if (!request.success)
|
|
2616
|
-
throw new HgwClientError("
|
|
2627
|
+
throw new HgwClientError("gateway-failure", "Invalid audio recording");
|
|
2617
2628
|
const path = `/agents/${encodeURIComponent(agentId)}/audio/transcribe`;
|
|
2618
2629
|
const response = await this.#read(path, SessionTranscriptionResponseSchema2, {
|
|
2619
2630
|
method: "POST",
|
|
@@ -2629,7 +2640,7 @@ class HgwRemoteClient {
|
|
|
2629
2640
|
async speakForAgent(agentId, text, signal) {
|
|
2630
2641
|
const request = SessionSpeechRequestSchema2.safeParse({ text });
|
|
2631
2642
|
if (!request.success)
|
|
2632
|
-
throw new HgwClientError("
|
|
2643
|
+
throw new HgwClientError("gateway-failure", "Invalid speech input");
|
|
2633
2644
|
const path = `/agents/${encodeURIComponent(agentId)}/audio/speak`;
|
|
2634
2645
|
return this.#readBlob(path, {
|
|
2635
2646
|
method: "POST",
|
|
@@ -2655,18 +2666,18 @@ class HgwRemoteClient {
|
|
|
2655
2666
|
}
|
|
2656
2667
|
if (!response.ok) {
|
|
2657
2668
|
const error = await normalizedError(response);
|
|
2658
|
-
throw new HgwClientError(response.status === 503 ? "provider-unavailable" : "
|
|
2669
|
+
throw new HgwClientError(response.status === 503 ? "provider-unavailable" : "gateway-failure", error?.description, error?.code, response.status);
|
|
2659
2670
|
}
|
|
2660
2671
|
}
|
|
2661
2672
|
#mcpAppPath(sessionId, subject, suffix = "") {
|
|
2662
2673
|
const [collection, id] = "toolCallId" in subject ? ["tool-calls", subject.toolCallId] : ["artifacts", subject.artifactId];
|
|
2663
2674
|
if (!id.trim() || id.length > 512)
|
|
2664
|
-
throw new HgwClientError("
|
|
2675
|
+
throw new HgwClientError("gateway-failure", "Invalid MCP App reference");
|
|
2665
2676
|
return this.#sessionPath(sessionId, `/${collection}/${encodeURIComponent(id)}/app${suffix}`);
|
|
2666
2677
|
}
|
|
2667
2678
|
async#postMcpApp(sessionId, subject, suffix, request, schema) {
|
|
2668
2679
|
if (!request.success)
|
|
2669
|
-
throw new HgwClientError("
|
|
2680
|
+
throw new HgwClientError("gateway-failure", "Invalid MCP App request");
|
|
2670
2681
|
return this.#read(this.#mcpAppPath(sessionId, subject, suffix), schema, {
|
|
2671
2682
|
method: "POST",
|
|
2672
2683
|
headers: { "content-type": "application/json" },
|
|
@@ -2677,7 +2688,7 @@ class HgwRemoteClient {
|
|
|
2677
2688
|
const agentId = this.#owner(sessionId);
|
|
2678
2689
|
return `/agents/${encodeURIComponent(agentId)}/sessions/${encodeURIComponent(sessionId)}${suffix}`;
|
|
2679
2690
|
}
|
|
2680
|
-
async#readBlob(path, init, notFound = "
|
|
2691
|
+
async#readBlob(path, init, notFound = "gateway-failure") {
|
|
2681
2692
|
let response;
|
|
2682
2693
|
try {
|
|
2683
2694
|
const headers = new Headers(init?.headers);
|
|
@@ -2694,15 +2705,15 @@ class HgwRemoteClient {
|
|
|
2694
2705
|
}
|
|
2695
2706
|
if (!response.ok) {
|
|
2696
2707
|
const error = await normalizedError(response);
|
|
2697
|
-
throw new HgwClientError(response.status === 503 ? "provider-unavailable" : response.status === 404 ? notFound : "
|
|
2708
|
+
throw new HgwClientError(response.status === 503 ? "provider-unavailable" : response.status === 404 ? notFound : "gateway-failure", error?.description, error?.code, response.status);
|
|
2698
2709
|
}
|
|
2699
2710
|
const contentType = response.headers.get("content-type");
|
|
2700
2711
|
if (!contentType || /[\r\n]/u.test(contentType))
|
|
2701
|
-
throw new HgwClientError("
|
|
2712
|
+
throw new HgwClientError("gateway-failure", "Invalid gateway response");
|
|
2702
2713
|
try {
|
|
2703
2714
|
return await response.blob();
|
|
2704
2715
|
} catch {
|
|
2705
|
-
throw new HgwClientError("
|
|
2716
|
+
throw new HgwClientError("gateway-failure", "Invalid gateway response");
|
|
2706
2717
|
}
|
|
2707
2718
|
}
|
|
2708
2719
|
#owner(sessionId) {
|
package/dist/client/limits.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ export declare const STABLE_AFTER_MS = 60000;
|
|
|
33
33
|
* neither a close nor a resume.
|
|
34
34
|
*/
|
|
35
35
|
export declare const PART_GRACE_MS = 2000;
|
|
36
|
-
/** The window a reopen waits in after the
|
|
36
|
+
/** The window a reopen waits in after the gateway closes at capacity. */
|
|
37
37
|
export declare const CAPACITY_BACKOFF: {
|
|
38
38
|
minMs: number;
|
|
39
39
|
maxMs: number;
|
package/dist/client/types.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type AcpPendingRequest = {
|
|
|
17
17
|
sessionId: string;
|
|
18
18
|
request: RequestPermissionRequest;
|
|
19
19
|
respond(response: RequestPermissionResponse): void;
|
|
20
|
-
/** Aborts when the
|
|
20
|
+
/** Aborts when the gateway withdraws the request, which needs no answer. */
|
|
21
21
|
signal: AbortSignal;
|
|
22
22
|
} | {
|
|
23
23
|
kind: "elicitation";
|
|
@@ -199,7 +199,7 @@ export declare const McpAppResourceReadRequestSchema: z.ZodObject<{
|
|
|
199
199
|
}, z.core.$strict>;
|
|
200
200
|
export type McpAppResourceReadRequest = z.infer<typeof McpAppResourceReadRequestSchema>;
|
|
201
201
|
/**
|
|
202
|
-
* The MCP App sandbox
|
|
202
|
+
* The MCP App sandbox gateway: a static page, served beside the workspace on
|
|
203
203
|
* every listener, that holds only the relay creating the App's own frame. It
|
|
204
204
|
* is a real document rather than `srcdoc` so it carries this policy instead of
|
|
205
205
|
* inheriting the embedding page's, which on the guest surface forbids inline
|
package/package.json
CHANGED