@skaile/workspaces 6.11.0 → 6.13.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/CHANGELOG.md +21 -0
- package/dist/app-sdk/index.js +47 -4
- package/dist/app-sdk/index.js.map +1 -1
- package/dist/app-sdk/src/app.d.ts.map +1 -1
- package/dist/app-sdk/src/define-app-capability.d.ts +48 -1
- package/dist/app-sdk/src/define-app-capability.d.ts.map +1 -1
- package/dist/app-sdk/src/index.d.ts +2 -2
- package/dist/app-sdk/src/index.d.ts.map +1 -1
- package/dist/app-sdk/src/transport.d.ts +13 -1
- package/dist/app-sdk/src/transport.d.ts.map +1 -1
- package/dist/{asset-feeds-PO2WC75V.js → asset-feeds-3USSYLBS.js} +6 -6
- package/dist/{asset-feeds-PO2WC75V.js.map → asset-feeds-3USSYLBS.js.map} +1 -1
- package/dist/asset-manager/index.js +1 -1
- package/dist/bridge/harnesses/claude-sdk.js +5 -5
- package/dist/bridge/harnesses/codex.js +3 -3
- package/dist/bridge/harnesses/continue.js +4 -4
- package/dist/bridge/harnesses/echo.js +4 -4
- package/dist/bridge/harnesses/gemini.js +5 -5
- package/dist/bridge/harnesses/goose.js +4 -4
- package/dist/bridge/harnesses/omp.js +34 -33
- package/dist/bridge/harnesses/omp.js.map +1 -1
- package/dist/bridge/harnesses/opencode.js +4 -4
- package/dist/bridge/harnesses/qwen.js +5 -5
- package/dist/bridge/index.js +9 -9
- package/dist/bridge/src/harnesses/omp.d.ts +2 -0
- package/dist/bridge/src/harnesses/omp.d.ts.map +1 -1
- package/dist/channels/adapters/msteams/activity.d.ts +2 -2
- package/dist/channels/adapters/msteams/activity.d.ts.map +1 -1
- package/dist/channels/adapters/msteams.js +21 -6
- package/dist/channels/adapters/msteams.js.map +1 -1
- package/dist/channels/index.js +175 -26
- package/dist/channels/index.js.map +1 -1
- package/dist/channels/src/context-batch.d.ts +63 -0
- package/dist/channels/src/context-batch.d.ts.map +1 -0
- package/dist/channels/src/egress/output-mirror.d.ts +7 -0
- package/dist/channels/src/egress/output-mirror.d.ts.map +1 -1
- package/dist/channels/src/engine.d.ts +21 -2
- package/dist/channels/src/engine.d.ts.map +1 -1
- package/dist/channels/src/index.d.ts +1 -0
- package/dist/channels/src/index.d.ts.map +1 -1
- package/dist/channels/src/ports.d.ts +9 -0
- package/dist/channels/src/ports.d.ts.map +1 -1
- package/dist/channels/src/types.d.ts +6 -0
- package/dist/channels/src/types.d.ts.map +1 -1
- package/dist/{chunk-5M3S6DLV.js → chunk-2LDLQWYI.js} +3 -3
- package/dist/{chunk-5M3S6DLV.js.map → chunk-2LDLQWYI.js.map} +1 -1
- package/dist/{chunk-RRKJHADP.js → chunk-42CN5YMX.js} +3 -3
- package/dist/{chunk-RRKJHADP.js.map → chunk-42CN5YMX.js.map} +1 -1
- package/dist/{chunk-DKRD6CZL.js → chunk-4CNJFSCM.js} +3 -3
- package/dist/{chunk-DKRD6CZL.js.map → chunk-4CNJFSCM.js.map} +1 -1
- package/dist/{chunk-WCHGUS7X.js → chunk-4QIVQAOP.js} +4 -4
- package/dist/{chunk-WCHGUS7X.js.map → chunk-4QIVQAOP.js.map} +1 -1
- package/dist/{chunk-L74W5TVC.js → chunk-6JAFK4ZU.js} +4 -4
- package/dist/{chunk-L74W5TVC.js.map → chunk-6JAFK4ZU.js.map} +1 -1
- package/dist/{chunk-CH6UVEM5.js → chunk-6M4BE42A.js} +7 -7
- package/dist/{chunk-CH6UVEM5.js.map → chunk-6M4BE42A.js.map} +1 -1
- package/dist/{chunk-WPTNYDDS.js → chunk-6OM5QDPA.js} +8 -5
- package/dist/chunk-6OM5QDPA.js.map +1 -0
- package/dist/{chunk-FVOQPTK2.js → chunk-7MISTEMW.js} +3 -3
- package/dist/{chunk-FVOQPTK2.js.map → chunk-7MISTEMW.js.map} +1 -1
- package/dist/{chunk-6NNXBXYI.js → chunk-DOG3YBRL.js} +3 -3
- package/dist/{chunk-6NNXBXYI.js.map → chunk-DOG3YBRL.js.map} +1 -1
- package/dist/{chunk-342UX2MG.js → chunk-DWPQBMCY.js} +2 -2
- package/dist/{chunk-342UX2MG.js.map → chunk-DWPQBMCY.js.map} +1 -1
- package/dist/{chunk-FO2GU5L7.js → chunk-DY52RNLI.js} +18 -17
- package/dist/chunk-DY52RNLI.js.map +1 -0
- package/dist/{chunk-YPX7LQSW.js → chunk-ERJOSW4V.js} +6 -6
- package/dist/{chunk-YPX7LQSW.js.map → chunk-ERJOSW4V.js.map} +1 -1
- package/dist/{chunk-IPCRQWNC.js → chunk-G2ZWIYZR.js} +3 -3
- package/dist/{chunk-IPCRQWNC.js.map → chunk-G2ZWIYZR.js.map} +1 -1
- package/dist/{chunk-7ZL23M3X.js → chunk-GJT2Q5XD.js} +3 -3
- package/dist/{chunk-7ZL23M3X.js.map → chunk-GJT2Q5XD.js.map} +1 -1
- package/dist/{chunk-PZS55HOX.js → chunk-GK5H52WR.js} +3 -3
- package/dist/{chunk-PZS55HOX.js.map → chunk-GK5H52WR.js.map} +1 -1
- package/dist/{chunk-CI4I34F4.js → chunk-GUKIUBMF.js} +4 -4
- package/dist/{chunk-CI4I34F4.js.map → chunk-GUKIUBMF.js.map} +1 -1
- package/dist/{chunk-RZQCBFIU.js → chunk-JPUKRWWS.js} +2 -2
- package/dist/{chunk-RZQCBFIU.js.map → chunk-JPUKRWWS.js.map} +1 -1
- package/dist/{chunk-S6NS3I3P.js → chunk-JTJYM3XT.js} +2 -2
- package/dist/{chunk-S6NS3I3P.js.map → chunk-JTJYM3XT.js.map} +1 -1
- package/dist/{chunk-GQQIMKEP.js → chunk-L4G7AAKM.js} +8 -8
- package/dist/{chunk-GQQIMKEP.js.map → chunk-L4G7AAKM.js.map} +1 -1
- package/dist/{chunk-IIELRGIZ.js → chunk-MPBLRRPK.js} +2 -2
- package/dist/{chunk-IIELRGIZ.js.map → chunk-MPBLRRPK.js.map} +1 -1
- package/dist/{chunk-GYRIO6RA.js → chunk-MVUXBWNW.js} +3 -3
- package/dist/{chunk-GYRIO6RA.js.map → chunk-MVUXBWNW.js.map} +1 -1
- package/dist/{chunk-7O6SCI7B.js → chunk-NGJAB5OS.js} +5 -5
- package/dist/{chunk-7O6SCI7B.js.map → chunk-NGJAB5OS.js.map} +1 -1
- package/dist/{chunk-5XYZNMMC.js → chunk-NJAE5DOC.js} +3 -3
- package/dist/{chunk-5XYZNMMC.js.map → chunk-NJAE5DOC.js.map} +1 -1
- package/dist/{chunk-4NJVF3DV.js → chunk-RFYL2HRA.js} +4 -4
- package/dist/{chunk-4NJVF3DV.js.map → chunk-RFYL2HRA.js.map} +1 -1
- package/dist/{chunk-UHTCOYFJ.js → chunk-RNQ26SKJ.js} +6 -6
- package/dist/{chunk-UHTCOYFJ.js.map → chunk-RNQ26SKJ.js.map} +1 -1
- package/dist/{chunk-FECB7RI7.js → chunk-SGCSP3GI.js} +5 -5
- package/dist/{chunk-FECB7RI7.js.map → chunk-SGCSP3GI.js.map} +1 -1
- package/dist/{chunk-MRTOKIEJ.js → chunk-T73IL57H.js} +3 -3
- package/dist/{chunk-MRTOKIEJ.js.map → chunk-T73IL57H.js.map} +1 -1
- package/dist/{chunk-ZRKU7BRI.js → chunk-U2JQ2EMW.js} +3 -3
- package/dist/{chunk-ZRKU7BRI.js.map → chunk-U2JQ2EMW.js.map} +1 -1
- package/dist/{chunk-GODUKPGD.js → chunk-UDH4KZ53.js} +3 -3
- package/dist/{chunk-GODUKPGD.js.map → chunk-UDH4KZ53.js.map} +1 -1
- package/dist/{chunk-F2IBDVBP.js → chunk-VHRGCLCR.js} +2 -2
- package/dist/{chunk-F2IBDVBP.js.map → chunk-VHRGCLCR.js.map} +1 -1
- package/dist/{chunk-SZ3XCZHC.js → chunk-WIZZUJ6R.js} +3 -3
- package/dist/{chunk-SZ3XCZHC.js.map → chunk-WIZZUJ6R.js.map} +1 -1
- package/dist/{chunk-LXD37LHF.js → chunk-XECD7XZT.js} +4 -4
- package/dist/{chunk-LXD37LHF.js.map → chunk-XECD7XZT.js.map} +1 -1
- package/dist/{chunk-45XL75RO.js → chunk-ZAU7IXIX.js} +9 -9
- package/dist/{chunk-45XL75RO.js.map → chunk-ZAU7IXIX.js.map} +1 -1
- package/dist/{chunk-CLMIZ4N6.js → chunk-ZCKZHOOU.js} +3 -3
- package/dist/{chunk-CLMIZ4N6.js.map → chunk-ZCKZHOOU.js.map} +1 -1
- package/dist/{claude-code-TDMSETH6.js → claude-code-IQHO7PGX.js} +4 -4
- package/dist/{claude-code-TDMSETH6.js.map → claude-code-IQHO7PGX.js.map} +1 -1
- package/dist/cli/index.js +54 -54
- package/dist/connectors/index.js +11 -11
- package/dist/{connectors-IUJRBTCW.js → connectors-S4W2HZ2J.js} +14 -14
- package/dist/{connectors-IUJRBTCW.js.map → connectors-S4W2HZ2J.js.map} +1 -1
- package/dist/connectors-shared/index.js +2 -2
- package/dist/{ensure-sources-Q2TTZVLX.js → ensure-sources-Z4PSTQE4.js} +13 -13
- package/dist/{ensure-sources-Q2TTZVLX.js.map → ensure-sources-Z4PSTQE4.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +11 -11
- package/dist/factory-assets/connectors/deploy.js +11 -11
- package/dist/factory-assets/connectors/devserver.js +11 -11
- package/dist/factory-assets/connectors/flow/adapter.js +11 -11
- package/dist/factory-assets/connectors/flow/run-flow.js +12 -12
- package/dist/factory-assets/connectors/flow.js +11 -11
- package/dist/factory-assets/connectors/git.js +11 -11
- package/dist/factory-assets/connectors/gmail.js +11 -11
- package/dist/factory-assets/connectors/googledrive.js +11 -11
- package/dist/factory-assets/connectors/local.js +11 -11
- package/dist/factory-assets/connectors/mattermost.js +11 -11
- package/dist/factory-assets/connectors/memory.js +11 -11
- package/dist/factory-assets/connectors/minio.js +11 -11
- package/dist/factory-assets/connectors/postgres.js +11 -11
- package/dist/factory-assets/connectors/s3.js +11 -11
- package/dist/factory-assets/connectors/sharepoint.js +11 -11
- package/dist/factory-assets/connectors/sqlite.js +11 -11
- package/dist/factory-assets/connectors/static-server.js +11 -11
- package/dist/factory-assets/connectors/tunnel.js +11 -11
- package/dist/factory-assets/connectors/webdav.js +11 -11
- package/dist/factory-assets/connectors/xstate-store.js +11 -11
- package/dist/factory-assets/connectors/xstate.js +11 -11
- package/dist/{helpers-S6AQGXRH.js → helpers-I537VC5V.js} +13 -13
- package/dist/{helpers-S6AQGXRH.js.map → helpers-I537VC5V.js.map} +1 -1
- package/dist/{library-publish-7U4TJNQX.js → library-publish-IS56MNSL.js} +26 -26
- package/dist/{library-publish-7U4TJNQX.js.map → library-publish-IS56MNSL.js.map} +1 -1
- package/dist/model-endpoint/index.js +3 -3
- package/dist/{open-library-K6O6OVW5.js → open-library-P7ESNVXK.js} +14 -14
- package/dist/{open-library-K6O6OVW5.js.map → open-library-P7ESNVXK.js.map} +1 -1
- package/dist/{project-ZWIZZIUT.js → project-XX4ZJ3IS.js} +22 -22
- package/dist/{project-ZWIZZIUT.js.map → project-XX4ZJ3IS.js.map} +1 -1
- package/dist/runner/index.js +17 -17
- package/dist/runner/src/capability-registry.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +1 -1
- package/dist/sdk/bridge.js +9 -9
- package/dist/sdk/index.js +17 -17
- package/dist/sdk/runner.js +17 -17
- package/dist/sdk/session.js +2 -2
- package/dist/sdk/types.js +1 -1
- package/dist/{serve-preflight-E3IEZTTF.js → serve-preflight-SHTST6WR.js} +15 -15
- package/dist/{serve-preflight-E3IEZTTF.js.map → serve-preflight-SHTST6WR.js.map} +1 -1
- package/dist/session/index.js +2 -2
- package/dist/{setup-V4EXOMVQ.js → setup-OBBZYSBG.js} +14 -14
- package/dist/{setup-V4EXOMVQ.js.map → setup-OBBZYSBG.js.map} +1 -1
- package/dist/{source-O7XXDX55.js → source-3STGEDUY.js} +24 -24
- package/dist/{source-O7XXDX55.js.map → source-3STGEDUY.js.map} +1 -1
- package/dist/{source-manifest-publish-GUU4LX2S.js → source-manifest-publish-EL5SCBGK.js} +26 -26
- package/dist/{source-manifest-publish-GUU4LX2S.js.map → source-manifest-publish-EL5SCBGK.js.map} +1 -1
- package/dist/tui/index.js +17 -17
- package/dist/types/index.js +1 -1
- package/dist/types/src/capabilities.d.ts +26 -0
- package/dist/types/src/capabilities.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +13 -13
- package/dist/workspace-plugin/adapters/omp.js +7 -7
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-FO2GU5L7.js.map +0 -1
- package/dist/chunk-WPTNYDDS.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 6.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1156](https://github.com/skaile-ai/workspaces/pull/1156) [`118248d`](https://github.com/skaile-ai/workspaces/commit/118248dc9735b5db655f040274475b3de0a03525) Thanks [@peteralbert](https://github.com/peteralbert)! - app-sdk: an embedded app can declare how sensitive an action is, and every handler learns who asked (skaile-ai/platform#9246).
|
|
8
|
+
|
|
9
|
+
- `defineAppCapability({ effectClass })` takes `'routine' | 'external' | 'privileged' | 'never'` and sends it on `capability_register`; `Capability` in `@skaile/workspaces/types` gains the optional `effectClass` field (new type `CapabilityEffectClass`). A class above `routine` needs `requiresApproval: true`: `defineAppCapability` throws otherwise, as the platform gateway refuses that registration.
|
|
10
|
+
- Handlers get `ctx.principal` (`AppInvocationPrincipal`): `{ kind: 'human', userId }`, `{ kind: 'trigger', source, userId }`, `{ kind: 'mixed', userIds }` or `{ kind: 'unknown' }`, read from the gateway's `capability_invoked`. A gateway that sends none, or a shape the SDK cannot read, gives `{ kind: 'unknown' }`.
|
|
11
|
+
|
|
12
|
+
The runner's capability registry keeps `effectClass` in `list()` and `serialize()` like the other wire fields. Both fields are additive; the runner contract is unchanged.
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [#1153](https://github.com/skaile-ai/workspaces/pull/1153) [`a745f38`](https://github.com/skaile-ai/workspaces/commit/a745f387aeaa8741ee78d7611f7a7e932d0215f7) Thanks [@mortegro](https://github.com/mortegro)! - omp harness: one `sendHostToolResult` helper for the duplicated `host_tool_result` send/catch blocks, and `tools.host_allowed` patterns are compiled once per config instead of on every `set_host_tools` resync. No behaviour change. `MIGRATION.md` now records the 6.11.0 omp host tool changes: `mcp__` host tools are advertised to omp as `host__…`, and a `host_tool_call` for an unregistered name returns an error instead of reaching the capability registry.
|
|
17
|
+
|
|
18
|
+
## 6.12.0
|
|
19
|
+
|
|
20
|
+
### Minor Changes
|
|
21
|
+
|
|
22
|
+
- [#1151](https://github.com/skaile-ai/workspaces/pull/1151) [`d24ffb9`](https://github.com/skaile-ai/workspaces/commit/d24ffb9d0f105f1183d1d76a4150ea2d00d04936) Thanks [@peteralbert](https://github.com/peteralbert)! - channels: read-along. In a bound conversation only a message addressed to the bot starts an agent turn; the rest go to a new optional `SessionPort.appendContext(binding, messages)` as context, batched every 30 s or at 20 messages and capped at 200 per conversation per hour (the excess is counted and logged; tune with the engine's `contextBatch` option). A host without `appendContext` keeps the old behaviour. Adapters mark addressed messages with `meta.channel.mentioned`; the Teams adapter sets it for an @mention of the bot and for every message in a 1:1 chat with it, and now strips the bot's own mention from the prompt. A new `lazyBindMirrorMode` engine option sets the mirror mode of a binding the engine creates on an @mention (default unchanged, `agent-only`). Also fixed: after a failed or stopped reply, the next reply no longer streams into the same chat message.
|
|
23
|
+
|
|
3
24
|
## 6.11.0
|
|
4
25
|
|
|
5
26
|
### Minor Changes
|
package/dist/app-sdk/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PROTOCOL_VERSION_STRING } from '../chunk-
|
|
1
|
+
import { PROTOCOL_VERSION_STRING } from '../chunk-DWPQBMCY.js';
|
|
2
2
|
import '../chunk-NSBPE2FW.js';
|
|
3
3
|
import * as z from 'zod';
|
|
4
4
|
|
|
@@ -44,6 +44,22 @@ var AppHandshakeRefusedError = class extends Error {
|
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
46
|
var delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
47
|
+
var unknownPrincipal = () => ({ kind: "unknown" });
|
|
48
|
+
function toInvocationPrincipal(raw) {
|
|
49
|
+
if (typeof raw !== "object" || raw === null) return unknownPrincipal();
|
|
50
|
+
const p = raw;
|
|
51
|
+
const isId = (v) => typeof v === "string" && v.length > 0;
|
|
52
|
+
switch (p.kind) {
|
|
53
|
+
case "human":
|
|
54
|
+
return isId(p.userId) ? { kind: "human", userId: p.userId } : unknownPrincipal();
|
|
55
|
+
case "trigger":
|
|
56
|
+
return isId(p.userId) && isId(p.source) ? { kind: "trigger", source: p.source, userId: p.userId } : unknownPrincipal();
|
|
57
|
+
case "mixed":
|
|
58
|
+
return Array.isArray(p.userIds) && p.userIds.length > 0 && p.userIds.every(isId) ? { kind: "mixed", userIds: [...p.userIds] } : unknownPrincipal();
|
|
59
|
+
default:
|
|
60
|
+
return unknownPrincipal();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
47
63
|
function createSkaileApp(options) {
|
|
48
64
|
const {
|
|
49
65
|
appId,
|
|
@@ -80,6 +96,7 @@ function createSkaileApp(options) {
|
|
|
80
96
|
origin: { kind: "app", appId },
|
|
81
97
|
scope: cap.scope,
|
|
82
98
|
requiresApproval: cap.requiresApproval,
|
|
99
|
+
effectClass: cap.effectClass,
|
|
83
100
|
kind: cap.kind,
|
|
84
101
|
audience: cap.audience,
|
|
85
102
|
promptFragment: cap.promptFragment,
|
|
@@ -105,7 +122,7 @@ function createSkaileApp(options) {
|
|
|
105
122
|
logger.debug("deregistered capability (live)", { name });
|
|
106
123
|
}
|
|
107
124
|
}
|
|
108
|
-
async function handleInvocation(callId, name, input) {
|
|
125
|
+
async function handleInvocation(callId, name, input, principal) {
|
|
109
126
|
const cap = registry.get(name);
|
|
110
127
|
if (!cap) {
|
|
111
128
|
transport.send({
|
|
@@ -117,7 +134,12 @@ function createSkaileApp(options) {
|
|
|
117
134
|
}
|
|
118
135
|
try {
|
|
119
136
|
const validated = cap.inputZod.parse(input);
|
|
120
|
-
const ctx = {
|
|
137
|
+
const ctx = {
|
|
138
|
+
sessionId: session?.sessionId ?? "",
|
|
139
|
+
appId,
|
|
140
|
+
log: logger,
|
|
141
|
+
principal: toInvocationPrincipal(principal)
|
|
142
|
+
};
|
|
121
143
|
const result = await cap.handler(validated, ctx);
|
|
122
144
|
transport.send({ type: "capability_result", callId, result });
|
|
123
145
|
} catch (err) {
|
|
@@ -137,7 +159,7 @@ function createSkaileApp(options) {
|
|
|
137
159
|
pending?.settle(new AppHandshakeRefusedError(message.error, message.retryable === true));
|
|
138
160
|
return;
|
|
139
161
|
case "capability_invoked":
|
|
140
|
-
void handleInvocation(message.callId, message.name, message.input);
|
|
162
|
+
void handleInvocation(message.callId, message.name, message.input, message.principal);
|
|
141
163
|
return;
|
|
142
164
|
}
|
|
143
165
|
}
|
|
@@ -373,6 +395,7 @@ function resolveAppConnectionParams(options) {
|
|
|
373
395
|
});
|
|
374
396
|
}
|
|
375
397
|
function defineAppCapability(args) {
|
|
398
|
+
assertEffectClassDeclarable(args.name, args.effectClass, args.requiresApproval);
|
|
376
399
|
const inputSchema = z.toJSONSchema(args.input, {
|
|
377
400
|
target: "draft-07",
|
|
378
401
|
io: "input"
|
|
@@ -390,6 +413,7 @@ function defineAppCapability(args) {
|
|
|
390
413
|
side: "app",
|
|
391
414
|
scope: args.scope,
|
|
392
415
|
requiresApproval: args.requiresApproval,
|
|
416
|
+
effectClass: args.effectClass,
|
|
393
417
|
kind: args.kind,
|
|
394
418
|
audience: args.audience,
|
|
395
419
|
promptFragment: args.promptFragment,
|
|
@@ -400,6 +424,25 @@ function defineAppCapability(args) {
|
|
|
400
424
|
handler: args.handler
|
|
401
425
|
};
|
|
402
426
|
}
|
|
427
|
+
var EFFECT_CLASSES = /* @__PURE__ */ new Set([
|
|
428
|
+
"routine",
|
|
429
|
+
"external",
|
|
430
|
+
"privileged",
|
|
431
|
+
"never"
|
|
432
|
+
]);
|
|
433
|
+
function assertEffectClassDeclarable(name, effectClass, requiresApproval) {
|
|
434
|
+
if (effectClass === void 0 || effectClass === "routine") return;
|
|
435
|
+
if (typeof effectClass !== "string" || !EFFECT_CLASSES.has(effectClass)) {
|
|
436
|
+
throw new Error(
|
|
437
|
+
`defineAppCapability(${name}): effectClass must be one of routine, external, privileged, never`
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
if (requiresApproval !== true) {
|
|
441
|
+
throw new Error(
|
|
442
|
+
`defineAppCapability(${name}): effectClass '${effectClass}' requires requiresApproval: true`
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
403
446
|
|
|
404
447
|
// app-sdk/src/ws-transport.ts
|
|
405
448
|
var BEARER_SUBPROTOCOL_PREFIX = "skaile-bearer.";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../app-sdk/src/logger.ts","../../app-sdk/src/app.ts","../../app-sdk/src/connection-params.ts","../../app-sdk/src/define-app-capability.ts","../../app-sdk/src/ws-transport.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Minimal logger for the app SDK.\n *\n * The SDK is meant to run inside an embedded app (browser or a lightweight\n * Node service), so it deliberately does not pull in the framework's\n * `LogStore` / SQLite logging stack. Authors can pass their own {@link AppLogger}\n * (e.g. a bridge to their app's telemetry) or accept the console-backed default.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\n/** Structured leveled logger the SDK and capability handlers emit through. */\nexport interface AppLogger {\n debug(message: string, data?: unknown): void;\n info(message: string, data?: unknown): void;\n warn(message: string, data?: unknown): void;\n error(message: string, data?: unknown): void;\n}\n\n/** A logger that discards everything. The default when none is supplied. */\nexport const noopLogger: AppLogger = {\n debug() {},\n info() {},\n warn() {},\n error() {},\n};\n\n/**\n * Console-backed logger, prefixing every line with `[skaile-app]`. Opt in by\n * passing `logger: consoleLogger` to {@link createSkaileApp} during development.\n */\nexport const consoleLogger: AppLogger = {\n debug(message, data) {\n console.debug(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n info(message, data) {\n console.info(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n warn(message, data) {\n console.warn(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n error(message, data) {\n console.error(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n};\n","/**\n * `createSkaileApp` — the thin app SDK that makes an embedded app\n * agent-controllable.\n *\n * An app becomes a *capability peer*, mirroring how the runner behaves toward\n * the platform:\n *\n * 1. Opens an authenticated back-connection (via an {@link AppTransport}).\n * 2. Performs the app handshake — sends `app_hello`, awaits `app_hello_ack`\n * (binding the connection to `session + appId`) or `app_hello_nack` (refusal).\n * 3. Registers its `app.*` capabilities via `capability_register`, stamping\n * each with `origin:{kind:'app', appId}` and `side:'app'`. Register /\n * deregister mid-session is supported.\n * 4. Answers inbound `capability_invoked` events by running the local handler\n * and replying with `capability_result` (a thrown handler rides back as\n * `{ error }`).\n *\n * The approval round-trip for `requiresApproval` capabilities is handled\n * entirely platform-side: the gateway parks the invocation for user approval\n * and only forwards `capability_invoked` to the app *after* approval, so the\n * app just marks the capability and answers when asked.\n *\n * The peer reconnects and replays its registrations on a dropped socket, and on\n * a *retryable* refusal — the app boots independently of the session's agent\n * container and can win that race. See {@link ReconnectOptions}.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { Capability } from \"@skaile/workspaces/types\";\nimport { PROTOCOL_VERSION_STRING } from \"@skaile/workspaces/types\";\nimport type { DefinedAppCapability } from \"./define-app-capability.js\";\nimport { type AppLogger, noopLogger } from \"./logger.js\";\nimport type { AppServerMessage, AppTransport } from \"./transport.js\";\n\n/** Default timeout waiting for the gateway's `app_hello_ack`. */\nexport const DEFAULT_HELLO_TIMEOUT_MS = 10_000;\n\n/** Defaults mirroring the agent `WebSocketClientTransport`'s backoff. */\nconst DEFAULT_RECONNECT: Required<ReconnectOptions> = {\n maxAttempts: 5,\n baseMs: 1_000,\n maxMs: 30_000,\n};\n\n/**\n * Reconnect / retry policy. Set `reconnect: false` on {@link SkaileAppOptions}\n * to disable entirely (one attempt, no replay).\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport interface ReconnectOptions {\n /** Attempts before giving up. Default 5. */\n maxAttempts?: number;\n /** First backoff delay in ms; doubles each attempt. Default 1000. */\n baseMs?: number;\n /** Backoff ceiling in ms. Default 30000. */\n maxMs?: number;\n}\n\n/**\n * The gateway refused the handshake. `retryable` mirrors the wire field: `true`\n * for a transient refusal (the agent container is not up yet), `false` for a\n * permanent one (bad token, `appId` not permitted).\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport class AppHandshakeRefusedError extends Error {\n readonly retryable: boolean;\n constructor(reason: string, retryable: boolean) {\n super(`app handshake refused: ${reason}`);\n this.name = \"AppHandshakeRefusedError\";\n this.retryable = retryable;\n }\n}\n\n/**\n * Options for {@link createSkaileApp}.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface SkaileAppOptions {\n /** The embedded app's stable id. Every registered capability is bound to it. */\n appId: string;\n /**\n * The app back-connection. Inject {@link createWebSocketAppTransport} in\n * production or a fake in tests. The transport carries the injected session\n * identity as its connection credential.\n *\n * With `reconnect` enabled the transport must be able to produce a **fresh**\n * token per attempt — the gateway consumes it at `app_hello`. Pass a\n * `SessionTokenProvider` rather than a bare string.\n */\n transport: AppTransport;\n /** Protocol version the SDK announces in `app_hello`. Defaults to the SDK's. */\n protocolVersion?: string;\n /** Timeout waiting for `app_hello_ack`. Defaults to {@link DEFAULT_HELLO_TIMEOUT_MS}. */\n helloTimeoutMs?: number;\n /** Logger for the SDK and (by default) capability handlers. Defaults to {@link noopLogger}. */\n logger?: AppLogger;\n /**\n * Reconnect policy, or `false` to disable. Defaults to\n * `{maxAttempts:5, baseMs:1000, maxMs:30000}`.\n *\n * @since 3.5.0\n */\n reconnect?: ReconnectOptions | false;\n}\n\n/** Result of a successful handshake — the bound session context. */\nexport interface AppSession {\n sessionId: string;\n userId?: string;\n}\n\n/**\n * A connected (or connectable) Skaile app. Register capabilities before or\n * after {@link connect}; post-connect changes are pushed to the gateway\n * immediately.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface SkaileApp {\n /**\n * Add (or replace, by name) an app capability. Registering the same name\n * again replaces it — mirrors the registry's replace-by-name semantics. When\n * already connected, a `capability_register` is sent immediately.\n */\n registerCapability(cap: DefinedAppCapability): void;\n /**\n * Remove a capability by name. No-op when unknown. When connected, a\n * `capability_deregister` is sent immediately so the tool disappears from the\n * agent's catalog.\n */\n deregisterCapability(name: string): void;\n /** Wire-format snapshot of the currently registered capabilities (origin stamped). */\n readonly capabilities: readonly Capability[];\n /** The bound session once connected; `null` before the handshake and while reconnecting. */\n readonly session: AppSession | null;\n /**\n * Open the connection, perform the handshake, and register the current\n * capabilities. Resolves with the bound {@link AppSession}. Rejects if the\n * gateway refuses permanently, the handshake times out, or the transport\n * disconnects mid-handshake. A *retryable* refusal is retried per the\n * reconnect policy before rejecting.\n */\n connect(): Promise<AppSession>;\n /**\n * Subscribe to session binding changes. Fires on **every** bind — including\n * the initial `connect()` — with the new {@link AppSession}, and with `null`\n * whenever the connection drops. Returns an unsubscribe function.\n *\n * @since 3.5.0\n */\n onSessionChange(handler: (session: AppSession | null) => void): () => void;\n /** Disconnect and tear down. Idempotent. Cancels any pending reconnect. */\n close(): Promise<void>;\n}\n\nconst delay = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n/**\n * Create a {@link SkaileApp}. See the module doc for the peer lifecycle.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function createSkaileApp(options: SkaileAppOptions): SkaileApp {\n const {\n appId,\n transport,\n protocolVersion = PROTOCOL_VERSION_STRING,\n helloTimeoutMs = DEFAULT_HELLO_TIMEOUT_MS,\n logger = noopLogger,\n } = options;\n\n const reconnectEnabled = options.reconnect !== false;\n const policy: Required<ReconnectOptions> = {\n ...DEFAULT_RECONNECT,\n ...(options.reconnect === false ? {} : options.reconnect),\n };\n\n const registry = new Map<string, DefinedAppCapability>();\n const sessionListeners = new Set<(s: AppSession | null) => void>();\n let session: AppSession | null = null;\n let closed = false;\n let connecting = false;\n let subscribed = false;\n // Bumped by close(). A reconnect loop parked on its backoff timer captures the\n // epoch it started in and abandons if close() (or a close/connect cycle) has\n // since happened — otherwise it would resurrect a torn-down app.\n let epoch = 0;\n let unsubscribeMessage: (() => void) | null = null;\n let unsubscribeDisconnect: (() => void) | null = null;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n /** Resolver of the backoff sleep, so close() can wake a parked loop. */\n let reconnectWake: (() => void) | null = null;\n\n /** Non-null only while a handshake is in flight. */\n let pending: { settle: (err: Error | null, s?: AppSession) => void } | null = null;\n\n function toWire(cap: DefinedAppCapability): Capability {\n return {\n name: cap.name,\n displayName: cap.displayName,\n description: cap.description,\n inputSchema: cap.inputSchema,\n outputSchema: cap.outputSchema,\n side: \"app\",\n origin: { kind: \"app\", appId },\n scope: cap.scope,\n requiresApproval: cap.requiresApproval,\n kind: cap.kind,\n audience: cap.audience,\n promptFragment: cap.promptFragment,\n callTimeoutMs: cap.callTimeoutMs,\n render: cap.render,\n };\n }\n\n function setSession(next: AppSession | null): void {\n session = next;\n for (const h of sessionListeners) h(next);\n }\n\n function registerCapability(cap: DefinedAppCapability): void {\n registry.set(cap.name, cap);\n if (session) {\n transport.send({ type: \"capability_register\", capabilities: [toWire(cap)] });\n logger.debug(\"registered capability (live)\", { name: cap.name });\n }\n }\n\n function deregisterCapability(name: string): void {\n if (!registry.delete(name)) return;\n if (session) {\n transport.send({ type: \"capability_deregister\", names: [name] });\n logger.debug(\"deregistered capability (live)\", { name });\n }\n }\n\n /** Dispatch an inbound invocation to the local handler; reply with the result. */\n async function handleInvocation(callId: string, name: string, input: unknown): Promise<void> {\n const cap = registry.get(name);\n if (!cap) {\n transport.send({\n type: \"capability_result\",\n callId,\n result: { error: `Unknown app capability: ${name}` },\n });\n return;\n }\n try {\n const validated = cap.inputZod.parse(input);\n const ctx = { sessionId: session?.sessionId ?? \"\", appId, log: logger };\n const result = await cap.handler(validated, ctx);\n transport.send({ type: \"capability_result\", callId, result });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.warn(\"capability handler failed\", { name, error: message });\n transport.send({ type: \"capability_result\", callId, result: { error: message } });\n }\n }\n\n function onMessage(message: AppServerMessage): void {\n switch (message.type) {\n case \"app_hello_ack\": {\n const bound: AppSession = { sessionId: message.sessionId, userId: message.userId };\n pending?.settle(null, bound);\n return;\n }\n case \"app_hello_nack\":\n pending?.settle(new AppHandshakeRefusedError(message.error, message.retryable === true));\n return;\n case \"capability_invoked\":\n void handleInvocation(message.callId, message.name, message.input);\n return;\n }\n }\n\n function onDisconnect(): void {\n if (pending) {\n pending.settle(new Error(\"transport disconnected during handshake\"));\n return;\n }\n if (closed || !session) return;\n logger.warn(\"app connection lost\", { appId });\n setSession(null);\n if (!reconnectEnabled) return;\n // The loop holds the same guard connect() uses: a manual connect() while it\n // runs would start a second handshake, and both would overwrite `pending`.\n connecting = true;\n const loopEpoch = epoch;\n void reconnectLoop().finally(() => {\n // A close()/connect() cycle since this loop started owns the guard now.\n if (epoch === loopEpoch) connecting = false;\n });\n }\n\n function ensureSubscribed(): void {\n if (subscribed) return;\n unsubscribeMessage = transport.onMessage(onMessage);\n unsubscribeDisconnect = transport.onDisconnect(onDisconnect);\n subscribed = true;\n }\n\n /** One attempt: open the socket, exchange hello/ack, replay the registry. */\n function performHandshake(): Promise<AppSession> {\n ensureSubscribed();\n return transport.connect().then(\n () =>\n new Promise<AppSession>((resolve, reject) => {\n if (closed) {\n reject(new Error(\"app closed during handshake\"));\n return;\n }\n let settled = false;\n const timer = setTimeout(() => {\n pending?.settle(new Error(`app handshake timed out after ${helloTimeoutMs}ms`));\n }, helloTimeoutMs);\n\n pending = {\n settle: (err, bound) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n pending = null;\n if (err || !bound) {\n reject(err ?? new Error(\"app handshake failed\"));\n return;\n }\n setSession(bound);\n logger.info(\"app connection established\", { appId, sessionId: bound.sessionId });\n // Replay the full surface — on a reconnect the gateway dropped our\n // caps when the previous connection closed.\n const wire = Array.from(registry.values()).map(toWire);\n if (wire.length > 0) {\n transport.send({ type: \"capability_register\", capabilities: wire });\n }\n resolve(bound);\n },\n };\n\n transport.send({ type: \"app_hello\", appId, protocolVersion });\n }),\n (err) => Promise.reject(err instanceof Error ? err : new Error(String(err))),\n );\n }\n\n const backoffMs = (attempt: number) =>\n Math.min(policy.maxMs, policy.baseMs * 2 ** Math.max(0, attempt - 1));\n\n /** Rebind after a dropped socket. Stops on a permanent refusal or close(). */\n async function reconnectLoop(): Promise<void> {\n const myEpoch = epoch;\n for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {\n const wait = backoffMs(attempt);\n logger.debug(\"scheduling app reconnect\", { appId, attempt, waitMs: wait });\n // close() cancels the timer, so it also has to hand us the resolver —\n // otherwise this await never settles and the loop frame parks forever,\n // pinning the transport and every other captured binding.\n await new Promise<void>((r) => {\n reconnectWake = r;\n reconnectTimer = setTimeout(r, wait);\n });\n reconnectWake = null;\n if (closed || epoch !== myEpoch) {\n logger.debug(\"app reconnect cancelled\", { appId, attempt });\n return;\n }\n try {\n await performHandshake();\n logger.info(\"app connection re-established\", { appId });\n return;\n } catch (err) {\n if (err instanceof AppHandshakeRefusedError && !err.retryable) {\n logger.error(\"app reconnect refused permanently\", { appId, error: err.message });\n return;\n }\n logger.warn(\"app reconnect attempt failed\", {\n appId,\n attempt,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n logger.error(\"app reconnect gave up\", { appId, attempts: policy.maxAttempts });\n }\n\n async function connect(): Promise<AppSession> {\n if (session) return session;\n if (connecting) throw new Error(\"app connect() is already in progress\");\n connecting = true;\n // A closed app is reusable: connect() reopens it. Any reconnect loop still\n // parked from the previous life is fenced off by the epoch close() bumped.\n closed = false;\n try {\n for (let attempt = 1; ; attempt++) {\n try {\n return await performHandshake();\n } catch (err) {\n // Only a *retryable* refusal is worth another attempt on the initial\n // connect: the app raced the session's agent container on boot. Every\n // other failure (bad token, timeout, transport error) is terminal here.\n const transient = err instanceof AppHandshakeRefusedError && err.retryable;\n if (!transient || !reconnectEnabled || attempt >= policy.maxAttempts || closed) {\n throw err;\n }\n logger.debug(\"app handshake refused (retryable), retrying\", { appId, attempt });\n await delay(backoffMs(attempt));\n }\n }\n } finally {\n connecting = false;\n }\n }\n\n async function close(): Promise<void> {\n closed = true;\n epoch++;\n clearTimeout(reconnectTimer);\n // Wake a loop parked on its backoff so it observes `closed` and returns,\n // rather than awaiting a timer that will now never fire.\n reconnectWake?.();\n reconnectWake = null;\n connecting = false;\n // Abort an in-flight handshake — rejects the pending connect and clears its\n // timer so nothing leaks past close().\n pending?.settle(new Error(\"app closed during handshake\"));\n unsubscribeMessage?.();\n unsubscribeDisconnect?.();\n unsubscribeMessage = null;\n unsubscribeDisconnect = null;\n subscribed = false;\n session = null;\n await transport.disconnect();\n }\n\n return {\n registerCapability,\n deregisterCapability,\n get capabilities() {\n return Array.from(registry.values()).map(toWire);\n },\n get session() {\n return session;\n },\n connect,\n onSessionChange(handler) {\n sessionListeners.add(handler);\n return () => sessionListeners.delete(handler);\n },\n close,\n };\n}\n","/**\n * Connection-param handshake between the embedding host and the app frame.\n *\n * An embedded app needs three things to open its back-connection: the gateway\n * URL, its own `appId`, and a session identity token. The token is a **secret**,\n * so it must not ride in the iframe `src` — the preview surface that serves the\n * app is unauthenticated, and a URL lands in access logs, `Referer` headers, and\n * browser history. Instead the host posts it over `postMessage`, and the app\n * asks for it with a ready ping once its script is running.\n *\n * The host cannot simply post on iframe `load`: it has no way to know when the\n * app's script is listening. So the app speaks first.\n *\n * ```text\n * app → host { type: 'skaile_app_ready', appId } targetOrigin = parentOrigin\n * host → app { type: 'skaile_app_params', appId, url, token } targetOrigin = appOrigin\n * ```\n *\n * Both sides pin the peer: the app checks `event.origin === parentOrigin` and\n * that the message came from its own parent; the host checks the origin and that\n * `event.source` is the very iframe it rendered. The app learns the expected\n * parent origin from a **non-secret** query param on its `src`\n * ({@link PARENT_ORIGIN_QUERY_PARAM}), which is safe to leak precisely because it\n * is not a credential.\n *\n * @category App SDK\n * @since 3.5.0\n */\n\n/**\n * Query param on the app's iframe `src` carrying the embedder's origin. Not a\n * secret — it is the value the app pins its `postMessage` peer to.\n */\nexport const PARENT_ORIGIN_QUERY_PARAM = \"skaileParentOrigin\";\n\n/** Default timeout waiting for the host's params reply. */\nexport const DEFAULT_PARAMS_TIMEOUT_MS = 10_000;\n\n/** First gap between ready pings. Grows by {@link PING_BACKOFF_FACTOR} up to {@link MAX_PING_INTERVAL_MS}. */\nexport const INITIAL_PING_INTERVAL_MS = 100;\n\n/**\n * Ceiling for the ready-ping gap. The host mints a fresh single-use ticket for\n * *every* ping it answers, so pinging is not free — back off rather than hammer.\n */\nexport const MAX_PING_INTERVAL_MS = 1_000;\n\nconst PING_BACKOFF_FACTOR = 2;\n\n/** App → host. Announces the app frame is listening and wants its params. */\nexport type SkaileAppReadyMessage = {\n type: \"skaile_app_ready\";\n /** The app asking. The host answers only for an app it actually embedded. */\n appId: string;\n};\n\n/** Host → app. Carries the gateway URL and the single-use session identity token. */\nexport type SkaileAppParamsMessage = {\n type: \"skaile_app_params\";\n /** Echoed back so a frame embedding several apps can disambiguate. */\n appId: string;\n /** The gateway app-channel URL, e.g. `wss://platform/app-connect`. */\n url: string;\n /** Single-use session identity token. Consumed by the gateway at `app_hello`. */\n token: string;\n};\n\n/** Resolved params, ready to hand to `createWebSocketAppTransport` + `createSkaileApp`. */\nexport type AppConnectionParams = {\n url: string;\n appId: string;\n token: string;\n};\n\n/** The subset of `postMessage` targets this module needs. */\nexport type PostMessageTarget = {\n postMessage(message: unknown, targetOrigin: string): void;\n};\n\n/** Structural view of a `MessageEvent`, so tests need no DOM. */\nexport type MessageEventLike = {\n readonly origin: string;\n readonly source: unknown;\n readonly data: unknown;\n};\n\n/** Structural view of the `window` bits this module needs. Injectable for tests. */\nexport type AppWindowLike = {\n readonly parent: PostMessageTarget | null;\n readonly location: { readonly search: string };\n addEventListener(type: \"message\", handler: (event: MessageEventLike) => void): void;\n removeEventListener(type: \"message\", handler: (event: MessageEventLike) => void): void;\n};\n\n/**\n * Options for {@link resolveAppConnectionParams}.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport interface ResolveAppConnectionParamsOptions {\n /** The embedded app's stable id. Must match the params the host replies with. */\n appId: string;\n /**\n * Expected origin of the embedding host. Defaults to the\n * {@link PARENT_ORIGIN_QUERY_PARAM} query param on the current URL. Never `\"*\"`\n * or `\"null\"` — an unpinned peer defeats the point of not putting the token in\n * the URL.\n */\n parentOrigin?: string;\n /** How long to wait for the host's reply. Defaults to {@link DEFAULT_PARAMS_TIMEOUT_MS}. */\n timeoutMs?: number;\n /** Window to listen and post on. Defaults to the global. Injectable for tests. */\n windowRef?: AppWindowLike;\n}\n\nfunction readParentOriginFromQuery(search: string): string | null {\n const value = new URLSearchParams(search).get(PARENT_ORIGIN_QUERY_PARAM);\n return value && value.length > 0 ? value : null;\n}\n\nfunction isParamsMessage(data: unknown, appId: string): data is SkaileAppParamsMessage {\n if (!data || typeof data !== \"object\") return false;\n const m = data as Partial<SkaileAppParamsMessage>;\n // Trim before the emptiness check: a whitespace-only token would survive to\n // the gateway and be refused there. Failing here is the clearer error.\n return (\n m.type === \"skaile_app_params\" &&\n m.appId === appId &&\n typeof m.url === \"string\" &&\n m.url.trim().length > 0 &&\n typeof m.token === \"string\" &&\n m.token.trim().length > 0\n );\n}\n\n/**\n * Ask the embedding host for this app's connection params.\n *\n * Posts a {@link SkaileAppReadyMessage} to the parent and resolves with the\n * matching {@link SkaileAppParamsMessage}. Messages from any other origin, from\n * any window other than the parent, or for another `appId` are ignored — not\n * rejected — because a page may legitimately receive unrelated `postMessage`\n * traffic.\n *\n * Call it again to re-request a fresh token: the token is single-use, so a\n * reconnecting transport needs a new one (see `createSkaileApp`'s `reconnect`).\n *\n * @throws when not embedded, when the parent origin cannot be determined or is\n * unpinnable, or when the host does not reply within `timeoutMs`.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport function resolveAppConnectionParams(\n options: ResolveAppConnectionParamsOptions,\n): Promise<AppConnectionParams> {\n const { appId, timeoutMs = DEFAULT_PARAMS_TIMEOUT_MS } = options;\n const win =\n options.windowRef ?? (globalThis as unknown as { window?: AppWindowLike }).window ?? null;\n if (!win) {\n return Promise.reject(\n new Error(\"resolveAppConnectionParams requires a window (pass `windowRef` off-browser)\"),\n );\n }\n\n const parent = win.parent;\n // A top-level page's `parent` is itself. An app that is not embedded has no\n // host to ask, and posting to itself would hang until the timeout.\n if (!parent || (parent as unknown) === (win as unknown)) {\n return Promise.reject(\n new Error(\"app is not embedded — no parent frame to request params from\"),\n );\n }\n\n const parentOrigin = options.parentOrigin ?? readParentOriginFromQuery(win.location.search);\n if (!parentOrigin) {\n return Promise.reject(\n new Error(\n `cannot determine the host origin: pass \\`parentOrigin\\` or set the \\`${PARENT_ORIGIN_QUERY_PARAM}\\` query param`,\n ),\n );\n }\n // `*` would broadcast the ready ping to whatever is above us, and `null` (an\n // opaque, sandboxed origin) cannot be pinned at all. Both make the peer check\n // meaningless, and the token would then be posted to an unverified window.\n if (parentOrigin === \"*\" || parentOrigin === \"null\") {\n return Promise.reject(new Error(`refusing to use an unpinnable host origin: ${parentOrigin}`));\n }\n\n // Bind the narrowed peer once: the guards above proved both are non-null, but\n // that narrowing does not survive into the hoisted `ping` declaration below.\n const peer: PostMessageTarget = parent;\n const peerOrigin: string = parentOrigin;\n\n return new Promise<AppConnectionParams>((resolve, reject) => {\n let settled = false;\n let pingTimer: ReturnType<typeof setTimeout> | undefined;\n let pingInterval = INITIAL_PING_INTERVAL_MS;\n\n const cleanup = () => {\n clearTimeout(timer);\n clearTimeout(pingTimer);\n win.removeEventListener(\"message\", onMessage);\n };\n\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(\n new Error(`timed out after ${timeoutMs}ms waiting for connection params from the host`),\n );\n }, timeoutMs);\n\n function onMessage(event: MessageEventLike): void {\n if (settled) return;\n // Pin the peer on both axes. Origin alone is not enough: any frame served\n // from the host's origin could otherwise answer.\n if (event.origin !== peerOrigin || event.source !== peer) return;\n if (!isParamsMessage(event.data, appId)) return;\n settled = true;\n cleanup();\n resolve({ url: event.data.url, appId, token: event.data.token });\n }\n\n win.addEventListener(\"message\", onMessage);\n\n const ready: SkaileAppReadyMessage = { type: \"skaile_app_ready\", appId };\n\n /**\n * Keep asking until the host answers. The host only mints in response to a\n * ping and never speaks unprompted, so a single ping is a race the app loses\n * whenever it boots before the host's own state has settled — the ping is\n * dropped and nothing re-asks. Backs off because each answered ping costs the\n * host a single-use ticket.\n */\n function ping(): void {\n if (settled) return;\n try {\n peer.postMessage(ready, peerOrigin);\n } catch (err) {\n settled = true;\n cleanup();\n reject(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n pingTimer = setTimeout(ping, pingInterval);\n pingInterval = Math.min(pingInterval * PING_BACKOFF_FACTOR, MAX_PING_INTERVAL_MS);\n }\n\n ping();\n });\n}\n","/**\n * `defineAppCapability` — author-friendly factory for an embedded app's\n * agent-controllable capabilities.\n *\n * It mirrors the runner's `defineCapability` (`@skaile/workspaces/runner`) but\n * is scoped to the app side: the handler always runs *in the app*, so `side` is\n * fixed to `'app'` and the author never passes it. The originating `appId` is\n * known to the SDK, not the individual capability, so `origin` is stamped by\n * {@link createSkaileApp} at registration time — not here.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { CapabilityAudience, CapabilityOrigin, RenderSpec } from \"@skaile/workspaces/types\";\nimport * as z from \"zod\";\nimport type { AppLogger } from \"./logger.js\";\n\n/**\n * Per-invocation context passed to an app capability handler. Analogous to the\n * runner's `HandlerContext`, but carries the `appId` and the SDK's\n * {@link AppLogger} (there is no server-side `capability:<origin>:<name>` log\n * slice inside the app process).\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type AppHandlerContext = {\n /** The session this app connection is bound to (from the hello ack). */\n sessionId: string;\n /** The embedded app's id. */\n appId: string;\n /** Logger to emit through; defaults to the SDK's configured logger. */\n log: AppLogger;\n};\n\n/**\n * Author-facing arguments to {@link defineAppCapability}. Mirrors the wire\n * {@link import(\"@skaile/workspaces/types\").Capability} fields an app may set,\n * minus `side` / `origin` (fixed / stamped by the SDK) and the runtime-only\n * fields that only make sense platform-side.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type DefineAppCapabilityArgs<\n T extends z.ZodTypeAny,\n R extends z.ZodTypeAny | undefined = undefined,\n> = {\n /**\n * Stable, session-unique name. Convention: `app.<sector>.<verb>` — e.g.\n * `app.ui.switch_contract` (UI sector) or `app.backend.insert_contract`\n * (backend sector). v1 distinguishes sectors by this naming convention.\n */\n name: string;\n /** Human-readable label for user-facing surfaces; falls back to `name`. */\n displayName?: string;\n /** Human/LLM-readable summary; shown to the agent at tool registration. */\n description: string;\n /** Zod schema validating the invocation arguments. Converted to JSON Schema. */\n input: T;\n /** Optional Zod schema for the return value. */\n output?: R;\n /** Lifetime: `session` survives the whole session, `turn` is dropped at end-of-turn. */\n scope?: \"session\" | \"turn\";\n /**\n * When true, the platform shows a confirm UI and waits for user approval\n * before the invocation is forwarded to the app. Mark every mutation\n * (backend writes, destructive UI actions) with this.\n */\n requiresApproval?: boolean;\n /** Documentation hint: side-effect, render, or pure query. */\n kind?: \"effect\" | \"render\" | \"query\";\n /**\n * Audience scoping. Omit for the default (`['llm','user']`). App capabilities\n * default to LLM-only in the command palette (`isUserInvokable`); set\n * `['llm','user']` on the user-facing actions you want to appear in cmdK.\n */\n audience?: CapabilityAudience[];\n /** Appended to the system prompt's `<CAPABILITIES>` section. */\n promptFragment?: string;\n /** Per-capability override for the platform's result round-trip timeout. */\n callTimeoutMs?: number;\n /** Render contract for capabilities that produce a UI surface. */\n render?: RenderSpec;\n /** The handler — runs in the app. Receives validated input + {@link AppHandlerContext}. */\n handler: (\n args: z.infer<T>,\n ctx: AppHandlerContext,\n ) => Promise<R extends z.ZodTypeAny ? z.infer<R> : unknown>;\n};\n\n/**\n * A defined app capability: the wire-format fields the SDK registers (minus the\n * SDK-stamped `origin`), plus the Zod schemas and type-erased handler used for\n * runtime input validation and inbound dispatch.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type DefinedAppCapability = {\n name: string;\n displayName?: string;\n description: string;\n inputSchema: Record<string, unknown>;\n outputSchema?: Record<string, unknown>;\n /** Always `'app'` — the handler runs in the embedded app. */\n side: \"app\";\n scope?: \"session\" | \"turn\";\n requiresApproval?: boolean;\n kind?: \"effect\" | \"render\" | \"query\";\n audience?: CapabilityAudience[];\n promptFragment?: string;\n callTimeoutMs?: number;\n render?: RenderSpec;\n /** Original Zod schema for runtime input validation. Not serialized to the wire. */\n readonly inputZod: z.ZodTypeAny;\n /** Original Zod schema for the optional output, if declared. Not serialized. */\n readonly outputZod?: z.ZodTypeAny;\n /** Type-erased handler dispatched on an inbound `capability_invoked`. */\n handler: (args: unknown, ctx: AppHandlerContext) => Promise<unknown>;\n};\n\n/** Origin kind an app capability always carries once stamped by the SDK. */\nexport type AppCapabilityOrigin = Extract<CapabilityOrigin, { kind: \"app\" }>;\n\n/**\n * Build a {@link DefinedAppCapability} from author-friendly Zod-typed args.\n * `side` is fixed to `'app'`; `origin` is added by the SDK at registration\n * time (it needs the SDK's `appId`).\n *\n * @example\n * ```ts\n * const switchContract = defineAppCapability({\n * name: 'app.ui.switch_contract',\n * description: 'Switch the visible contract in the app UI.',\n * input: z.object({ contractId: z.string() }),\n * handler: async ({ contractId }, ctx) => {\n * ctx.log.info('switching contract', { contractId });\n * ui.select(contractId);\n * },\n * });\n * ```\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function defineAppCapability<\n T extends z.ZodTypeAny,\n R extends z.ZodTypeAny | undefined = undefined,\n>(args: DefineAppCapabilityArgs<T, R>): DefinedAppCapability {\n const inputSchema = z.toJSONSchema(args.input, {\n target: \"draft-07\",\n io: \"input\",\n }) as Record<string, unknown>;\n const outputSchema = args.output\n ? (z.toJSONSchema(args.output, {\n target: \"draft-07\",\n io: \"output\",\n }) as Record<string, unknown>)\n : undefined;\n\n return {\n name: args.name,\n displayName: args.displayName,\n description: args.description,\n inputSchema,\n outputSchema,\n side: \"app\",\n scope: args.scope,\n requiresApproval: args.requiresApproval,\n kind: args.kind,\n audience: args.audience,\n promptFragment: args.promptFragment,\n callTimeoutMs: args.callTimeoutMs,\n render: args.render,\n inputZod: args.input,\n outputZod: args.output,\n handler: args.handler as (args: unknown, ctx: AppHandlerContext) => Promise<unknown>,\n };\n}\n","/**\n * WebSocket-backed {@link AppTransport}.\n *\n * Browser-safe: uses the standard `WebSocket` global (no `ws` dependency, no\n * `node:*` imports), so it runs unchanged in an embedded app's iframe and in a\n * lightweight Node 22+ service. The injected session identity is presented as a\n * bearer **subprotocol** — the same `skaile-bearer.<base64url>` scheme the\n * platform gateway already decodes for runner connections — so the gateway\n * verifies identity at the WS upgrade, before any `app_hello` is processed.\n *\n * Frames are bare JSON of the app-channel messages (no `MessageV2` envelope),\n * matching the runner peer's on-the-wire convention.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { AppClientMessage, AppServerMessage, AppTransport } from \"./transport.js\";\n\n/** Matches `BEARER_SUBPROTOCOL_PREFIX` in `@skaile/workspaces/transport`'s ws/auth. */\nconst BEARER_SUBPROTOCOL_PREFIX = \"skaile-bearer.\";\n\n/** Encode a bearer token as a WS subprotocol token (base64url, no padding). */\nfunction encodeBearerSubprotocol(token: string): string {\n // Encode via UTF-8 bytes so a non-ASCII token doesn't make `btoa` throw\n // (btoa is Latin-1 only). The gateway decodes the mirror of this.\n const binary = String.fromCharCode(...new TextEncoder().encode(token));\n const b64 = btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n return BEARER_SUBPROTOCOL_PREFIX + b64;\n}\n\n/** Minimal structural view of the parts of the `WebSocket` API the transport uses. */\ntype WebSocketLike = {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n addEventListener(type: \"open\", handler: () => void): void;\n addEventListener(type: \"close\", handler: () => void): void;\n addEventListener(type: \"error\", handler: () => void): void;\n addEventListener(type: \"message\", handler: (ev: { data: unknown }) => void): void;\n};\n\n/** Constructor shape for a `WebSocket` implementation (global or injected). */\nexport type WebSocketFactory = new (url: string, protocols?: string | string[]) => WebSocketLike;\n\n/**\n * Supplies a session identity token for one connection attempt.\n *\n * The gateway's token is **single-use** — it is consumed at `app_hello` — so a\n * reconnecting transport cannot replay the one it opened with. Pass a provider\n * (typically a call to `resolveAppConnectionParams`) and every `connect()` mints\n * a fresh token.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport type SessionTokenProvider = () => string | Promise<string>;\n\n/**\n * Options for {@link createWebSocketAppTransport}.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface WebSocketAppTransportOptions {\n /** The gateway app-channel URL (e.g. `wss://platform/app-connect`). */\n url: string;\n /**\n * The injected session identity token, or a {@link SessionTokenProvider} that\n * yields a fresh one per connection attempt. Presented as the WS bearer\n * subprotocol; the gateway verifies it and resolves the session/user.\n *\n * A bare string cannot survive a reconnect (the gateway consumes the token at\n * `app_hello`) — pass a provider whenever `reconnect` is enabled on the peer.\n */\n sessionToken: string | SessionTokenProvider;\n /**\n * WebSocket implementation. Defaults to the `WebSocket` global (browser /\n * Node 22+). Injectable for tests or non-standard runtimes.\n */\n WebSocketImpl?: WebSocketFactory;\n /** Connect timeout in ms. Default 10000. */\n connectTimeoutMs?: number;\n}\n\nconst OPEN = 1;\n\n/**\n * Create a WebSocket-backed {@link AppTransport}. The session token is sent as a\n * bearer subprotocol at connect time; app-channel messages are JSON-framed.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function createWebSocketAppTransport(options: WebSocketAppTransportOptions): AppTransport {\n const { url, sessionToken, connectTimeoutMs = 10_000 } = options;\n const resolved =\n options.WebSocketImpl ?? (globalThis as { WebSocket?: WebSocketFactory }).WebSocket;\n if (!resolved) {\n throw new Error(\n \"No WebSocket implementation available. Pass `WebSocketImpl` in a runtime without a global WebSocket.\",\n );\n }\n const WebSocketImpl: WebSocketFactory = resolved;\n\n let ws: WebSocketLike | null = null;\n const messageHandlers = new Set<(m: AppServerMessage) => void>();\n const disconnectHandlers = new Set<() => void>();\n\n function connect(): Promise<void> {\n // A provider is resolved per attempt — the gateway consumes the token at\n // `app_hello`, so a reconnect must present a new one. A bare string opens the\n // socket synchronously, so `connect()` still constructs it before it yields.\n if (typeof sessionToken === \"function\") {\n return Promise.resolve(sessionToken()).then(openSocket);\n }\n return openSocket(sessionToken);\n }\n\n function openSocket(token: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let opened = false;\n const socket = new WebSocketImpl(url, [encodeBearerSubprotocol(token)]);\n ws = socket;\n\n const timer = setTimeout(() => {\n if (opened) return;\n socket.close(4008, \"connect timeout\");\n reject(new Error(`app transport connect timed out after ${connectTimeoutMs}ms`));\n }, connectTimeoutMs);\n\n socket.addEventListener(\"open\", () => {\n opened = true;\n clearTimeout(timer);\n resolve();\n });\n socket.addEventListener(\"error\", () => {\n if (!opened) {\n clearTimeout(timer);\n reject(new Error(\"app transport connection error\"));\n }\n });\n socket.addEventListener(\"close\", () => {\n clearTimeout(timer);\n if (ws === socket) ws = null;\n for (const h of disconnectHandlers) h();\n });\n socket.addEventListener(\"message\", (ev) => {\n let parsed: AppServerMessage;\n try {\n parsed = JSON.parse(String(ev.data)) as AppServerMessage;\n } catch {\n return;\n }\n for (const h of messageHandlers) h(parsed);\n });\n });\n }\n\n return {\n connect,\n async disconnect() {\n const socket = ws;\n ws = null;\n socket?.close(1000, \"client closed\");\n },\n send(message: AppClientMessage) {\n if (!ws || ws.readyState !== OPEN) {\n throw new Error(\"app transport is not connected\");\n }\n ws.send(JSON.stringify(message));\n },\n onMessage(handler) {\n messageHandlers.add(handler);\n return () => messageHandlers.delete(handler);\n },\n onDisconnect(handler) {\n disconnectHandlers.add(handler);\n return () => disconnectHandlers.delete(handler);\n },\n get connected() {\n return ws?.readyState === OPEN;\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../app-sdk/src/logger.ts","../../app-sdk/src/app.ts","../../app-sdk/src/connection-params.ts","../../app-sdk/src/define-app-capability.ts","../../app-sdk/src/ws-transport.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Minimal logger for the app SDK.\n *\n * The SDK is meant to run inside an embedded app (browser or a lightweight\n * Node service), so it deliberately does not pull in the framework's\n * `LogStore` / SQLite logging stack. Authors can pass their own {@link AppLogger}\n * (e.g. a bridge to their app's telemetry) or accept the console-backed default.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\n/** Structured leveled logger the SDK and capability handlers emit through. */\nexport interface AppLogger {\n debug(message: string, data?: unknown): void;\n info(message: string, data?: unknown): void;\n warn(message: string, data?: unknown): void;\n error(message: string, data?: unknown): void;\n}\n\n/** A logger that discards everything. The default when none is supplied. */\nexport const noopLogger: AppLogger = {\n debug() {},\n info() {},\n warn() {},\n error() {},\n};\n\n/**\n * Console-backed logger, prefixing every line with `[skaile-app]`. Opt in by\n * passing `logger: consoleLogger` to {@link createSkaileApp} during development.\n */\nexport const consoleLogger: AppLogger = {\n debug(message, data) {\n console.debug(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n info(message, data) {\n console.info(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n warn(message, data) {\n console.warn(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n error(message, data) {\n console.error(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n};\n","/**\n * `createSkaileApp` — the thin app SDK that makes an embedded app\n * agent-controllable.\n *\n * An app becomes a *capability peer*, mirroring how the runner behaves toward\n * the platform:\n *\n * 1. Opens an authenticated back-connection (via an {@link AppTransport}).\n * 2. Performs the app handshake — sends `app_hello`, awaits `app_hello_ack`\n * (binding the connection to `session + appId`) or `app_hello_nack` (refusal).\n * 3. Registers its `app.*` capabilities via `capability_register`, stamping\n * each with `origin:{kind:'app', appId}` and `side:'app'`. Register /\n * deregister mid-session is supported.\n * 4. Answers inbound `capability_invoked` events by running the local handler\n * and replying with `capability_result` (a thrown handler rides back as\n * `{ error }`).\n *\n * The approval round-trip for `requiresApproval` capabilities is handled\n * entirely platform-side: the gateway parks the invocation for user approval\n * and only forwards `capability_invoked` to the app *after* approval, so the\n * app just marks the capability and answers when asked.\n *\n * The peer reconnects and replays its registrations on a dropped socket, and on\n * a *retryable* refusal — the app boots independently of the session's agent\n * container and can win that race. See {@link ReconnectOptions}.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { Capability } from \"@skaile/workspaces/types\";\nimport { PROTOCOL_VERSION_STRING } from \"@skaile/workspaces/types\";\nimport type {\n AppHandlerContext,\n AppInvocationPrincipal,\n DefinedAppCapability,\n} from \"./define-app-capability.js\";\nimport { type AppLogger, noopLogger } from \"./logger.js\";\nimport type { AppServerMessage, AppTransport } from \"./transport.js\";\n\n/** Default timeout waiting for the gateway's `app_hello_ack`. */\nexport const DEFAULT_HELLO_TIMEOUT_MS = 10_000;\n\n/** Defaults mirroring the agent `WebSocketClientTransport`'s backoff. */\nconst DEFAULT_RECONNECT: Required<ReconnectOptions> = {\n maxAttempts: 5,\n baseMs: 1_000,\n maxMs: 30_000,\n};\n\n/**\n * Reconnect / retry policy. Set `reconnect: false` on {@link SkaileAppOptions}\n * to disable entirely (one attempt, no replay).\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport interface ReconnectOptions {\n /** Attempts before giving up. Default 5. */\n maxAttempts?: number;\n /** First backoff delay in ms; doubles each attempt. Default 1000. */\n baseMs?: number;\n /** Backoff ceiling in ms. Default 30000. */\n maxMs?: number;\n}\n\n/**\n * The gateway refused the handshake. `retryable` mirrors the wire field: `true`\n * for a transient refusal (the agent container is not up yet), `false` for a\n * permanent one (bad token, `appId` not permitted).\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport class AppHandshakeRefusedError extends Error {\n readonly retryable: boolean;\n constructor(reason: string, retryable: boolean) {\n super(`app handshake refused: ${reason}`);\n this.name = \"AppHandshakeRefusedError\";\n this.retryable = retryable;\n }\n}\n\n/**\n * Options for {@link createSkaileApp}.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface SkaileAppOptions {\n /** The embedded app's stable id. Every registered capability is bound to it. */\n appId: string;\n /**\n * The app back-connection. Inject {@link createWebSocketAppTransport} in\n * production or a fake in tests. The transport carries the injected session\n * identity as its connection credential.\n *\n * With `reconnect` enabled the transport must be able to produce a **fresh**\n * token per attempt — the gateway consumes it at `app_hello`. Pass a\n * `SessionTokenProvider` rather than a bare string.\n */\n transport: AppTransport;\n /** Protocol version the SDK announces in `app_hello`. Defaults to the SDK's. */\n protocolVersion?: string;\n /** Timeout waiting for `app_hello_ack`. Defaults to {@link DEFAULT_HELLO_TIMEOUT_MS}. */\n helloTimeoutMs?: number;\n /** Logger for the SDK and (by default) capability handlers. Defaults to {@link noopLogger}. */\n logger?: AppLogger;\n /**\n * Reconnect policy, or `false` to disable. Defaults to\n * `{maxAttempts:5, baseMs:1000, maxMs:30000}`.\n *\n * @since 3.5.0\n */\n reconnect?: ReconnectOptions | false;\n}\n\n/** Result of a successful handshake — the bound session context. */\nexport interface AppSession {\n sessionId: string;\n userId?: string;\n}\n\n/**\n * A connected (or connectable) Skaile app. Register capabilities before or\n * after {@link connect}; post-connect changes are pushed to the gateway\n * immediately.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface SkaileApp {\n /**\n * Add (or replace, by name) an app capability. Registering the same name\n * again replaces it — mirrors the registry's replace-by-name semantics. When\n * already connected, a `capability_register` is sent immediately.\n */\n registerCapability(cap: DefinedAppCapability): void;\n /**\n * Remove a capability by name. No-op when unknown. When connected, a\n * `capability_deregister` is sent immediately so the tool disappears from the\n * agent's catalog.\n */\n deregisterCapability(name: string): void;\n /** Wire-format snapshot of the currently registered capabilities (origin stamped). */\n readonly capabilities: readonly Capability[];\n /** The bound session once connected; `null` before the handshake and while reconnecting. */\n readonly session: AppSession | null;\n /**\n * Open the connection, perform the handshake, and register the current\n * capabilities. Resolves with the bound {@link AppSession}. Rejects if the\n * gateway refuses permanently, the handshake times out, or the transport\n * disconnects mid-handshake. A *retryable* refusal is retried per the\n * reconnect policy before rejecting.\n */\n connect(): Promise<AppSession>;\n /**\n * Subscribe to session binding changes. Fires on **every** bind — including\n * the initial `connect()` — with the new {@link AppSession}, and with `null`\n * whenever the connection drops. Returns an unsubscribe function.\n *\n * @since 3.5.0\n */\n onSessionChange(handler: (session: AppSession | null) => void): () => void;\n /** Disconnect and tear down. Idempotent. Cancels any pending reconnect. */\n close(): Promise<void>;\n}\n\nconst delay = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n// A fresh object per call: a handler that mutates its ctx must not change the next one's.\nconst unknownPrincipal = (): AppInvocationPrincipal => ({ kind: \"unknown\" });\n\n/**\n * The frame's principal, or `unknown` when it is absent (an older gateway) or\n * malformed. A handler may grant per-user rights on it, so a shape the SDK\n * cannot read must never pass through as if it named someone.\n */\nfunction toInvocationPrincipal(raw: unknown): AppInvocationPrincipal {\n if (typeof raw !== \"object\" || raw === null) return unknownPrincipal();\n const p = raw as Record<string, unknown>;\n const isId = (v: unknown): v is string => typeof v === \"string\" && v.length > 0;\n switch (p.kind) {\n case \"human\":\n return isId(p.userId) ? { kind: \"human\", userId: p.userId } : unknownPrincipal();\n case \"trigger\":\n return isId(p.userId) && isId(p.source)\n ? { kind: \"trigger\", source: p.source, userId: p.userId }\n : unknownPrincipal();\n case \"mixed\":\n return Array.isArray(p.userIds) && p.userIds.length > 0 && p.userIds.every(isId)\n ? { kind: \"mixed\", userIds: [...p.userIds] }\n : unknownPrincipal();\n default:\n return unknownPrincipal();\n }\n}\n\n/**\n * Create a {@link SkaileApp}. See the module doc for the peer lifecycle.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function createSkaileApp(options: SkaileAppOptions): SkaileApp {\n const {\n appId,\n transport,\n protocolVersion = PROTOCOL_VERSION_STRING,\n helloTimeoutMs = DEFAULT_HELLO_TIMEOUT_MS,\n logger = noopLogger,\n } = options;\n\n const reconnectEnabled = options.reconnect !== false;\n const policy: Required<ReconnectOptions> = {\n ...DEFAULT_RECONNECT,\n ...(options.reconnect === false ? {} : options.reconnect),\n };\n\n const registry = new Map<string, DefinedAppCapability>();\n const sessionListeners = new Set<(s: AppSession | null) => void>();\n let session: AppSession | null = null;\n let closed = false;\n let connecting = false;\n let subscribed = false;\n // Bumped by close(). A reconnect loop parked on its backoff timer captures the\n // epoch it started in and abandons if close() (or a close/connect cycle) has\n // since happened — otherwise it would resurrect a torn-down app.\n let epoch = 0;\n let unsubscribeMessage: (() => void) | null = null;\n let unsubscribeDisconnect: (() => void) | null = null;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n /** Resolver of the backoff sleep, so close() can wake a parked loop. */\n let reconnectWake: (() => void) | null = null;\n\n /** Non-null only while a handshake is in flight. */\n let pending: { settle: (err: Error | null, s?: AppSession) => void } | null = null;\n\n function toWire(cap: DefinedAppCapability): Capability {\n return {\n name: cap.name,\n displayName: cap.displayName,\n description: cap.description,\n inputSchema: cap.inputSchema,\n outputSchema: cap.outputSchema,\n side: \"app\",\n origin: { kind: \"app\", appId },\n scope: cap.scope,\n requiresApproval: cap.requiresApproval,\n effectClass: cap.effectClass,\n kind: cap.kind,\n audience: cap.audience,\n promptFragment: cap.promptFragment,\n callTimeoutMs: cap.callTimeoutMs,\n render: cap.render,\n };\n }\n\n function setSession(next: AppSession | null): void {\n session = next;\n for (const h of sessionListeners) h(next);\n }\n\n function registerCapability(cap: DefinedAppCapability): void {\n registry.set(cap.name, cap);\n if (session) {\n transport.send({ type: \"capability_register\", capabilities: [toWire(cap)] });\n logger.debug(\"registered capability (live)\", { name: cap.name });\n }\n }\n\n function deregisterCapability(name: string): void {\n if (!registry.delete(name)) return;\n if (session) {\n transport.send({ type: \"capability_deregister\", names: [name] });\n logger.debug(\"deregistered capability (live)\", { name });\n }\n }\n\n /** Dispatch an inbound invocation to the local handler; reply with the result. */\n async function handleInvocation(\n callId: string,\n name: string,\n input: unknown,\n principal: unknown,\n ): Promise<void> {\n const cap = registry.get(name);\n if (!cap) {\n transport.send({\n type: \"capability_result\",\n callId,\n result: { error: `Unknown app capability: ${name}` },\n });\n return;\n }\n try {\n const validated = cap.inputZod.parse(input);\n const ctx: AppHandlerContext = {\n sessionId: session?.sessionId ?? \"\",\n appId,\n log: logger,\n principal: toInvocationPrincipal(principal),\n };\n const result = await cap.handler(validated, ctx);\n transport.send({ type: \"capability_result\", callId, result });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.warn(\"capability handler failed\", { name, error: message });\n transport.send({ type: \"capability_result\", callId, result: { error: message } });\n }\n }\n\n function onMessage(message: AppServerMessage): void {\n switch (message.type) {\n case \"app_hello_ack\": {\n const bound: AppSession = { sessionId: message.sessionId, userId: message.userId };\n pending?.settle(null, bound);\n return;\n }\n case \"app_hello_nack\":\n pending?.settle(new AppHandshakeRefusedError(message.error, message.retryable === true));\n return;\n case \"capability_invoked\":\n void handleInvocation(message.callId, message.name, message.input, message.principal);\n return;\n }\n }\n\n function onDisconnect(): void {\n if (pending) {\n pending.settle(new Error(\"transport disconnected during handshake\"));\n return;\n }\n if (closed || !session) return;\n logger.warn(\"app connection lost\", { appId });\n setSession(null);\n if (!reconnectEnabled) return;\n // The loop holds the same guard connect() uses: a manual connect() while it\n // runs would start a second handshake, and both would overwrite `pending`.\n connecting = true;\n const loopEpoch = epoch;\n void reconnectLoop().finally(() => {\n // A close()/connect() cycle since this loop started owns the guard now.\n if (epoch === loopEpoch) connecting = false;\n });\n }\n\n function ensureSubscribed(): void {\n if (subscribed) return;\n unsubscribeMessage = transport.onMessage(onMessage);\n unsubscribeDisconnect = transport.onDisconnect(onDisconnect);\n subscribed = true;\n }\n\n /** One attempt: open the socket, exchange hello/ack, replay the registry. */\n function performHandshake(): Promise<AppSession> {\n ensureSubscribed();\n return transport.connect().then(\n () =>\n new Promise<AppSession>((resolve, reject) => {\n if (closed) {\n reject(new Error(\"app closed during handshake\"));\n return;\n }\n let settled = false;\n const timer = setTimeout(() => {\n pending?.settle(new Error(`app handshake timed out after ${helloTimeoutMs}ms`));\n }, helloTimeoutMs);\n\n pending = {\n settle: (err, bound) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n pending = null;\n if (err || !bound) {\n reject(err ?? new Error(\"app handshake failed\"));\n return;\n }\n setSession(bound);\n logger.info(\"app connection established\", { appId, sessionId: bound.sessionId });\n // Replay the full surface — on a reconnect the gateway dropped our\n // caps when the previous connection closed.\n const wire = Array.from(registry.values()).map(toWire);\n if (wire.length > 0) {\n transport.send({ type: \"capability_register\", capabilities: wire });\n }\n resolve(bound);\n },\n };\n\n transport.send({ type: \"app_hello\", appId, protocolVersion });\n }),\n (err) => Promise.reject(err instanceof Error ? err : new Error(String(err))),\n );\n }\n\n const backoffMs = (attempt: number) =>\n Math.min(policy.maxMs, policy.baseMs * 2 ** Math.max(0, attempt - 1));\n\n /** Rebind after a dropped socket. Stops on a permanent refusal or close(). */\n async function reconnectLoop(): Promise<void> {\n const myEpoch = epoch;\n for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {\n const wait = backoffMs(attempt);\n logger.debug(\"scheduling app reconnect\", { appId, attempt, waitMs: wait });\n // close() cancels the timer, so it also has to hand us the resolver —\n // otherwise this await never settles and the loop frame parks forever,\n // pinning the transport and every other captured binding.\n await new Promise<void>((r) => {\n reconnectWake = r;\n reconnectTimer = setTimeout(r, wait);\n });\n reconnectWake = null;\n if (closed || epoch !== myEpoch) {\n logger.debug(\"app reconnect cancelled\", { appId, attempt });\n return;\n }\n try {\n await performHandshake();\n logger.info(\"app connection re-established\", { appId });\n return;\n } catch (err) {\n if (err instanceof AppHandshakeRefusedError && !err.retryable) {\n logger.error(\"app reconnect refused permanently\", { appId, error: err.message });\n return;\n }\n logger.warn(\"app reconnect attempt failed\", {\n appId,\n attempt,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n logger.error(\"app reconnect gave up\", { appId, attempts: policy.maxAttempts });\n }\n\n async function connect(): Promise<AppSession> {\n if (session) return session;\n if (connecting) throw new Error(\"app connect() is already in progress\");\n connecting = true;\n // A closed app is reusable: connect() reopens it. Any reconnect loop still\n // parked from the previous life is fenced off by the epoch close() bumped.\n closed = false;\n try {\n for (let attempt = 1; ; attempt++) {\n try {\n return await performHandshake();\n } catch (err) {\n // Only a *retryable* refusal is worth another attempt on the initial\n // connect: the app raced the session's agent container on boot. Every\n // other failure (bad token, timeout, transport error) is terminal here.\n const transient = err instanceof AppHandshakeRefusedError && err.retryable;\n if (!transient || !reconnectEnabled || attempt >= policy.maxAttempts || closed) {\n throw err;\n }\n logger.debug(\"app handshake refused (retryable), retrying\", { appId, attempt });\n await delay(backoffMs(attempt));\n }\n }\n } finally {\n connecting = false;\n }\n }\n\n async function close(): Promise<void> {\n closed = true;\n epoch++;\n clearTimeout(reconnectTimer);\n // Wake a loop parked on its backoff so it observes `closed` and returns,\n // rather than awaiting a timer that will now never fire.\n reconnectWake?.();\n reconnectWake = null;\n connecting = false;\n // Abort an in-flight handshake — rejects the pending connect and clears its\n // timer so nothing leaks past close().\n pending?.settle(new Error(\"app closed during handshake\"));\n unsubscribeMessage?.();\n unsubscribeDisconnect?.();\n unsubscribeMessage = null;\n unsubscribeDisconnect = null;\n subscribed = false;\n session = null;\n await transport.disconnect();\n }\n\n return {\n registerCapability,\n deregisterCapability,\n get capabilities() {\n return Array.from(registry.values()).map(toWire);\n },\n get session() {\n return session;\n },\n connect,\n onSessionChange(handler) {\n sessionListeners.add(handler);\n return () => sessionListeners.delete(handler);\n },\n close,\n };\n}\n","/**\n * Connection-param handshake between the embedding host and the app frame.\n *\n * An embedded app needs three things to open its back-connection: the gateway\n * URL, its own `appId`, and a session identity token. The token is a **secret**,\n * so it must not ride in the iframe `src` — the preview surface that serves the\n * app is unauthenticated, and a URL lands in access logs, `Referer` headers, and\n * browser history. Instead the host posts it over `postMessage`, and the app\n * asks for it with a ready ping once its script is running.\n *\n * The host cannot simply post on iframe `load`: it has no way to know when the\n * app's script is listening. So the app speaks first.\n *\n * ```text\n * app → host { type: 'skaile_app_ready', appId } targetOrigin = parentOrigin\n * host → app { type: 'skaile_app_params', appId, url, token } targetOrigin = appOrigin\n * ```\n *\n * Both sides pin the peer: the app checks `event.origin === parentOrigin` and\n * that the message came from its own parent; the host checks the origin and that\n * `event.source` is the very iframe it rendered. The app learns the expected\n * parent origin from a **non-secret** query param on its `src`\n * ({@link PARENT_ORIGIN_QUERY_PARAM}), which is safe to leak precisely because it\n * is not a credential.\n *\n * @category App SDK\n * @since 3.5.0\n */\n\n/**\n * Query param on the app's iframe `src` carrying the embedder's origin. Not a\n * secret — it is the value the app pins its `postMessage` peer to.\n */\nexport const PARENT_ORIGIN_QUERY_PARAM = \"skaileParentOrigin\";\n\n/** Default timeout waiting for the host's params reply. */\nexport const DEFAULT_PARAMS_TIMEOUT_MS = 10_000;\n\n/** First gap between ready pings. Grows by {@link PING_BACKOFF_FACTOR} up to {@link MAX_PING_INTERVAL_MS}. */\nexport const INITIAL_PING_INTERVAL_MS = 100;\n\n/**\n * Ceiling for the ready-ping gap. The host mints a fresh single-use ticket for\n * *every* ping it answers, so pinging is not free — back off rather than hammer.\n */\nexport const MAX_PING_INTERVAL_MS = 1_000;\n\nconst PING_BACKOFF_FACTOR = 2;\n\n/** App → host. Announces the app frame is listening and wants its params. */\nexport type SkaileAppReadyMessage = {\n type: \"skaile_app_ready\";\n /** The app asking. The host answers only for an app it actually embedded. */\n appId: string;\n};\n\n/** Host → app. Carries the gateway URL and the single-use session identity token. */\nexport type SkaileAppParamsMessage = {\n type: \"skaile_app_params\";\n /** Echoed back so a frame embedding several apps can disambiguate. */\n appId: string;\n /** The gateway app-channel URL, e.g. `wss://platform/app-connect`. */\n url: string;\n /** Single-use session identity token. Consumed by the gateway at `app_hello`. */\n token: string;\n};\n\n/** Resolved params, ready to hand to `createWebSocketAppTransport` + `createSkaileApp`. */\nexport type AppConnectionParams = {\n url: string;\n appId: string;\n token: string;\n};\n\n/** The subset of `postMessage` targets this module needs. */\nexport type PostMessageTarget = {\n postMessage(message: unknown, targetOrigin: string): void;\n};\n\n/** Structural view of a `MessageEvent`, so tests need no DOM. */\nexport type MessageEventLike = {\n readonly origin: string;\n readonly source: unknown;\n readonly data: unknown;\n};\n\n/** Structural view of the `window` bits this module needs. Injectable for tests. */\nexport type AppWindowLike = {\n readonly parent: PostMessageTarget | null;\n readonly location: { readonly search: string };\n addEventListener(type: \"message\", handler: (event: MessageEventLike) => void): void;\n removeEventListener(type: \"message\", handler: (event: MessageEventLike) => void): void;\n};\n\n/**\n * Options for {@link resolveAppConnectionParams}.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport interface ResolveAppConnectionParamsOptions {\n /** The embedded app's stable id. Must match the params the host replies with. */\n appId: string;\n /**\n * Expected origin of the embedding host. Defaults to the\n * {@link PARENT_ORIGIN_QUERY_PARAM} query param on the current URL. Never `\"*\"`\n * or `\"null\"` — an unpinned peer defeats the point of not putting the token in\n * the URL.\n */\n parentOrigin?: string;\n /** How long to wait for the host's reply. Defaults to {@link DEFAULT_PARAMS_TIMEOUT_MS}. */\n timeoutMs?: number;\n /** Window to listen and post on. Defaults to the global. Injectable for tests. */\n windowRef?: AppWindowLike;\n}\n\nfunction readParentOriginFromQuery(search: string): string | null {\n const value = new URLSearchParams(search).get(PARENT_ORIGIN_QUERY_PARAM);\n return value && value.length > 0 ? value : null;\n}\n\nfunction isParamsMessage(data: unknown, appId: string): data is SkaileAppParamsMessage {\n if (!data || typeof data !== \"object\") return false;\n const m = data as Partial<SkaileAppParamsMessage>;\n // Trim before the emptiness check: a whitespace-only token would survive to\n // the gateway and be refused there. Failing here is the clearer error.\n return (\n m.type === \"skaile_app_params\" &&\n m.appId === appId &&\n typeof m.url === \"string\" &&\n m.url.trim().length > 0 &&\n typeof m.token === \"string\" &&\n m.token.trim().length > 0\n );\n}\n\n/**\n * Ask the embedding host for this app's connection params.\n *\n * Posts a {@link SkaileAppReadyMessage} to the parent and resolves with the\n * matching {@link SkaileAppParamsMessage}. Messages from any other origin, from\n * any window other than the parent, or for another `appId` are ignored — not\n * rejected — because a page may legitimately receive unrelated `postMessage`\n * traffic.\n *\n * Call it again to re-request a fresh token: the token is single-use, so a\n * reconnecting transport needs a new one (see `createSkaileApp`'s `reconnect`).\n *\n * @throws when not embedded, when the parent origin cannot be determined or is\n * unpinnable, or when the host does not reply within `timeoutMs`.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport function resolveAppConnectionParams(\n options: ResolveAppConnectionParamsOptions,\n): Promise<AppConnectionParams> {\n const { appId, timeoutMs = DEFAULT_PARAMS_TIMEOUT_MS } = options;\n const win =\n options.windowRef ?? (globalThis as unknown as { window?: AppWindowLike }).window ?? null;\n if (!win) {\n return Promise.reject(\n new Error(\"resolveAppConnectionParams requires a window (pass `windowRef` off-browser)\"),\n );\n }\n\n const parent = win.parent;\n // A top-level page's `parent` is itself. An app that is not embedded has no\n // host to ask, and posting to itself would hang until the timeout.\n if (!parent || (parent as unknown) === (win as unknown)) {\n return Promise.reject(\n new Error(\"app is not embedded — no parent frame to request params from\"),\n );\n }\n\n const parentOrigin = options.parentOrigin ?? readParentOriginFromQuery(win.location.search);\n if (!parentOrigin) {\n return Promise.reject(\n new Error(\n `cannot determine the host origin: pass \\`parentOrigin\\` or set the \\`${PARENT_ORIGIN_QUERY_PARAM}\\` query param`,\n ),\n );\n }\n // `*` would broadcast the ready ping to whatever is above us, and `null` (an\n // opaque, sandboxed origin) cannot be pinned at all. Both make the peer check\n // meaningless, and the token would then be posted to an unverified window.\n if (parentOrigin === \"*\" || parentOrigin === \"null\") {\n return Promise.reject(new Error(`refusing to use an unpinnable host origin: ${parentOrigin}`));\n }\n\n // Bind the narrowed peer once: the guards above proved both are non-null, but\n // that narrowing does not survive into the hoisted `ping` declaration below.\n const peer: PostMessageTarget = parent;\n const peerOrigin: string = parentOrigin;\n\n return new Promise<AppConnectionParams>((resolve, reject) => {\n let settled = false;\n let pingTimer: ReturnType<typeof setTimeout> | undefined;\n let pingInterval = INITIAL_PING_INTERVAL_MS;\n\n const cleanup = () => {\n clearTimeout(timer);\n clearTimeout(pingTimer);\n win.removeEventListener(\"message\", onMessage);\n };\n\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(\n new Error(`timed out after ${timeoutMs}ms waiting for connection params from the host`),\n );\n }, timeoutMs);\n\n function onMessage(event: MessageEventLike): void {\n if (settled) return;\n // Pin the peer on both axes. Origin alone is not enough: any frame served\n // from the host's origin could otherwise answer.\n if (event.origin !== peerOrigin || event.source !== peer) return;\n if (!isParamsMessage(event.data, appId)) return;\n settled = true;\n cleanup();\n resolve({ url: event.data.url, appId, token: event.data.token });\n }\n\n win.addEventListener(\"message\", onMessage);\n\n const ready: SkaileAppReadyMessage = { type: \"skaile_app_ready\", appId };\n\n /**\n * Keep asking until the host answers. The host only mints in response to a\n * ping and never speaks unprompted, so a single ping is a race the app loses\n * whenever it boots before the host's own state has settled — the ping is\n * dropped and nothing re-asks. Backs off because each answered ping costs the\n * host a single-use ticket.\n */\n function ping(): void {\n if (settled) return;\n try {\n peer.postMessage(ready, peerOrigin);\n } catch (err) {\n settled = true;\n cleanup();\n reject(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n pingTimer = setTimeout(ping, pingInterval);\n pingInterval = Math.min(pingInterval * PING_BACKOFF_FACTOR, MAX_PING_INTERVAL_MS);\n }\n\n ping();\n });\n}\n","/**\n * `defineAppCapability` — author-friendly factory for an embedded app's\n * agent-controllable capabilities.\n *\n * It mirrors the runner's `defineCapability` (`@skaile/workspaces/runner`) but\n * is scoped to the app side: the handler always runs *in the app*, so `side` is\n * fixed to `'app'` and the author never passes it. The originating `appId` is\n * known to the SDK, not the individual capability, so `origin` is stamped by\n * {@link createSkaileApp} at registration time — not here.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type {\n CapabilityAudience,\n CapabilityEffectClass,\n CapabilityOrigin,\n RenderSpec,\n} from \"@skaile/workspaces/types\";\nimport * as z from \"zod\";\nimport type { AppLogger } from \"./logger.js\";\n\n/**\n * Who set off the turn an invocation belongs to, as the gateway reports it on\n * `capability_invoked`. `trigger` is a turn no person typed (a schedule, a\n * webhook, a peer agent, a flow, ...) credited to `userId`; `source` names the\n * trigger and is a plain string so a new platform source does not break the\n * app's types. `mixed` names several people and `unknown` names nobody: an app\n * that acts for one person should refuse both.\n *\n * @category App SDK\n * @since 6.13.0\n */\nexport type AppInvocationPrincipal =\n | { kind: \"human\"; userId: string }\n | { kind: \"trigger\"; source: string; userId: string }\n | { kind: \"mixed\"; userIds: string[] }\n | { kind: \"unknown\" };\n\n/**\n * Per-invocation context passed to an app capability handler. Analogous to the\n * runner's `HandlerContext`, but carries the `appId` and the SDK's\n * {@link AppLogger} (there is no server-side `capability:<origin>:<name>` log\n * slice inside the app process).\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type AppHandlerContext = {\n /** The session this app connection is bound to (from the hello ack). */\n sessionId: string;\n /** The embedded app's id. */\n appId: string;\n /** Logger to emit through; defaults to the SDK's configured logger. */\n log: AppLogger;\n /**\n * Who set off the turn that invoked this call. `{ kind: 'unknown' }` when the\n * gateway sent none (a gateway older than the field) or sent a shape the SDK\n * does not recognise.\n *\n * @since 6.13.0\n */\n principal: AppInvocationPrincipal;\n};\n\n/**\n * Author-facing arguments to {@link defineAppCapability}. Mirrors the wire\n * {@link import(\"@skaile/workspaces/types\").Capability} fields an app may set,\n * minus `side` / `origin` (fixed / stamped by the SDK) and the runtime-only\n * fields that only make sense platform-side.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type DefineAppCapabilityArgs<\n T extends z.ZodTypeAny,\n R extends z.ZodTypeAny | undefined = undefined,\n> = {\n /**\n * Stable, session-unique name. Convention: `app.<sector>.<verb>` — e.g.\n * `app.ui.switch_contract` (UI sector) or `app.backend.insert_contract`\n * (backend sector). v1 distinguishes sectors by this naming convention.\n */\n name: string;\n /** Human-readable label for user-facing surfaces; falls back to `name`. */\n displayName?: string;\n /** Human/LLM-readable summary; shown to the agent at tool registration. */\n description: string;\n /** Zod schema validating the invocation arguments. Converted to JSON Schema. */\n input: T;\n /** Optional Zod schema for the return value. */\n output?: R;\n /** Lifetime: `session` survives the whole session, `turn` is dropped at end-of-turn. */\n scope?: \"session\" | \"turn\";\n /**\n * When true, the platform shows a confirm UI and waits for user approval\n * before the invocation is forwarded to the app. Mark every mutation\n * (backend writes, destructive UI actions) with this.\n */\n requiresApproval?: boolean;\n /**\n * How sensitive the action is on the approval card. Absent reads as\n * `routine`. A stricter class (`external`, `privileged`, `never`) needs\n * `requiresApproval: true`: {@link defineAppCapability} throws otherwise,\n * because the platform would refuse the registration.\n *\n * @since 6.13.0\n */\n effectClass?: CapabilityEffectClass;\n /** Documentation hint: side-effect, render, or pure query. */\n kind?: \"effect\" | \"render\" | \"query\";\n /**\n * Audience scoping. Omit for the default (`['llm','user']`). App capabilities\n * default to LLM-only in the command palette (`isUserInvokable`); set\n * `['llm','user']` on the user-facing actions you want to appear in cmdK.\n */\n audience?: CapabilityAudience[];\n /** Appended to the system prompt's `<CAPABILITIES>` section. */\n promptFragment?: string;\n /** Per-capability override for the platform's result round-trip timeout. */\n callTimeoutMs?: number;\n /** Render contract for capabilities that produce a UI surface. */\n render?: RenderSpec;\n /** The handler — runs in the app. Receives validated input + {@link AppHandlerContext}. */\n handler: (\n args: z.infer<T>,\n ctx: AppHandlerContext,\n ) => Promise<R extends z.ZodTypeAny ? z.infer<R> : unknown>;\n};\n\n/**\n * A defined app capability: the wire-format fields the SDK registers (minus the\n * SDK-stamped `origin`), plus the Zod schemas and type-erased handler used for\n * runtime input validation and inbound dispatch.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type DefinedAppCapability = {\n name: string;\n displayName?: string;\n description: string;\n inputSchema: Record<string, unknown>;\n outputSchema?: Record<string, unknown>;\n /** Always `'app'` — the handler runs in the embedded app. */\n side: \"app\";\n scope?: \"session\" | \"turn\";\n requiresApproval?: boolean;\n effectClass?: CapabilityEffectClass;\n kind?: \"effect\" | \"render\" | \"query\";\n audience?: CapabilityAudience[];\n promptFragment?: string;\n callTimeoutMs?: number;\n render?: RenderSpec;\n /** Original Zod schema for runtime input validation. Not serialized to the wire. */\n readonly inputZod: z.ZodTypeAny;\n /** Original Zod schema for the optional output, if declared. Not serialized. */\n readonly outputZod?: z.ZodTypeAny;\n /** Type-erased handler dispatched on an inbound `capability_invoked`. */\n handler: (args: unknown, ctx: AppHandlerContext) => Promise<unknown>;\n};\n\n/** Origin kind an app capability always carries once stamped by the SDK. */\nexport type AppCapabilityOrigin = Extract<CapabilityOrigin, { kind: \"app\" }>;\n\n/**\n * Build a {@link DefinedAppCapability} from author-friendly Zod-typed args.\n * `side` is fixed to `'app'`; `origin` is added by the SDK at registration\n * time (it needs the SDK's `appId`).\n *\n * Throws when `effectClass` is not one of the four classes, or is stricter\n * than `routine` on a capability without `requiresApproval: true`: the gateway\n * refuses that registration, so failing here surfaces it at startup instead of\n * as a missing tool.\n *\n * @example\n * ```ts\n * const switchContract = defineAppCapability({\n * name: 'app.ui.switch_contract',\n * description: 'Switch the visible contract in the app UI.',\n * input: z.object({ contractId: z.string() }),\n * handler: async ({ contractId }, ctx) => {\n * ctx.log.info('switching contract', { contractId });\n * ui.select(contractId);\n * },\n * });\n * ```\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function defineAppCapability<\n T extends z.ZodTypeAny,\n R extends z.ZodTypeAny | undefined = undefined,\n>(args: DefineAppCapabilityArgs<T, R>): DefinedAppCapability {\n assertEffectClassDeclarable(args.name, args.effectClass, args.requiresApproval);\n const inputSchema = z.toJSONSchema(args.input, {\n target: \"draft-07\",\n io: \"input\",\n }) as Record<string, unknown>;\n const outputSchema = args.output\n ? (z.toJSONSchema(args.output, {\n target: \"draft-07\",\n io: \"output\",\n }) as Record<string, unknown>)\n : undefined;\n\n return {\n name: args.name,\n displayName: args.displayName,\n description: args.description,\n inputSchema,\n outputSchema,\n side: \"app\",\n scope: args.scope,\n requiresApproval: args.requiresApproval,\n effectClass: args.effectClass,\n kind: args.kind,\n audience: args.audience,\n promptFragment: args.promptFragment,\n callTimeoutMs: args.callTimeoutMs,\n render: args.render,\n inputZod: args.input,\n outputZod: args.output,\n handler: args.handler as (args: unknown, ctx: AppHandlerContext) => Promise<unknown>,\n };\n}\n\nconst EFFECT_CLASSES: ReadonlySet<string> = new Set<CapabilityEffectClass>([\n \"routine\",\n \"external\",\n \"privileged\",\n \"never\",\n]);\n\n/** Mirrors the gateway's registration rule, so a refused declaration fails at define time. */\nfunction assertEffectClassDeclarable(\n name: string,\n effectClass: unknown,\n requiresApproval: boolean | undefined,\n): void {\n if (effectClass === undefined || effectClass === \"routine\") return;\n if (typeof effectClass !== \"string\" || !EFFECT_CLASSES.has(effectClass)) {\n throw new Error(\n `defineAppCapability(${name}): effectClass must be one of routine, external, privileged, never`,\n );\n }\n if (requiresApproval !== true) {\n throw new Error(\n `defineAppCapability(${name}): effectClass '${effectClass}' requires requiresApproval: true`,\n );\n }\n}\n","/**\n * WebSocket-backed {@link AppTransport}.\n *\n * Browser-safe: uses the standard `WebSocket` global (no `ws` dependency, no\n * `node:*` imports), so it runs unchanged in an embedded app's iframe and in a\n * lightweight Node 22+ service. The injected session identity is presented as a\n * bearer **subprotocol** — the same `skaile-bearer.<base64url>` scheme the\n * platform gateway already decodes for runner connections — so the gateway\n * verifies identity at the WS upgrade, before any `app_hello` is processed.\n *\n * Frames are bare JSON of the app-channel messages (no `MessageV2` envelope),\n * matching the runner peer's on-the-wire convention.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { AppClientMessage, AppServerMessage, AppTransport } from \"./transport.js\";\n\n/** Matches `BEARER_SUBPROTOCOL_PREFIX` in `@skaile/workspaces/transport`'s ws/auth. */\nconst BEARER_SUBPROTOCOL_PREFIX = \"skaile-bearer.\";\n\n/** Encode a bearer token as a WS subprotocol token (base64url, no padding). */\nfunction encodeBearerSubprotocol(token: string): string {\n // Encode via UTF-8 bytes so a non-ASCII token doesn't make `btoa` throw\n // (btoa is Latin-1 only). The gateway decodes the mirror of this.\n const binary = String.fromCharCode(...new TextEncoder().encode(token));\n const b64 = btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n return BEARER_SUBPROTOCOL_PREFIX + b64;\n}\n\n/** Minimal structural view of the parts of the `WebSocket` API the transport uses. */\ntype WebSocketLike = {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n addEventListener(type: \"open\", handler: () => void): void;\n addEventListener(type: \"close\", handler: () => void): void;\n addEventListener(type: \"error\", handler: () => void): void;\n addEventListener(type: \"message\", handler: (ev: { data: unknown }) => void): void;\n};\n\n/** Constructor shape for a `WebSocket` implementation (global or injected). */\nexport type WebSocketFactory = new (url: string, protocols?: string | string[]) => WebSocketLike;\n\n/**\n * Supplies a session identity token for one connection attempt.\n *\n * The gateway's token is **single-use** — it is consumed at `app_hello` — so a\n * reconnecting transport cannot replay the one it opened with. Pass a provider\n * (typically a call to `resolveAppConnectionParams`) and every `connect()` mints\n * a fresh token.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport type SessionTokenProvider = () => string | Promise<string>;\n\n/**\n * Options for {@link createWebSocketAppTransport}.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface WebSocketAppTransportOptions {\n /** The gateway app-channel URL (e.g. `wss://platform/app-connect`). */\n url: string;\n /**\n * The injected session identity token, or a {@link SessionTokenProvider} that\n * yields a fresh one per connection attempt. Presented as the WS bearer\n * subprotocol; the gateway verifies it and resolves the session/user.\n *\n * A bare string cannot survive a reconnect (the gateway consumes the token at\n * `app_hello`) — pass a provider whenever `reconnect` is enabled on the peer.\n */\n sessionToken: string | SessionTokenProvider;\n /**\n * WebSocket implementation. Defaults to the `WebSocket` global (browser /\n * Node 22+). Injectable for tests or non-standard runtimes.\n */\n WebSocketImpl?: WebSocketFactory;\n /** Connect timeout in ms. Default 10000. */\n connectTimeoutMs?: number;\n}\n\nconst OPEN = 1;\n\n/**\n * Create a WebSocket-backed {@link AppTransport}. The session token is sent as a\n * bearer subprotocol at connect time; app-channel messages are JSON-framed.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function createWebSocketAppTransport(options: WebSocketAppTransportOptions): AppTransport {\n const { url, sessionToken, connectTimeoutMs = 10_000 } = options;\n const resolved =\n options.WebSocketImpl ?? (globalThis as { WebSocket?: WebSocketFactory }).WebSocket;\n if (!resolved) {\n throw new Error(\n \"No WebSocket implementation available. Pass `WebSocketImpl` in a runtime without a global WebSocket.\",\n );\n }\n const WebSocketImpl: WebSocketFactory = resolved;\n\n let ws: WebSocketLike | null = null;\n const messageHandlers = new Set<(m: AppServerMessage) => void>();\n const disconnectHandlers = new Set<() => void>();\n\n function connect(): Promise<void> {\n // A provider is resolved per attempt — the gateway consumes the token at\n // `app_hello`, so a reconnect must present a new one. A bare string opens the\n // socket synchronously, so `connect()` still constructs it before it yields.\n if (typeof sessionToken === \"function\") {\n return Promise.resolve(sessionToken()).then(openSocket);\n }\n return openSocket(sessionToken);\n }\n\n function openSocket(token: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let opened = false;\n const socket = new WebSocketImpl(url, [encodeBearerSubprotocol(token)]);\n ws = socket;\n\n const timer = setTimeout(() => {\n if (opened) return;\n socket.close(4008, \"connect timeout\");\n reject(new Error(`app transport connect timed out after ${connectTimeoutMs}ms`));\n }, connectTimeoutMs);\n\n socket.addEventListener(\"open\", () => {\n opened = true;\n clearTimeout(timer);\n resolve();\n });\n socket.addEventListener(\"error\", () => {\n if (!opened) {\n clearTimeout(timer);\n reject(new Error(\"app transport connection error\"));\n }\n });\n socket.addEventListener(\"close\", () => {\n clearTimeout(timer);\n if (ws === socket) ws = null;\n for (const h of disconnectHandlers) h();\n });\n socket.addEventListener(\"message\", (ev) => {\n let parsed: AppServerMessage;\n try {\n parsed = JSON.parse(String(ev.data)) as AppServerMessage;\n } catch {\n return;\n }\n for (const h of messageHandlers) h(parsed);\n });\n });\n }\n\n return {\n connect,\n async disconnect() {\n const socket = ws;\n ws = null;\n socket?.close(1000, \"client closed\");\n },\n send(message: AppClientMessage) {\n if (!ws || ws.readyState !== OPEN) {\n throw new Error(\"app transport is not connected\");\n }\n ws.send(JSON.stringify(message));\n },\n onMessage(handler) {\n messageHandlers.add(handler);\n return () => messageHandlers.delete(handler);\n },\n onDisconnect(handler) {\n disconnectHandlers.add(handler);\n return () => disconnectHandlers.delete(handler);\n },\n get connected() {\n return ws?.readyState === OPEN;\n },\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,KAAK,EAGV,oBAAoB,EACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAErE,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAS/C;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAChB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO;CAK/C;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,SAAS,EAAE,YAAY,CAAC;IACxB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAC;CACtC;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,kBAAkB,CAAC,GAAG,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACpD;;;;OAIG;IACH,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,sFAAsF;IACtF,QAAQ,CAAC,YAAY,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7C,4FAA4F;IAC5F,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC;;;;;;OAMG;IACH,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/B;;;;;;OAMG;IACH,eAAe,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC3E,2EAA2E;IAC3E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAgCD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CA0SpE"}
|
|
@@ -11,9 +11,33 @@
|
|
|
11
11
|
* @category App SDK
|
|
12
12
|
* @since 3.4.0
|
|
13
13
|
*/
|
|
14
|
-
import type { CapabilityAudience, CapabilityOrigin, RenderSpec } from "@skaile/workspaces/types";
|
|
14
|
+
import type { CapabilityAudience, CapabilityEffectClass, CapabilityOrigin, RenderSpec } from "@skaile/workspaces/types";
|
|
15
15
|
import * as z from "zod";
|
|
16
16
|
import type { AppLogger } from "./logger.js";
|
|
17
|
+
/**
|
|
18
|
+
* Who set off the turn an invocation belongs to, as the gateway reports it on
|
|
19
|
+
* `capability_invoked`. `trigger` is a turn no person typed (a schedule, a
|
|
20
|
+
* webhook, a peer agent, a flow, ...) credited to `userId`; `source` names the
|
|
21
|
+
* trigger and is a plain string so a new platform source does not break the
|
|
22
|
+
* app's types. `mixed` names several people and `unknown` names nobody: an app
|
|
23
|
+
* that acts for one person should refuse both.
|
|
24
|
+
*
|
|
25
|
+
* @category App SDK
|
|
26
|
+
* @since 6.13.0
|
|
27
|
+
*/
|
|
28
|
+
export type AppInvocationPrincipal = {
|
|
29
|
+
kind: "human";
|
|
30
|
+
userId: string;
|
|
31
|
+
} | {
|
|
32
|
+
kind: "trigger";
|
|
33
|
+
source: string;
|
|
34
|
+
userId: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "mixed";
|
|
37
|
+
userIds: string[];
|
|
38
|
+
} | {
|
|
39
|
+
kind: "unknown";
|
|
40
|
+
};
|
|
17
41
|
/**
|
|
18
42
|
* Per-invocation context passed to an app capability handler. Analogous to the
|
|
19
43
|
* runner's `HandlerContext`, but carries the `appId` and the SDK's
|
|
@@ -30,6 +54,14 @@ export type AppHandlerContext = {
|
|
|
30
54
|
appId: string;
|
|
31
55
|
/** Logger to emit through; defaults to the SDK's configured logger. */
|
|
32
56
|
log: AppLogger;
|
|
57
|
+
/**
|
|
58
|
+
* Who set off the turn that invoked this call. `{ kind: 'unknown' }` when the
|
|
59
|
+
* gateway sent none (a gateway older than the field) or sent a shape the SDK
|
|
60
|
+
* does not recognise.
|
|
61
|
+
*
|
|
62
|
+
* @since 6.13.0
|
|
63
|
+
*/
|
|
64
|
+
principal: AppInvocationPrincipal;
|
|
33
65
|
};
|
|
34
66
|
/**
|
|
35
67
|
* Author-facing arguments to {@link defineAppCapability}. Mirrors the wire
|
|
@@ -63,6 +95,15 @@ export type DefineAppCapabilityArgs<T extends z.ZodTypeAny, R extends z.ZodTypeA
|
|
|
63
95
|
* (backend writes, destructive UI actions) with this.
|
|
64
96
|
*/
|
|
65
97
|
requiresApproval?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* How sensitive the action is on the approval card. Absent reads as
|
|
100
|
+
* `routine`. A stricter class (`external`, `privileged`, `never`) needs
|
|
101
|
+
* `requiresApproval: true`: {@link defineAppCapability} throws otherwise,
|
|
102
|
+
* because the platform would refuse the registration.
|
|
103
|
+
*
|
|
104
|
+
* @since 6.13.0
|
|
105
|
+
*/
|
|
106
|
+
effectClass?: CapabilityEffectClass;
|
|
66
107
|
/** Documentation hint: side-effect, render, or pure query. */
|
|
67
108
|
kind?: "effect" | "render" | "query";
|
|
68
109
|
/**
|
|
@@ -98,6 +139,7 @@ export type DefinedAppCapability = {
|
|
|
98
139
|
side: "app";
|
|
99
140
|
scope?: "session" | "turn";
|
|
100
141
|
requiresApproval?: boolean;
|
|
142
|
+
effectClass?: CapabilityEffectClass;
|
|
101
143
|
kind?: "effect" | "render" | "query";
|
|
102
144
|
audience?: CapabilityAudience[];
|
|
103
145
|
promptFragment?: string;
|
|
@@ -119,6 +161,11 @@ export type AppCapabilityOrigin = Extract<CapabilityOrigin, {
|
|
|
119
161
|
* `side` is fixed to `'app'`; `origin` is added by the SDK at registration
|
|
120
162
|
* time (it needs the SDK's `appId`).
|
|
121
163
|
*
|
|
164
|
+
* Throws when `effectClass` is not one of the four classes, or is stricter
|
|
165
|
+
* than `routine` on a capability without `requiresApproval: true`: the gateway
|
|
166
|
+
* refuses that registration, so failing here surfaces it at startup instead of
|
|
167
|
+
* as a missing tool.
|
|
168
|
+
*
|
|
122
169
|
* @example
|
|
123
170
|
* ```ts
|
|
124
171
|
* const switchContract = defineAppCapability({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-app-capability.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/define-app-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"define-app-capability.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/define-app-capability.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export { AppHandshakeRefusedError, type AppSession, createSkaileApp, DEFAULT_HELLO_TIMEOUT_MS, type ReconnectOptions, type SkaileApp, type SkaileAppOptions, } from "./app.js";
|
|
12
12
|
export { type AppConnectionParams, type AppWindowLike, DEFAULT_PARAMS_TIMEOUT_MS, type MessageEventLike, PARENT_ORIGIN_QUERY_PARAM, type PostMessageTarget, resolveAppConnectionParams, type ResolveAppConnectionParamsOptions, type SkaileAppParamsMessage, type SkaileAppReadyMessage, } from "./connection-params.js";
|
|
13
|
-
export { type AppCapabilityOrigin, type AppHandlerContext, defineAppCapability, type DefineAppCapabilityArgs, type DefinedAppCapability, } from "./define-app-capability.js";
|
|
13
|
+
export { type AppCapabilityOrigin, type AppHandlerContext, type AppInvocationPrincipal, defineAppCapability, type DefineAppCapabilityArgs, type DefinedAppCapability, } from "./define-app-capability.js";
|
|
14
14
|
export { type AppLogger, consoleLogger, noopLogger } from "./logger.js";
|
|
15
|
-
export type { AppClientMessage, AppHelloAckMessage, AppHelloMessage, AppHelloNackMessage, AppServerMessage, AppTransport, } from "./transport.js";
|
|
15
|
+
export type { AppCapabilityInvokedMessage, AppClientMessage, AppHelloAckMessage, AppHelloMessage, AppHelloNackMessage, AppServerMessage, AppTransport, } from "./transport.js";
|
|
16
16
|
export { createWebSocketAppTransport, type SessionTokenProvider, type WebSocketAppTransportOptions, type WebSocketFactory, } from "./ws-transport.js";
|
|
17
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,wBAAwB,EACxB,KAAK,UAAU,EACf,eAAe,EACf,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,yBAAyB,EACzB,KAAK,gBAAgB,EACrB,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,0BAA0B,EAC1B,KAAK,iCAAiC,EACtC,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,mBAAmB,EACnB,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,KAAK,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxE,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,2BAA2B,EAC3B,KAAK,oBAAoB,EACzB,KAAK,4BAA4B,EACjC,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,wBAAwB,EACxB,KAAK,UAAU,EACf,eAAe,EACf,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,yBAAyB,EACzB,KAAK,gBAAgB,EACrB,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,0BAA0B,EAC1B,KAAK,iCAAiC,EACtC,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,mBAAmB,EACnB,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,KAAK,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxE,YAAY,EACV,2BAA2B,EAC3B,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,2BAA2B,EAC3B,KAAK,oBAAoB,EACzB,KAAK,4BAA4B,EACjC,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC"}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* @category App SDK
|
|
18
18
|
* @since 3.4.0
|
|
19
19
|
*/
|
|
20
|
+
import type { AppInvocationPrincipal } from "./define-app-capability.js";
|
|
20
21
|
import type { CapabilityDeregisterCommand, CapabilityInvokedEvent, CapabilityRegisterCommand, CapabilityResultCommand } from "@skaile/workspaces/types";
|
|
21
22
|
/**
|
|
22
23
|
* First message an app sends after the socket opens. Declares which app this
|
|
@@ -61,12 +62,23 @@ export type AppHelloNackMessage = {
|
|
|
61
62
|
* only channel-specific addition.
|
|
62
63
|
*/
|
|
63
64
|
export type AppClientMessage = AppHelloMessage | CapabilityRegisterCommand | CapabilityDeregisterCommand | CapabilityResultCommand;
|
|
65
|
+
/**
|
|
66
|
+
* `capability_invoked` as the gateway sends it on the app channel: the agent
|
|
67
|
+
* protocol event plus the turn's {@link AppInvocationPrincipal}. `principal` is
|
|
68
|
+
* optional because an older gateway does not send it.
|
|
69
|
+
*
|
|
70
|
+
* @category App SDK
|
|
71
|
+
* @since 6.13.0
|
|
72
|
+
*/
|
|
73
|
+
export type AppCapabilityInvokedMessage = CapabilityInvokedEvent & {
|
|
74
|
+
principal?: AppInvocationPrincipal;
|
|
75
|
+
};
|
|
64
76
|
/**
|
|
65
77
|
* Messages the gateway sends to an app: the handshake outcome, then inbound
|
|
66
78
|
* `capability_invoked` events routed from the agent for this app's `side:'app'`
|
|
67
79
|
* capabilities.
|
|
68
80
|
*/
|
|
69
|
-
export type AppServerMessage = AppHelloAckMessage | AppHelloNackMessage |
|
|
81
|
+
export type AppServerMessage = AppHelloAckMessage | AppHelloNackMessage | AppCapabilityInvokedMessage;
|
|
70
82
|
/**
|
|
71
83
|
* The app back-connection transport. Mirrors the shape of the agent
|
|
72
84
|
* `ClientTransport` but is typed to the app channel's message unions. A
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EACV,2BAA2B,EAC3B,sBAAsB,EACtB,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,0BAA0B,CAAC;AAElC;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,eAAe,CAAC;IACtB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,oFAAoF;AACpF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,gBAAgB,CAAC;IACvB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB,eAAe,GACf,yBAAyB,GACzB,2BAA2B,GAC3B,uBAAuB,CAAC;AAE5B;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EACV,2BAA2B,EAC3B,sBAAsB,EACtB,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,0BAA0B,CAAC;AAElC;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,eAAe,CAAC;IACtB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,oFAAoF;AACpF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,gBAAgB,CAAC;IACvB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB,eAAe,GACf,yBAAyB,GACzB,2BAA2B,GAC3B,uBAAuB,CAAC;AAE5B;;;;;;;GAOG;AACH,MAAM,MAAM,2BAA2B,GAAG,sBAAsB,GAAG;IACjE,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB,kBAAkB,GAClB,mBAAmB,GACnB,2BAA2B,CAAC;AAEhC;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,8DAA8D;IAC9D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,qCAAqC;IACrC,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtC,+EAA+E;IAC/E,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACpE,iEAAiE;IACjE,YAAY,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC9C,oDAAoD;IACpD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B"}
|