@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/sandboxUtils.cjs
CHANGED
|
@@ -18,39 +18,84 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var sandboxUtils_exports = {};
|
|
20
20
|
__export(sandboxUtils_exports, {
|
|
21
|
-
addListener: () => addListener,
|
|
21
|
+
addListener: () => import_hostTransport2.addListener,
|
|
22
22
|
protocolRequest: () => protocolRequest,
|
|
23
|
-
sendMessage: () => sendMessage
|
|
23
|
+
sendMessage: () => import_hostTransport2.sendMessage,
|
|
24
|
+
withDeadline: () => withDeadline
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(sandboxUtils_exports);
|
|
26
|
-
var
|
|
27
|
-
|
|
27
|
+
var import_hostTransport = require("./hostTransport");
|
|
28
|
+
var import_hostAttention = require("./hostAttention");
|
|
29
|
+
var import_protocolDeadline = require("./protocolDeadline");
|
|
30
|
+
var import_hostTransport2 = require("./hostTransport");
|
|
31
|
+
const protocolRequest = (protocolName, method, params, opts) => withDeadline(protocolName, method, () => (0, import_hostTransport.transport)().protocolRequest(protocolName, method, params), opts);
|
|
32
|
+
async function withDeadline(scheme, method, start, opts) {
|
|
33
|
+
const call = `${scheme}:${method}`;
|
|
34
|
+
const attendance = (0, import_protocolDeadline.attendanceOf)(scheme, method);
|
|
35
|
+
const bounds = opts?.timeoutMs !== void 0 ? { idleMs: opts.timeoutMs, ceilingMs: opts.timeoutMs } : (0, import_protocolDeadline.boundsFor)(scheme, method);
|
|
36
|
+
const signal = opts?.signal;
|
|
37
|
+
if (signal?.aborted) throw new import_protocolDeadline.ProtocolCancelledError(call);
|
|
38
|
+
const work = start();
|
|
39
|
+
if (!Number.isFinite(bounds.ceilingMs) && !signal && !opts?.onPending) return work;
|
|
40
|
+
let deadline;
|
|
41
|
+
let notice;
|
|
42
|
+
let unsubscribeAttention;
|
|
43
|
+
let onAbort;
|
|
44
|
+
const started = Date.now();
|
|
28
45
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
return await new Promise((resolve, reject) => {
|
|
47
|
+
work.then(resolve, reject);
|
|
48
|
+
let attention;
|
|
49
|
+
let noticeFired = false;
|
|
50
|
+
const fireNotice = () => {
|
|
51
|
+
try {
|
|
52
|
+
opts?.onPending?.({
|
|
53
|
+
call,
|
|
54
|
+
attendance,
|
|
55
|
+
elapsedMs: Date.now() - started,
|
|
56
|
+
...(0, import_protocolDeadline.attendanceReason)(scheme, method) ? { reason: (0, import_protocolDeadline.attendanceReason)(scheme, method) } : {},
|
|
57
|
+
...attention?.awaiting ? { awaiting: { kind: attention.kind, since: attention.since } } : {}
|
|
58
|
+
});
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
deadline = (0, import_protocolDeadline.createSuspendableDeadline)({
|
|
63
|
+
bounds,
|
|
64
|
+
onExpire: (bound, elapsedBoundMs) => reject(new import_protocolDeadline.ProtocolTimeoutError(call, elapsedBoundMs, attendance, bound))
|
|
65
|
+
});
|
|
66
|
+
if (opts?.onPending) {
|
|
67
|
+
notice = setTimeout(() => {
|
|
68
|
+
noticeFired = true;
|
|
69
|
+
fireNotice();
|
|
70
|
+
}, import_protocolDeadline.PENDING_NOTICE_MS);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
unsubscribeAttention = (0, import_hostAttention.onHostAttentionChange)((next) => {
|
|
74
|
+
const wasAwaiting = attention?.awaiting ?? false;
|
|
75
|
+
attention = next;
|
|
76
|
+
deadline?.setAwaiting(next.awaiting);
|
|
77
|
+
if (noticeFired && next.awaiting !== wasAwaiting) fireNotice();
|
|
78
|
+
});
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
if (signal) {
|
|
82
|
+
onAbort = () => reject(new import_protocolDeadline.ProtocolCancelledError(call));
|
|
83
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
} finally {
|
|
87
|
+
deadline?.dispose();
|
|
88
|
+
if (notice !== void 0) clearTimeout(notice);
|
|
89
|
+
unsubscribeAttention?.();
|
|
90
|
+
if (signal && onAbort) signal.removeEventListener("abort", onAbort);
|
|
91
|
+
work.catch(() => void 0);
|
|
32
92
|
}
|
|
33
|
-
const t = (0, import_hostRuntime.getHostRuntime)()?.transport;
|
|
34
|
-
if (t && typeof t.sendMessage === "function") return t;
|
|
35
|
-
throw new Error("immediately.run: no host transport (neither injected nor __immediatelyRun__)");
|
|
36
93
|
}
|
|
37
|
-
const sendMessage = (type, data = {}) => {
|
|
38
|
-
transport().sendMessage(type, data);
|
|
39
|
-
};
|
|
40
|
-
const protocolRequest = (protocolName, method, params) => transport().protocolRequest(protocolName, method, params);
|
|
41
|
-
const addListener = (msgType, handler, event) => {
|
|
42
|
-
const onMessage = event ?? transport().onMessage;
|
|
43
|
-
const disposable = onMessage((msg) => {
|
|
44
|
-
if (msg.type === msgType) {
|
|
45
|
-
handler(msg);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
return () => disposable.dispose();
|
|
49
|
-
};
|
|
50
94
|
// Annotate the CommonJS export names for ESM import in node:
|
|
51
95
|
0 && (module.exports = {
|
|
52
96
|
addListener,
|
|
53
97
|
protocolRequest,
|
|
54
|
-
sendMessage
|
|
98
|
+
sendMessage,
|
|
99
|
+
withDeadline
|
|
55
100
|
});
|
|
56
101
|
//# sourceMappingURL=sandboxUtils.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sandboxUtils.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"sources":["../src/sandboxUtils.ts"],"sourcesContent":["// Bounded host calls (R3-298) + the host-attention-aware deadline (R3-307).\n//\n// The transport primitives this builds on moved to `hostTransport.ts` — see that file for\n// why. `sendMessage` and `addListener` are re-exported here unchanged, so every existing\n// import site (and this module's api-snapshot entry) is exactly as it was.\nimport { transport } from './hostTransport';\nimport { onHostAttentionChange, type HostAttention } from './hostAttention';\n\nimport {\n attendanceOf,\n attendanceReason,\n boundsFor,\n createSuspendableDeadline,\n PENDING_NOTICE_MS,\n ProtocolCancelledError,\n ProtocolTimeoutError,\n type BoundedCallOptions,\n type CallBounds,\n} from './protocolDeadline';\n\nexport { sendMessage, addListener } from './hostTransport';\n\n/**\n * One host protocol request, BOUNDED (R3-298).\n *\n * The bound comes from the call's classification (`protocolDeadline.ts`): an unattended\n * channel round-trip gets tens of seconds, a call that may draw host chrome and wait for a\n * person gets minutes. Nothing is unbounded — an unbounded wait is the failure this fixes.\n *\n * Since R3-307 a call whose prompts the host actually announces runs on the SHORT bound and\n * is suspended only while the host says a person is being asked, so the common\n * grant-already-held path reports a fault in seconds instead of minutes. The absolute\n * ceiling still applies: the signal may extend a deadline, never remove it.\n *\n * The host's own work is NOT cancelled by `signal` or by the deadline. The one-shot\n * transport allocates its `msgId` internally, so the SDK has no handle to send the host a\n * cancel for (streams do, and `consumeStream` uses it). What the caller gets back is\n * control: it stops waiting and gets a typed error instead of hanging. A host prompt that\n * is already on screen stays there until the user dismisses it, which is correct — the SDK\n * must not be able to tear down host chrome the user is looking at.\n */\nexport const protocolRequest = (\n protocolName: string,\n method: string,\n params: Array<any>,\n opts?: BoundedCallOptions,\n): Promise<any> =>\n withDeadline(protocolName, method, () => transport().protocolRequest(protocolName, method, params), opts);\n\n/**\n * Race a host call against its deadline, a caller abort, and a pending notice.\n *\n * Kept separate from the transport so it is unit-testable against a promise that simply\n * never settles — which is the whole scenario, and one no live transport reproduces on\n * demand.\n *\n * An explicit `opts.timeoutMs` is the WHOLE bound and is never suspended: a caller that\n * names a number owns the wait, and silently stretching it past what they asked for would\n * be the same class of surprise this machinery exists to remove.\n */\nexport async function withDeadline<T>(\n scheme: string,\n method: string,\n start: () => Promise<T>,\n opts?: BoundedCallOptions,\n): Promise<T> {\n const call = `${scheme}:${method}`;\n const attendance = attendanceOf(scheme, method);\n const bounds: CallBounds =\n opts?.timeoutMs !== undefined ? { idleMs: opts.timeoutMs, ceilingMs: opts.timeoutMs } : boundsFor(scheme, method);\n const signal = opts?.signal;\n\n if (signal?.aborted) throw new ProtocolCancelledError(call);\n\n const work = start();\n // No bound and no abort wanted: hand back the untouched promise rather than wrapping it\n // in timers that would never fire.\n if (!Number.isFinite(bounds.ceilingMs) && !signal && !opts?.onPending) return work;\n\n let deadline: { setAwaiting: (a: boolean) => void; dispose: () => void } | undefined;\n let notice: ReturnType<typeof setTimeout> | undefined;\n let unsubscribeAttention: (() => void) | undefined;\n let onAbort: (() => void) | undefined;\n const started = Date.now();\n\n try {\n return await new Promise<T>((resolve, reject) => {\n // `work` settling always wins — a call that answered a millisecond before its\n // deadline must not be reported as a timeout.\n work.then(resolve, reject);\n\n // The host's live \"a person is being asked\" signal. Read defensively: a host that\n // never pushes the channel leaves this at \"not awaiting\", which is exactly the\n // pre-R3-307 behaviour for a call on its idle bound.\n let attention: HostAttention | undefined;\n let noticeFired = false;\n const fireNotice = () => {\n try {\n opts?.onPending?.({\n call,\n attendance,\n elapsedMs: Date.now() - started,\n ...(attendanceReason(scheme, method) ? { reason: attendanceReason(scheme, method) as string } : {}),\n ...(attention?.awaiting ? { awaiting: { kind: attention.kind, since: attention.since } } : {}),\n });\n } catch {\n /* a caller's render callback must never break the call it describes */\n }\n };\n\n deadline = createSuspendableDeadline({\n bounds,\n onExpire: (bound, elapsedBoundMs) => reject(new ProtocolTimeoutError(call, elapsedBoundMs, attendance, bound)),\n });\n\n if (opts?.onPending) {\n notice = setTimeout(() => {\n noticeFired = true;\n fireNotice();\n }, PENDING_NOTICE_MS);\n }\n\n // Subscribing invokes the listener immediately with the current value, so the\n // deadline starts in the right state even if a prompt was already up.\n //\n // Guarded because this sits on the path of EVERY host call: a fault in the attention\n // channel must degrade to \"no signal\" (the pre-R3-307 behaviour, bounds unsuspended),\n // never take down every request in the SDK.\n try {\n unsubscribeAttention = onHostAttentionChange((next) => {\n const wasAwaiting = attention?.awaiting ?? false;\n attention = next;\n deadline?.setAwaiting(next.awaiting);\n // Re-notify once the notice has fired: \"still waiting\" → \"tap your passkey\" is\n // the whole point, and a caller rendering a waiting state wants the live\n // sentence, not the one that was true three seconds in.\n if (noticeFired && next.awaiting !== wasAwaiting) fireNotice();\n });\n } catch {\n /* no attention signal available — the bounds simply never suspend */\n }\n\n if (signal) {\n onAbort = () => reject(new ProtocolCancelledError(call));\n signal.addEventListener('abort', onAbort, { once: true });\n }\n });\n } finally {\n deadline?.dispose();\n if (notice !== undefined) clearTimeout(notice);\n unsubscribeAttention?.();\n if (signal && onAbort) signal.removeEventListener('abort', onAbort);\n // The host may still answer after we stopped waiting; swallow it so a late rejection\n // does not surface as an unhandled promise rejection in the app's console.\n work.catch(() => undefined);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,2BAA0B;AAC1B,2BAA0D;AAE1D,8BAUO;AAEP,IAAAA,wBAAyC;AAqBlC,MAAM,kBAAkB,CAC7B,cACA,QACA,QACA,SAEA,aAAa,cAAc,QAAQ,UAAM,gCAAU,EAAE,gBAAgB,cAAc,QAAQ,MAAM,GAAG,IAAI;AAa1G,eAAsB,aACpB,QACA,QACA,OACA,MACY;AACZ,QAAM,OAAO,GAAG,MAAM,IAAI,MAAM;AAChC,QAAM,iBAAa,sCAAa,QAAQ,MAAM;AAC9C,QAAM,SACJ,MAAM,cAAc,SAAY,EAAE,QAAQ,KAAK,WAAW,WAAW,KAAK,UAAU,QAAI,mCAAU,QAAQ,MAAM;AAClH,QAAM,SAAS,MAAM;AAErB,MAAI,QAAQ,QAAS,OAAM,IAAI,+CAAuB,IAAI;AAE1D,QAAM,OAAO,MAAM;AAGnB,MAAI,CAAC,OAAO,SAAS,OAAO,SAAS,KAAK,CAAC,UAAU,CAAC,MAAM,UAAW,QAAO;AAE9E,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,QAAM,UAAU,KAAK,IAAI;AAEzB,MAAI;AACF,WAAO,MAAM,IAAI,QAAW,CAAC,SAAS,WAAW;AAG/C,WAAK,KAAK,SAAS,MAAM;AAKzB,UAAI;AACJ,UAAI,cAAc;AAClB,YAAM,aAAa,MAAM;AACvB,YAAI;AACF,gBAAM,YAAY;AAAA,YAChB;AAAA,YACA;AAAA,YACA,WAAW,KAAK,IAAI,IAAI;AAAA,YACxB,OAAI,0CAAiB,QAAQ,MAAM,IAAI,EAAE,YAAQ,0CAAiB,QAAQ,MAAM,EAAY,IAAI,CAAC;AAAA,YACjG,GAAI,WAAW,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,MAAM,OAAO,UAAU,MAAM,EAAE,IAAI,CAAC;AAAA,UAC9F,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,qBAAW,mDAA0B;AAAA,QACnC;AAAA,QACA,UAAU,CAAC,OAAO,mBAAmB,OAAO,IAAI,6CAAqB,MAAM,gBAAgB,YAAY,KAAK,CAAC;AAAA,MAC/G,CAAC;AAED,UAAI,MAAM,WAAW;AACnB,iBAAS,WAAW,MAAM;AACxB,wBAAc;AACd,qBAAW;AAAA,QACb,GAAG,yCAAiB;AAAA,MACtB;AAQA,UAAI;AACF,mCAAuB,4CAAsB,CAAC,SAAS;AACrD,gBAAM,cAAc,WAAW,YAAY;AAC3C,sBAAY;AACZ,oBAAU,YAAY,KAAK,QAAQ;AAInC,cAAI,eAAe,KAAK,aAAa,YAAa,YAAW;AAAA,QAC/D,CAAC;AAAA,MACH,QAAQ;AAAA,MAER;AAEA,UAAI,QAAQ;AACV,kBAAU,MAAM,OAAO,IAAI,+CAAuB,IAAI,CAAC;AACvD,eAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,MAC1D;AAAA,IACF,CAAC;AAAA,EACH,UAAE;AACA,cAAU,QAAQ;AAClB,QAAI,WAAW,OAAW,cAAa,MAAM;AAC7C,2BAAuB;AACvB,QAAI,UAAU,QAAS,QAAO,oBAAoB,SAAS,OAAO;AAGlE,SAAK,MAAM,MAAM,MAAS;AAAA,EAC5B;AACF;","names":["import_hostTransport"]}
|
package/dist/sandboxUtils.d.cts
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { BoundedCallOptions } from './protocolDeadline.cjs';
|
|
2
|
+
export { addListener, sendMessage } from './hostTransport.cjs';
|
|
3
|
+
import './hostAttention.cjs';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* One host protocol request, BOUNDED (R3-298).
|
|
7
|
+
*
|
|
8
|
+
* The bound comes from the call's classification (`protocolDeadline.ts`): an unattended
|
|
9
|
+
* channel round-trip gets tens of seconds, a call that may draw host chrome and wait for a
|
|
10
|
+
* person gets minutes. Nothing is unbounded — an unbounded wait is the failure this fixes.
|
|
11
|
+
*
|
|
12
|
+
* Since R3-307 a call whose prompts the host actually announces runs on the SHORT bound and
|
|
13
|
+
* is suspended only while the host says a person is being asked, so the common
|
|
14
|
+
* grant-already-held path reports a fault in seconds instead of minutes. The absolute
|
|
15
|
+
* ceiling still applies: the signal may extend a deadline, never remove it.
|
|
16
|
+
*
|
|
17
|
+
* The host's own work is NOT cancelled by `signal` or by the deadline. The one-shot
|
|
18
|
+
* transport allocates its `msgId` internally, so the SDK has no handle to send the host a
|
|
19
|
+
* cancel for (streams do, and `consumeStream` uses it). What the caller gets back is
|
|
20
|
+
* control: it stops waiting and gets a typed error instead of hanging. A host prompt that
|
|
21
|
+
* is already on screen stays there until the user dismisses it, which is correct — the SDK
|
|
22
|
+
* must not be able to tear down host chrome the user is looking at.
|
|
23
|
+
*/
|
|
24
|
+
declare const protocolRequest: (protocolName: string, method: string, params: Array<any>, opts?: BoundedCallOptions) => Promise<any>;
|
|
25
|
+
/**
|
|
26
|
+
* Race a host call against its deadline, a caller abort, and a pending notice.
|
|
27
|
+
*
|
|
28
|
+
* Kept separate from the transport so it is unit-testable against a promise that simply
|
|
29
|
+
* never settles — which is the whole scenario, and one no live transport reproduces on
|
|
30
|
+
* demand.
|
|
31
|
+
*
|
|
32
|
+
* An explicit `opts.timeoutMs` is the WHOLE bound and is never suspended: a caller that
|
|
33
|
+
* names a number owns the wait, and silently stretching it past what they asked for would
|
|
34
|
+
* be the same class of surprise this machinery exists to remove.
|
|
35
|
+
*/
|
|
36
|
+
declare function withDeadline<T>(scheme: string, method: string, start: () => Promise<T>, opts?: BoundedCallOptions): Promise<T>;
|
|
37
|
+
|
|
38
|
+
export { protocolRequest, withDeadline };
|
package/dist/sandboxUtils.d.ts
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { BoundedCallOptions } from './protocolDeadline.js';
|
|
2
|
+
export { addListener, sendMessage } from './hostTransport.js';
|
|
3
|
+
import './hostAttention.js';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* One host protocol request, BOUNDED (R3-298).
|
|
7
|
+
*
|
|
8
|
+
* The bound comes from the call's classification (`protocolDeadline.ts`): an unattended
|
|
9
|
+
* channel round-trip gets tens of seconds, a call that may draw host chrome and wait for a
|
|
10
|
+
* person gets minutes. Nothing is unbounded — an unbounded wait is the failure this fixes.
|
|
11
|
+
*
|
|
12
|
+
* Since R3-307 a call whose prompts the host actually announces runs on the SHORT bound and
|
|
13
|
+
* is suspended only while the host says a person is being asked, so the common
|
|
14
|
+
* grant-already-held path reports a fault in seconds instead of minutes. The absolute
|
|
15
|
+
* ceiling still applies: the signal may extend a deadline, never remove it.
|
|
16
|
+
*
|
|
17
|
+
* The host's own work is NOT cancelled by `signal` or by the deadline. The one-shot
|
|
18
|
+
* transport allocates its `msgId` internally, so the SDK has no handle to send the host a
|
|
19
|
+
* cancel for (streams do, and `consumeStream` uses it). What the caller gets back is
|
|
20
|
+
* control: it stops waiting and gets a typed error instead of hanging. A host prompt that
|
|
21
|
+
* is already on screen stays there until the user dismisses it, which is correct — the SDK
|
|
22
|
+
* must not be able to tear down host chrome the user is looking at.
|
|
23
|
+
*/
|
|
24
|
+
declare const protocolRequest: (protocolName: string, method: string, params: Array<any>, opts?: BoundedCallOptions) => Promise<any>;
|
|
25
|
+
/**
|
|
26
|
+
* Race a host call against its deadline, a caller abort, and a pending notice.
|
|
27
|
+
*
|
|
28
|
+
* Kept separate from the transport so it is unit-testable against a promise that simply
|
|
29
|
+
* never settles — which is the whole scenario, and one no live transport reproduces on
|
|
30
|
+
* demand.
|
|
31
|
+
*
|
|
32
|
+
* An explicit `opts.timeoutMs` is the WHOLE bound and is never suspended: a caller that
|
|
33
|
+
* names a number owns the wait, and silently stretching it past what they asked for would
|
|
34
|
+
* be the same class of surprise this machinery exists to remove.
|
|
35
|
+
*/
|
|
36
|
+
declare function withDeadline<T>(scheme: string, method: string, start: () => Promise<T>, opts?: BoundedCallOptions): Promise<T>;
|
|
37
|
+
|
|
38
|
+
export { protocolRequest, withDeadline };
|
package/dist/sandboxUtils.js
CHANGED
|
@@ -1,31 +1,83 @@
|
|
|
1
1
|
import "./chunk-VHAA22YE.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { transport } from "./hostTransport";
|
|
3
|
+
import { onHostAttentionChange } from "./hostAttention";
|
|
4
|
+
import {
|
|
5
|
+
attendanceOf,
|
|
6
|
+
attendanceReason,
|
|
7
|
+
boundsFor,
|
|
8
|
+
createSuspendableDeadline,
|
|
9
|
+
PENDING_NOTICE_MS,
|
|
10
|
+
ProtocolCancelledError,
|
|
11
|
+
ProtocolTimeoutError
|
|
12
|
+
} from "./protocolDeadline";
|
|
13
|
+
import { sendMessage, addListener } from "./hostTransport";
|
|
14
|
+
const protocolRequest = (protocolName, method, params, opts) => withDeadline(protocolName, method, () => transport().protocolRequest(protocolName, method, params), opts);
|
|
15
|
+
async function withDeadline(scheme, method, start, opts) {
|
|
16
|
+
const call = `${scheme}:${method}`;
|
|
17
|
+
const attendance = attendanceOf(scheme, method);
|
|
18
|
+
const bounds = opts?.timeoutMs !== void 0 ? { idleMs: opts.timeoutMs, ceilingMs: opts.timeoutMs } : boundsFor(scheme, method);
|
|
19
|
+
const signal = opts?.signal;
|
|
20
|
+
if (signal?.aborted) throw new ProtocolCancelledError(call);
|
|
21
|
+
const work = start();
|
|
22
|
+
if (!Number.isFinite(bounds.ceilingMs) && !signal && !opts?.onPending) return work;
|
|
23
|
+
let deadline;
|
|
24
|
+
let notice;
|
|
25
|
+
let unsubscribeAttention;
|
|
26
|
+
let onAbort;
|
|
27
|
+
const started = Date.now();
|
|
4
28
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
29
|
+
return await new Promise((resolve, reject) => {
|
|
30
|
+
work.then(resolve, reject);
|
|
31
|
+
let attention;
|
|
32
|
+
let noticeFired = false;
|
|
33
|
+
const fireNotice = () => {
|
|
34
|
+
try {
|
|
35
|
+
opts?.onPending?.({
|
|
36
|
+
call,
|
|
37
|
+
attendance,
|
|
38
|
+
elapsedMs: Date.now() - started,
|
|
39
|
+
...attendanceReason(scheme, method) ? { reason: attendanceReason(scheme, method) } : {},
|
|
40
|
+
...attention?.awaiting ? { awaiting: { kind: attention.kind, since: attention.since } } : {}
|
|
41
|
+
});
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
deadline = createSuspendableDeadline({
|
|
46
|
+
bounds,
|
|
47
|
+
onExpire: (bound, elapsedBoundMs) => reject(new ProtocolTimeoutError(call, elapsedBoundMs, attendance, bound))
|
|
48
|
+
});
|
|
49
|
+
if (opts?.onPending) {
|
|
50
|
+
notice = setTimeout(() => {
|
|
51
|
+
noticeFired = true;
|
|
52
|
+
fireNotice();
|
|
53
|
+
}, PENDING_NOTICE_MS);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
unsubscribeAttention = onHostAttentionChange((next) => {
|
|
57
|
+
const wasAwaiting = attention?.awaiting ?? false;
|
|
58
|
+
attention = next;
|
|
59
|
+
deadline?.setAwaiting(next.awaiting);
|
|
60
|
+
if (noticeFired && next.awaiting !== wasAwaiting) fireNotice();
|
|
61
|
+
});
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
if (signal) {
|
|
65
|
+
onAbort = () => reject(new ProtocolCancelledError(call));
|
|
66
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
} finally {
|
|
70
|
+
deadline?.dispose();
|
|
71
|
+
if (notice !== void 0) clearTimeout(notice);
|
|
72
|
+
unsubscribeAttention?.();
|
|
73
|
+
if (signal && onAbort) signal.removeEventListener("abort", onAbort);
|
|
74
|
+
work.catch(() => void 0);
|
|
8
75
|
}
|
|
9
|
-
const t = getHostRuntime()?.transport;
|
|
10
|
-
if (t && typeof t.sendMessage === "function") return t;
|
|
11
|
-
throw new Error("immediately.run: no host transport (neither injected nor __immediatelyRun__)");
|
|
12
76
|
}
|
|
13
|
-
const sendMessage = (type, data = {}) => {
|
|
14
|
-
transport().sendMessage(type, data);
|
|
15
|
-
};
|
|
16
|
-
const protocolRequest = (protocolName, method, params) => transport().protocolRequest(protocolName, method, params);
|
|
17
|
-
const addListener = (msgType, handler, event) => {
|
|
18
|
-
const onMessage = event ?? transport().onMessage;
|
|
19
|
-
const disposable = onMessage((msg) => {
|
|
20
|
-
if (msg.type === msgType) {
|
|
21
|
-
handler(msg);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
return () => disposable.dispose();
|
|
25
|
-
};
|
|
26
77
|
export {
|
|
27
78
|
addListener,
|
|
28
79
|
protocolRequest,
|
|
29
|
-
sendMessage
|
|
80
|
+
sendMessage,
|
|
81
|
+
withDeadline
|
|
30
82
|
};
|
|
31
83
|
//# sourceMappingURL=sandboxUtils.js.map
|
package/dist/sandboxUtils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sandboxUtils.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"sources":["../src/sandboxUtils.ts"],"sourcesContent":["// Bounded host calls (R3-298) + the host-attention-aware deadline (R3-307).\n//\n// The transport primitives this builds on moved to `hostTransport.ts` — see that file for\n// why. `sendMessage` and `addListener` are re-exported here unchanged, so every existing\n// import site (and this module's api-snapshot entry) is exactly as it was.\nimport { transport } from './hostTransport';\nimport { onHostAttentionChange, type HostAttention } from './hostAttention';\n\nimport {\n attendanceOf,\n attendanceReason,\n boundsFor,\n createSuspendableDeadline,\n PENDING_NOTICE_MS,\n ProtocolCancelledError,\n ProtocolTimeoutError,\n type BoundedCallOptions,\n type CallBounds,\n} from './protocolDeadline';\n\nexport { sendMessage, addListener } from './hostTransport';\n\n/**\n * One host protocol request, BOUNDED (R3-298).\n *\n * The bound comes from the call's classification (`protocolDeadline.ts`): an unattended\n * channel round-trip gets tens of seconds, a call that may draw host chrome and wait for a\n * person gets minutes. Nothing is unbounded — an unbounded wait is the failure this fixes.\n *\n * Since R3-307 a call whose prompts the host actually announces runs on the SHORT bound and\n * is suspended only while the host says a person is being asked, so the common\n * grant-already-held path reports a fault in seconds instead of minutes. The absolute\n * ceiling still applies: the signal may extend a deadline, never remove it.\n *\n * The host's own work is NOT cancelled by `signal` or by the deadline. The one-shot\n * transport allocates its `msgId` internally, so the SDK has no handle to send the host a\n * cancel for (streams do, and `consumeStream` uses it). What the caller gets back is\n * control: it stops waiting and gets a typed error instead of hanging. A host prompt that\n * is already on screen stays there until the user dismisses it, which is correct — the SDK\n * must not be able to tear down host chrome the user is looking at.\n */\nexport const protocolRequest = (\n protocolName: string,\n method: string,\n params: Array<any>,\n opts?: BoundedCallOptions,\n): Promise<any> =>\n withDeadline(protocolName, method, () => transport().protocolRequest(protocolName, method, params), opts);\n\n/**\n * Race a host call against its deadline, a caller abort, and a pending notice.\n *\n * Kept separate from the transport so it is unit-testable against a promise that simply\n * never settles — which is the whole scenario, and one no live transport reproduces on\n * demand.\n *\n * An explicit `opts.timeoutMs` is the WHOLE bound and is never suspended: a caller that\n * names a number owns the wait, and silently stretching it past what they asked for would\n * be the same class of surprise this machinery exists to remove.\n */\nexport async function withDeadline<T>(\n scheme: string,\n method: string,\n start: () => Promise<T>,\n opts?: BoundedCallOptions,\n): Promise<T> {\n const call = `${scheme}:${method}`;\n const attendance = attendanceOf(scheme, method);\n const bounds: CallBounds =\n opts?.timeoutMs !== undefined ? { idleMs: opts.timeoutMs, ceilingMs: opts.timeoutMs } : boundsFor(scheme, method);\n const signal = opts?.signal;\n\n if (signal?.aborted) throw new ProtocolCancelledError(call);\n\n const work = start();\n // No bound and no abort wanted: hand back the untouched promise rather than wrapping it\n // in timers that would never fire.\n if (!Number.isFinite(bounds.ceilingMs) && !signal && !opts?.onPending) return work;\n\n let deadline: { setAwaiting: (a: boolean) => void; dispose: () => void } | undefined;\n let notice: ReturnType<typeof setTimeout> | undefined;\n let unsubscribeAttention: (() => void) | undefined;\n let onAbort: (() => void) | undefined;\n const started = Date.now();\n\n try {\n return await new Promise<T>((resolve, reject) => {\n // `work` settling always wins — a call that answered a millisecond before its\n // deadline must not be reported as a timeout.\n work.then(resolve, reject);\n\n // The host's live \"a person is being asked\" signal. Read defensively: a host that\n // never pushes the channel leaves this at \"not awaiting\", which is exactly the\n // pre-R3-307 behaviour for a call on its idle bound.\n let attention: HostAttention | undefined;\n let noticeFired = false;\n const fireNotice = () => {\n try {\n opts?.onPending?.({\n call,\n attendance,\n elapsedMs: Date.now() - started,\n ...(attendanceReason(scheme, method) ? { reason: attendanceReason(scheme, method) as string } : {}),\n ...(attention?.awaiting ? { awaiting: { kind: attention.kind, since: attention.since } } : {}),\n });\n } catch {\n /* a caller's render callback must never break the call it describes */\n }\n };\n\n deadline = createSuspendableDeadline({\n bounds,\n onExpire: (bound, elapsedBoundMs) => reject(new ProtocolTimeoutError(call, elapsedBoundMs, attendance, bound)),\n });\n\n if (opts?.onPending) {\n notice = setTimeout(() => {\n noticeFired = true;\n fireNotice();\n }, PENDING_NOTICE_MS);\n }\n\n // Subscribing invokes the listener immediately with the current value, so the\n // deadline starts in the right state even if a prompt was already up.\n //\n // Guarded because this sits on the path of EVERY host call: a fault in the attention\n // channel must degrade to \"no signal\" (the pre-R3-307 behaviour, bounds unsuspended),\n // never take down every request in the SDK.\n try {\n unsubscribeAttention = onHostAttentionChange((next) => {\n const wasAwaiting = attention?.awaiting ?? false;\n attention = next;\n deadline?.setAwaiting(next.awaiting);\n // Re-notify once the notice has fired: \"still waiting\" → \"tap your passkey\" is\n // the whole point, and a caller rendering a waiting state wants the live\n // sentence, not the one that was true three seconds in.\n if (noticeFired && next.awaiting !== wasAwaiting) fireNotice();\n });\n } catch {\n /* no attention signal available — the bounds simply never suspend */\n }\n\n if (signal) {\n onAbort = () => reject(new ProtocolCancelledError(call));\n signal.addEventListener('abort', onAbort, { once: true });\n }\n });\n } finally {\n deadline?.dispose();\n if (notice !== undefined) clearTimeout(notice);\n unsubscribeAttention?.();\n if (signal && onAbort) signal.removeEventListener('abort', onAbort);\n // The host may still answer after we stopped waiting; swallow it so a late rejection\n // does not surface as an unhandled promise rejection in the app's console.\n work.catch(() => undefined);\n }\n}\n"],"mappings":";AAKA,SAAS,iBAAiB;AAC1B,SAAS,6BAAiD;AAE1D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAEP,SAAS,aAAa,mBAAmB;AAqBlC,MAAM,kBAAkB,CAC7B,cACA,QACA,QACA,SAEA,aAAa,cAAc,QAAQ,MAAM,UAAU,EAAE,gBAAgB,cAAc,QAAQ,MAAM,GAAG,IAAI;AAa1G,eAAsB,aACpB,QACA,QACA,OACA,MACY;AACZ,QAAM,OAAO,GAAG,MAAM,IAAI,MAAM;AAChC,QAAM,aAAa,aAAa,QAAQ,MAAM;AAC9C,QAAM,SACJ,MAAM,cAAc,SAAY,EAAE,QAAQ,KAAK,WAAW,WAAW,KAAK,UAAU,IAAI,UAAU,QAAQ,MAAM;AAClH,QAAM,SAAS,MAAM;AAErB,MAAI,QAAQ,QAAS,OAAM,IAAI,uBAAuB,IAAI;AAE1D,QAAM,OAAO,MAAM;AAGnB,MAAI,CAAC,OAAO,SAAS,OAAO,SAAS,KAAK,CAAC,UAAU,CAAC,MAAM,UAAW,QAAO;AAE9E,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,QAAM,UAAU,KAAK,IAAI;AAEzB,MAAI;AACF,WAAO,MAAM,IAAI,QAAW,CAAC,SAAS,WAAW;AAG/C,WAAK,KAAK,SAAS,MAAM;AAKzB,UAAI;AACJ,UAAI,cAAc;AAClB,YAAM,aAAa,MAAM;AACvB,YAAI;AACF,gBAAM,YAAY;AAAA,YAChB;AAAA,YACA;AAAA,YACA,WAAW,KAAK,IAAI,IAAI;AAAA,YACxB,GAAI,iBAAiB,QAAQ,MAAM,IAAI,EAAE,QAAQ,iBAAiB,QAAQ,MAAM,EAAY,IAAI,CAAC;AAAA,YACjG,GAAI,WAAW,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,MAAM,OAAO,UAAU,MAAM,EAAE,IAAI,CAAC;AAAA,UAC9F,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,iBAAW,0BAA0B;AAAA,QACnC;AAAA,QACA,UAAU,CAAC,OAAO,mBAAmB,OAAO,IAAI,qBAAqB,MAAM,gBAAgB,YAAY,KAAK,CAAC;AAAA,MAC/G,CAAC;AAED,UAAI,MAAM,WAAW;AACnB,iBAAS,WAAW,MAAM;AACxB,wBAAc;AACd,qBAAW;AAAA,QACb,GAAG,iBAAiB;AAAA,MACtB;AAQA,UAAI;AACF,+BAAuB,sBAAsB,CAAC,SAAS;AACrD,gBAAM,cAAc,WAAW,YAAY;AAC3C,sBAAY;AACZ,oBAAU,YAAY,KAAK,QAAQ;AAInC,cAAI,eAAe,KAAK,aAAa,YAAa,YAAW;AAAA,QAC/D,CAAC;AAAA,MACH,QAAQ;AAAA,MAER;AAEA,UAAI,QAAQ;AACV,kBAAU,MAAM,OAAO,IAAI,uBAAuB,IAAI,CAAC;AACvD,eAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,MAC1D;AAAA,IACF,CAAC;AAAA,EACH,UAAE;AACA,cAAU,QAAQ;AAClB,QAAI,WAAW,OAAW,cAAa,MAAM;AAC7C,2BAAuB;AACvB,QAAI,UAAU,QAAS,QAAO,oBAAoB,SAAS,OAAO;AAGlE,SAAK,MAAM,MAAM,MAAS;AAAA,EAC5B;AACF;","names":[]}
|
package/dist/scrollToId.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/scrollToId.ts"],"sourcesContent":["/**\n * Scroll the element addressed by a fragment id into view (deep-linking Capability\n * C, MARKDOWN_SYNTAX_SPEC §13.5). Tries the element's own `id` first — a heading's\n * `sec-…`/text-slug id (§15) — then the `[data-slug]` fallback, so a citation that\n * used the prose text slug still lands even when the heading's own id is the\n * prose-independent `sec-…` id. Returns whether a target was found; the caller\n * decides whether to fall back to top-of-page. **Never throws** — a missing\n * fragment is a soft failure, not an error.\n */\nexport const scrollToId = (id: string): boolean => {\n if (!id || typeof document === 'undefined') return false;\n let el: Element | null = null;\n try {\n el = document.getElementById(id);\n if (!el) {\n // A citation that targeted the prose text slug still resolves via the\n // `data-slug` hook the kernel emits alongside the `sec-…` id.\n const escaped =\n typeof CSS !== 'undefined' && typeof CSS.escape === 'function'
|
|
1
|
+
{"version":3,"sources":["../src/scrollToId.ts"],"sourcesContent":["/**\n * Scroll the element addressed by a fragment id into view (deep-linking Capability\n * C, MARKDOWN_SYNTAX_SPEC §13.5). Tries the element's own `id` first — a heading's\n * `sec-…`/text-slug id (§15) — then the `[data-slug]` fallback, so a citation that\n * used the prose text slug still lands even when the heading's own id is the\n * prose-independent `sec-…` id. Returns whether a target was found; the caller\n * decides whether to fall back to top-of-page. **Never throws** — a missing\n * fragment is a soft failure, not an error.\n */\nexport const scrollToId = (id: string): boolean => {\n if (!id || typeof document === 'undefined') return false;\n let el: Element | null = null;\n try {\n el = document.getElementById(id);\n if (!el) {\n // A citation that targeted the prose text slug still resolves via the\n // `data-slug` hook the kernel emits alongside the `sec-…` id.\n const escaped =\n typeof CSS !== 'undefined' && typeof CSS.escape === 'function' ? CSS.escape(id) : id.replace(/[\"\\\\]/g, '\\\\$&');\n el = document.querySelector(`[data-slug=\"${escaped}\"]`);\n }\n } catch {\n return false;\n }\n if (el && typeof (el as HTMLElement).scrollIntoView === 'function') {\n (el as HTMLElement).scrollIntoView();\n return true;\n }\n return false;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AASO,MAAM,aAAa,CAAC,OAAwB;AACjD,MAAI,CAAC,MAAM,OAAO,aAAa,YAAa,QAAO;AACnD,MAAI,KAAqB;AACzB,MAAI;AACF,SAAK,SAAS,eAAe,EAAE;AAC/B,QAAI,CAAC,IAAI;AAGP,YAAM,UACJ,OAAO,QAAQ,eAAe,OAAO,IAAI,WAAW,aAAa,IAAI,OAAO,EAAE,IAAI,GAAG,QAAQ,UAAU,MAAM;AAC/G,WAAK,SAAS,cAAc,eAAe,OAAO,IAAI;AAAA,IACxD;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,MAAM,OAAQ,GAAmB,mBAAmB,YAAY;AAClE,IAAC,GAAmB,eAAe;AACnC,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
|
package/dist/scrollToId.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/scrollToId.ts"],"sourcesContent":["/**\n * Scroll the element addressed by a fragment id into view (deep-linking Capability\n * C, MARKDOWN_SYNTAX_SPEC §13.5). Tries the element's own `id` first — a heading's\n * `sec-…`/text-slug id (§15) — then the `[data-slug]` fallback, so a citation that\n * used the prose text slug still lands even when the heading's own id is the\n * prose-independent `sec-…` id. Returns whether a target was found; the caller\n * decides whether to fall back to top-of-page. **Never throws** — a missing\n * fragment is a soft failure, not an error.\n */\nexport const scrollToId = (id: string): boolean => {\n if (!id || typeof document === 'undefined') return false;\n let el: Element | null = null;\n try {\n el = document.getElementById(id);\n if (!el) {\n // A citation that targeted the prose text slug still resolves via the\n // `data-slug` hook the kernel emits alongside the `sec-…` id.\n const escaped =\n typeof CSS !== 'undefined' && typeof CSS.escape === 'function'
|
|
1
|
+
{"version":3,"sources":["../src/scrollToId.ts"],"sourcesContent":["/**\n * Scroll the element addressed by a fragment id into view (deep-linking Capability\n * C, MARKDOWN_SYNTAX_SPEC §13.5). Tries the element's own `id` first — a heading's\n * `sec-…`/text-slug id (§15) — then the `[data-slug]` fallback, so a citation that\n * used the prose text slug still lands even when the heading's own id is the\n * prose-independent `sec-…` id. Returns whether a target was found; the caller\n * decides whether to fall back to top-of-page. **Never throws** — a missing\n * fragment is a soft failure, not an error.\n */\nexport const scrollToId = (id: string): boolean => {\n if (!id || typeof document === 'undefined') return false;\n let el: Element | null = null;\n try {\n el = document.getElementById(id);\n if (!el) {\n // A citation that targeted the prose text slug still resolves via the\n // `data-slug` hook the kernel emits alongside the `sec-…` id.\n const escaped =\n typeof CSS !== 'undefined' && typeof CSS.escape === 'function' ? CSS.escape(id) : id.replace(/[\"\\\\]/g, '\\\\$&');\n el = document.querySelector(`[data-slug=\"${escaped}\"]`);\n }\n } catch {\n return false;\n }\n if (el && typeof (el as HTMLElement).scrollIntoView === 'function') {\n (el as HTMLElement).scrollIntoView();\n return true;\n }\n return false;\n};\n"],"mappings":";AASO,MAAM,aAAa,CAAC,OAAwB;AACjD,MAAI,CAAC,MAAM,OAAO,aAAa,YAAa,QAAO;AACnD,MAAI,KAAqB;AACzB,MAAI;AACF,SAAK,SAAS,eAAe,EAAE;AAC/B,QAAI,CAAC,IAAI;AAGP,YAAM,UACJ,OAAO,QAAQ,eAAe,OAAO,IAAI,WAAW,aAAa,IAAI,OAAO,EAAE,IAAI,GAAG,QAAQ,UAAU,MAAM;AAC/G,WAAK,SAAS,cAAc,eAAe,OAAO,IAAI;AAAA,IACxD;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,MAAM,OAAQ,GAAmB,mBAAmB,YAAY;AAClE,IAAC,GAAmB,eAAe;AACnC,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
|
package/dist/secrets.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/secrets.ts"],"sourcesContent":["// The host-owned secret store — app-facing surface (SECRETS_SPEC §4/§5).\n//\n// An app can ASK the user to store a secret (`requestAddSecret`), be GRANTED the\n// right to USE a specific secret (`requestSecret`, the powerbox flow), LIST\n// secret METADATA (`getSecrets`/`useSecrets`, never values), and REVOKE one\n// (`revokeSecret`). The secret VALUE never crosses this boundary: it is read\n// host-side, once, at the `net:fetch` injection point (SECRETS_SPEC §6). These\n// functions move only hints, metadata, and grant handles.\n//\n// Resolves LLM_AND_AGENTS_SPEC D2 — the host-mediated BYOK key store. Inert until\n// the host implements `protocol-secrets` + the `secrets-metadata` channel\n// (SECRETS_SPEC §3/§6 host work, roadmap P1.E); the contract is shipped here so\n// apps (e.g. the in-browser coding agent, P3-73) can be written against it.\nimport { protocolRequest } from './sandboxUtils';\nimport { createPushChannel } from './pushChannel';\nimport { PROTOCOL_SECRETS, REQUEST_SECRETS_METADATA, SECRETS_METADATA } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** The closed secret-type vocabulary (SECRETS_SPEC §2). `api-key` is always\n * origin-bound; `oauth-refresh` is reserved (no substitution in v1). */\nexport type SecretType = 'api-key' | 'bearer-token' | 'oauth-refresh';\n\n/**\n * The metadata-only projection of a stored secret (SECRETS_SPEC §2/§4) — exactly\n * what `secrets:list` and the powerbox return. **There is no `value` field by\n * design**: the plaintext is never part of any record an app receives.\n */\nexport interface SecretView {\n id: string;\n type: SecretType;\n family?: string;\n description: string;\n /** Required for `type:'api-key'` — the one https origin it may be sent to. */\n boundOrigin?: string;\n /** ISO-8601, or null if never used (drives the §8.15 90-day expiry). */\n lastUsedAt: string | null;\n}\n\n/** Hints for the host's \"add secret\" modal (SECRETS_SPEC §4 `secrets:add`). The\n * app supplies only hints; the user types the value into host chrome. */\nexport interface SecretHints {\n type?: SecretType;\n family?: string;\n /** Pre-fill the bound origin (e.g. `https://api.anthropic.com`). */\n suggestedOrigin?: string;\n description?: string;\n}\n\n/** What `requestSecret()` matches against in the powerbox picker (SECRETS_SPEC §5). */\nexport interface SecretQuery {\n type?: SecretType;\n family?: string;\n}\n\n/**\n * The result of a granted `requestSecret()` — a durable `(appKey, secretId)` use\n * grant plus the secret's metadata. **Never the value.** Hold onto nothing but\n * this; the host substitutes the value into matching `net:fetch` requests.\n */\nexport interface SecretGrant {\n /** Opaque handle for the minted `(appKey, secretId)` grant. */\n grantId: string;\n /** Metadata of the bound secret (no value). */\n secret: SecretView;\n}\n\n/** An error from a secret operation, carrying a machine-readable `code`. */\nexport interface SecretError extends Error {\n code: 'auth-required' | 'cancelled' | 'forbidden' | 'not-found' | 'invalid-params' | 'unknown';\n}\n\ntype SecretResult
|
|
1
|
+
{"version":3,"sources":["../src/secrets.ts"],"sourcesContent":["// The host-owned secret store — app-facing surface (SECRETS_SPEC §4/§5).\n//\n// An app can ASK the user to store a secret (`requestAddSecret`), be GRANTED the\n// right to USE a specific secret (`requestSecret`, the powerbox flow), LIST\n// secret METADATA (`getSecrets`/`useSecrets`, never values), and REVOKE one\n// (`revokeSecret`). The secret VALUE never crosses this boundary: it is read\n// host-side, once, at the `net:fetch` injection point (SECRETS_SPEC §6). These\n// functions move only hints, metadata, and grant handles.\n//\n// Resolves LLM_AND_AGENTS_SPEC D2 — the host-mediated BYOK key store. Inert until\n// the host implements `protocol-secrets` + the `secrets-metadata` channel\n// (SECRETS_SPEC §3/§6 host work, roadmap P1.E); the contract is shipped here so\n// apps (e.g. the in-browser coding agent, P3-73) can be written against it.\nimport { protocolRequest } from './sandboxUtils';\nimport { createPushChannel } from './pushChannel';\nimport { PROTOCOL_SECRETS, REQUEST_SECRETS_METADATA, SECRETS_METADATA } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** The closed secret-type vocabulary (SECRETS_SPEC §2). `api-key` is always\n * origin-bound; `oauth-refresh` is reserved (no substitution in v1). */\nexport type SecretType = 'api-key' | 'bearer-token' | 'oauth-refresh';\n\n/**\n * The metadata-only projection of a stored secret (SECRETS_SPEC §2/§4) — exactly\n * what `secrets:list` and the powerbox return. **There is no `value` field by\n * design**: the plaintext is never part of any record an app receives.\n */\nexport interface SecretView {\n id: string;\n type: SecretType;\n family?: string;\n description: string;\n /** Required for `type:'api-key'` — the one https origin it may be sent to. */\n boundOrigin?: string;\n /** ISO-8601, or null if never used (drives the §8.15 90-day expiry). */\n lastUsedAt: string | null;\n}\n\n/** Hints for the host's \"add secret\" modal (SECRETS_SPEC §4 `secrets:add`). The\n * app supplies only hints; the user types the value into host chrome. */\nexport interface SecretHints {\n type?: SecretType;\n family?: string;\n /** Pre-fill the bound origin (e.g. `https://api.anthropic.com`). */\n suggestedOrigin?: string;\n description?: string;\n}\n\n/** What `requestSecret()` matches against in the powerbox picker (SECRETS_SPEC §5). */\nexport interface SecretQuery {\n type?: SecretType;\n family?: string;\n}\n\n/**\n * The result of a granted `requestSecret()` — a durable `(appKey, secretId)` use\n * grant plus the secret's metadata. **Never the value.** Hold onto nothing but\n * this; the host substitutes the value into matching `net:fetch` requests.\n */\nexport interface SecretGrant {\n /** Opaque handle for the minted `(appKey, secretId)` grant. */\n grantId: string;\n /** Metadata of the bound secret (no value). */\n secret: SecretView;\n}\n\n/** An error from a secret operation, carrying a machine-readable `code`. */\nexport interface SecretError extends Error {\n code: 'auth-required' | 'cancelled' | 'forbidden' | 'not-found' | 'invalid-params' | 'unknown';\n}\n\ntype SecretResult = { ok: true; data: unknown } | { ok: false; code: string; message: string };\n\n// Issue a `protocol-secrets` request, unwrapping the host's {ok,data} envelope\n// and throwing a typed SecretError on failure (mirrors mounts.ts `request`).\nconst request = async <T = unknown>(method: string, query: object = {}): Promise<T> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_SECRETS], method, [query])) as SecretResult;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'secret request failed') as SecretError;\n err.code = (res?.code as SecretError['code']) ?? 'unknown';\n throw err;\n }\n return res.data as T;\n};\n\n/**\n * Ask the user to store a new secret (SECRETS_SPEC §4 `secrets:add`). Opens a\n * **host-drawn** modal (the value is typed into host chrome, never via the app);\n * resolves with the new secret's {@link SecretView} metadata, or rejects with a\n * {@link SecretError} (`cancelled` if the user dismisses the modal). Requires the\n * `secrets:add` capability.\n */\nexport const requestAddSecret = (hints: SecretHints = {}): Promise<SecretView> => request<SecretView>('add', hints);\n\n/**\n * Ask the user to bind one of their stored secrets to this app (SECRETS_SPEC §5,\n * the powerbox flow — modeled on `requestSpace()`). The host draws a picker of\n * **only the user's matching secrets**; the user picks, declines, or creates one.\n * On success the host records a durable `(appKey, secretId)` grant and resolves\n * with a {@link SecretGrant} (handle + metadata, **never the value**).\n *\n * **No existence oracle (T20/T27):** a decline, an ungranted secret, and a\n * nonexistent secret are indistinguishable — all reject with a {@link SecretError}\n * `cancelled`; the app never sees the list it chose from.\n */\nexport const requestSecret = (query: SecretQuery = {}): Promise<SecretGrant> => request<SecretGrant>('request', query);\n\n/**\n * Delete a stored secret and tombstone every dependent per-app use grant\n * (SECRETS_SPEC §4 `secrets:revoke`, §8.15 cascade). Requires `secrets:revoke`.\n */\nexport const revokeSecret = async (id: string): Promise<void> => {\n await request('revoke', { id });\n};\n\n// The metadata-only `secrets-metadata` channel (Recipe A): the host pushes the\n// current secret metadata on change and replays it on register-frame; gated by\n// `secrets:list`. NEVER carries a value (SECRETS_SPEC §4).\nconst channel = createPushChannel<SecretView[]>({\n pushType: SECRETS_METADATA,\n requestType: REQUEST_SECRETS_METADATA,\n initial: [],\n parse: (msg) => (Array.isArray(msg.secrets) ? (msg.secrets as SecretView[]) : undefined),\n});\n\n/** The metadata of the user's stored secrets (never values), `secrets:list`. Poll\n * for a one-off read; use {@link onSecretsChange}/{@link useSecrets} to react. */\nexport const getSecrets = (): SecretView[] => channel.get();\n\n/** Subscribe to secret-metadata changes (added/revoked). Invoked immediately with\n * the current list, then on every change. Returns an unsubscribe fn. */\nexport const onSecretsChange = (listener: (secrets: SecretView[]) => void): (() => void) => channel.onChange(listener);\n\n/** React hook returning the user's secret metadata (never values), re-rendering\n * on change. For the Settings app (SECRETS_SPEC §7). */\nexport const useSecrets = (): SecretView[] => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,0BAAgC;AAChC,yBAAkC;AAClC,sBAA6E;AAC7E,6BAAwB;AA2DxB,MAAM,UAAU,OAAoB,QAAgB,QAAgB,CAAC,MAAkB;AACrF,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,gCAAgB,GAAG,QAAQ,CAAC,KAAK,CAAC;AAC7E,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,uBAAuB;AAC7D,QAAI,OAAQ,KAAK,QAAgC;AACjD,UAAM;AAAA,EACR;AACA,SAAO,IAAI;AACb;AASO,MAAM,mBAAmB,CAAC,QAAqB,CAAC,MAA2B,QAAoB,OAAO,KAAK;AAa3G,MAAM,gBAAgB,CAAC,QAAqB,CAAC,MAA4B,QAAqB,WAAW,KAAK;AAM9G,MAAM,eAAe,OAAO,OAA8B;AAC/D,QAAM,QAAQ,UAAU,EAAE,GAAG,CAAC;AAChC;AAKA,MAAM,cAAU,sCAAgC;AAAA,EAC9C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,CAAC;AAAA,EACV,OAAO,CAAC,QAAS,MAAM,QAAQ,IAAI,OAAO,IAAK,IAAI,UAA2B;AAChF,CAAC;AAIM,MAAM,aAAa,MAAoB,QAAQ,IAAI;AAInD,MAAM,kBAAkB,CAAC,aAA4D,QAAQ,SAAS,QAAQ;AAI9G,MAAM,aAAa,MAAoB,QAAQ,IAAI;","names":[]}
|
package/dist/secrets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/secrets.ts"],"sourcesContent":["// The host-owned secret store — app-facing surface (SECRETS_SPEC §4/§5).\n//\n// An app can ASK the user to store a secret (`requestAddSecret`), be GRANTED the\n// right to USE a specific secret (`requestSecret`, the powerbox flow), LIST\n// secret METADATA (`getSecrets`/`useSecrets`, never values), and REVOKE one\n// (`revokeSecret`). The secret VALUE never crosses this boundary: it is read\n// host-side, once, at the `net:fetch` injection point (SECRETS_SPEC §6). These\n// functions move only hints, metadata, and grant handles.\n//\n// Resolves LLM_AND_AGENTS_SPEC D2 — the host-mediated BYOK key store. Inert until\n// the host implements `protocol-secrets` + the `secrets-metadata` channel\n// (SECRETS_SPEC §3/§6 host work, roadmap P1.E); the contract is shipped here so\n// apps (e.g. the in-browser coding agent, P3-73) can be written against it.\nimport { protocolRequest } from './sandboxUtils';\nimport { createPushChannel } from './pushChannel';\nimport { PROTOCOL_SECRETS, REQUEST_SECRETS_METADATA, SECRETS_METADATA } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** The closed secret-type vocabulary (SECRETS_SPEC §2). `api-key` is always\n * origin-bound; `oauth-refresh` is reserved (no substitution in v1). */\nexport type SecretType = 'api-key' | 'bearer-token' | 'oauth-refresh';\n\n/**\n * The metadata-only projection of a stored secret (SECRETS_SPEC §2/§4) — exactly\n * what `secrets:list` and the powerbox return. **There is no `value` field by\n * design**: the plaintext is never part of any record an app receives.\n */\nexport interface SecretView {\n id: string;\n type: SecretType;\n family?: string;\n description: string;\n /** Required for `type:'api-key'` — the one https origin it may be sent to. */\n boundOrigin?: string;\n /** ISO-8601, or null if never used (drives the §8.15 90-day expiry). */\n lastUsedAt: string | null;\n}\n\n/** Hints for the host's \"add secret\" modal (SECRETS_SPEC §4 `secrets:add`). The\n * app supplies only hints; the user types the value into host chrome. */\nexport interface SecretHints {\n type?: SecretType;\n family?: string;\n /** Pre-fill the bound origin (e.g. `https://api.anthropic.com`). */\n suggestedOrigin?: string;\n description?: string;\n}\n\n/** What `requestSecret()` matches against in the powerbox picker (SECRETS_SPEC §5). */\nexport interface SecretQuery {\n type?: SecretType;\n family?: string;\n}\n\n/**\n * The result of a granted `requestSecret()` — a durable `(appKey, secretId)` use\n * grant plus the secret's metadata. **Never the value.** Hold onto nothing but\n * this; the host substitutes the value into matching `net:fetch` requests.\n */\nexport interface SecretGrant {\n /** Opaque handle for the minted `(appKey, secretId)` grant. */\n grantId: string;\n /** Metadata of the bound secret (no value). */\n secret: SecretView;\n}\n\n/** An error from a secret operation, carrying a machine-readable `code`. */\nexport interface SecretError extends Error {\n code: 'auth-required' | 'cancelled' | 'forbidden' | 'not-found' | 'invalid-params' | 'unknown';\n}\n\ntype SecretResult
|
|
1
|
+
{"version":3,"sources":["../src/secrets.ts"],"sourcesContent":["// The host-owned secret store — app-facing surface (SECRETS_SPEC §4/§5).\n//\n// An app can ASK the user to store a secret (`requestAddSecret`), be GRANTED the\n// right to USE a specific secret (`requestSecret`, the powerbox flow), LIST\n// secret METADATA (`getSecrets`/`useSecrets`, never values), and REVOKE one\n// (`revokeSecret`). The secret VALUE never crosses this boundary: it is read\n// host-side, once, at the `net:fetch` injection point (SECRETS_SPEC §6). These\n// functions move only hints, metadata, and grant handles.\n//\n// Resolves LLM_AND_AGENTS_SPEC D2 — the host-mediated BYOK key store. Inert until\n// the host implements `protocol-secrets` + the `secrets-metadata` channel\n// (SECRETS_SPEC §3/§6 host work, roadmap P1.E); the contract is shipped here so\n// apps (e.g. the in-browser coding agent, P3-73) can be written against it.\nimport { protocolRequest } from './sandboxUtils';\nimport { createPushChannel } from './pushChannel';\nimport { PROTOCOL_SECRETS, REQUEST_SECRETS_METADATA, SECRETS_METADATA } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** The closed secret-type vocabulary (SECRETS_SPEC §2). `api-key` is always\n * origin-bound; `oauth-refresh` is reserved (no substitution in v1). */\nexport type SecretType = 'api-key' | 'bearer-token' | 'oauth-refresh';\n\n/**\n * The metadata-only projection of a stored secret (SECRETS_SPEC §2/§4) — exactly\n * what `secrets:list` and the powerbox return. **There is no `value` field by\n * design**: the plaintext is never part of any record an app receives.\n */\nexport interface SecretView {\n id: string;\n type: SecretType;\n family?: string;\n description: string;\n /** Required for `type:'api-key'` — the one https origin it may be sent to. */\n boundOrigin?: string;\n /** ISO-8601, or null if never used (drives the §8.15 90-day expiry). */\n lastUsedAt: string | null;\n}\n\n/** Hints for the host's \"add secret\" modal (SECRETS_SPEC §4 `secrets:add`). The\n * app supplies only hints; the user types the value into host chrome. */\nexport interface SecretHints {\n type?: SecretType;\n family?: string;\n /** Pre-fill the bound origin (e.g. `https://api.anthropic.com`). */\n suggestedOrigin?: string;\n description?: string;\n}\n\n/** What `requestSecret()` matches against in the powerbox picker (SECRETS_SPEC §5). */\nexport interface SecretQuery {\n type?: SecretType;\n family?: string;\n}\n\n/**\n * The result of a granted `requestSecret()` — a durable `(appKey, secretId)` use\n * grant plus the secret's metadata. **Never the value.** Hold onto nothing but\n * this; the host substitutes the value into matching `net:fetch` requests.\n */\nexport interface SecretGrant {\n /** Opaque handle for the minted `(appKey, secretId)` grant. */\n grantId: string;\n /** Metadata of the bound secret (no value). */\n secret: SecretView;\n}\n\n/** An error from a secret operation, carrying a machine-readable `code`. */\nexport interface SecretError extends Error {\n code: 'auth-required' | 'cancelled' | 'forbidden' | 'not-found' | 'invalid-params' | 'unknown';\n}\n\ntype SecretResult = { ok: true; data: unknown } | { ok: false; code: string; message: string };\n\n// Issue a `protocol-secrets` request, unwrapping the host's {ok,data} envelope\n// and throwing a typed SecretError on failure (mirrors mounts.ts `request`).\nconst request = async <T = unknown>(method: string, query: object = {}): Promise<T> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_SECRETS], method, [query])) as SecretResult;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'secret request failed') as SecretError;\n err.code = (res?.code as SecretError['code']) ?? 'unknown';\n throw err;\n }\n return res.data as T;\n};\n\n/**\n * Ask the user to store a new secret (SECRETS_SPEC §4 `secrets:add`). Opens a\n * **host-drawn** modal (the value is typed into host chrome, never via the app);\n * resolves with the new secret's {@link SecretView} metadata, or rejects with a\n * {@link SecretError} (`cancelled` if the user dismisses the modal). Requires the\n * `secrets:add` capability.\n */\nexport const requestAddSecret = (hints: SecretHints = {}): Promise<SecretView> => request<SecretView>('add', hints);\n\n/**\n * Ask the user to bind one of their stored secrets to this app (SECRETS_SPEC §5,\n * the powerbox flow — modeled on `requestSpace()`). The host draws a picker of\n * **only the user's matching secrets**; the user picks, declines, or creates one.\n * On success the host records a durable `(appKey, secretId)` grant and resolves\n * with a {@link SecretGrant} (handle + metadata, **never the value**).\n *\n * **No existence oracle (T20/T27):** a decline, an ungranted secret, and a\n * nonexistent secret are indistinguishable — all reject with a {@link SecretError}\n * `cancelled`; the app never sees the list it chose from.\n */\nexport const requestSecret = (query: SecretQuery = {}): Promise<SecretGrant> => request<SecretGrant>('request', query);\n\n/**\n * Delete a stored secret and tombstone every dependent per-app use grant\n * (SECRETS_SPEC §4 `secrets:revoke`, §8.15 cascade). Requires `secrets:revoke`.\n */\nexport const revokeSecret = async (id: string): Promise<void> => {\n await request('revoke', { id });\n};\n\n// The metadata-only `secrets-metadata` channel (Recipe A): the host pushes the\n// current secret metadata on change and replays it on register-frame; gated by\n// `secrets:list`. NEVER carries a value (SECRETS_SPEC §4).\nconst channel = createPushChannel<SecretView[]>({\n pushType: SECRETS_METADATA,\n requestType: REQUEST_SECRETS_METADATA,\n initial: [],\n parse: (msg) => (Array.isArray(msg.secrets) ? (msg.secrets as SecretView[]) : undefined),\n});\n\n/** The metadata of the user's stored secrets (never values), `secrets:list`. Poll\n * for a one-off read; use {@link onSecretsChange}/{@link useSecrets} to react. */\nexport const getSecrets = (): SecretView[] => channel.get();\n\n/** Subscribe to secret-metadata changes (added/revoked). Invoked immediately with\n * the current list, then on every change. Returns an unsubscribe fn. */\nexport const onSecretsChange = (listener: (secrets: SecretView[]) => void): (() => void) => channel.onChange(listener);\n\n/** React hook returning the user's secret metadata (never values), re-rendering\n * on change. For the Settings app (SECRETS_SPEC §7). */\nexport const useSecrets = (): SecretView[] => channel.use();\n"],"mappings":";AAaA,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,kBAAkB,0BAA0B,wBAAwB;AAC7E,SAAS,eAAe;AA2DxB,MAAM,UAAU,OAAoB,QAAgB,QAAgB,CAAC,MAAkB;AACrF,QAAM,MAAO,MAAM,gBAAgB,QAAQ,gBAAgB,GAAG,QAAQ,CAAC,KAAK,CAAC;AAC7E,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,uBAAuB;AAC7D,QAAI,OAAQ,KAAK,QAAgC;AACjD,UAAM;AAAA,EACR;AACA,SAAO,IAAI;AACb;AASO,MAAM,mBAAmB,CAAC,QAAqB,CAAC,MAA2B,QAAoB,OAAO,KAAK;AAa3G,MAAM,gBAAgB,CAAC,QAAqB,CAAC,MAA4B,QAAqB,WAAW,KAAK;AAM9G,MAAM,eAAe,OAAO,OAA8B;AAC/D,QAAM,QAAQ,UAAU,EAAE,GAAG,CAAC;AAChC;AAKA,MAAM,UAAU,kBAAgC;AAAA,EAC9C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,CAAC;AAAA,EACV,OAAO,CAAC,QAAS,MAAM,QAAQ,IAAI,OAAO,IAAK,IAAI,UAA2B;AAChF,CAAC;AAIM,MAAM,aAAa,MAAoB,QAAQ,IAAI;AAInD,MAAM,kBAAkB,CAAC,aAA4D,QAAQ,SAAS,QAAQ;AAI9G,MAAM,aAAa,MAAoB,QAAQ,IAAI;","names":[]}
|
package/dist/tasks.cjs
CHANGED
|
@@ -31,8 +31,18 @@ var import_react = require("react");
|
|
|
31
31
|
var import_sandboxUtils = require("./sandboxUtils");
|
|
32
32
|
var import_protocol = require("./generated/protocol");
|
|
33
33
|
var import_protocolSchemes = require("./protocolSchemes");
|
|
34
|
-
const capFile = (ref, opts) => ({
|
|
35
|
-
|
|
34
|
+
const capFile = (ref, opts) => ({
|
|
35
|
+
$cap: "file",
|
|
36
|
+
mountId: ref.mountId,
|
|
37
|
+
relPath: ref.relPath,
|
|
38
|
+
mode: opts.mode
|
|
39
|
+
});
|
|
40
|
+
const capDir = (ref, opts) => ({
|
|
41
|
+
$cap: "dir",
|
|
42
|
+
mountId: ref.mountId,
|
|
43
|
+
relPath: ref.relPath,
|
|
44
|
+
mode: opts.mode
|
|
45
|
+
});
|
|
36
46
|
const invokeTask = async (task, params = {}) => {
|
|
37
47
|
const res = await (0, import_sandboxUtils.protocolRequest)(import_protocolSchemes.SCHEMES[import_protocol.PROTOCOL_TASK], "invoke", [{ task, params }]);
|
|
38
48
|
if (!res || res.ok !== true) {
|
package/dist/tasks.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tasks.ts"],"sourcesContent":["// Task invocation — apps invoking apps (UI_AS_APPS_SPEC §5.7). The\n// `startActivityForResult` pattern: one app invokes another by TASK CONTRACT\n// (never by app name — the user's override picks the bound app), passes typed\n// params, and awaits a typed result. The callee runs in a host-owned overlay\n// under ITS OWN grants — data crosses, your authority does not (§5.7).\n//\n// Two roles:\n// - CALLER: `invokeTask(task, params)` (Recipe B — a deferred reply the host\n// holds open until the callee finishes). Delegate a file with `capFile(...)`:\n// the host resolves it against YOUR grants and mints an attenuated chroot.\n// - CALLEE: read `useTaskInput()`, then `completeTask(result)` / `cancelTask()`.\nimport { useEffect, useState } from 'react';\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { PROTOCOL_TASK, TASK_CANCEL, TASK_COMPLETE, TASK_INPUT } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n// ── caller side ─────────────────────────────────────────────────────────────\n\n/** A delegated FILE capability marker for a task param (§5.7). */\nexport interface FileCap {\n $cap: 'file';\n mountId: string;\n relPath: string;\n mode: 'ro' | 'rw';\n}\n\n/**\n * Build a delegated file reference for a task param. The host resolves it against\n * YOUR OWN grants and mints an attenuated, task-scoped chroot for the callee — you\n * can only delegate a path you already hold (attenuation only, never escalation).\n *\n * file: capFile({ mountId: 'space:abc', relPath: 'photos/cat.jpg' }, { mode: 'rw' })\n */\nexport const capFile = (
|
|
1
|
+
{"version":3,"sources":["../src/tasks.ts"],"sourcesContent":["// Task invocation — apps invoking apps (UI_AS_APPS_SPEC §5.7). The\n// `startActivityForResult` pattern: one app invokes another by TASK CONTRACT\n// (never by app name — the user's override picks the bound app), passes typed\n// params, and awaits a typed result. The callee runs in a host-owned overlay\n// under ITS OWN grants — data crosses, your authority does not (§5.7).\n//\n// Two roles:\n// - CALLER: `invokeTask(task, params)` (Recipe B — a deferred reply the host\n// holds open until the callee finishes). Delegate a file with `capFile(...)`:\n// the host resolves it against YOUR grants and mints an attenuated chroot.\n// - CALLEE: read `useTaskInput()`, then `completeTask(result)` / `cancelTask()`.\nimport { useEffect, useState } from 'react';\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { PROTOCOL_TASK, TASK_CANCEL, TASK_COMPLETE, TASK_INPUT } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n// ── caller side ─────────────────────────────────────────────────────────────\n\n/** A delegated FILE capability marker for a task param (§5.7). */\nexport interface FileCap {\n $cap: 'file';\n mountId: string;\n relPath: string;\n mode: 'ro' | 'rw';\n}\n\n/**\n * Build a delegated file reference for a task param. The host resolves it against\n * YOUR OWN grants and mints an attenuated, task-scoped chroot for the callee — you\n * can only delegate a path you already hold (attenuation only, never escalation).\n *\n * file: capFile({ mountId: 'space:abc', relPath: 'photos/cat.jpg' }, { mode: 'rw' })\n */\nexport const capFile = (ref: { mountId: string; relPath: string }, opts: { mode: 'ro' | 'rw' }): FileCap => ({\n $cap: 'file',\n mountId: ref.mountId,\n relPath: ref.relPath,\n mode: opts.mode,\n});\n\n/** A delegated DIRECTORY capability marker for a task param (D2). Like {@link FileCap}\n * but `relPath` names a DIRECTORY: the host chroots the callee AT that directory\n * (the whole subtree). Used for the `pick-file` `roots` — one chroot per root. */\nexport interface DirCap {\n $cap: 'dir';\n mountId: string;\n relPath: string;\n mode: 'ro' | 'rw';\n}\n\n/**\n * Build a delegated DIRECTORY reference for a task param (the directory analogue of\n * {@link capFile}). The host resolves it against YOUR OWN grants and mints an\n * attenuated, task-scoped chroot of that directory for the callee — you can only\n * delegate a directory you already hold (attenuation only, never escalation):\n *\n * roots: [capDir({ mountId: 'space:abc', relPath: 'boards' }, { mode: 'rw' })]\n */\nexport const capDir = (ref: { mountId: string; relPath: string }, opts: { mode: 'ro' | 'rw' }): DirCap => ({\n $cap: 'dir',\n mountId: ref.mountId,\n relPath: ref.relPath,\n mode: opts.mode,\n});\n\n/**\n * Invoke another app via a task contract and await its typed result (Recipe B).\n * Rejects with a machine `.code` on refusal: `cancelled` (user dismissed the\n * overlay), `timeout` (§5.7.1 liveness), `forbidden` (undeclared task or a file\n * delegation you don't hold), `no-such-task`, `task-cycle`/`task-depth-exceeded`/\n * `task-version-mismatch`, or `invalid-params` (result failed the contract schema).\n */\nexport const invokeTask = async <R = unknown>(task: string, params: Record<string, unknown> = {}): Promise<R> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_TASK], 'invoke', [{ task, params }])) as\n | { ok: true; data: R }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? `task '${task}' failed`) as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n return res.data;\n};\n\n// ── callee side ─────────────────────────────────────────────────────────────\n\n/** The params this app was invoked with as a task callee. */\nexport interface TaskInput {\n task: string;\n params: Record<string, unknown>;\n}\n\nlet latestInput: TaskInput | null = null;\nconst inputListeners = new Set<(i: TaskInput) => void>();\n\n// The host delivers a `task-input` message to the callee's iframe right after it\n// mounts the overlay (the §5.7 \"params via the region's mount event\").\naddListener(TASK_INPUT, (m: { task: string; params?: Record<string, unknown> }) => {\n latestInput = { task: m.task, params: m.params ?? {} };\n inputListeners.forEach((l) => l(latestInput!));\n});\n\n/** The task params this app was invoked with, or null if it isn't a task callee. */\nexport const getTaskInput = (): TaskInput | null => latestInput;\n\n/**\n * Finish the task, returning a result to the caller. The host validates it against\n * the contract's result schema before resolving the caller (`invalid-params` on\n * violation), then tears down this overlay.\n */\nexport const completeTask = (result: unknown): void => sendMessage(TASK_COMPLETE, { result });\n\n/** Abort the task; the caller's `invokeTask` rejects with `cancelled`. */\nexport const cancelTask = (): void => sendMessage(TASK_CANCEL, {});\n\n/** React hook: the task input for this callee, re-rendering when it arrives. */\nexport const useTaskInput = (): TaskInput | null => {\n const [input, setInput] = useState<TaskInput | null>(getTaskInput);\n useEffect(() => {\n const l = (i: TaskInput) => setInput(i);\n inputListeners.add(l);\n if (latestInput) setInput(latestInput);\n return () => {\n inputListeners.delete(l);\n };\n }, []);\n return input;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,mBAAoC;AACpC,0BAA0D;AAC1D,sBAAsE;AACtE,6BAAwB;AAmBjB,MAAM,UAAU,CAAC,KAA2C,UAA0C;AAAA,EAC3G,MAAM;AAAA,EACN,SAAS,IAAI;AAAA,EACb,SAAS,IAAI;AAAA,EACb,MAAM,KAAK;AACb;AAoBO,MAAM,SAAS,CAAC,KAA2C,UAAyC;AAAA,EACzG,MAAM;AAAA,EACN,SAAS,IAAI;AAAA,EACb,SAAS,IAAI;AAAA,EACb,MAAM,KAAK;AACb;AASO,MAAM,aAAa,OAAoB,MAAc,SAAkC,CAAC,MAAkB;AAC/G,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,6BAAa,GAAG,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,CAAC;AAIvF,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,SAAS,IAAI,UAAU;AAC7D,QAAI,OAAO,KAAK,QAAQ;AACxB,UAAM;AAAA,EACR;AACA,SAAO,IAAI;AACb;AAUA,IAAI,cAAgC;AACpC,MAAM,iBAAiB,oBAAI,IAA4B;AAAA,IAIvD,iCAAY,4BAAY,CAAC,MAA0D;AACjF,gBAAc,EAAE,MAAM,EAAE,MAAM,QAAQ,EAAE,UAAU,CAAC,EAAE;AACrD,iBAAe,QAAQ,CAAC,MAAM,EAAE,WAAY,CAAC;AAC/C,CAAC;AAGM,MAAM,eAAe,MAAwB;AAO7C,MAAM,eAAe,CAAC,eAA0B,iCAAY,+BAAe,EAAE,OAAO,CAAC;AAGrF,MAAM,aAAa,UAAY,iCAAY,6BAAa,CAAC,CAAC;AAG1D,MAAM,eAAe,MAAwB;AAClD,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAA2B,YAAY;AACjE,8BAAU,MAAM;AACd,UAAM,IAAI,CAAC,MAAiB,SAAS,CAAC;AACtC,mBAAe,IAAI,CAAC;AACpB,QAAI,YAAa,UAAS,WAAW;AACrC,WAAO,MAAM;AACX,qBAAe,OAAO,CAAC;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,CAAC;AACL,SAAO;AACT;","names":[]}
|
package/dist/tasks.js
CHANGED
|
@@ -3,8 +3,18 @@ import { useEffect, useState } from "react";
|
|
|
3
3
|
import { protocolRequest, sendMessage, addListener } from "./sandboxUtils";
|
|
4
4
|
import { PROTOCOL_TASK, TASK_CANCEL, TASK_COMPLETE, TASK_INPUT } from "./generated/protocol";
|
|
5
5
|
import { SCHEMES } from "./protocolSchemes";
|
|
6
|
-
const capFile = (ref, opts) => ({
|
|
7
|
-
|
|
6
|
+
const capFile = (ref, opts) => ({
|
|
7
|
+
$cap: "file",
|
|
8
|
+
mountId: ref.mountId,
|
|
9
|
+
relPath: ref.relPath,
|
|
10
|
+
mode: opts.mode
|
|
11
|
+
});
|
|
12
|
+
const capDir = (ref, opts) => ({
|
|
13
|
+
$cap: "dir",
|
|
14
|
+
mountId: ref.mountId,
|
|
15
|
+
relPath: ref.relPath,
|
|
16
|
+
mode: opts.mode
|
|
17
|
+
});
|
|
8
18
|
const invokeTask = async (task, params = {}) => {
|
|
9
19
|
const res = await protocolRequest(SCHEMES[PROTOCOL_TASK], "invoke", [{ task, params }]);
|
|
10
20
|
if (!res || res.ok !== true) {
|