@immediately-run/sdk 0.45.3 → 0.47.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/dist/MDXProvider.cjs +1 -5
- package/dist/MDXProvider.cjs.map +1 -1
- package/dist/MDXProvider.js +1 -5
- package/dist/MDXProvider.js.map +1 -1
- package/dist/RoutingSpec.cjs.map +1 -1
- package/dist/TinkerableContext.cjs.map +1 -1
- package/dist/TinkerableContext.js.map +1 -1
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.js.map +1 -1
- package/dist/boot.cjs +2 -9
- package/dist/boot.cjs.map +1 -1
- package/dist/boot.d.cts +2 -2
- package/dist/boot.d.ts +2 -2
- package/dist/boot.js +2 -9
- package/dist/boot.js.map +1 -1
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.js.map +1 -1
- package/dist/components/Admonition.cjs +4 -13
- package/dist/components/Admonition.cjs.map +1 -1
- package/dist/components/Admonition.js +4 -13
- package/dist/components/Admonition.js.map +1 -1
- package/dist/components/FileRouter.cjs +11 -3
- package/dist/components/FileRouter.cjs.map +1 -1
- package/dist/components/FileRouter.js +11 -3
- package/dist/components/FileRouter.js.map +1 -1
- package/dist/components/HeadingAnchor.cjs.map +1 -1
- package/dist/components/HeadingAnchor.js.map +1 -1
- package/dist/components/Link.cjs.map +1 -1
- package/dist/components/Link.js.map +1 -1
- package/dist/components/MainContent.cjs +11 -1
- package/dist/components/MainContent.cjs.map +1 -1
- package/dist/components/MainContent.js +11 -1
- package/dist/components/MainContent.js.map +1 -1
- package/dist/components/MountImage.cjs +1 -9
- package/dist/components/MountImage.cjs.map +1 -1
- package/dist/components/MountImage.js +1 -9
- package/dist/components/MountImage.js.map +1 -1
- package/dist/components/Routes.cjs +1 -4
- package/dist/components/Routes.cjs.map +1 -1
- package/dist/components/Routes.d.cts +1 -1
- package/dist/components/Routes.d.ts +1 -1
- package/dist/components/Routes.js +1 -4
- package/dist/components/Routes.js.map +1 -1
- package/dist/components/SafeInclude.cjs +1 -4
- package/dist/components/SafeInclude.cjs.map +1 -1
- package/dist/components/SafeInclude.js +1 -4
- package/dist/components/SafeInclude.js.map +1 -1
- package/dist/components/WikiLink.cjs +1 -10
- package/dist/components/WikiLink.cjs.map +1 -1
- package/dist/components/WikiLink.js +1 -10
- package/dist/components/WikiLink.js.map +1 -1
- package/dist/components/defaults.cjs.map +1 -1
- package/dist/components/defaults.d.cts +1 -1
- package/dist/components/defaults.d.ts +1 -1
- package/dist/components/defaults.js.map +1 -1
- package/dist/components/errors.cjs +3 -1
- package/dist/components/errors.cjs.map +1 -1
- package/dist/components/errors.js +3 -1
- package/dist/components/errors.js.map +1 -1
- package/dist/contextUtils.cjs.map +1 -1
- package/dist/contextUtils.js.map +1 -1
- package/dist/contribute.cjs.map +1 -1
- package/dist/contribute.js.map +1 -1
- package/dist/debug.cjs +2 -1
- package/dist/debug.cjs.map +1 -1
- package/dist/debug.js +3 -8
- package/dist/debug.js.map +1 -1
- package/dist/diagnostics.cjs.map +1 -1
- package/dist/diagnostics.js.map +1 -1
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js.map +1 -1
- package/dist/editorContext.cjs.map +1 -1
- package/dist/editorContext.js.map +1 -1
- package/dist/fs.cjs.map +1 -1
- package/dist/fs.js.map +1 -1
- package/dist/hostAttention.cjs +57 -0
- package/dist/hostAttention.cjs.map +1 -0
- package/dist/hostAttention.d.cts +39 -0
- package/dist/hostAttention.d.ts +39 -0
- package/dist/hostAttention.js +31 -0
- package/dist/hostAttention.js.map +1 -0
- package/dist/hostTransport.cjs +55 -0
- package/dist/hostTransport.cjs.map +1 -0
- package/dist/hostTransport.d.cts +12 -0
- package/dist/hostTransport.d.ts +12 -0
- package/dist/hostTransport.js +30 -0
- package/dist/hostTransport.js.map +1 -0
- package/dist/index.cjs +4 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/injectedBundler.cjs.map +1 -1
- package/dist/injectedBundler.js.map +1 -1
- package/dist/ipc.cjs +1 -4
- package/dist/ipc.cjs.map +1 -1
- package/dist/ipc.js +1 -4
- package/dist/ipc.js.map +1 -1
- package/dist/irMarkers.cjs.map +1 -1
- package/dist/irMarkers.d.cts +11 -11
- package/dist/irMarkers.d.ts +11 -11
- package/dist/irMarkers.js.map +1 -1
- package/dist/launch.cjs.map +1 -1
- package/dist/launch.js.map +1 -1
- package/dist/linkSpace.cjs +11 -2
- package/dist/linkSpace.cjs.map +1 -1
- package/dist/linkSpace.d.cts +23 -0
- package/dist/linkSpace.d.ts +23 -0
- package/dist/linkSpace.js +11 -2
- package/dist/linkSpace.js.map +1 -1
- package/dist/llm.cjs +26 -8
- package/dist/llm.cjs.map +1 -1
- package/dist/llm.d.cts +75 -3
- package/dist/llm.d.ts +75 -3
- package/dist/llm.js +21 -7
- package/dist/llm.js.map +1 -1
- package/dist/loading.cjs +18 -14
- package/dist/loading.cjs.map +1 -1
- package/dist/loading.d.cts +2 -2
- package/dist/loading.d.ts +2 -2
- package/dist/loading.js +19 -19
- package/dist/loading.js.map +1 -1
- package/dist/markers.cjs.map +1 -1
- package/dist/markers.js.map +1 -1
- package/dist/metadataSource.cjs +1 -5
- package/dist/metadataSource.cjs.map +1 -1
- package/dist/metadataSource.d.cts +1 -1
- package/dist/metadataSource.d.ts +1 -1
- package/dist/metadataSource.js +1 -5
- package/dist/metadataSource.js.map +1 -1
- package/dist/moduleCache.cjs +4 -1
- package/dist/moduleCache.cjs.map +1 -1
- package/dist/moduleCache.d.cts +1 -1
- package/dist/moduleCache.d.ts +1 -1
- package/dist/moduleCache.js +4 -1
- package/dist/moduleCache.js.map +1 -1
- package/dist/mountMatch.cjs.map +1 -1
- package/dist/mountMatch.js.map +1 -1
- package/dist/mounts.cjs +6 -1
- package/dist/mounts.cjs.map +1 -1
- package/dist/mounts.d.cts +2 -2
- package/dist/mounts.d.ts +2 -2
- package/dist/mounts.js +6 -1
- package/dist/mounts.js.map +1 -1
- package/dist/netFetch.cjs +3 -5
- package/dist/netFetch.cjs.map +1 -1
- package/dist/netFetch.js +3 -5
- package/dist/netFetch.js.map +1 -1
- package/dist/onFsChange.cjs.map +1 -1
- package/dist/onFsChange.js.map +1 -1
- package/dist/pathUtils.cjs +9 -12
- package/dist/pathUtils.cjs.map +1 -1
- package/dist/pathUtils.js +9 -12
- package/dist/pathUtils.js.map +1 -1
- package/dist/protocolDeadline.cjs +205 -0
- package/dist/protocolDeadline.cjs.map +1 -0
- package/dist/protocolDeadline.d.cts +146 -0
- package/dist/protocolDeadline.d.ts +146 -0
- package/dist/protocolDeadline.js +168 -0
- package/dist/protocolDeadline.js.map +1 -0
- package/dist/protocolStream.cjs +49 -6
- package/dist/protocolStream.cjs.map +1 -1
- package/dist/protocolStream.d.cts +9 -2
- package/dist/protocolStream.d.ts +9 -2
- package/dist/protocolStream.js +56 -6
- package/dist/protocolStream.js.map +1 -1
- package/dist/pushChannel.cjs +13 -9
- package/dist/pushChannel.cjs.map +1 -1
- package/dist/pushChannel.js +12 -8
- package/dist/pushChannel.js.map +1 -1
- package/dist/ready.cjs.map +1 -1
- package/dist/ready.js.map +1 -1
- package/dist/routing.cjs +3 -1
- package/dist/routing.cjs.map +1 -1
- package/dist/routing.js +3 -1
- package/dist/routing.js.map +1 -1
- package/dist/safeContent/index.cjs.map +1 -1
- package/dist/safeContent/index.js.map +1 -1
- package/dist/safeContent/parseSafeMdast.cjs.map +1 -1
- package/dist/safeContent/parseSafeMdast.js.map +1 -1
- package/dist/safeContent/renderMdast.cjs.map +1 -1
- package/dist/safeContent/renderMdast.js.map +1 -1
- package/dist/sandboxTypes.cjs.map +1 -1
- package/dist/sandboxUtils.cjs +69 -24
- package/dist/sandboxUtils.cjs.map +1 -1
- package/dist/sandboxUtils.d.cts +37 -4
- package/dist/sandboxUtils.d.ts +37 -4
- package/dist/sandboxUtils.js +74 -22
- package/dist/sandboxUtils.js.map +1 -1
- package/dist/scrollToId.cjs.map +1 -1
- package/dist/scrollToId.js.map +1 -1
- package/dist/secrets.cjs.map +1 -1
- package/dist/secrets.js.map +1 -1
- package/dist/tasks.cjs +12 -2
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +12 -2
- package/dist/tasks.js.map +1 -1
- package/dist/testing.cjs +1 -3
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +1 -3
- package/dist/testing.js.map +1 -1
- package/dist/theme.cjs.map +1 -1
- package/dist/theme.js.map +1 -1
- package/dist/urlUtils.cjs +7 -18
- package/dist/urlUtils.cjs.map +1 -1
- package/dist/urlUtils.js +7 -18
- package/dist/urlUtils.js.map +1 -1
- package/dist/vcs.cjs +1 -3
- package/dist/vcs.cjs.map +1 -1
- package/dist/vcs.js +1 -3
- package/dist/vcs.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +11 -5
package/dist/pathUtils.cjs
CHANGED
|
@@ -37,18 +37,15 @@ const joinPaths = (...pathPart) => pathPart.reduce((acc, part) => {
|
|
|
37
37
|
const absPath = (rawPath) => {
|
|
38
38
|
const absCandidate = joinPaths.apply(
|
|
39
39
|
null,
|
|
40
|
-
rawPath.split(PATH_SEPARATOR).reduce(
|
|
41
|
-
(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
[]
|
|
51
|
-
)
|
|
40
|
+
rawPath.split(PATH_SEPARATOR).reduce((partialAbsPath, currentPathPart) => {
|
|
41
|
+
if (currentPathPart == ".") {
|
|
42
|
+
return partialAbsPath;
|
|
43
|
+
}
|
|
44
|
+
if (currentPathPart == "..") {
|
|
45
|
+
return partialAbsPath.slice(0, -1);
|
|
46
|
+
}
|
|
47
|
+
return partialAbsPath.concat(currentPathPart);
|
|
48
|
+
}, [])
|
|
52
49
|
);
|
|
53
50
|
if (absCandidate === "" && rawPath.startsWith(PATH_SEPARATOR)) {
|
|
54
51
|
return PATH_SEPARATOR;
|
package/dist/pathUtils.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pathUtils.ts"],"sourcesContent":["const PATH_SEPARATOR =
|
|
1
|
+
{"version":3,"sources":["../src/pathUtils.ts"],"sourcesContent":["const PATH_SEPARATOR = '/';\n\nexport const joinPaths = (...pathPart: string[]) =>\n pathPart.reduce((acc, part) => {\n const left = acc.endsWith(PATH_SEPARATOR) ? acc.slice(0, -1) : acc;\n const right = part.startsWith(PATH_SEPARATOR) ? part.substring(1) : part;\n if (left || acc === PATH_SEPARATOR) {\n return `${left}${PATH_SEPARATOR}${right}`;\n }\n if (part.startsWith(PATH_SEPARATOR)) {\n return `${PATH_SEPARATOR}${right}`;\n }\n return right;\n }, '');\n\nexport const absPath = (rawPath: string): string => {\n const absCandidate = joinPaths.apply(\n null,\n rawPath.split(PATH_SEPARATOR).reduce((partialAbsPath: string[], currentPathPart: string) => {\n if (currentPathPart == '.') {\n return partialAbsPath;\n }\n if (currentPathPart == '..') {\n return partialAbsPath.slice(0, -1);\n }\n return partialAbsPath.concat(currentPathPart);\n }, []),\n );\n if (absCandidate === '' && rawPath.startsWith(PATH_SEPARATOR)) {\n return PATH_SEPARATOR;\n }\n return absCandidate;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAM,iBAAiB;AAEhB,MAAM,YAAY,IAAI,aAC3B,SAAS,OAAO,CAAC,KAAK,SAAS;AAC7B,QAAM,OAAO,IAAI,SAAS,cAAc,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI;AAC/D,QAAM,QAAQ,KAAK,WAAW,cAAc,IAAI,KAAK,UAAU,CAAC,IAAI;AACpE,MAAI,QAAQ,QAAQ,gBAAgB;AAClC,WAAO,GAAG,IAAI,GAAG,cAAc,GAAG,KAAK;AAAA,EACzC;AACA,MAAI,KAAK,WAAW,cAAc,GAAG;AACnC,WAAO,GAAG,cAAc,GAAG,KAAK;AAAA,EAClC;AACA,SAAO;AACT,GAAG,EAAE;AAEA,MAAM,UAAU,CAAC,YAA4B;AAClD,QAAM,eAAe,UAAU;AAAA,IAC7B;AAAA,IACA,QAAQ,MAAM,cAAc,EAAE,OAAO,CAAC,gBAA0B,oBAA4B;AAC1F,UAAI,mBAAmB,KAAK;AAC1B,eAAO;AAAA,MACT;AACA,UAAI,mBAAmB,MAAM;AAC3B,eAAO,eAAe,MAAM,GAAG,EAAE;AAAA,MACnC;AACA,aAAO,eAAe,OAAO,eAAe;AAAA,IAC9C,GAAG,CAAC,CAAC;AAAA,EACP;AACA,MAAI,iBAAiB,MAAM,QAAQ,WAAW,cAAc,GAAG;AAC7D,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
|
package/dist/pathUtils.js
CHANGED
|
@@ -14,18 +14,15 @@ const joinPaths = (...pathPart) => pathPart.reduce((acc, part) => {
|
|
|
14
14
|
const absPath = (rawPath) => {
|
|
15
15
|
const absCandidate = joinPaths.apply(
|
|
16
16
|
null,
|
|
17
|
-
rawPath.split(PATH_SEPARATOR).reduce(
|
|
18
|
-
(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
[]
|
|
28
|
-
)
|
|
17
|
+
rawPath.split(PATH_SEPARATOR).reduce((partialAbsPath, currentPathPart) => {
|
|
18
|
+
if (currentPathPart == ".") {
|
|
19
|
+
return partialAbsPath;
|
|
20
|
+
}
|
|
21
|
+
if (currentPathPart == "..") {
|
|
22
|
+
return partialAbsPath.slice(0, -1);
|
|
23
|
+
}
|
|
24
|
+
return partialAbsPath.concat(currentPathPart);
|
|
25
|
+
}, [])
|
|
29
26
|
);
|
|
30
27
|
if (absCandidate === "" && rawPath.startsWith(PATH_SEPARATOR)) {
|
|
31
28
|
return PATH_SEPARATOR;
|
package/dist/pathUtils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pathUtils.ts"],"sourcesContent":["const PATH_SEPARATOR =
|
|
1
|
+
{"version":3,"sources":["../src/pathUtils.ts"],"sourcesContent":["const PATH_SEPARATOR = '/';\n\nexport const joinPaths = (...pathPart: string[]) =>\n pathPart.reduce((acc, part) => {\n const left = acc.endsWith(PATH_SEPARATOR) ? acc.slice(0, -1) : acc;\n const right = part.startsWith(PATH_SEPARATOR) ? part.substring(1) : part;\n if (left || acc === PATH_SEPARATOR) {\n return `${left}${PATH_SEPARATOR}${right}`;\n }\n if (part.startsWith(PATH_SEPARATOR)) {\n return `${PATH_SEPARATOR}${right}`;\n }\n return right;\n }, '');\n\nexport const absPath = (rawPath: string): string => {\n const absCandidate = joinPaths.apply(\n null,\n rawPath.split(PATH_SEPARATOR).reduce((partialAbsPath: string[], currentPathPart: string) => {\n if (currentPathPart == '.') {\n return partialAbsPath;\n }\n if (currentPathPart == '..') {\n return partialAbsPath.slice(0, -1);\n }\n return partialAbsPath.concat(currentPathPart);\n }, []),\n );\n if (absCandidate === '' && rawPath.startsWith(PATH_SEPARATOR)) {\n return PATH_SEPARATOR;\n }\n return absCandidate;\n};\n"],"mappings":";AAAA,MAAM,iBAAiB;AAEhB,MAAM,YAAY,IAAI,aAC3B,SAAS,OAAO,CAAC,KAAK,SAAS;AAC7B,QAAM,OAAO,IAAI,SAAS,cAAc,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI;AAC/D,QAAM,QAAQ,KAAK,WAAW,cAAc,IAAI,KAAK,UAAU,CAAC,IAAI;AACpE,MAAI,QAAQ,QAAQ,gBAAgB;AAClC,WAAO,GAAG,IAAI,GAAG,cAAc,GAAG,KAAK;AAAA,EACzC;AACA,MAAI,KAAK,WAAW,cAAc,GAAG;AACnC,WAAO,GAAG,cAAc,GAAG,KAAK;AAAA,EAClC;AACA,SAAO;AACT,GAAG,EAAE;AAEA,MAAM,UAAU,CAAC,YAA4B;AAClD,QAAM,eAAe,UAAU;AAAA,IAC7B;AAAA,IACA,QAAQ,MAAM,cAAc,EAAE,OAAO,CAAC,gBAA0B,oBAA4B;AAC1F,UAAI,mBAAmB,KAAK;AAC1B,eAAO;AAAA,MACT;AACA,UAAI,mBAAmB,MAAM;AAC3B,eAAO,eAAe,MAAM,GAAG,EAAE;AAAA,MACnC;AACA,aAAO,eAAe,OAAO,eAAe;AAAA,IAC9C,GAAG,CAAC,CAAC;AAAA,EACP;AACA,MAAI,iBAAiB,MAAM,QAAQ,WAAW,cAAc,GAAG;AAC7D,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var protocolDeadline_exports = {};
|
|
20
|
+
__export(protocolDeadline_exports, {
|
|
21
|
+
ATTENDED_FIRST_FRAME_MS: () => ATTENDED_FIRST_FRAME_MS,
|
|
22
|
+
ATTENDED_TIMEOUT_MS: () => ATTENDED_TIMEOUT_MS,
|
|
23
|
+
NETWORK_TIMEOUT_MS: () => NETWORK_TIMEOUT_MS,
|
|
24
|
+
PENDING_NOTICE_MS: () => PENDING_NOTICE_MS,
|
|
25
|
+
ProtocolCancelledError: () => ProtocolCancelledError,
|
|
26
|
+
ProtocolTimeoutError: () => ProtocolTimeoutError,
|
|
27
|
+
STREAM_IDLE_TIMEOUT_MS: () => STREAM_IDLE_TIMEOUT_MS,
|
|
28
|
+
UNATTENDED_TIMEOUT_MS: () => UNATTENDED_TIMEOUT_MS,
|
|
29
|
+
attendanceOf: () => attendanceOf,
|
|
30
|
+
attendanceReason: () => attendanceReason,
|
|
31
|
+
boundsFor: () => boundsFor,
|
|
32
|
+
createSuspendableDeadline: () => createSuspendableDeadline,
|
|
33
|
+
firstFrameBoundsFor: () => firstFrameBoundsFor,
|
|
34
|
+
firstFrameTimeoutFor: () => firstFrameTimeoutFor,
|
|
35
|
+
timeoutFor: () => timeoutFor
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(protocolDeadline_exports);
|
|
38
|
+
const UNATTENDED_TIMEOUT_MS = 3e4;
|
|
39
|
+
const NETWORK_TIMEOUT_MS = 12e4;
|
|
40
|
+
const ATTENDED_TIMEOUT_MS = 6e5;
|
|
41
|
+
const ATTENDED_FIRST_FRAME_MS = 3e5;
|
|
42
|
+
const STREAM_IDLE_TIMEOUT_MS = 12e4;
|
|
43
|
+
const PENDING_NOTICE_MS = 3e3;
|
|
44
|
+
const ATTENDED = {
|
|
45
|
+
// The powerbox and the add-secret modal are host-drawn and wait for the user to type or
|
|
46
|
+
// pick; the first use of any stored secret additionally raises a WebAuthn assertion
|
|
47
|
+
// (SECRETS_SPEC §3 — one unlock per session, from a live gesture). All three are wrapped
|
|
48
|
+
// presenters, so the signal covers this scheme completely.
|
|
49
|
+
secrets: {
|
|
50
|
+
reason: "host-drawn key entry / picker, and the per-session passkey unlock",
|
|
51
|
+
idleMs: UNATTENDED_TIMEOUT_MS
|
|
52
|
+
},
|
|
53
|
+
// Consent is raised INSIDE the request: presentMountConsent, presentGrantPicker,
|
|
54
|
+
// presentCreateConsent, presentShareDisclosure, presentReferenceConsent — every one of
|
|
55
|
+
// them a wrapped presenter. Unattended once the grant is held, attended on first use, and
|
|
56
|
+
// since R3-307 the host says which of those is happening.
|
|
57
|
+
spaces: {
|
|
58
|
+
reason: "first-use mount/share/create consent is drawn inside the request",
|
|
59
|
+
idleMs: UNATTENDED_TIMEOUT_MS
|
|
60
|
+
},
|
|
61
|
+
settings: {
|
|
62
|
+
reason: "settings verbs reach the same consent and picker surfaces as spaces",
|
|
63
|
+
idleMs: UNATTENDED_TIMEOUT_MS
|
|
64
|
+
},
|
|
65
|
+
// The contribute flow shows the full diff for approval before anything is written
|
|
66
|
+
// (TRUST_AND_SAFETY TS-19b: the approval MUST show the real diff, so a human reads it).
|
|
67
|
+
// NOT a wrapped presenter — no `idleMs`.
|
|
68
|
+
contribute: { reason: "the diff-approval step is a human read of the whole change" },
|
|
69
|
+
// A task is an app bound to a transient slot that the user interacts with; it returns
|
|
70
|
+
// when they finish, which is human-paced by construction. That is an APP's interaction,
|
|
71
|
+
// not a host prompt, so the attention channel never fires for it — no `idleMs`.
|
|
72
|
+
task: { reason: "a task app runs an interaction and returns when the user finishes" },
|
|
73
|
+
// Launching a target can raise consent for a not-yet-granted app — through the launch
|
|
74
|
+
// flow's own surface, not one of the wrapped presenters. No `idleMs`.
|
|
75
|
+
launch: { reason: "may raise first-use consent for the launched target" },
|
|
76
|
+
// A drag is a gesture in progress — its duration is the user's hand, and no host prompt
|
|
77
|
+
// is up while it happens. No `idleMs`.
|
|
78
|
+
dnd: { reason: "a drag is a human gesture in flight" },
|
|
79
|
+
// The chat stream's FIRST frame sits behind the session's first passkey unseal — the
|
|
80
|
+
// exact hang the dogfood run found — and that unseal IS a wrapped presenter. But the idle
|
|
81
|
+
// bound here is the NETWORK one, not the channel one: with no prompt up, this call is
|
|
82
|
+
// waiting on an arbitrary upstream model, and thirty seconds is a normal generation.
|
|
83
|
+
llm: {
|
|
84
|
+
reason: "the first frame can sit behind the session passkey unseal",
|
|
85
|
+
idleMs: NETWORK_TIMEOUT_MS
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function attendedEntry(scheme, method) {
|
|
89
|
+
return ATTENDED[`${scheme}:${method}`] ?? ATTENDED[scheme];
|
|
90
|
+
}
|
|
91
|
+
function attendedReason(scheme, method) {
|
|
92
|
+
return attendedEntry(scheme, method)?.reason;
|
|
93
|
+
}
|
|
94
|
+
function attendanceOf(scheme, method) {
|
|
95
|
+
return attendedReason(scheme, method) ? "attended" : "unattended";
|
|
96
|
+
}
|
|
97
|
+
function attendanceReason(scheme, method) {
|
|
98
|
+
return attendedReason(scheme, method);
|
|
99
|
+
}
|
|
100
|
+
function timeoutFor(scheme, method) {
|
|
101
|
+
if (attendanceOf(scheme, method) === "attended") return ATTENDED_TIMEOUT_MS;
|
|
102
|
+
if (scheme === "fetch") return NETWORK_TIMEOUT_MS;
|
|
103
|
+
return UNATTENDED_TIMEOUT_MS;
|
|
104
|
+
}
|
|
105
|
+
function firstFrameTimeoutFor(scheme, method) {
|
|
106
|
+
return attendanceOf(scheme, method) === "attended" ? ATTENDED_FIRST_FRAME_MS : NETWORK_TIMEOUT_MS;
|
|
107
|
+
}
|
|
108
|
+
function boundsFor(scheme, method) {
|
|
109
|
+
const ceilingMs = timeoutFor(scheme, method);
|
|
110
|
+
const idleMs = attendedEntry(scheme, method)?.idleMs;
|
|
111
|
+
return { idleMs: idleMs === void 0 ? ceilingMs : Math.min(idleMs, ceilingMs), ceilingMs };
|
|
112
|
+
}
|
|
113
|
+
function firstFrameBoundsFor(scheme, method) {
|
|
114
|
+
const ceilingMs = firstFrameTimeoutFor(scheme, method);
|
|
115
|
+
const idleMs = attendedEntry(scheme, method)?.idleMs;
|
|
116
|
+
return { idleMs: idleMs === void 0 ? ceilingMs : Math.min(idleMs, ceilingMs), ceilingMs };
|
|
117
|
+
}
|
|
118
|
+
function createSuspendableDeadline(opts) {
|
|
119
|
+
const setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
120
|
+
const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
|
|
121
|
+
const { idleMs, ceilingMs } = opts.bounds;
|
|
122
|
+
const hasIdleLeg = Number.isFinite(idleMs) && idleMs < ceilingMs;
|
|
123
|
+
let done = false;
|
|
124
|
+
let idle;
|
|
125
|
+
let ceiling;
|
|
126
|
+
const expire = (bound, boundMs) => {
|
|
127
|
+
if (done) return;
|
|
128
|
+
done = true;
|
|
129
|
+
opts.onExpire(bound, boundMs);
|
|
130
|
+
};
|
|
131
|
+
const armIdle = () => {
|
|
132
|
+
if (done || !hasIdleLeg || idle !== void 0) return;
|
|
133
|
+
idle = setTimer(() => {
|
|
134
|
+
idle = void 0;
|
|
135
|
+
expire("idle", idleMs);
|
|
136
|
+
}, idleMs);
|
|
137
|
+
};
|
|
138
|
+
const disarmIdle = () => {
|
|
139
|
+
if (idle !== void 0) {
|
|
140
|
+
clearTimer(idle);
|
|
141
|
+
idle = void 0;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
if (Number.isFinite(ceilingMs)) {
|
|
145
|
+
ceiling = setTimer(() => {
|
|
146
|
+
ceiling = void 0;
|
|
147
|
+
expire("ceiling", ceilingMs);
|
|
148
|
+
}, ceilingMs);
|
|
149
|
+
}
|
|
150
|
+
armIdle();
|
|
151
|
+
return {
|
|
152
|
+
setAwaiting(awaiting) {
|
|
153
|
+
if (done) return;
|
|
154
|
+
if (awaiting) disarmIdle();
|
|
155
|
+
else armIdle();
|
|
156
|
+
},
|
|
157
|
+
dispose() {
|
|
158
|
+
done = true;
|
|
159
|
+
disarmIdle();
|
|
160
|
+
if (ceiling !== void 0) {
|
|
161
|
+
clearTimer(ceiling);
|
|
162
|
+
ceiling = void 0;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
class ProtocolTimeoutError extends Error {
|
|
168
|
+
constructor(call, timeoutMs, attendance, bound = attendance === "attended" ? "ceiling" : "idle") {
|
|
169
|
+
super(
|
|
170
|
+
attendance === "attended" && bound === "ceiling" ? `immediately.run: ${call} was abandoned after ${Math.round(timeoutMs / 1e3)}s waiting for you` : `immediately.run: ${call} did not respond within ${Math.round(timeoutMs / 1e3)}s`
|
|
171
|
+
);
|
|
172
|
+
this.code = "timeout";
|
|
173
|
+
this.name = "ProtocolTimeoutError";
|
|
174
|
+
this.call = call;
|
|
175
|
+
this.timeoutMs = timeoutMs;
|
|
176
|
+
this.attendance = attendance;
|
|
177
|
+
this.bound = bound;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
class ProtocolCancelledError extends Error {
|
|
181
|
+
constructor(call) {
|
|
182
|
+
super(`immediately.run: ${call} was cancelled`);
|
|
183
|
+
this.code = "cancelled";
|
|
184
|
+
this.name = "ProtocolCancelledError";
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
188
|
+
0 && (module.exports = {
|
|
189
|
+
ATTENDED_FIRST_FRAME_MS,
|
|
190
|
+
ATTENDED_TIMEOUT_MS,
|
|
191
|
+
NETWORK_TIMEOUT_MS,
|
|
192
|
+
PENDING_NOTICE_MS,
|
|
193
|
+
ProtocolCancelledError,
|
|
194
|
+
ProtocolTimeoutError,
|
|
195
|
+
STREAM_IDLE_TIMEOUT_MS,
|
|
196
|
+
UNATTENDED_TIMEOUT_MS,
|
|
197
|
+
attendanceOf,
|
|
198
|
+
attendanceReason,
|
|
199
|
+
boundsFor,
|
|
200
|
+
createSuspendableDeadline,
|
|
201
|
+
firstFrameBoundsFor,
|
|
202
|
+
firstFrameTimeoutFor,
|
|
203
|
+
timeoutFor
|
|
204
|
+
});
|
|
205
|
+
//# sourceMappingURL=protocolDeadline.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/protocolDeadline.ts"],"sourcesContent":["// Deadlines for host protocol calls (R3-298) — so no platform request can hang forever.\n//\n// THE FAILURE THIS FIXES. `protocolRequest` and `hostFetch` carried no timeout, so a host\n// operation that never resolved presented as an indefinite \"Running…\" with no error, no\n// cancel, and nothing in the console. The GLM dogfood run hit exactly this: the first\n// `chat()` of a session parks on a WebAuthn unseal that never completes, and the surface\n// simply waits. A first-run user inside the setup wizard (R3-299) would be stranded on\n// \"Testing…\" — at the worst possible moment, on the screen whose whole purpose is to prove\n// setup worked.\n//\n// WHY A SINGLE CONSTANT IS THE WRONG ANSWER, AND WHAT IS DONE INSTEAD. A blanket timeout\n// was deferred once as risky, correctly: some host operations legitimately await a HUMAN —\n// a passkey tap, a consent decision, a file picker — and a flat deadline aborts those. So\n// calls are classified, and the two classes get very different bounds:\n//\n// unattended — a network call or a channel round-trip. Nobody is being asked anything, so\n// a reply that has not arrived in tens of seconds is a fault. Short bound.\n// attended — the host may draw chrome and wait for the user. The bound exists only to\n// stop an ABANDONED prompt from pinning the caller forever, so it is set far\n// beyond human reaction time.\n//\n// NOTHING IS UNBOUNDED. \"A long or absent deadline\" was the licence; absent is declined,\n// because absent is the bug. An attended bound of minutes never aborts a person who is\n// actually deciding, and does release a caller whose user walked away — which is the\n// difference between a slow flow and a wedged one.\n//\n// THE IMPRECISION, AND HOW R3-307 REMOVED IT. Attendedness is classified per\n// (scheme, method), but it is really a property of a MOMENT: `spaces:mount` is unattended\n// when the grant is already held and attended on first use, because the host raises consent\n// inside the request (`spaceHandler` `presentMountConsent`). A per-method table cannot see\n// that, so R3-298 classified any method that MAY prompt as `attended` and gave it the long\n// bound — which meant the common grant-held path also waited ten minutes before reporting a\n// fault.\n//\n// R3-307 added the host-attention channel (`hostAttention.ts`), on which the host says\n// whether a person is being asked something RIGHT NOW. So a call now carries TWO bounds:\n//\n// idle — in force while the host is not prompting. A may-prompt call runs on this,\n// which is a real correctness gain: the grant-held `spaces:mount` faults in\n// seconds, as it always should have.\n// ceiling — absolute, from call start, NEVER suspended. An abandoned prompt still\n// releases the caller. The signal may EXTEND a deadline, never remove it.\n//\n// AND THE SHORTENING IS OPT-IN PER ENTRY, which is the part worth not losing. A scheme\n// drops to the short idle bound only when EVERY prompt it can raise is one the host\n// actually announces on that channel (the powerbox, the add-secret modal, the passkey\n// unlock, and the spaceHandler consent surfaces — the presenters site-main wraps). A task\n// app's interaction and the contribute diff-approval are human-paced but are NOT host\n// prompts, so no signal would ever fire for them and they keep the full attended bound. A\n// signal that cannot fire must never be allowed to shorten a deadline.\n\nimport type { HostAttentionKind } from './hostAttention';\n\n/** Whether a call may block on a human being asked something. */\nexport type Attendance = 'unattended' | 'attended';\n\n/** Milliseconds. Exported so callers can reason about the defaults they are overriding. */\nexport const UNATTENDED_TIMEOUT_MS = 30_000;\n/** Network calls reach arbitrary upstreams; the host bounds the fetch itself, so this is a\n * backstop against the host never replying, not a request budget. */\nexport const NETWORK_TIMEOUT_MS = 120_000;\n/** Far beyond human reaction time — this exists only so an ABANDONED prompt releases the\n * caller. It must never be short enough to abort someone who is deciding. */\nexport const ATTENDED_TIMEOUT_MS = 600_000;\n/** A stream's first frame may be behind an unseal, so it gets an attended-scale bound of\n * its own. See `firstFrameTimeoutFor`. */\nexport const ATTENDED_FIRST_FRAME_MS = 300_000;\n/** After the first frame, silence this long means the stream is wedged: the host is no\n * longer producing and no human is being asked. */\nexport const STREAM_IDLE_TIMEOUT_MS = 120_000;\n/** When a call passes this, `onPending` fires so a caller can render a waiting state\n * instead of an unexplained stall. */\nexport const PENDING_NOTICE_MS = 3_000;\n\n/**\n * Methods that may draw host chrome and wait for the user.\n *\n * Each entry is a `scheme:method` or a bare `scheme` (matching every method of it). The\n * REASON is recorded per entry, because this table is the item's actual content — a future\n * reader must be able to see why a method is on the long bound without re-deriving it from\n * the host source.\n */\ninterface AttendedEntry {\n /** Why this call may block on a human. */\n reason: string;\n /**\n * The bound this call runs on while the host reports NOBODY is being asked (R3-307).\n *\n * Set it ONLY when every prompt this entry can raise is one the host announces on the\n * host-attention channel — i.e. a presenter site-main wraps (`hostAttention.ts` in that\n * repo). Omitted ⇒ the call keeps the full attended bound at all times, because a signal\n * that never fires must not be allowed to shorten a deadline.\n */\n idleMs?: number;\n}\n\nconst ATTENDED: Record<string, AttendedEntry> = {\n // The powerbox and the add-secret modal are host-drawn and wait for the user to type or\n // pick; the first use of any stored secret additionally raises a WebAuthn assertion\n // (SECRETS_SPEC §3 — one unlock per session, from a live gesture). All three are wrapped\n // presenters, so the signal covers this scheme completely.\n secrets: {\n reason: 'host-drawn key entry / picker, and the per-session passkey unlock',\n idleMs: UNATTENDED_TIMEOUT_MS,\n },\n // Consent is raised INSIDE the request: presentMountConsent, presentGrantPicker,\n // presentCreateConsent, presentShareDisclosure, presentReferenceConsent — every one of\n // them a wrapped presenter. Unattended once the grant is held, attended on first use, and\n // since R3-307 the host says which of those is happening.\n spaces: {\n reason: 'first-use mount/share/create consent is drawn inside the request',\n idleMs: UNATTENDED_TIMEOUT_MS,\n },\n settings: {\n reason: 'settings verbs reach the same consent and picker surfaces as spaces',\n idleMs: UNATTENDED_TIMEOUT_MS,\n },\n // The contribute flow shows the full diff for approval before anything is written\n // (TRUST_AND_SAFETY TS-19b: the approval MUST show the real diff, so a human reads it).\n // NOT a wrapped presenter — no `idleMs`.\n contribute: { reason: 'the diff-approval step is a human read of the whole change' },\n // A task is an app bound to a transient slot that the user interacts with; it returns\n // when they finish, which is human-paced by construction. That is an APP's interaction,\n // not a host prompt, so the attention channel never fires for it — no `idleMs`.\n task: { reason: 'a task app runs an interaction and returns when the user finishes' },\n // Launching a target can raise consent for a not-yet-granted app — through the launch\n // flow's own surface, not one of the wrapped presenters. No `idleMs`.\n launch: { reason: 'may raise first-use consent for the launched target' },\n // A drag is a gesture in progress — its duration is the user's hand, and no host prompt\n // is up while it happens. No `idleMs`.\n dnd: { reason: 'a drag is a human gesture in flight' },\n // The chat stream's FIRST frame sits behind the session's first passkey unseal — the\n // exact hang the dogfood run found — and that unseal IS a wrapped presenter. But the idle\n // bound here is the NETWORK one, not the channel one: with no prompt up, this call is\n // waiting on an arbitrary upstream model, and thirty seconds is a normal generation.\n llm: {\n reason: 'the first frame can sit behind the session passkey unseal',\n idleMs: NETWORK_TIMEOUT_MS,\n },\n};\n\n/** Look up a scheme/method in the attended table, preferring the exact method entry. */\nfunction attendedEntry(scheme: string, method: string): AttendedEntry | undefined {\n return ATTENDED[`${scheme}:${method}`] ?? ATTENDED[scheme];\n}\n\n/** Look up a scheme/method in the attended table, preferring the exact method entry. */\nfunction attendedReason(scheme: string, method: string): string | undefined {\n return attendedEntry(scheme, method)?.reason;\n}\n\n/** Whether a call may block on a human. Exported for the classification test + tooling. */\nexport function attendanceOf(scheme: string, method: string): Attendance {\n return attendedReason(scheme, method) ? 'attended' : 'unattended';\n}\n\n/** Why a call is classified attended, or `undefined` when it is not. Exported so the\n * classification is legible from a test failure rather than only from this source. */\nexport function attendanceReason(scheme: string, method: string): string | undefined {\n return attendedReason(scheme, method);\n}\n\n/** The default deadline for a one-shot `protocolRequest`. */\nexport function timeoutFor(scheme: string, method: string): number {\n if (attendanceOf(scheme, method) === 'attended') return ATTENDED_TIMEOUT_MS;\n // `fetch` reaches an arbitrary upstream, so it gets the network bound rather than the\n // channel-round-trip one.\n if (scheme === 'fetch') return NETWORK_TIMEOUT_MS;\n return UNATTENDED_TIMEOUT_MS;\n}\n\n/**\n * The default TIME-TO-FIRST-FRAME bound for a stream.\n *\n * Deliberately not a total-duration bound: a long generation that is streaming normally is\n * healthy, and killing it would be a worse bug than the one being fixed. The hang has a\n * distinct shape — NO frames at all — so that is what is bounded, plus an idle gap between\n * frames once flowing. Together they fire exactly on a wedged stream and never on a slow\n * one.\n */\nexport function firstFrameTimeoutFor(scheme: string, method: string): number {\n return attendanceOf(scheme, method) === 'attended' ? ATTENDED_FIRST_FRAME_MS : NETWORK_TIMEOUT_MS;\n}\n\n/**\n * The two bounds a call runs under (R3-307).\n *\n * `idleMs` is in force while the host reports nobody is being asked; it is cleared while a\n * host prompt is up and restarted, in full, when the prompt goes away. `ceilingMs` runs from\n * call start and is NEVER suspended — it is what releases a caller whose user walked away.\n */\nexport interface CallBounds {\n /** The bound in force while the host is not waiting on a person. */\n idleMs: number;\n /** The absolute bound from call start. Never suspended. */\n ceilingMs: number;\n}\n\n/** Which of a call's two bounds elapsed. `idle` means the host was NOT prompting — nobody\n * was being asked anything, so this is a fault. `ceiling` means the absolute bound ran out,\n * which for an attended call is an abandoned prompt. */\nexport type DeadlineBound = 'idle' | 'ceiling';\n\n/** The bounds for a one-shot `protocolRequest`. */\nexport function boundsFor(scheme: string, method: string): CallBounds {\n const ceilingMs = timeoutFor(scheme, method);\n const idleMs = attendedEntry(scheme, method)?.idleMs;\n // `Math.min` so an `idleMs` can only ever tighten: an entry that named a bound longer than\n // its own ceiling would otherwise silently disable the idle leg.\n return { idleMs: idleMs === undefined ? ceilingMs : Math.min(idleMs, ceilingMs), ceilingMs };\n}\n\n/** The bounds for a stream's time-to-first-frame. */\nexport function firstFrameBoundsFor(scheme: string, method: string): CallBounds {\n const ceilingMs = firstFrameTimeoutFor(scheme, method);\n const idleMs = attendedEntry(scheme, method)?.idleMs;\n return { idleMs: idleMs === undefined ? ceilingMs : Math.min(idleMs, ceilingMs), ceilingMs };\n}\n\n/** A live deadline that the host-attention signal can suspend. */\nexport interface SuspendableDeadline {\n /** Tell the deadline whether the host is waiting on a person right now. */\n setAwaiting(awaiting: boolean): void;\n /** Clear every timer. Idempotent — safe to call from a `finally`. */\n dispose(): void;\n}\n\n/**\n * A deadline with a suspendable idle leg and an unsuspendable ceiling.\n *\n * Pure and injectable (`setTimer`/`clearTimer`) so the suspension rules are unit-testable\n * against a fake clock rather than by waiting minutes for real ones.\n *\n * The idle leg RESTARTS in full when a prompt clears rather than resuming where it left off.\n * That is deliberate: after the user dismisses a prompt the host begins fresh work, and the\n * seconds that elapsed before the prompt say nothing about how long that work should take.\n * The ceiling is what stops a repeatedly-prompting call from running forever.\n */\nexport function createSuspendableDeadline(opts: {\n bounds: CallBounds;\n /** Called once, with the bound that elapsed and its length in ms. */\n onExpire: (bound: DeadlineBound, boundMs: number) => void;\n setTimer?: (fn: () => void, ms: number) => unknown;\n clearTimer?: (handle: unknown) => void;\n}): SuspendableDeadline {\n const setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));\n const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h as ReturnType<typeof setTimeout>));\n const { idleMs, ceilingMs } = opts.bounds;\n // An idle leg at or above the ceiling can never fire first, so don't arm one — that keeps\n // the common unattended case (idle === ceiling) on exactly one timer, as before R3-307.\n const hasIdleLeg = Number.isFinite(idleMs) && idleMs < ceilingMs;\n\n let done = false;\n let idle: unknown;\n let ceiling: unknown;\n\n const expire = (bound: DeadlineBound, boundMs: number) => {\n if (done) return;\n done = true;\n opts.onExpire(bound, boundMs);\n };\n\n const armIdle = () => {\n if (done || !hasIdleLeg || idle !== undefined) return;\n idle = setTimer(() => {\n idle = undefined;\n expire('idle', idleMs);\n }, idleMs);\n };\n const disarmIdle = () => {\n if (idle !== undefined) {\n clearTimer(idle);\n idle = undefined;\n }\n };\n\n if (Number.isFinite(ceilingMs)) {\n ceiling = setTimer(() => {\n ceiling = undefined;\n expire('ceiling', ceilingMs);\n }, ceilingMs);\n }\n armIdle();\n\n return {\n setAwaiting(awaiting: boolean) {\n if (done) return;\n if (awaiting) disarmIdle();\n else armIdle();\n },\n dispose() {\n done = true;\n disarmIdle();\n if (ceiling !== undefined) {\n clearTimer(ceiling);\n ceiling = undefined;\n }\n },\n };\n}\n\n/** The error a bounded call rejects with. `code` is `'timeout'` — the code R3-303's typed\n * provider-error taxonomy adopts, so apps see one vocabulary. */\nexport class ProtocolTimeoutError extends Error {\n readonly code = 'timeout';\n /** `scheme:method` of the call that timed out. */\n readonly call: string;\n /** The bound that elapsed, in ms. */\n readonly timeoutMs: number;\n /** Whether the call was on the attended or unattended bound — the first thing anyone\n * debugging a timeout needs, and otherwise invisible. */\n readonly attendance: Attendance;\n /** WHICH bound elapsed (R3-307). An attended call that faults on its `idle` bound was not\n * waiting on anyone — the host said so — and that is a genuinely different diagnosis from\n * an abandoned prompt hitting the `ceiling`. */\n readonly bound: DeadlineBound;\n constructor(\n call: string,\n timeoutMs: number,\n attendance: Attendance,\n bound: DeadlineBound = attendance === 'attended' ? 'ceiling' : 'idle',\n ) {\n super(\n attendance === 'attended' && bound === 'ceiling'\n ? `immediately.run: ${call} was abandoned after ${Math.round(timeoutMs / 1000)}s waiting for you`\n : `immediately.run: ${call} did not respond within ${Math.round(timeoutMs / 1000)}s`,\n );\n this.name = 'ProtocolTimeoutError';\n this.call = call;\n this.timeoutMs = timeoutMs;\n this.attendance = attendance;\n this.bound = bound;\n }\n}\n\n/** The error a cancelled call rejects with. */\nexport class ProtocolCancelledError extends Error {\n readonly code = 'cancelled';\n constructor(call: string) {\n super(`immediately.run: ${call} was cancelled`);\n this.name = 'ProtocolCancelledError';\n }\n}\n\n/** What the host is waiting for at this moment, as reported on the host-attention channel\n * (R3-307). Present on a {@link PendingState} only while the host IS prompting. */\nexport interface PendingAttention {\n /** The kind of prompt on screen, or `null` when the host reports a wait it cannot name. */\n kind: HostAttentionKind | null;\n /** `Date.now()` when the wait began, or `null`. */\n since: number | null;\n}\n\n/** What `onPending` is told when a call is taking a while. */\nexport interface PendingState {\n call: string;\n attendance: Attendance;\n elapsedMs: number;\n /** Why this call *may* be waiting on a person — present only when attended. It comes from\n * the classification table, so it is a standing possibility, not a live fact. */\n reason?: string;\n /**\n * What the host is waiting for RIGHT NOW (R3-307) — present only while a host prompt is\n * actually up. Prefer it over {@link reason} when rendering: \"tap your passkey\" is a\n * sentence the user can act on; \"this may need you\" is not.\n */\n awaiting?: PendingAttention;\n}\n\n/** Options accepted by every bounded host call. */\nexport interface BoundedCallOptions {\n /** Override the classified default. `Infinity` disables the bound — an escape hatch for a\n * caller that genuinely owns the wait (it must then provide its own way out).\n *\n * An explicit value is the WHOLE bound: it is never suspended by the host-attention\n * signal, because a caller that named a number owns the wait. */\n timeoutMs?: number;\n /** Abort the wait. The SDK stops waiting and rejects with `code: 'cancelled'`. */\n signal?: AbortSignal;\n /** Fired when the call passes `PENDING_NOTICE_MS`, so a caller can render a waiting state\n * rather than an unexplained pause — and again, after that, whenever the host starts or\n * stops waiting on the user, so the waiting state can name what is on screen now. */\n onPending?: (state: PendingState) => void;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyDO,MAAM,wBAAwB;AAG9B,MAAM,qBAAqB;AAG3B,MAAM,sBAAsB;AAG5B,MAAM,0BAA0B;AAGhC,MAAM,yBAAyB;AAG/B,MAAM,oBAAoB;AAwBjC,MAAM,WAA0C;AAAA;AAAA;AAAA;AAAA;AAAA,EAK9C,SAAS;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA;AAAA;AAAA;AAAA,EAIA,YAAY,EAAE,QAAQ,6DAA6D;AAAA;AAAA;AAAA;AAAA,EAInF,MAAM,EAAE,QAAQ,oEAAoE;AAAA;AAAA;AAAA,EAGpF,QAAQ,EAAE,QAAQ,sDAAsD;AAAA;AAAA;AAAA,EAGxE,KAAK,EAAE,QAAQ,sCAAsC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKrD,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF;AAGA,SAAS,cAAc,QAAgB,QAA2C;AAChF,SAAO,SAAS,GAAG,MAAM,IAAI,MAAM,EAAE,KAAK,SAAS,MAAM;AAC3D;AAGA,SAAS,eAAe,QAAgB,QAAoC;AAC1E,SAAO,cAAc,QAAQ,MAAM,GAAG;AACxC;AAGO,SAAS,aAAa,QAAgB,QAA4B;AACvE,SAAO,eAAe,QAAQ,MAAM,IAAI,aAAa;AACvD;AAIO,SAAS,iBAAiB,QAAgB,QAAoC;AACnF,SAAO,eAAe,QAAQ,MAAM;AACtC;AAGO,SAAS,WAAW,QAAgB,QAAwB;AACjE,MAAI,aAAa,QAAQ,MAAM,MAAM,WAAY,QAAO;AAGxD,MAAI,WAAW,QAAS,QAAO;AAC/B,SAAO;AACT;AAWO,SAAS,qBAAqB,QAAgB,QAAwB;AAC3E,SAAO,aAAa,QAAQ,MAAM,MAAM,aAAa,0BAA0B;AACjF;AAsBO,SAAS,UAAU,QAAgB,QAA4B;AACpE,QAAM,YAAY,WAAW,QAAQ,MAAM;AAC3C,QAAM,SAAS,cAAc,QAAQ,MAAM,GAAG;AAG9C,SAAO,EAAE,QAAQ,WAAW,SAAY,YAAY,KAAK,IAAI,QAAQ,SAAS,GAAG,UAAU;AAC7F;AAGO,SAAS,oBAAoB,QAAgB,QAA4B;AAC9E,QAAM,YAAY,qBAAqB,QAAQ,MAAM;AACrD,QAAM,SAAS,cAAc,QAAQ,MAAM,GAAG;AAC9C,SAAO,EAAE,QAAQ,WAAW,SAAY,YAAY,KAAK,IAAI,QAAQ,SAAS,GAAG,UAAU;AAC7F;AAqBO,SAAS,0BAA0B,MAMlB;AACtB,QAAM,WAAW,KAAK,aAAa,CAAC,IAAI,OAAO,WAAW,IAAI,EAAE;AAChE,QAAM,aAAa,KAAK,eAAe,CAAC,MAAM,aAAa,CAAkC;AAC7F,QAAM,EAAE,QAAQ,UAAU,IAAI,KAAK;AAGnC,QAAM,aAAa,OAAO,SAAS,MAAM,KAAK,SAAS;AAEvD,MAAI,OAAO;AACX,MAAI;AACJ,MAAI;AAEJ,QAAM,SAAS,CAAC,OAAsB,YAAoB;AACxD,QAAI,KAAM;AACV,WAAO;AACP,SAAK,SAAS,OAAO,OAAO;AAAA,EAC9B;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,QAAQ,CAAC,cAAc,SAAS,OAAW;AAC/C,WAAO,SAAS,MAAM;AACpB,aAAO;AACP,aAAO,QAAQ,MAAM;AAAA,IACvB,GAAG,MAAM;AAAA,EACX;AACA,QAAM,aAAa,MAAM;AACvB,QAAI,SAAS,QAAW;AACtB,iBAAW,IAAI;AACf,aAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,cAAU,SAAS,MAAM;AACvB,gBAAU;AACV,aAAO,WAAW,SAAS;AAAA,IAC7B,GAAG,SAAS;AAAA,EACd;AACA,UAAQ;AAER,SAAO;AAAA,IACL,YAAY,UAAmB;AAC7B,UAAI,KAAM;AACV,UAAI,SAAU,YAAW;AAAA,UACpB,SAAQ;AAAA,IACf;AAAA,IACA,UAAU;AACR,aAAO;AACP,iBAAW;AACX,UAAI,YAAY,QAAW;AACzB,mBAAW,OAAO;AAClB,kBAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF;AAIO,MAAM,6BAA6B,MAAM;AAAA,EAa9C,YACE,MACA,WACA,YACA,QAAuB,eAAe,aAAa,YAAY,QAC/D;AACA;AAAA,MACE,eAAe,cAAc,UAAU,YACnC,oBAAoB,IAAI,wBAAwB,KAAK,MAAM,YAAY,GAAI,CAAC,sBAC5E,oBAAoB,IAAI,2BAA2B,KAAK,MAAM,YAAY,GAAI,CAAC;AAAA,IACrF;AAtBF,SAAS,OAAO;AAuBd,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,aAAa;AAClB,SAAK,QAAQ;AAAA,EACf;AACF;AAGO,MAAM,+BAA+B,MAAM;AAAA,EAEhD,YAAY,MAAc;AACxB,UAAM,oBAAoB,IAAI,gBAAgB;AAFhD,SAAS,OAAO;AAGd,SAAK,OAAO;AAAA,EACd;AACF;","names":[]}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { HostAttentionKind } from './hostAttention.cjs';
|
|
2
|
+
|
|
3
|
+
/** Whether a call may block on a human being asked something. */
|
|
4
|
+
type Attendance = 'unattended' | 'attended';
|
|
5
|
+
/** Milliseconds. Exported so callers can reason about the defaults they are overriding. */
|
|
6
|
+
declare const UNATTENDED_TIMEOUT_MS = 30000;
|
|
7
|
+
/** Network calls reach arbitrary upstreams; the host bounds the fetch itself, so this is a
|
|
8
|
+
* backstop against the host never replying, not a request budget. */
|
|
9
|
+
declare const NETWORK_TIMEOUT_MS = 120000;
|
|
10
|
+
/** Far beyond human reaction time — this exists only so an ABANDONED prompt releases the
|
|
11
|
+
* caller. It must never be short enough to abort someone who is deciding. */
|
|
12
|
+
declare const ATTENDED_TIMEOUT_MS = 600000;
|
|
13
|
+
/** A stream's first frame may be behind an unseal, so it gets an attended-scale bound of
|
|
14
|
+
* its own. See `firstFrameTimeoutFor`. */
|
|
15
|
+
declare const ATTENDED_FIRST_FRAME_MS = 300000;
|
|
16
|
+
/** After the first frame, silence this long means the stream is wedged: the host is no
|
|
17
|
+
* longer producing and no human is being asked. */
|
|
18
|
+
declare const STREAM_IDLE_TIMEOUT_MS = 120000;
|
|
19
|
+
/** When a call passes this, `onPending` fires so a caller can render a waiting state
|
|
20
|
+
* instead of an unexplained stall. */
|
|
21
|
+
declare const PENDING_NOTICE_MS = 3000;
|
|
22
|
+
/** Whether a call may block on a human. Exported for the classification test + tooling. */
|
|
23
|
+
declare function attendanceOf(scheme: string, method: string): Attendance;
|
|
24
|
+
/** Why a call is classified attended, or `undefined` when it is not. Exported so the
|
|
25
|
+
* classification is legible from a test failure rather than only from this source. */
|
|
26
|
+
declare function attendanceReason(scheme: string, method: string): string | undefined;
|
|
27
|
+
/** The default deadline for a one-shot `protocolRequest`. */
|
|
28
|
+
declare function timeoutFor(scheme: string, method: string): number;
|
|
29
|
+
/**
|
|
30
|
+
* The default TIME-TO-FIRST-FRAME bound for a stream.
|
|
31
|
+
*
|
|
32
|
+
* Deliberately not a total-duration bound: a long generation that is streaming normally is
|
|
33
|
+
* healthy, and killing it would be a worse bug than the one being fixed. The hang has a
|
|
34
|
+
* distinct shape — NO frames at all — so that is what is bounded, plus an idle gap between
|
|
35
|
+
* frames once flowing. Together they fire exactly on a wedged stream and never on a slow
|
|
36
|
+
* one.
|
|
37
|
+
*/
|
|
38
|
+
declare function firstFrameTimeoutFor(scheme: string, method: string): number;
|
|
39
|
+
/**
|
|
40
|
+
* The two bounds a call runs under (R3-307).
|
|
41
|
+
*
|
|
42
|
+
* `idleMs` is in force while the host reports nobody is being asked; it is cleared while a
|
|
43
|
+
* host prompt is up and restarted, in full, when the prompt goes away. `ceilingMs` runs from
|
|
44
|
+
* call start and is NEVER suspended — it is what releases a caller whose user walked away.
|
|
45
|
+
*/
|
|
46
|
+
interface CallBounds {
|
|
47
|
+
/** The bound in force while the host is not waiting on a person. */
|
|
48
|
+
idleMs: number;
|
|
49
|
+
/** The absolute bound from call start. Never suspended. */
|
|
50
|
+
ceilingMs: number;
|
|
51
|
+
}
|
|
52
|
+
/** Which of a call's two bounds elapsed. `idle` means the host was NOT prompting — nobody
|
|
53
|
+
* was being asked anything, so this is a fault. `ceiling` means the absolute bound ran out,
|
|
54
|
+
* which for an attended call is an abandoned prompt. */
|
|
55
|
+
type DeadlineBound = 'idle' | 'ceiling';
|
|
56
|
+
/** The bounds for a one-shot `protocolRequest`. */
|
|
57
|
+
declare function boundsFor(scheme: string, method: string): CallBounds;
|
|
58
|
+
/** The bounds for a stream's time-to-first-frame. */
|
|
59
|
+
declare function firstFrameBoundsFor(scheme: string, method: string): CallBounds;
|
|
60
|
+
/** A live deadline that the host-attention signal can suspend. */
|
|
61
|
+
interface SuspendableDeadline {
|
|
62
|
+
/** Tell the deadline whether the host is waiting on a person right now. */
|
|
63
|
+
setAwaiting(awaiting: boolean): void;
|
|
64
|
+
/** Clear every timer. Idempotent — safe to call from a `finally`. */
|
|
65
|
+
dispose(): void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A deadline with a suspendable idle leg and an unsuspendable ceiling.
|
|
69
|
+
*
|
|
70
|
+
* Pure and injectable (`setTimer`/`clearTimer`) so the suspension rules are unit-testable
|
|
71
|
+
* against a fake clock rather than by waiting minutes for real ones.
|
|
72
|
+
*
|
|
73
|
+
* The idle leg RESTARTS in full when a prompt clears rather than resuming where it left off.
|
|
74
|
+
* That is deliberate: after the user dismisses a prompt the host begins fresh work, and the
|
|
75
|
+
* seconds that elapsed before the prompt say nothing about how long that work should take.
|
|
76
|
+
* The ceiling is what stops a repeatedly-prompting call from running forever.
|
|
77
|
+
*/
|
|
78
|
+
declare function createSuspendableDeadline(opts: {
|
|
79
|
+
bounds: CallBounds;
|
|
80
|
+
/** Called once, with the bound that elapsed and its length in ms. */
|
|
81
|
+
onExpire: (bound: DeadlineBound, boundMs: number) => void;
|
|
82
|
+
setTimer?: (fn: () => void, ms: number) => unknown;
|
|
83
|
+
clearTimer?: (handle: unknown) => void;
|
|
84
|
+
}): SuspendableDeadline;
|
|
85
|
+
/** The error a bounded call rejects with. `code` is `'timeout'` — the code R3-303's typed
|
|
86
|
+
* provider-error taxonomy adopts, so apps see one vocabulary. */
|
|
87
|
+
declare class ProtocolTimeoutError extends Error {
|
|
88
|
+
readonly code = "timeout";
|
|
89
|
+
/** `scheme:method` of the call that timed out. */
|
|
90
|
+
readonly call: string;
|
|
91
|
+
/** The bound that elapsed, in ms. */
|
|
92
|
+
readonly timeoutMs: number;
|
|
93
|
+
/** Whether the call was on the attended or unattended bound — the first thing anyone
|
|
94
|
+
* debugging a timeout needs, and otherwise invisible. */
|
|
95
|
+
readonly attendance: Attendance;
|
|
96
|
+
/** WHICH bound elapsed (R3-307). An attended call that faults on its `idle` bound was not
|
|
97
|
+
* waiting on anyone — the host said so — and that is a genuinely different diagnosis from
|
|
98
|
+
* an abandoned prompt hitting the `ceiling`. */
|
|
99
|
+
readonly bound: DeadlineBound;
|
|
100
|
+
constructor(call: string, timeoutMs: number, attendance: Attendance, bound?: DeadlineBound);
|
|
101
|
+
}
|
|
102
|
+
/** The error a cancelled call rejects with. */
|
|
103
|
+
declare class ProtocolCancelledError extends Error {
|
|
104
|
+
readonly code = "cancelled";
|
|
105
|
+
constructor(call: string);
|
|
106
|
+
}
|
|
107
|
+
/** What the host is waiting for at this moment, as reported on the host-attention channel
|
|
108
|
+
* (R3-307). Present on a {@link PendingState} only while the host IS prompting. */
|
|
109
|
+
interface PendingAttention {
|
|
110
|
+
/** The kind of prompt on screen, or `null` when the host reports a wait it cannot name. */
|
|
111
|
+
kind: HostAttentionKind | null;
|
|
112
|
+
/** `Date.now()` when the wait began, or `null`. */
|
|
113
|
+
since: number | null;
|
|
114
|
+
}
|
|
115
|
+
/** What `onPending` is told when a call is taking a while. */
|
|
116
|
+
interface PendingState {
|
|
117
|
+
call: string;
|
|
118
|
+
attendance: Attendance;
|
|
119
|
+
elapsedMs: number;
|
|
120
|
+
/** Why this call *may* be waiting on a person — present only when attended. It comes from
|
|
121
|
+
* the classification table, so it is a standing possibility, not a live fact. */
|
|
122
|
+
reason?: string;
|
|
123
|
+
/**
|
|
124
|
+
* What the host is waiting for RIGHT NOW (R3-307) — present only while a host prompt is
|
|
125
|
+
* actually up. Prefer it over {@link reason} when rendering: "tap your passkey" is a
|
|
126
|
+
* sentence the user can act on; "this may need you" is not.
|
|
127
|
+
*/
|
|
128
|
+
awaiting?: PendingAttention;
|
|
129
|
+
}
|
|
130
|
+
/** Options accepted by every bounded host call. */
|
|
131
|
+
interface BoundedCallOptions {
|
|
132
|
+
/** Override the classified default. `Infinity` disables the bound — an escape hatch for a
|
|
133
|
+
* caller that genuinely owns the wait (it must then provide its own way out).
|
|
134
|
+
*
|
|
135
|
+
* An explicit value is the WHOLE bound: it is never suspended by the host-attention
|
|
136
|
+
* signal, because a caller that named a number owns the wait. */
|
|
137
|
+
timeoutMs?: number;
|
|
138
|
+
/** Abort the wait. The SDK stops waiting and rejects with `code: 'cancelled'`. */
|
|
139
|
+
signal?: AbortSignal;
|
|
140
|
+
/** Fired when the call passes `PENDING_NOTICE_MS`, so a caller can render a waiting state
|
|
141
|
+
* rather than an unexplained pause — and again, after that, whenever the host starts or
|
|
142
|
+
* stops waiting on the user, so the waiting state can name what is on screen now. */
|
|
143
|
+
onPending?: (state: PendingState) => void;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { ATTENDED_FIRST_FRAME_MS, ATTENDED_TIMEOUT_MS, type Attendance, type BoundedCallOptions, type CallBounds, type DeadlineBound, NETWORK_TIMEOUT_MS, PENDING_NOTICE_MS, type PendingAttention, type PendingState, ProtocolCancelledError, ProtocolTimeoutError, STREAM_IDLE_TIMEOUT_MS, type SuspendableDeadline, UNATTENDED_TIMEOUT_MS, attendanceOf, attendanceReason, boundsFor, createSuspendableDeadline, firstFrameBoundsFor, firstFrameTimeoutFor, timeoutFor };
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { HostAttentionKind } from './hostAttention.js';
|
|
2
|
+
|
|
3
|
+
/** Whether a call may block on a human being asked something. */
|
|
4
|
+
type Attendance = 'unattended' | 'attended';
|
|
5
|
+
/** Milliseconds. Exported so callers can reason about the defaults they are overriding. */
|
|
6
|
+
declare const UNATTENDED_TIMEOUT_MS = 30000;
|
|
7
|
+
/** Network calls reach arbitrary upstreams; the host bounds the fetch itself, so this is a
|
|
8
|
+
* backstop against the host never replying, not a request budget. */
|
|
9
|
+
declare const NETWORK_TIMEOUT_MS = 120000;
|
|
10
|
+
/** Far beyond human reaction time — this exists only so an ABANDONED prompt releases the
|
|
11
|
+
* caller. It must never be short enough to abort someone who is deciding. */
|
|
12
|
+
declare const ATTENDED_TIMEOUT_MS = 600000;
|
|
13
|
+
/** A stream's first frame may be behind an unseal, so it gets an attended-scale bound of
|
|
14
|
+
* its own. See `firstFrameTimeoutFor`. */
|
|
15
|
+
declare const ATTENDED_FIRST_FRAME_MS = 300000;
|
|
16
|
+
/** After the first frame, silence this long means the stream is wedged: the host is no
|
|
17
|
+
* longer producing and no human is being asked. */
|
|
18
|
+
declare const STREAM_IDLE_TIMEOUT_MS = 120000;
|
|
19
|
+
/** When a call passes this, `onPending` fires so a caller can render a waiting state
|
|
20
|
+
* instead of an unexplained stall. */
|
|
21
|
+
declare const PENDING_NOTICE_MS = 3000;
|
|
22
|
+
/** Whether a call may block on a human. Exported for the classification test + tooling. */
|
|
23
|
+
declare function attendanceOf(scheme: string, method: string): Attendance;
|
|
24
|
+
/** Why a call is classified attended, or `undefined` when it is not. Exported so the
|
|
25
|
+
* classification is legible from a test failure rather than only from this source. */
|
|
26
|
+
declare function attendanceReason(scheme: string, method: string): string | undefined;
|
|
27
|
+
/** The default deadline for a one-shot `protocolRequest`. */
|
|
28
|
+
declare function timeoutFor(scheme: string, method: string): number;
|
|
29
|
+
/**
|
|
30
|
+
* The default TIME-TO-FIRST-FRAME bound for a stream.
|
|
31
|
+
*
|
|
32
|
+
* Deliberately not a total-duration bound: a long generation that is streaming normally is
|
|
33
|
+
* healthy, and killing it would be a worse bug than the one being fixed. The hang has a
|
|
34
|
+
* distinct shape — NO frames at all — so that is what is bounded, plus an idle gap between
|
|
35
|
+
* frames once flowing. Together they fire exactly on a wedged stream and never on a slow
|
|
36
|
+
* one.
|
|
37
|
+
*/
|
|
38
|
+
declare function firstFrameTimeoutFor(scheme: string, method: string): number;
|
|
39
|
+
/**
|
|
40
|
+
* The two bounds a call runs under (R3-307).
|
|
41
|
+
*
|
|
42
|
+
* `idleMs` is in force while the host reports nobody is being asked; it is cleared while a
|
|
43
|
+
* host prompt is up and restarted, in full, when the prompt goes away. `ceilingMs` runs from
|
|
44
|
+
* call start and is NEVER suspended — it is what releases a caller whose user walked away.
|
|
45
|
+
*/
|
|
46
|
+
interface CallBounds {
|
|
47
|
+
/** The bound in force while the host is not waiting on a person. */
|
|
48
|
+
idleMs: number;
|
|
49
|
+
/** The absolute bound from call start. Never suspended. */
|
|
50
|
+
ceilingMs: number;
|
|
51
|
+
}
|
|
52
|
+
/** Which of a call's two bounds elapsed. `idle` means the host was NOT prompting — nobody
|
|
53
|
+
* was being asked anything, so this is a fault. `ceiling` means the absolute bound ran out,
|
|
54
|
+
* which for an attended call is an abandoned prompt. */
|
|
55
|
+
type DeadlineBound = 'idle' | 'ceiling';
|
|
56
|
+
/** The bounds for a one-shot `protocolRequest`. */
|
|
57
|
+
declare function boundsFor(scheme: string, method: string): CallBounds;
|
|
58
|
+
/** The bounds for a stream's time-to-first-frame. */
|
|
59
|
+
declare function firstFrameBoundsFor(scheme: string, method: string): CallBounds;
|
|
60
|
+
/** A live deadline that the host-attention signal can suspend. */
|
|
61
|
+
interface SuspendableDeadline {
|
|
62
|
+
/** Tell the deadline whether the host is waiting on a person right now. */
|
|
63
|
+
setAwaiting(awaiting: boolean): void;
|
|
64
|
+
/** Clear every timer. Idempotent — safe to call from a `finally`. */
|
|
65
|
+
dispose(): void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A deadline with a suspendable idle leg and an unsuspendable ceiling.
|
|
69
|
+
*
|
|
70
|
+
* Pure and injectable (`setTimer`/`clearTimer`) so the suspension rules are unit-testable
|
|
71
|
+
* against a fake clock rather than by waiting minutes for real ones.
|
|
72
|
+
*
|
|
73
|
+
* The idle leg RESTARTS in full when a prompt clears rather than resuming where it left off.
|
|
74
|
+
* That is deliberate: after the user dismisses a prompt the host begins fresh work, and the
|
|
75
|
+
* seconds that elapsed before the prompt say nothing about how long that work should take.
|
|
76
|
+
* The ceiling is what stops a repeatedly-prompting call from running forever.
|
|
77
|
+
*/
|
|
78
|
+
declare function createSuspendableDeadline(opts: {
|
|
79
|
+
bounds: CallBounds;
|
|
80
|
+
/** Called once, with the bound that elapsed and its length in ms. */
|
|
81
|
+
onExpire: (bound: DeadlineBound, boundMs: number) => void;
|
|
82
|
+
setTimer?: (fn: () => void, ms: number) => unknown;
|
|
83
|
+
clearTimer?: (handle: unknown) => void;
|
|
84
|
+
}): SuspendableDeadline;
|
|
85
|
+
/** The error a bounded call rejects with. `code` is `'timeout'` — the code R3-303's typed
|
|
86
|
+
* provider-error taxonomy adopts, so apps see one vocabulary. */
|
|
87
|
+
declare class ProtocolTimeoutError extends Error {
|
|
88
|
+
readonly code = "timeout";
|
|
89
|
+
/** `scheme:method` of the call that timed out. */
|
|
90
|
+
readonly call: string;
|
|
91
|
+
/** The bound that elapsed, in ms. */
|
|
92
|
+
readonly timeoutMs: number;
|
|
93
|
+
/** Whether the call was on the attended or unattended bound — the first thing anyone
|
|
94
|
+
* debugging a timeout needs, and otherwise invisible. */
|
|
95
|
+
readonly attendance: Attendance;
|
|
96
|
+
/** WHICH bound elapsed (R3-307). An attended call that faults on its `idle` bound was not
|
|
97
|
+
* waiting on anyone — the host said so — and that is a genuinely different diagnosis from
|
|
98
|
+
* an abandoned prompt hitting the `ceiling`. */
|
|
99
|
+
readonly bound: DeadlineBound;
|
|
100
|
+
constructor(call: string, timeoutMs: number, attendance: Attendance, bound?: DeadlineBound);
|
|
101
|
+
}
|
|
102
|
+
/** The error a cancelled call rejects with. */
|
|
103
|
+
declare class ProtocolCancelledError extends Error {
|
|
104
|
+
readonly code = "cancelled";
|
|
105
|
+
constructor(call: string);
|
|
106
|
+
}
|
|
107
|
+
/** What the host is waiting for at this moment, as reported on the host-attention channel
|
|
108
|
+
* (R3-307). Present on a {@link PendingState} only while the host IS prompting. */
|
|
109
|
+
interface PendingAttention {
|
|
110
|
+
/** The kind of prompt on screen, or `null` when the host reports a wait it cannot name. */
|
|
111
|
+
kind: HostAttentionKind | null;
|
|
112
|
+
/** `Date.now()` when the wait began, or `null`. */
|
|
113
|
+
since: number | null;
|
|
114
|
+
}
|
|
115
|
+
/** What `onPending` is told when a call is taking a while. */
|
|
116
|
+
interface PendingState {
|
|
117
|
+
call: string;
|
|
118
|
+
attendance: Attendance;
|
|
119
|
+
elapsedMs: number;
|
|
120
|
+
/** Why this call *may* be waiting on a person — present only when attended. It comes from
|
|
121
|
+
* the classification table, so it is a standing possibility, not a live fact. */
|
|
122
|
+
reason?: string;
|
|
123
|
+
/**
|
|
124
|
+
* What the host is waiting for RIGHT NOW (R3-307) — present only while a host prompt is
|
|
125
|
+
* actually up. Prefer it over {@link reason} when rendering: "tap your passkey" is a
|
|
126
|
+
* sentence the user can act on; "this may need you" is not.
|
|
127
|
+
*/
|
|
128
|
+
awaiting?: PendingAttention;
|
|
129
|
+
}
|
|
130
|
+
/** Options accepted by every bounded host call. */
|
|
131
|
+
interface BoundedCallOptions {
|
|
132
|
+
/** Override the classified default. `Infinity` disables the bound — an escape hatch for a
|
|
133
|
+
* caller that genuinely owns the wait (it must then provide its own way out).
|
|
134
|
+
*
|
|
135
|
+
* An explicit value is the WHOLE bound: it is never suspended by the host-attention
|
|
136
|
+
* signal, because a caller that named a number owns the wait. */
|
|
137
|
+
timeoutMs?: number;
|
|
138
|
+
/** Abort the wait. The SDK stops waiting and rejects with `code: 'cancelled'`. */
|
|
139
|
+
signal?: AbortSignal;
|
|
140
|
+
/** Fired when the call passes `PENDING_NOTICE_MS`, so a caller can render a waiting state
|
|
141
|
+
* rather than an unexplained pause — and again, after that, whenever the host starts or
|
|
142
|
+
* stops waiting on the user, so the waiting state can name what is on screen now. */
|
|
143
|
+
onPending?: (state: PendingState) => void;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { ATTENDED_FIRST_FRAME_MS, ATTENDED_TIMEOUT_MS, type Attendance, type BoundedCallOptions, type CallBounds, type DeadlineBound, NETWORK_TIMEOUT_MS, PENDING_NOTICE_MS, type PendingAttention, type PendingState, ProtocolCancelledError, ProtocolTimeoutError, STREAM_IDLE_TIMEOUT_MS, type SuspendableDeadline, UNATTENDED_TIMEOUT_MS, attendanceOf, attendanceReason, boundsFor, createSuspendableDeadline, firstFrameBoundsFor, firstFrameTimeoutFor, timeoutFor };
|