@immediately-run/sdk 0.8.1 → 0.9.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.
Files changed (59) hide show
  1. package/dist/boot.cjs +4 -3
  2. package/dist/boot.cjs.map +1 -1
  3. package/dist/boot.js +4 -3
  4. package/dist/boot.js.map +1 -1
  5. package/dist/editor.cjs +9 -0
  6. package/dist/editor.cjs.map +1 -1
  7. package/dist/editor.d.cts +47 -1
  8. package/dist/editor.d.ts +47 -1
  9. package/dist/editor.js +6 -0
  10. package/dist/editor.js.map +1 -1
  11. package/dist/index.cjs +4 -0
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +5 -3
  14. package/dist/index.d.ts +5 -3
  15. package/dist/index.js +2 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/injectedBundler.cjs +49 -0
  18. package/dist/injectedBundler.cjs.map +1 -0
  19. package/dist/injectedBundler.d.cts +29 -0
  20. package/dist/injectedBundler.d.ts +29 -0
  21. package/dist/injectedBundler.js +24 -0
  22. package/dist/injectedBundler.js.map +1 -0
  23. package/dist/irMarkers.cjs +72 -0
  24. package/dist/irMarkers.cjs.map +1 -0
  25. package/dist/irMarkers.d.cts +54 -0
  26. package/dist/irMarkers.d.ts +54 -0
  27. package/dist/irMarkers.js +44 -0
  28. package/dist/irMarkers.js.map +1 -0
  29. package/dist/mountMatch.cjs +29 -0
  30. package/dist/mountMatch.cjs.map +1 -0
  31. package/dist/mountMatch.d.cts +21 -0
  32. package/dist/mountMatch.d.ts +21 -0
  33. package/dist/mountMatch.js +5 -0
  34. package/dist/mountMatch.js.map +1 -0
  35. package/dist/mounts.cjs +51 -4
  36. package/dist/mounts.cjs.map +1 -1
  37. package/dist/mounts.d.cts +145 -19
  38. package/dist/mounts.d.ts +145 -19
  39. package/dist/mounts.js +44 -3
  40. package/dist/mounts.js.map +1 -1
  41. package/dist/ready.cjs +69 -0
  42. package/dist/ready.cjs.map +1 -0
  43. package/dist/ready.d.cts +32 -0
  44. package/dist/ready.d.ts +32 -0
  45. package/dist/ready.js +41 -0
  46. package/dist/ready.js.map +1 -0
  47. package/dist/tasks.cjs +3 -0
  48. package/dist/tasks.cjs.map +1 -1
  49. package/dist/tasks.d.cts +24 -1
  50. package/dist/tasks.d.ts +24 -1
  51. package/dist/tasks.js +2 -0
  52. package/dist/tasks.js.map +1 -1
  53. package/dist/version.cjs +1 -1
  54. package/dist/version.cjs.map +1 -1
  55. package/dist/version.d.cts +1 -1
  56. package/dist/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/dist/version.js.map +1 -1
  59. package/package.json +1 -1
package/dist/ready.cjs ADDED
@@ -0,0 +1,69 @@
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 ready_exports = {};
20
+ __export(ready_exports, {
21
+ __resetReady: () => __resetReady,
22
+ __setReadyDeps: () => __setReadyDeps,
23
+ getReadyState: () => getReadyState,
24
+ onReady: () => onReady,
25
+ reportReady: () => reportReady
26
+ });
27
+ module.exports = __toCommonJS(ready_exports);
28
+ var import_sandboxUtils = require("./sandboxUtils");
29
+ const realNow = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
30
+ const defaultDeps = { send: import_sandboxUtils.sendMessage, now: realNow };
31
+ let deps = defaultDeps;
32
+ let state = { reported: false };
33
+ const listeners = /* @__PURE__ */ new Set();
34
+ function reportReady() {
35
+ if (state.reported) return;
36
+ state = { reported: true, reportedAt: deps.now() };
37
+ try {
38
+ deps.send("ir-report-ready", { at: state.reportedAt });
39
+ } catch {
40
+ }
41
+ for (const l of listeners) l(state);
42
+ }
43
+ function getReadyState() {
44
+ return state;
45
+ }
46
+ function onReady(listener) {
47
+ listeners.add(listener);
48
+ listener(state);
49
+ return () => {
50
+ listeners.delete(listener);
51
+ };
52
+ }
53
+ function __setReadyDeps(d) {
54
+ deps = { ...defaultDeps, ...d };
55
+ }
56
+ function __resetReady() {
57
+ deps = defaultDeps;
58
+ state = { reported: false };
59
+ listeners.clear();
60
+ }
61
+ // Annotate the CommonJS export names for ESM import in node:
62
+ 0 && (module.exports = {
63
+ __resetReady,
64
+ __setReadyDeps,
65
+ getReadyState,
66
+ onReady,
67
+ reportReady
68
+ });
69
+ //# sourceMappingURL=ready.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ready.ts"],"sourcesContent":["// The `ir.interactive` boot signal — the app-facing `reportReady()` / `onReady()` /\n// `getReadyState()` surface (LOAD_PROFILING_SPEC §3.1, R3-46). This closes the\n// \"existing SDK boot signal\" that `UI_AS_APPS_SPEC §6.2` referenced but never\n// defined.\n//\n// The runtime marks `ir.interactive` when the app's root render commits. An app\n// whose USEFULLY-interactive moment is later than first commit (e.g. after an\n// initial data load) calls `reportReady()` to DELAY the signal — which, per LP2-3,\n// can only ever push interactive later, never earlier than the commit (the host\n// resolves `max(commit, reportReady)`; see `resolveInteractive`). `onReady` /\n// `getReadyState` expose the report state in the same poll+subscribe shape as\n// `auth` / `mounts`.\n\nimport { sendMessage as defaultSend } from \"./sandboxUtils\";\n\nexport interface ReadyState {\n /** Whether the app has called `reportReady()`. */\n reported: boolean;\n /** The app-reported timestamp (`performance.now()`), if it has reported. */\n reportedAt?: number;\n}\n\ninterface ReadyDeps {\n send: (type: string, data?: Record<string, unknown>) => void;\n now: () => number;\n}\n\nconst realNow = (): number =>\n typeof performance !== \"undefined\" && typeof performance.now === \"function\"\n ? performance.now()\n : Date.now();\n\nconst defaultDeps: ReadyDeps = { send: defaultSend, now: realNow };\n\nlet deps: ReadyDeps = defaultDeps;\nlet state: ReadyState = { reported: false };\nconst listeners = new Set<(s: ReadyState) => void>();\n\n/**\n * Signal that the app is usefully interactive (e.g. after an initial data load).\n * IDEMPOTENT — only the FIRST call counts; later calls are ignored. Forwards the\n * report to the runtime (`ir-report-ready`) so the host can resolve\n * `ir.interactive = max(rootRenderCommit, reportedAt)` (LP2-3) — calling it before\n * the root render commits can only delay the signal, never advance it.\n */\nexport function reportReady(): void {\n if (state.reported) return;\n state = { reported: true, reportedAt: deps.now() };\n try {\n deps.send(\"ir-report-ready\", { at: state.reportedAt });\n } catch {\n /* transport not ready — the runtime still marks interactive at root commit */\n }\n for (const l of listeners) l(state);\n}\n\n/** Pollable snapshot of the report state. */\nexport function getReadyState(): ReadyState {\n return state;\n}\n\n/**\n * Subscribe to the ready signal. Invoked immediately with the current state (so a\n * late subscriber after `reportReady()` still fires) and again whenever it reports.\n * Returns an unsubscribe.\n */\nexport function onReady(listener: (s: ReadyState) => void): () => void {\n listeners.add(listener);\n listener(state);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/** Test seam: override the transport/clock. */\nexport function __setReadyDeps(d: Partial<ReadyDeps>): void {\n deps = { ...defaultDeps, ...d };\n}\n\n/** Test seam: reset module state between cases. */\nexport function __resetReady(): void {\n deps = defaultDeps;\n state = { reported: false };\n listeners.clear();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,0BAA2C;AAc3C,MAAM,UAAU,MACd,OAAO,gBAAgB,eAAe,OAAO,YAAY,QAAQ,aAC7D,YAAY,IAAI,IAChB,KAAK,IAAI;AAEf,MAAM,cAAyB,EAAE,MAAM,oBAAAA,aAAa,KAAK,QAAQ;AAEjE,IAAI,OAAkB;AACtB,IAAI,QAAoB,EAAE,UAAU,MAAM;AAC1C,MAAM,YAAY,oBAAI,IAA6B;AAS5C,SAAS,cAAoB;AAClC,MAAI,MAAM,SAAU;AACpB,UAAQ,EAAE,UAAU,MAAM,YAAY,KAAK,IAAI,EAAE;AACjD,MAAI;AACF,SAAK,KAAK,mBAAmB,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,EACvD,QAAQ;AAAA,EAER;AACA,aAAW,KAAK,UAAW,GAAE,KAAK;AACpC;AAGO,SAAS,gBAA4B;AAC1C,SAAO;AACT;AAOO,SAAS,QAAQ,UAA+C;AACrE,YAAU,IAAI,QAAQ;AACtB,WAAS,KAAK;AACd,SAAO,MAAM;AACX,cAAU,OAAO,QAAQ;AAAA,EAC3B;AACF;AAGO,SAAS,eAAe,GAA6B;AAC1D,SAAO,EAAE,GAAG,aAAa,GAAG,EAAE;AAChC;AAGO,SAAS,eAAqB;AACnC,SAAO;AACP,UAAQ,EAAE,UAAU,MAAM;AAC1B,YAAU,MAAM;AAClB;","names":["defaultSend"]}
@@ -0,0 +1,32 @@
1
+ interface ReadyState {
2
+ /** Whether the app has called `reportReady()`. */
3
+ reported: boolean;
4
+ /** The app-reported timestamp (`performance.now()`), if it has reported. */
5
+ reportedAt?: number;
6
+ }
7
+ interface ReadyDeps {
8
+ send: (type: string, data?: Record<string, unknown>) => void;
9
+ now: () => number;
10
+ }
11
+ /**
12
+ * Signal that the app is usefully interactive (e.g. after an initial data load).
13
+ * IDEMPOTENT — only the FIRST call counts; later calls are ignored. Forwards the
14
+ * report to the runtime (`ir-report-ready`) so the host can resolve
15
+ * `ir.interactive = max(rootRenderCommit, reportedAt)` (LP2-3) — calling it before
16
+ * the root render commits can only delay the signal, never advance it.
17
+ */
18
+ declare function reportReady(): void;
19
+ /** Pollable snapshot of the report state. */
20
+ declare function getReadyState(): ReadyState;
21
+ /**
22
+ * Subscribe to the ready signal. Invoked immediately with the current state (so a
23
+ * late subscriber after `reportReady()` still fires) and again whenever it reports.
24
+ * Returns an unsubscribe.
25
+ */
26
+ declare function onReady(listener: (s: ReadyState) => void): () => void;
27
+ /** Test seam: override the transport/clock. */
28
+ declare function __setReadyDeps(d: Partial<ReadyDeps>): void;
29
+ /** Test seam: reset module state between cases. */
30
+ declare function __resetReady(): void;
31
+
32
+ export { type ReadyState, __resetReady, __setReadyDeps, getReadyState, onReady, reportReady };
@@ -0,0 +1,32 @@
1
+ interface ReadyState {
2
+ /** Whether the app has called `reportReady()`. */
3
+ reported: boolean;
4
+ /** The app-reported timestamp (`performance.now()`), if it has reported. */
5
+ reportedAt?: number;
6
+ }
7
+ interface ReadyDeps {
8
+ send: (type: string, data?: Record<string, unknown>) => void;
9
+ now: () => number;
10
+ }
11
+ /**
12
+ * Signal that the app is usefully interactive (e.g. after an initial data load).
13
+ * IDEMPOTENT — only the FIRST call counts; later calls are ignored. Forwards the
14
+ * report to the runtime (`ir-report-ready`) so the host can resolve
15
+ * `ir.interactive = max(rootRenderCommit, reportedAt)` (LP2-3) — calling it before
16
+ * the root render commits can only delay the signal, never advance it.
17
+ */
18
+ declare function reportReady(): void;
19
+ /** Pollable snapshot of the report state. */
20
+ declare function getReadyState(): ReadyState;
21
+ /**
22
+ * Subscribe to the ready signal. Invoked immediately with the current state (so a
23
+ * late subscriber after `reportReady()` still fires) and again whenever it reports.
24
+ * Returns an unsubscribe.
25
+ */
26
+ declare function onReady(listener: (s: ReadyState) => void): () => void;
27
+ /** Test seam: override the transport/clock. */
28
+ declare function __setReadyDeps(d: Partial<ReadyDeps>): void;
29
+ /** Test seam: reset module state between cases. */
30
+ declare function __resetReady(): void;
31
+
32
+ export { type ReadyState, __resetReady, __setReadyDeps, getReadyState, onReady, reportReady };
package/dist/ready.js ADDED
@@ -0,0 +1,41 @@
1
+ import { sendMessage as defaultSend } from "./sandboxUtils";
2
+ const realNow = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
3
+ const defaultDeps = { send: defaultSend, now: realNow };
4
+ let deps = defaultDeps;
5
+ let state = { reported: false };
6
+ const listeners = /* @__PURE__ */ new Set();
7
+ function reportReady() {
8
+ if (state.reported) return;
9
+ state = { reported: true, reportedAt: deps.now() };
10
+ try {
11
+ deps.send("ir-report-ready", { at: state.reportedAt });
12
+ } catch {
13
+ }
14
+ for (const l of listeners) l(state);
15
+ }
16
+ function getReadyState() {
17
+ return state;
18
+ }
19
+ function onReady(listener) {
20
+ listeners.add(listener);
21
+ listener(state);
22
+ return () => {
23
+ listeners.delete(listener);
24
+ };
25
+ }
26
+ function __setReadyDeps(d) {
27
+ deps = { ...defaultDeps, ...d };
28
+ }
29
+ function __resetReady() {
30
+ deps = defaultDeps;
31
+ state = { reported: false };
32
+ listeners.clear();
33
+ }
34
+ export {
35
+ __resetReady,
36
+ __setReadyDeps,
37
+ getReadyState,
38
+ onReady,
39
+ reportReady
40
+ };
41
+ //# sourceMappingURL=ready.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ready.ts"],"sourcesContent":["// The `ir.interactive` boot signal — the app-facing `reportReady()` / `onReady()` /\n// `getReadyState()` surface (LOAD_PROFILING_SPEC §3.1, R3-46). This closes the\n// \"existing SDK boot signal\" that `UI_AS_APPS_SPEC §6.2` referenced but never\n// defined.\n//\n// The runtime marks `ir.interactive` when the app's root render commits. An app\n// whose USEFULLY-interactive moment is later than first commit (e.g. after an\n// initial data load) calls `reportReady()` to DELAY the signal — which, per LP2-3,\n// can only ever push interactive later, never earlier than the commit (the host\n// resolves `max(commit, reportReady)`; see `resolveInteractive`). `onReady` /\n// `getReadyState` expose the report state in the same poll+subscribe shape as\n// `auth` / `mounts`.\n\nimport { sendMessage as defaultSend } from \"./sandboxUtils\";\n\nexport interface ReadyState {\n /** Whether the app has called `reportReady()`. */\n reported: boolean;\n /** The app-reported timestamp (`performance.now()`), if it has reported. */\n reportedAt?: number;\n}\n\ninterface ReadyDeps {\n send: (type: string, data?: Record<string, unknown>) => void;\n now: () => number;\n}\n\nconst realNow = (): number =>\n typeof performance !== \"undefined\" && typeof performance.now === \"function\"\n ? performance.now()\n : Date.now();\n\nconst defaultDeps: ReadyDeps = { send: defaultSend, now: realNow };\n\nlet deps: ReadyDeps = defaultDeps;\nlet state: ReadyState = { reported: false };\nconst listeners = new Set<(s: ReadyState) => void>();\n\n/**\n * Signal that the app is usefully interactive (e.g. after an initial data load).\n * IDEMPOTENT — only the FIRST call counts; later calls are ignored. Forwards the\n * report to the runtime (`ir-report-ready`) so the host can resolve\n * `ir.interactive = max(rootRenderCommit, reportedAt)` (LP2-3) — calling it before\n * the root render commits can only delay the signal, never advance it.\n */\nexport function reportReady(): void {\n if (state.reported) return;\n state = { reported: true, reportedAt: deps.now() };\n try {\n deps.send(\"ir-report-ready\", { at: state.reportedAt });\n } catch {\n /* transport not ready — the runtime still marks interactive at root commit */\n }\n for (const l of listeners) l(state);\n}\n\n/** Pollable snapshot of the report state. */\nexport function getReadyState(): ReadyState {\n return state;\n}\n\n/**\n * Subscribe to the ready signal. Invoked immediately with the current state (so a\n * late subscriber after `reportReady()` still fires) and again whenever it reports.\n * Returns an unsubscribe.\n */\nexport function onReady(listener: (s: ReadyState) => void): () => void {\n listeners.add(listener);\n listener(state);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/** Test seam: override the transport/clock. */\nexport function __setReadyDeps(d: Partial<ReadyDeps>): void {\n deps = { ...defaultDeps, ...d };\n}\n\n/** Test seam: reset module state between cases. */\nexport function __resetReady(): void {\n deps = defaultDeps;\n state = { reported: false };\n listeners.clear();\n}\n"],"mappings":"AAaA,SAAS,eAAe,mBAAmB;AAc3C,MAAM,UAAU,MACd,OAAO,gBAAgB,eAAe,OAAO,YAAY,QAAQ,aAC7D,YAAY,IAAI,IAChB,KAAK,IAAI;AAEf,MAAM,cAAyB,EAAE,MAAM,aAAa,KAAK,QAAQ;AAEjE,IAAI,OAAkB;AACtB,IAAI,QAAoB,EAAE,UAAU,MAAM;AAC1C,MAAM,YAAY,oBAAI,IAA6B;AAS5C,SAAS,cAAoB;AAClC,MAAI,MAAM,SAAU;AACpB,UAAQ,EAAE,UAAU,MAAM,YAAY,KAAK,IAAI,EAAE;AACjD,MAAI;AACF,SAAK,KAAK,mBAAmB,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,EACvD,QAAQ;AAAA,EAER;AACA,aAAW,KAAK,UAAW,GAAE,KAAK;AACpC;AAGO,SAAS,gBAA4B;AAC1C,SAAO;AACT;AAOO,SAAS,QAAQ,UAA+C;AACrE,YAAU,IAAI,QAAQ;AACtB,WAAS,KAAK;AACd,SAAO,MAAM;AACX,cAAU,OAAO,QAAQ;AAAA,EAC3B;AACF;AAGO,SAAS,eAAe,GAA6B;AAC1D,SAAO,EAAE,GAAG,aAAa,GAAG,EAAE;AAChC;AAGO,SAAS,eAAqB;AACnC,SAAO;AACP,UAAQ,EAAE,UAAU,MAAM;AAC1B,YAAU,MAAM;AAClB;","names":[]}
package/dist/tasks.cjs CHANGED
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var tasks_exports = {};
20
20
  __export(tasks_exports, {
21
21
  cancelTask: () => cancelTask,
22
+ capDir: () => capDir,
22
23
  capFile: () => capFile,
23
24
  completeTask: () => completeTask,
24
25
  getTaskInput: () => getTaskInput,
@@ -29,6 +30,7 @@ module.exports = __toCommonJS(tasks_exports);
29
30
  var import_react = require("react");
30
31
  var import_sandboxUtils = require("./sandboxUtils");
31
32
  const capFile = (ref, opts) => ({ $cap: "file", mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });
33
+ const capDir = (ref, opts) => ({ $cap: "dir", mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });
32
34
  const invokeTask = async (task, params = {}) => {
33
35
  const res = await (0, import_sandboxUtils.protocolRequest)("task", "invoke", [{ task, params }]);
34
36
  if (!res || res.ok !== true) {
@@ -62,6 +64,7 @@ const useTaskInput = () => {
62
64
  // Annotate the CommonJS export names for ESM import in node:
63
65
  0 && (module.exports = {
64
66
  cancelTask,
67
+ capDir,
65
68
  capFile,
66
69
  completeTask,
67
70
  getTaskInput,
@@ -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';\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 = (\n ref: { mountId: string; relPath: string },\n opts: { mode: 'ro' | 'rw' },\n): FileCap => ({ $cap: 'file', mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });\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>(\n task: string,\n params: Record<string, unknown> = {},\n): Promise<R> => {\n const res = (await protocolRequest('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;AAWA,mBAAoC;AACpC,0BAA0D;AAmBnD,MAAM,UAAU,CACrB,KACA,UACa,EAAE,MAAM,QAAQ,SAAS,IAAI,SAAS,SAAS,IAAI,SAAS,MAAM,KAAK,KAAK;AASpF,MAAM,aAAa,OACxB,MACA,SAAkC,CAAC,MACpB;AACf,QAAM,MAAO,UAAM,qCAAgB,QAAQ,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,CAAC;AAIvE,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,cAAc,CAAC,MAA0D;AACnF,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,iBAAiB,EAAE,OAAO,CAAC;AAGvF,MAAM,aAAa,UAAY,iCAAY,eAAe,CAAC,CAAC;AAG5D,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":[]}
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';\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 = (\n ref: { mountId: string; relPath: string },\n opts: { mode: 'ro' | 'rw' },\n): FileCap => ({ $cap: 'file', mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });\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 = (\n ref: { mountId: string; relPath: string },\n opts: { mode: 'ro' | 'rw' },\n): DirCap => ({ $cap: 'dir', mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });\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>(\n task: string,\n params: Record<string, unknown> = {},\n): Promise<R> => {\n const res = (await protocolRequest('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;AAmBnD,MAAM,UAAU,CACrB,KACA,UACa,EAAE,MAAM,QAAQ,SAAS,IAAI,SAAS,SAAS,IAAI,SAAS,MAAM,KAAK,KAAK;AAoBpF,MAAM,SAAS,CACpB,KACA,UACY,EAAE,MAAM,OAAO,SAAS,IAAI,SAAS,SAAS,IAAI,SAAS,MAAM,KAAK,KAAK;AASlF,MAAM,aAAa,OACxB,MACA,SAAkC,CAAC,MACpB;AACf,QAAM,MAAO,UAAM,qCAAgB,QAAQ,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,CAAC;AAIvE,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,cAAc,CAAC,MAA0D;AACnF,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,iBAAiB,EAAE,OAAO,CAAC;AAGvF,MAAM,aAAa,UAAY,iCAAY,eAAe,CAAC,CAAC;AAG5D,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.d.cts CHANGED
@@ -18,6 +18,29 @@ declare const capFile: (ref: {
18
18
  }, opts: {
19
19
  mode: "ro" | "rw";
20
20
  }) => FileCap;
21
+ /** A delegated DIRECTORY capability marker for a task param (D2). Like {@link FileCap}
22
+ * but `relPath` names a DIRECTORY: the host chroots the callee AT that directory
23
+ * (the whole subtree). Used for the `pick-file` `roots` — one chroot per root. */
24
+ interface DirCap {
25
+ $cap: 'dir';
26
+ mountId: string;
27
+ relPath: string;
28
+ mode: 'ro' | 'rw';
29
+ }
30
+ /**
31
+ * Build a delegated DIRECTORY reference for a task param (the directory analogue of
32
+ * {@link capFile}). The host resolves it against YOUR OWN grants and mints an
33
+ * attenuated, task-scoped chroot of that directory for the callee — you can only
34
+ * delegate a directory you already hold (attenuation only, never escalation):
35
+ *
36
+ * roots: [capDir({ mountId: 'space:abc', relPath: 'boards' }, { mode: 'rw' })]
37
+ */
38
+ declare const capDir: (ref: {
39
+ mountId: string;
40
+ relPath: string;
41
+ }, opts: {
42
+ mode: "ro" | "rw";
43
+ }) => DirCap;
21
44
  /**
22
45
  * Invoke another app via a task contract and await its typed result (Recipe B).
23
46
  * Rejects with a machine `.code` on refusal: `cancelled` (user dismissed the
@@ -44,4 +67,4 @@ declare const cancelTask: () => void;
44
67
  /** React hook: the task input for this callee, re-rendering when it arrives. */
45
68
  declare const useTaskInput: () => TaskInput | null;
46
69
 
47
- export { type FileCap, type TaskInput, cancelTask, capFile, completeTask, getTaskInput, invokeTask, useTaskInput };
70
+ export { type DirCap, type FileCap, type TaskInput, cancelTask, capDir, capFile, completeTask, getTaskInput, invokeTask, useTaskInput };
package/dist/tasks.d.ts CHANGED
@@ -18,6 +18,29 @@ declare const capFile: (ref: {
18
18
  }, opts: {
19
19
  mode: "ro" | "rw";
20
20
  }) => FileCap;
21
+ /** A delegated DIRECTORY capability marker for a task param (D2). Like {@link FileCap}
22
+ * but `relPath` names a DIRECTORY: the host chroots the callee AT that directory
23
+ * (the whole subtree). Used for the `pick-file` `roots` — one chroot per root. */
24
+ interface DirCap {
25
+ $cap: 'dir';
26
+ mountId: string;
27
+ relPath: string;
28
+ mode: 'ro' | 'rw';
29
+ }
30
+ /**
31
+ * Build a delegated DIRECTORY reference for a task param (the directory analogue of
32
+ * {@link capFile}). The host resolves it against YOUR OWN grants and mints an
33
+ * attenuated, task-scoped chroot of that directory for the callee — you can only
34
+ * delegate a directory you already hold (attenuation only, never escalation):
35
+ *
36
+ * roots: [capDir({ mountId: 'space:abc', relPath: 'boards' }, { mode: 'rw' })]
37
+ */
38
+ declare const capDir: (ref: {
39
+ mountId: string;
40
+ relPath: string;
41
+ }, opts: {
42
+ mode: "ro" | "rw";
43
+ }) => DirCap;
21
44
  /**
22
45
  * Invoke another app via a task contract and await its typed result (Recipe B).
23
46
  * Rejects with a machine `.code` on refusal: `cancelled` (user dismissed the
@@ -44,4 +67,4 @@ declare const cancelTask: () => void;
44
67
  /** React hook: the task input for this callee, re-rendering when it arrives. */
45
68
  declare const useTaskInput: () => TaskInput | null;
46
69
 
47
- export { type FileCap, type TaskInput, cancelTask, capFile, completeTask, getTaskInput, invokeTask, useTaskInput };
70
+ export { type DirCap, type FileCap, type TaskInput, cancelTask, capDir, capFile, completeTask, getTaskInput, invokeTask, useTaskInput };
package/dist/tasks.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { protocolRequest, sendMessage, addListener } from "./sandboxUtils";
3
3
  const capFile = (ref, opts) => ({ $cap: "file", mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });
4
+ const capDir = (ref, opts) => ({ $cap: "dir", mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });
4
5
  const invokeTask = async (task, params = {}) => {
5
6
  const res = await protocolRequest("task", "invoke", [{ task, params }]);
6
7
  if (!res || res.ok !== true) {
@@ -33,6 +34,7 @@ const useTaskInput = () => {
33
34
  };
34
35
  export {
35
36
  cancelTask,
37
+ capDir,
36
38
  capFile,
37
39
  completeTask,
38
40
  getTaskInput,
package/dist/tasks.js.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';\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 = (\n ref: { mountId: string; relPath: string },\n opts: { mode: 'ro' | 'rw' },\n): FileCap => ({ $cap: 'file', mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });\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>(\n task: string,\n params: Record<string, unknown> = {},\n): Promise<R> => {\n const res = (await protocolRequest('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":"AAWA,SAAS,WAAW,gBAAgB;AACpC,SAAS,iBAAiB,aAAa,mBAAmB;AAmBnD,MAAM,UAAU,CACrB,KACA,UACa,EAAE,MAAM,QAAQ,SAAS,IAAI,SAAS,SAAS,IAAI,SAAS,MAAM,KAAK,KAAK;AASpF,MAAM,aAAa,OACxB,MACA,SAAkC,CAAC,MACpB;AACf,QAAM,MAAO,MAAM,gBAAgB,QAAQ,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,CAAC;AAIvE,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;AAIvD,YAAY,cAAc,CAAC,MAA0D;AACnF,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,WAA0B,YAAY,iBAAiB,EAAE,OAAO,CAAC;AAGvF,MAAM,aAAa,MAAY,YAAY,eAAe,CAAC,CAAC;AAG5D,MAAM,eAAe,MAAwB;AAClD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA2B,YAAY;AACjE,YAAU,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":[]}
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';\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 = (\n ref: { mountId: string; relPath: string },\n opts: { mode: 'ro' | 'rw' },\n): FileCap => ({ $cap: 'file', mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });\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 = (\n ref: { mountId: string; relPath: string },\n opts: { mode: 'ro' | 'rw' },\n): DirCap => ({ $cap: 'dir', mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });\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>(\n task: string,\n params: Record<string, unknown> = {},\n): Promise<R> => {\n const res = (await protocolRequest('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":"AAWA,SAAS,WAAW,gBAAgB;AACpC,SAAS,iBAAiB,aAAa,mBAAmB;AAmBnD,MAAM,UAAU,CACrB,KACA,UACa,EAAE,MAAM,QAAQ,SAAS,IAAI,SAAS,SAAS,IAAI,SAAS,MAAM,KAAK,KAAK;AAoBpF,MAAM,SAAS,CACpB,KACA,UACY,EAAE,MAAM,OAAO,SAAS,IAAI,SAAS,SAAS,IAAI,SAAS,MAAM,KAAK,KAAK;AASlF,MAAM,aAAa,OACxB,MACA,SAAkC,CAAC,MACpB;AACf,QAAM,MAAO,MAAM,gBAAgB,QAAQ,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,CAAC;AAIvE,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;AAIvD,YAAY,cAAc,CAAC,MAA0D;AACnF,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,WAA0B,YAAY,iBAAiB,EAAE,OAAO,CAAC;AAGvF,MAAM,aAAa,MAAY,YAAY,eAAe,CAAC,CAAC;AAG5D,MAAM,eAAe,MAAwB;AAClD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA2B,YAAY;AACjE,YAAU,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/version.cjs CHANGED
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  SDK_VERSION: () => SDK_VERSION
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const SDK_VERSION = "0.8.1";
24
+ const SDK_VERSION = "0.9.0";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  SDK_VERSION
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.8.1';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,MAAM,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.9.0';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,MAAM,cAAc;","names":[]}
@@ -1,4 +1,4 @@
1
1
  /** This SDK's package version, baked from package.json at build (SP2-6). */
2
- declare const SDK_VERSION = "0.8.1";
2
+ declare const SDK_VERSION = "0.9.0";
3
3
 
4
4
  export { SDK_VERSION };
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /** This SDK's package version, baked from package.json at build (SP2-6). */
2
- declare const SDK_VERSION = "0.8.1";
2
+ declare const SDK_VERSION = "0.9.0";
3
3
 
4
4
  export { SDK_VERSION };
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const SDK_VERSION = "0.8.1";
1
+ const SDK_VERSION = "0.9.0";
2
2
  export {
3
3
  SDK_VERSION
4
4
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.8.1';\n"],"mappings":"AAIO,MAAM,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.9.0';\n"],"mappings":"AAIO,MAAM,cAAc;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immediately-run/sdk",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Runtime SDK for code executing inside an immediately.run sandbox.",
5
5
  "license": "MIT",
6
6
  "repository": "github:immediately-run/immediately-run-sdk",