@immediately-run/sdk 0.57.1 → 0.58.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 (131) hide show
  1. package/dist/agentChatClient.cjs +112 -0
  2. package/dist/agentChatClient.cjs.map +1 -0
  3. package/dist/agentChatClient.d.cts +9 -0
  4. package/dist/agentChatClient.d.ts +9 -0
  5. package/dist/agentChatClient.js +89 -0
  6. package/dist/agentChatClient.js.map +1 -0
  7. package/dist/agentContext.cjs +69 -0
  8. package/dist/agentContext.cjs.map +1 -0
  9. package/dist/agentContext.d.cts +45 -0
  10. package/dist/agentContext.d.ts +45 -0
  11. package/dist/agentContext.js +45 -0
  12. package/dist/agentContext.js.map +1 -0
  13. package/dist/agentLoop.cjs +301 -0
  14. package/dist/agentLoop.cjs.map +1 -0
  15. package/dist/agentLoop.d.cts +267 -0
  16. package/dist/agentLoop.d.ts +267 -0
  17. package/dist/agentLoop.js +271 -0
  18. package/dist/agentLoop.js.map +1 -0
  19. package/dist/agentSteering.cjs +127 -0
  20. package/dist/agentSteering.cjs.map +1 -0
  21. package/dist/agentSteering.d.cts +80 -0
  22. package/dist/agentSteering.d.ts +80 -0
  23. package/dist/agentSteering.js +98 -0
  24. package/dist/agentSteering.js.map +1 -0
  25. package/dist/auth.cjs.map +1 -1
  26. package/dist/auth.d.cts +33 -2
  27. package/dist/auth.d.ts +33 -2
  28. package/dist/auth.js.map +1 -1
  29. package/dist/bundle.cjs +91 -0
  30. package/dist/bundle.cjs.map +1 -0
  31. package/dist/bundle.d.cts +63 -0
  32. package/dist/bundle.d.ts +63 -0
  33. package/dist/bundle.js +62 -0
  34. package/dist/bundle.js.map +1 -0
  35. package/dist/collectHeadings.cjs +59 -0
  36. package/dist/collectHeadings.cjs.map +1 -0
  37. package/dist/collectHeadings.d.cts +16 -0
  38. package/dist/collectHeadings.d.ts +16 -0
  39. package/dist/collectHeadings.js +36 -0
  40. package/dist/collectHeadings.js.map +1 -0
  41. package/dist/corpus.cjs +9 -58
  42. package/dist/corpus.cjs.map +1 -1
  43. package/dist/corpus.d.cts +3 -63
  44. package/dist/corpus.d.ts +3 -63
  45. package/dist/corpus.js +15 -57
  46. package/dist/corpus.js.map +1 -1
  47. package/dist/debug.cjs +115 -0
  48. package/dist/debug.cjs.map +1 -1
  49. package/dist/debug.js +115 -0
  50. package/dist/debug.js.map +1 -1
  51. package/dist/fence.cjs +49 -0
  52. package/dist/fence.cjs.map +1 -0
  53. package/dist/fence.d.cts +4 -0
  54. package/dist/fence.d.ts +4 -0
  55. package/dist/fence.js +26 -0
  56. package/dist/fence.js.map +1 -0
  57. package/dist/formFactor.cjs.map +1 -1
  58. package/dist/formFactor.d.cts +11 -2
  59. package/dist/formFactor.d.ts +11 -2
  60. package/dist/formFactor.js.map +1 -1
  61. package/dist/fs.cjs.map +1 -1
  62. package/dist/fs.d.cts +16 -6
  63. package/dist/fs.d.ts +16 -6
  64. package/dist/fs.js.map +1 -1
  65. package/dist/hooks.cjs.map +1 -1
  66. package/dist/hooks.d.cts +7 -0
  67. package/dist/hooks.d.ts +7 -0
  68. package/dist/hooks.js.map +1 -1
  69. package/dist/index.cjs +21 -1
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +12 -3
  72. package/dist/index.d.ts +12 -3
  73. package/dist/index.js +10 -0
  74. package/dist/index.js.map +1 -1
  75. package/dist/launch.cjs +45 -6
  76. package/dist/launch.cjs.map +1 -1
  77. package/dist/launch.d.cts +3 -0
  78. package/dist/launch.d.ts +3 -0
  79. package/dist/launch.js +45 -6
  80. package/dist/launch.js.map +1 -1
  81. package/dist/llm.cjs.map +1 -1
  82. package/dist/llm.d.cts +20 -3
  83. package/dist/llm.d.ts +20 -3
  84. package/dist/llm.js.map +1 -1
  85. package/dist/metadataQueryTool.cjs +271 -0
  86. package/dist/metadataQueryTool.cjs.map +1 -0
  87. package/dist/metadataQueryTool.d.cts +159 -0
  88. package/dist/metadataQueryTool.d.ts +159 -0
  89. package/dist/metadataQueryTool.js +241 -0
  90. package/dist/metadataQueryTool.js.map +1 -0
  91. package/dist/mounts.cjs.map +1 -1
  92. package/dist/mounts.d.cts +22 -3
  93. package/dist/mounts.d.ts +22 -3
  94. package/dist/mounts.js.map +1 -1
  95. package/dist/onFsChange.cjs +13 -1
  96. package/dist/onFsChange.cjs.map +1 -1
  97. package/dist/onFsChange.d.cts +21 -1
  98. package/dist/onFsChange.d.ts +21 -1
  99. package/dist/onFsChange.js +13 -1
  100. package/dist/onFsChange.js.map +1 -1
  101. package/dist/protocolSchemes.cjs +1 -0
  102. package/dist/protocolSchemes.cjs.map +1 -1
  103. package/dist/protocolSchemes.d.cts +1 -0
  104. package/dist/protocolSchemes.d.ts +1 -0
  105. package/dist/protocolSchemes.js +2 -0
  106. package/dist/protocolSchemes.js.map +1 -1
  107. package/dist/recents.cjs +41 -0
  108. package/dist/recents.cjs.map +1 -0
  109. package/dist/recents.d.cts +23 -0
  110. package/dist/recents.d.ts +23 -0
  111. package/dist/recents.js +17 -0
  112. package/dist/recents.js.map +1 -0
  113. package/dist/tasks.cjs +41 -7
  114. package/dist/tasks.cjs.map +1 -1
  115. package/dist/tasks.d.cts +136 -7
  116. package/dist/tasks.d.ts +136 -7
  117. package/dist/tasks.js +39 -7
  118. package/dist/tasks.js.map +1 -1
  119. package/dist/version.cjs +1 -1
  120. package/dist/version.cjs.map +1 -1
  121. package/dist/version.d.cts +1 -1
  122. package/dist/version.d.ts +1 -1
  123. package/dist/version.js +1 -1
  124. package/dist/version.js.map +1 -1
  125. package/dist/workspace.cjs +53 -0
  126. package/dist/workspace.cjs.map +1 -0
  127. package/dist/workspace.d.cts +58 -0
  128. package/dist/workspace.d.ts +58 -0
  129. package/dist/workspace.js +28 -0
  130. package/dist/workspace.js.map +1 -0
  131. package/package.json +6 -5
package/dist/tasks.d.ts CHANGED
@@ -44,27 +44,156 @@ declare const capDir: (ref: {
44
44
  /**
45
45
  * Invoke another app via a task contract and await its typed result (Recipe B).
46
46
  * Rejects with a machine `.code` on refusal: `cancelled` (user dismissed the
47
- * overlay), `timeout` (§5.7.1 liveness), `forbidden` (undeclared task or a file
48
- * delegation you don't hold), `no-such-task`, `task-cycle`/`task-depth-exceeded`/
49
- * `task-version-mismatch`, or `invalid-params` (result failed the contract schema).
47
+ * overlay), `timeout` (§5.7.1 liveness), `forbidden` (missing `task:invoke`, or a
48
+ * file delegation you don't hold), `not-declared` (the task is missing from your
49
+ * `immediately.run.invokes`), `no-such-task` (no app is bound to that contract),
50
+ * `task-cycle`/`task-depth-exceeded`/`task-version-mismatch`, or `invalid-params`
51
+ * (result failed the contract schema). **Read `err.message`** — since R3-418 the host
52
+ * says which capability, which declaration or which param was at fault; the code alone
53
+ * does not distinguish them.
54
+ *
55
+ * Two things must BOTH be true before any invoke is admitted, and they are separate:
56
+ * 1. your `package.json` `immediately.run.invokes` lists the task (else `not-declared`);
57
+ * 2. your app holds the `task:invoke` capability — declare it in
58
+ * `immediately.run.capabilities` so the user can grant it (else `forbidden`).
59
+ * Declaring the invoke does not imply the capability.
60
+ *
61
+ * ── `pick-file`: `roots` is required (SPACES_UI_SPEC §4.1) ───────────────────
62
+ * ```ts
63
+ * import { invokeTask, capDir, requestMount } from '@immediately-run/sdk';
64
+ *
65
+ * const mount = await requestMount(); // the user picks a space
66
+ * // `invokeTask` returns `unknown` — name the result type, or the destructuring
67
+ * // below does not compile.
68
+ * const { root, path } = await invokeTask<{ root: number; path: string }>('pick-file', {
69
+ * // `SandboxMount.id` is optional (absent on the primary repo mount) — fall back to
70
+ * // `path`, which is always present and is what the host resolves against.
71
+ * roots: [capDir({ mountId: mount.id ?? mount.path, relPath: 'boards' }, { mode: 'ro' })],
72
+ * });
73
+ * ```
74
+ * - `roots` is a NON-EMPTY `DirCap[]` of directories you **already hold**. The result
75
+ * names its target as `{ root, path }` where `root` INDEXES `params.roots`, so
76
+ * `invokeTask('pick-file', {})` can never produce a valid result and is rejected.
77
+ * There is no rootless "pick from any of my spaces" mode by design: enumerating the
78
+ * user's spaces is the host's powerbox (`requestMount()`), and composing the two —
79
+ * as above — is that flow. See SPACES_UI_SPEC R-SPACES-5/R-SPACES-12.
80
+ * - Each `capDir` is attenuated against your own grant. **`{ mode: 'rw' }` over a space
81
+ * the user granted `ro` is refused `forbidden`** — the commonest failure, because the
82
+ * mount *was* granted and the path *is* inside it. Pass the mode you actually hold;
83
+ * `ro` is right for a plain pick.
84
+ * - The `requestMount()` grant is DURABLE and survives a cancelled pick: the user
85
+ * consented to the mount in host chrome, separately from the pick (SPACES_UI_SPEC §4.1).
86
+ * Reuse it for later picks rather than re-requesting.
87
+ *
88
+ * Off-host (plain `vite dev` — no host transport) it rejects with a plain
89
+ * "no host transport" error: there is no host to resolve the task binding.
50
90
  */
51
91
  declare const invokeTask: <R = unknown>(task: string, params?: Record<string, unknown>) => Promise<R>;
92
+ /** Bytes captured by the host, as `capturePhoto()` / `captureAudio()` return them.
93
+ * `bytes` is a transferable `ArrayBuffer` — write it to a space, decode it, upload
94
+ * it. You never receive a `MediaStream` or a `MediaStreamTrack`, and that is not an
95
+ * omission: a track cannot be transferred into an app frame at all
96
+ * (`DataCloneError`), and the frame's opaque origin cannot open a device itself
97
+ * (`getUserMedia` throws `SecurityError: Invalid security origin`). */
98
+ interface CaptureResult {
99
+ bytes: ArrayBuffer;
100
+ /** e.g. `image/jpeg`, `audio/webm`. Chosen by the HOST, not by you. */
101
+ mimeType: string;
102
+ /** How long the device was open, in ms. */
103
+ durationMs: number;
104
+ }
105
+ /** Optional hints for a capture. They deliberately name NOTHING — no device id, no
106
+ * camera index — because the host chooses everything about how the capture is
107
+ * performed. That is what lets the grant be a plain on/off instead of a per-call
108
+ * target list the way `net:fetch` needs. */
109
+ interface CaptureOptions {
110
+ /** Which way the camera should face, if the device has a choice. A HINT: the host
111
+ * may ignore it. Ignored for audio. */
112
+ facing?: 'user' | 'environment';
113
+ }
114
+ /**
115
+ * Take one photo, or record one audio clip, through the HOST's capture UI
116
+ * (`BROWSER_CAPABILITIES_SPEC` §3 grade 1).
117
+ *
118
+ * The host opens the device at its own origin, draws the viewfinder, and hands you
119
+ * the bytes when the user presses Done. **You are never in the loop while the device
120
+ * is live** — that is the point of the design, not a limitation of it:
121
+ *
122
+ * - The capture UI is drawn by the host, so a user can trust it the way they trust
123
+ * the sign-in dialog. Do not build a lookalike; imitating host chrome is spoofing.
124
+ * - While the device is open the host shows a persistent "camera on" / "microphone
125
+ * on" indicator in its own chrome, for the whole session. You cannot hide it and
126
+ * should not try to.
127
+ * - If the user cancels, this rejects `cancelled` and **nothing was recorded**. There
128
+ * is no partial result — do not write error handling that hopes for one.
129
+ *
130
+ * ```ts
131
+ * import { capturePhoto, requestMount, openFs } from '@immediately-run/sdk';
132
+ *
133
+ * try {
134
+ * const { bytes, mimeType } = await capturePhoto({ facing: 'environment' });
135
+ * const mount = await requestMount(); // the user picks a space to save into
136
+ * const fs = openFs(mount); // writeFile is a METHOD on the MountFs
137
+ * await fs.mkdir('photos', { recursive: true }); // writeFile does not create parents
138
+ * await fs.writeFile(`photos/latest.${mimeType === 'image/png' ? 'png' : 'jpg'}`, new Uint8Array(bytes));
139
+ * } catch (e) {
140
+ * if ((e as { code?: string }).code === 'cancelled') return; // the user said no
141
+ * throw e;
142
+ * }
143
+ * ```
144
+ * (The extension follows `mimeType` because the HOST chooses the format — do not assume
145
+ * JPEG. `openAppFs()` is the variant that writes to your own app space with no pick.)
146
+ *
147
+ * Two things must BOTH be true, exactly as for any other task:
148
+ * 1. `immediately.run.invokes` lists `capture-photo` (or `capture-audio`);
149
+ * 2. your app holds `task:invoke` **and** `device:camera` (or `device:microphone`)
150
+ * — declare them in `immediately.run.capabilities` so the user can grant them.
151
+ *
152
+ * Rejects with `.code`: `cancelled` (the user dismissed the capture — nothing was
153
+ * recorded), `forbidden` (you lack the capability; `message: 'browser-denied'` means
154
+ * the BROWSER refused immediately.run itself, which your consent cannot fix),
155
+ * `unavailable` (no such device on this machine, or it would not start),
156
+ * `unsupported` (this host cannot capture), `not-declared`, `timeout`.
157
+ *
158
+ * *Zero-platform alternative, still worth knowing:* `<input type="file"
159
+ * accept="image/*" capture="environment">` opens the OS camera from inside the
160
+ * sandbox (file choosers are not permission-gated). It needs no capability and no
161
+ * host support; it also gives you no indicator and no host-drawn surface.
162
+ */
163
+ declare const capturePhoto: (options?: CaptureOptions) => Promise<CaptureResult>;
164
+ /** Record one audio clip through the host's capture UI. See {@link capturePhoto} —
165
+ * every rule there applies verbatim, with `device:microphone` and `capture-audio`. */
166
+ declare const captureAudio: (options?: CaptureOptions) => Promise<CaptureResult>;
52
167
  /** The params this app was invoked with as a task callee. */
53
168
  interface TaskInput {
54
169
  task: string;
55
170
  params: Record<string, unknown>;
56
171
  }
57
- /** The task params this app was invoked with, or null if it isn't a task callee. */
172
+ /** The task params this app was invoked with, or null if it isn't a task callee.
173
+ * Off-host (plain `vite dev`) this is always `null` — there is no host to invoke
174
+ * this app as a callee. */
58
175
  declare const getTaskInput: () => TaskInput | null;
59
176
  /**
60
177
  * Finish the task, returning a result to the caller. The host validates it against
61
178
  * the contract's result schema before resolving the caller (`invalid-params` on
62
179
  * violation), then tears down this overlay.
180
+ *
181
+ * `result` must be STRUCTURED-CLONEABLE — it crosses a frame boundary. A DOM node, a
182
+ * function, a class instance with methods, a `MediaStreamTrack`: all `DataCloneError`.
183
+ * **That throws**, deliberately and loudly: the alternative is a caller left hanging to
184
+ * its `invokeTask` deadline while this app believes it answered.
185
+ *
186
+ * Off-host (plain `vite dev` — no host transport) this is a no-op: there is no caller
187
+ * to answer. That case, and only that case, is silent.
63
188
  */
64
189
  declare const completeTask: (result: unknown) => void;
65
- /** Abort the task; the caller's `invokeTask` rejects with `cancelled`. */
190
+ /** Abort the task; the caller's `invokeTask` rejects with `cancelled`.
191
+ * Off-host (plain `vite dev` — no host transport) this is a no-op; on-host a failed
192
+ * send throws rather than leaving the caller waiting, exactly as {@link completeTask}. */
66
193
  declare const cancelTask: () => void;
67
- /** React hook: the task input for this callee, re-rendering when it arrives. */
194
+ /** React hook: the task input for this callee, re-rendering when it arrives.
195
+ * Off-host (plain `vite dev`) it stays `null` forever — render the non-callee
196
+ * state rather than waiting on it. */
68
197
  declare const useTaskInput: () => TaskInput | null;
69
198
 
70
- export { type DirCap, type FileCap, type TaskInput, cancelTask, capDir, capFile, completeTask, getTaskInput, invokeTask, useTaskInput };
199
+ export { type CaptureOptions, type CaptureResult, type DirCap, type FileCap, type TaskInput, cancelTask, capDir, capFile, captureAudio, capturePhoto, completeTask, getTaskInput, invokeTask, useTaskInput };
package/dist/tasks.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import "./chunk-VHAA22YE.js";
2
2
  import { useEffect, useState } from "react";
3
3
  import { protocolRequest, sendMessage, addListener } from "./sandboxUtils";
4
+ import { transport } from "./hostTransport";
4
5
  import { PROTOCOL_TASK, TASK_CANCEL, TASK_COMPLETE, TASK_INPUT } from "./generated/protocol";
5
6
  import { SCHEMES } from "./protocolSchemes";
6
7
  const capFile = (ref, opts) => ({
@@ -24,15 +25,44 @@ const invokeTask = async (task, params = {}) => {
24
25
  }
25
26
  return res.data;
26
27
  };
28
+ const capturePhoto = (options = {}) => invokeTask("capture-photo", { ...options });
29
+ const captureAudio = (options = {}) => invokeTask("capture-audio", { ...options });
27
30
  let latestInput = null;
28
31
  const inputListeners = /* @__PURE__ */ new Set();
29
- addListener(TASK_INPUT, (m) => {
30
- latestInput = { task: m.task, params: m.params ?? {} };
31
- inputListeners.forEach((l) => l(latestInput));
32
- });
33
- const getTaskInput = () => latestInput;
34
- const completeTask = (result) => sendMessage(TASK_COMPLETE, { result });
35
- const cancelTask = () => sendMessage(TASK_CANCEL, {});
32
+ let inputListenerRegistered = false;
33
+ const ensureInputListener = () => {
34
+ if (inputListenerRegistered) return;
35
+ try {
36
+ addListener(TASK_INPUT, (m) => {
37
+ latestInput = { task: m.task, params: m.params ?? {} };
38
+ inputListeners.forEach((l) => l(latestInput));
39
+ });
40
+ } catch {
41
+ return;
42
+ }
43
+ inputListenerRegistered = true;
44
+ };
45
+ ensureInputListener();
46
+ const getTaskInput = () => {
47
+ ensureInputListener();
48
+ return latestInput;
49
+ };
50
+ const hostReachable = () => {
51
+ try {
52
+ transport();
53
+ return true;
54
+ } catch {
55
+ return false;
56
+ }
57
+ };
58
+ const completeTask = (result) => {
59
+ if (!hostReachable()) return;
60
+ sendMessage(TASK_COMPLETE, { result });
61
+ };
62
+ const cancelTask = () => {
63
+ if (!hostReachable()) return;
64
+ sendMessage(TASK_CANCEL, {});
65
+ };
36
66
  const useTaskInput = () => {
37
67
  const [input, setInput] = useState(getTaskInput);
38
68
  useEffect(() => {
@@ -49,6 +79,8 @@ export {
49
79
  cancelTask,
50
80
  capDir,
51
81
  capFile,
82
+ captureAudio,
83
+ capturePhoto,
52
84
  completeTask,
53
85
  getTaskInput,
54
86
  invokeTask,
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';\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":";AAWA,SAAS,WAAW,gBAAgB;AACpC,SAAS,iBAAiB,aAAa,mBAAmB;AAC1D,SAAS,eAAe,aAAa,eAAe,kBAAkB;AACtE,SAAS,eAAe;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,MAAM,gBAAgB,QAAQ,aAAa,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;AAIvD,YAAY,YAAY,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,WAA0B,YAAY,eAAe,EAAE,OAAO,CAAC;AAGrF,MAAM,aAAa,MAAY,YAAY,aAAa,CAAC,CAAC;AAG1D,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()`.\n//\n// OFF-HOST (plain `vite dev`, node/jsdom tests — no sandbox, no host transport):\n// importing this module is always safe (R3-421 — no subpath may throw at import\n// time), and the surface degrades instead of crashing: `getTaskInput()` /\n// `useTaskInput()` stay `null` forever (you are never a task callee without a\n// host), `completeTask` / `cancelTask` are no-ops, and `invokeTask` rejects with\n// a \"no host transport\" error. Under the @immediately-run/dev-fs substrate the\n// transport resolves but no task ever arrives — same observable behaviour.\n// The no-op is scoped to the ABSENCE of a host: when there IS one, a failed send\n// throws instead of leaving the caller to hang out its deadline.\nimport { useEffect, useState } from 'react';\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { transport } from './hostTransport';\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` (missing `task:invoke`, or a\n * file delegation you don't hold), `not-declared` (the task is missing from your\n * `immediately.run.invokes`), `no-such-task` (no app is bound to that contract),\n * `task-cycle`/`task-depth-exceeded`/`task-version-mismatch`, or `invalid-params`\n * (result failed the contract schema). **Read `err.message`** — since R3-418 the host\n * says which capability, which declaration or which param was at fault; the code alone\n * does not distinguish them.\n *\n * Two things must BOTH be true before any invoke is admitted, and they are separate:\n * 1. your `package.json` `immediately.run.invokes` lists the task (else `not-declared`);\n * 2. your app holds the `task:invoke` capability — declare it in\n * `immediately.run.capabilities` so the user can grant it (else `forbidden`).\n * Declaring the invoke does not imply the capability.\n *\n * ── `pick-file`: `roots` is required (SPACES_UI_SPEC §4.1) ───────────────────\n * ```ts\n * import { invokeTask, capDir, requestMount } from '@immediately-run/sdk';\n *\n * const mount = await requestMount(); // the user picks a space\n * // `invokeTask` returns `unknown` — name the result type, or the destructuring\n * // below does not compile.\n * const { root, path } = await invokeTask<{ root: number; path: string }>('pick-file', {\n * // `SandboxMount.id` is optional (absent on the primary repo mount) — fall back to\n * // `path`, which is always present and is what the host resolves against.\n * roots: [capDir({ mountId: mount.id ?? mount.path, relPath: 'boards' }, { mode: 'ro' })],\n * });\n * ```\n * - `roots` is a NON-EMPTY `DirCap[]` of directories you **already hold**. The result\n * names its target as `{ root, path }` where `root` INDEXES `params.roots`, so\n * `invokeTask('pick-file', {})` can never produce a valid result and is rejected.\n * There is no rootless \"pick from any of my spaces\" mode by design: enumerating the\n * user's spaces is the host's powerbox (`requestMount()`), and composing the two —\n * as above — is that flow. See SPACES_UI_SPEC R-SPACES-5/R-SPACES-12.\n * - Each `capDir` is attenuated against your own grant. **`{ mode: 'rw' }` over a space\n * the user granted `ro` is refused `forbidden`** — the commonest failure, because the\n * mount *was* granted and the path *is* inside it. Pass the mode you actually hold;\n * `ro` is right for a plain pick.\n * - The `requestMount()` grant is DURABLE and survives a cancelled pick: the user\n * consented to the mount in host chrome, separately from the pick (SPACES_UI_SPEC §4.1).\n * Reuse it for later picks rather than re-requesting.\n *\n * Off-host (plain `vite dev` — no host transport) it rejects with a plain\n * \"no host transport\" error: there is no host to resolve the task binding.\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// ── the host-provided CAPTURE contracts (R3-425) ────────────────────────────\n\n/** Bytes captured by the host, as `capturePhoto()` / `captureAudio()` return them.\n * `bytes` is a transferable `ArrayBuffer` — write it to a space, decode it, upload\n * it. You never receive a `MediaStream` or a `MediaStreamTrack`, and that is not an\n * omission: a track cannot be transferred into an app frame at all\n * (`DataCloneError`), and the frame's opaque origin cannot open a device itself\n * (`getUserMedia` throws `SecurityError: Invalid security origin`). */\nexport interface CaptureResult {\n bytes: ArrayBuffer;\n /** e.g. `image/jpeg`, `audio/webm`. Chosen by the HOST, not by you. */\n mimeType: string;\n /** How long the device was open, in ms. */\n durationMs: number;\n}\n\n/** Optional hints for a capture. They deliberately name NOTHING — no device id, no\n * camera index — because the host chooses everything about how the capture is\n * performed. That is what lets the grant be a plain on/off instead of a per-call\n * target list the way `net:fetch` needs. */\nexport interface CaptureOptions {\n /** Which way the camera should face, if the device has a choice. A HINT: the host\n * may ignore it. Ignored for audio. */\n facing?: 'user' | 'environment';\n}\n\n/**\n * Take one photo, or record one audio clip, through the HOST's capture UI\n * (`BROWSER_CAPABILITIES_SPEC` §3 grade 1).\n *\n * The host opens the device at its own origin, draws the viewfinder, and hands you\n * the bytes when the user presses Done. **You are never in the loop while the device\n * is live** — that is the point of the design, not a limitation of it:\n *\n * - The capture UI is drawn by the host, so a user can trust it the way they trust\n * the sign-in dialog. Do not build a lookalike; imitating host chrome is spoofing.\n * - While the device is open the host shows a persistent \"camera on\" / \"microphone\n * on\" indicator in its own chrome, for the whole session. You cannot hide it and\n * should not try to.\n * - If the user cancels, this rejects `cancelled` and **nothing was recorded**. There\n * is no partial result — do not write error handling that hopes for one.\n *\n * ```ts\n * import { capturePhoto, requestMount, openFs } from '@immediately-run/sdk';\n *\n * try {\n * const { bytes, mimeType } = await capturePhoto({ facing: 'environment' });\n * const mount = await requestMount(); // the user picks a space to save into\n * const fs = openFs(mount); // writeFile is a METHOD on the MountFs\n * await fs.mkdir('photos', { recursive: true }); // writeFile does not create parents\n * await fs.writeFile(`photos/latest.${mimeType === 'image/png' ? 'png' : 'jpg'}`, new Uint8Array(bytes));\n * } catch (e) {\n * if ((e as { code?: string }).code === 'cancelled') return; // the user said no\n * throw e;\n * }\n * ```\n * (The extension follows `mimeType` because the HOST chooses the format — do not assume\n * JPEG. `openAppFs()` is the variant that writes to your own app space with no pick.)\n *\n * Two things must BOTH be true, exactly as for any other task:\n * 1. `immediately.run.invokes` lists `capture-photo` (or `capture-audio`);\n * 2. your app holds `task:invoke` **and** `device:camera` (or `device:microphone`)\n * — declare them in `immediately.run.capabilities` so the user can grant them.\n *\n * Rejects with `.code`: `cancelled` (the user dismissed the capture — nothing was\n * recorded), `forbidden` (you lack the capability; `message: 'browser-denied'` means\n * the BROWSER refused immediately.run itself, which your consent cannot fix),\n * `unavailable` (no such device on this machine, or it would not start),\n * `unsupported` (this host cannot capture), `not-declared`, `timeout`.\n *\n * *Zero-platform alternative, still worth knowing:* `<input type=\"file\"\n * accept=\"image/*\" capture=\"environment\">` opens the OS camera from inside the\n * sandbox (file choosers are not permission-gated). It needs no capability and no\n * host support; it also gives you no indicator and no host-drawn surface.\n */\nexport const capturePhoto = (options: CaptureOptions = {}): Promise<CaptureResult> =>\n invokeTask<CaptureResult>('capture-photo', { ...options });\n\n/** Record one audio clip through the host's capture UI. See {@link capturePhoto} —\n * every rule there applies verbatim, with `device:microphone` and `capture-audio`. */\nexport const captureAudio = (options: CaptureOptions = {}): Promise<CaptureResult> =>\n invokeTask<CaptureResult>('capture-audio', { ...options });\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>();\nlet inputListenerRegistered = false;\n\n// Register the `task-input` listener IF a host transport is reachable; otherwise do\n// nothing (and let the next call retry — the same pattern as pushChannel's `start`).\n// Split out so both the module-eval attempt below and the first-use call sites share\n// one idempotent path.\nconst ensureInputListener = (): void => {\n if (inputListenerRegistered) return;\n try {\n addListener(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 } catch {\n return; // off-host: the transport resolver threw — no host to listen on\n }\n inputListenerRegistered = true;\n};\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\"). That wire\n// message is a plain one-shot host→app message with NO replay/poll counterpart —\n// the contract (`@immediately-run/sandbox-protocol/sdk`) marks replayable state as\n// \"push … polled with request-*\" (mounts, theme, auth-state, …) and `task-input`\n// is not one of them — so a listener registered only on first use could miss an\n// input delivered between module evaluation and the app's first render. Hence:\n// register EAGERLY when a host transport is already present at module eval\n// (on-host, byte-for-byte the pre-R3-421 behaviour), and lazily-on-first-use\n// otherwise, so importing this module off-host (plain `vite dev`) never throws.\nensureInputListener();\n\n/** The task params this app was invoked with, or null if it isn't a task callee.\n * Off-host (plain `vite dev`) this is always `null` — there is no host to invoke\n * this app as a callee. */\nexport const getTaskInput = (): TaskInput | null => {\n ensureInputListener();\n return latestInput;\n};\n\n/**\n * Is a host transport reachable from this realm at all?\n *\n * The R3-421 off-host no-op below must mean EXACTLY \"there is nobody to answer\" —\n * plain `vite dev`, node, jsdom. A blanket try/catch around the send meant it also\n * swallowed a genuine failure against a REAL host (`DataCloneError` because the\n * result holds a DOM node, a function or a live class instance, being the usual\n * one): the host was then never told the task finished, and the caller's\n * `invokeTask` hung to its deadline with no diagnostic anywhere. Absence is\n * therefore decided BEFORE the send, and the send itself is left to throw.\n */\nconst hostReachable = (): boolean => {\n try {\n transport();\n return true;\n } catch {\n return false; // no injected bundler bus and no §4 discovery global — off-host\n }\n};\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 *\n * `result` must be STRUCTURED-CLONEABLE — it crosses a frame boundary. A DOM node, a\n * function, a class instance with methods, a `MediaStreamTrack`: all `DataCloneError`.\n * **That throws**, deliberately and loudly: the alternative is a caller left hanging to\n * its `invokeTask` deadline while this app believes it answered.\n *\n * Off-host (plain `vite dev` — no host transport) this is a no-op: there is no caller\n * to answer. That case, and only that case, is silent.\n */\nexport const completeTask = (result: unknown): void => {\n if (!hostReachable()) return; // off-host: no caller to answer — documented no-op\n // On-host, a failure here is REAL and the caller is waiting on this exact message.\n sendMessage(TASK_COMPLETE, { result });\n};\n\n/** Abort the task; the caller's `invokeTask` rejects with `cancelled`.\n * Off-host (plain `vite dev` — no host transport) this is a no-op; on-host a failed\n * send throws rather than leaving the caller waiting, exactly as {@link completeTask}. */\nexport const cancelTask = (): void => {\n if (!hostReachable()) return; // off-host: no caller to answer — documented no-op\n sendMessage(TASK_CANCEL, {});\n};\n\n/** React hook: the task input for this callee, re-rendering when it arrives.\n * Off-host (plain `vite dev`) it stays `null` forever — render the non-callee\n * state rather than waiting on it. */\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":";AAqBA,SAAS,WAAW,gBAAgB;AACpC,SAAS,iBAAiB,aAAa,mBAAmB;AAC1D,SAAS,iBAAiB;AAC1B,SAAS,eAAe,aAAa,eAAe,kBAAkB;AACtE,SAAS,eAAe;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;AAiDO,MAAM,aAAa,OAAoB,MAAc,SAAkC,CAAC,MAAkB;AAC/G,QAAM,MAAO,MAAM,gBAAgB,QAAQ,aAAa,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;AA6EO,MAAM,eAAe,CAAC,UAA0B,CAAC,MACtD,WAA0B,iBAAiB,EAAE,GAAG,QAAQ,CAAC;AAIpD,MAAM,eAAe,CAAC,UAA0B,CAAC,MACtD,WAA0B,iBAAiB,EAAE,GAAG,QAAQ,CAAC;AAU3D,IAAI,cAAgC;AACpC,MAAM,iBAAiB,oBAAI,IAA4B;AACvD,IAAI,0BAA0B;AAM9B,MAAM,sBAAsB,MAAY;AACtC,MAAI,wBAAyB;AAC7B,MAAI;AACF,gBAAY,YAAY,CAAC,MAA0D;AACjF,oBAAc,EAAE,MAAM,EAAE,MAAM,QAAQ,EAAE,UAAU,CAAC,EAAE;AACrD,qBAAe,QAAQ,CAAC,MAAM,EAAE,WAAY,CAAC;AAAA,IAC/C,CAAC;AAAA,EACH,QAAQ;AACN;AAAA,EACF;AACA,4BAA0B;AAC5B;AAYA,oBAAoB;AAKb,MAAM,eAAe,MAAwB;AAClD,sBAAoB;AACpB,SAAO;AACT;AAaA,MAAM,gBAAgB,MAAe;AACnC,MAAI;AACF,cAAU;AACV,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAeO,MAAM,eAAe,CAAC,WAA0B;AACrD,MAAI,CAAC,cAAc,EAAG;AAEtB,cAAY,eAAe,EAAE,OAAO,CAAC;AACvC;AAKO,MAAM,aAAa,MAAY;AACpC,MAAI,CAAC,cAAc,EAAG;AACtB,cAAY,aAAa,CAAC,CAAC;AAC7B;AAKO,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.57.1";
24
+ const SDK_VERSION = "0.58.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.57.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.58.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.57.1";
2
+ declare const SDK_VERSION = "0.58.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.57.1";
2
+ declare const SDK_VERSION = "0.58.0";
3
3
 
4
4
  export { SDK_VERSION };
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./chunk-VHAA22YE.js";
2
- const SDK_VERSION = "0.57.1";
2
+ const SDK_VERSION = "0.58.0";
3
3
  export {
4
4
  SDK_VERSION
5
5
  };
@@ -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.57.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.58.0';\n"],"mappings":";AAIO,MAAM,cAAc;","names":[]}
@@ -0,0 +1,53 @@
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 workspace_exports = {};
20
+ __export(workspace_exports, {
21
+ getWorkspace: () => getWorkspace,
22
+ onWorkspaceChange: () => onWorkspaceChange,
23
+ useWorkspace: () => useWorkspace
24
+ });
25
+ module.exports = __toCommonJS(workspace_exports);
26
+ var import_pushChannel = require("./pushChannel");
27
+ var import_protocol = require("./generated/protocol");
28
+ const DEFAULT_WORKSPACE = null;
29
+ const isWorkspace = (v) => {
30
+ const w = v;
31
+ return !!w && typeof w === "object" && typeof w.provider === "string" && typeof w.namespace === "string" && typeof w.repository === "string" && typeof w.ref === "string" && typeof w.label === "string";
32
+ };
33
+ const channel = (0, import_pushChannel.createPushChannel)({
34
+ pushType: import_protocol.WORKSPACE,
35
+ requestType: import_protocol.REQUEST_WORKSPACE,
36
+ initial: DEFAULT_WORKSPACE,
37
+ parse: (msg) => {
38
+ if (msg.workspace === null) return null;
39
+ if (!isWorkspace(msg.workspace)) return void 0;
40
+ const { provider, namespace, repository, ref, label } = msg.workspace;
41
+ return { provider, namespace, repository, ref, label };
42
+ }
43
+ });
44
+ const getWorkspace = () => channel.get();
45
+ const onWorkspaceChange = (listener) => channel.onChange(listener);
46
+ const useWorkspace = () => channel.use();
47
+ // Annotate the CommonJS export names for ESM import in node:
48
+ 0 && (module.exports = {
49
+ getWorkspace,
50
+ onWorkspaceChange,
51
+ useWorkspace
52
+ });
53
+ //# sourceMappingURL=workspace.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/workspace.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\nimport { WORKSPACE, REQUEST_WORKSPACE } from './generated/protocol';\n\n/**\n * Which project the immediately.run workbench currently has open — the repository\n * the user is editing, not your app's own repository. `null` when your app is not\n * running inside an editing session at all (a standalone full-tab route, a task\n * overlay): that is a real answer, not a failure, and code here must handle it.\n *\n * ⚠ **`namespace` is not necessarily a GitHub owner.** A `local` session is\n * well-formed but not GitHub-shaped — `/edit/local/my-app-3fa9c2d1/my-app/live`\n * reads back as `{ provider: 'local', namespace: 'my-app-3fa9c2d1', repository:\n * 'my-app', ref: 'live' }`. Branch on `provider` before rendering a GitHub URL.\n *\n * Baseline capability `workspace:read` — every app may read it. It discloses\n * nothing that is not already in the session URL your app can read from\n * {@link onUrlChange} when it drives the host route; this channel exists for the\n * apps that *don't* — a self-routed panel keeps its own route, never receives\n * `urlchange`, and would otherwise have no way to learn which project it is in.\n *\n * There is deliberately no counterpart that lets an app *change* the workspace:\n * navigating the workbench stays a host action under the ordinary consent.\n *\n * ```ts\n * import { useWorkspace } from '@immediately-run/sdk';\n *\n * const workspace = useWorkspace();\n * // Scope your app's stored data to the project it belongs to.\n * const scope = workspace?.label ?? null;\n * ```\n */\nexport interface Workspace {\n /** Loader provider — `'github'`, `'local'`, … Branch on this, never assume. */\n provider: string;\n /** Owner/namespace segment. See the warning above: NOT always a GitHub owner. */\n namespace: string;\n /** Repository/project segment. */\n repository: string;\n /** The branch, tag, or ref-shaped segment of the session. */\n ref: string;\n /**\n * The session's display identity, `` `${namespace}/${repository}` `` — e.g.\n * `neumark-family/recipes`.\n *\n * This is the string to key durable per-project data on. It is the SAME value the\n * host labels the working-tree mount with, so an app that already scopes by that\n * mount's label needs no migration to move onto this channel.\n */\n label: string;\n}\n\n/**\n * Assumed before the host reports — and the value that stands forever on a host too\n * old to push this channel. `null` means \"no project here\", which is also the honest\n * answer outside an editing session, so an app written against it degrades to\n * unscoped rather than to wrong.\n */\nconst DEFAULT_WORKSPACE: Workspace | null = null;\n\nconst isWorkspace = (v: unknown): v is Workspace => {\n const w = v as Partial<Workspace> | null;\n return (\n !!w &&\n typeof w === 'object' &&\n typeof w.provider === 'string' &&\n typeof w.namespace === 'string' &&\n typeof w.repository === 'string' &&\n typeof w.ref === 'string' &&\n typeof w.label === 'string'\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `workspace` and\n// answers `request-workspace` (wire format: site-main channelBridge.ts).\n//\n// `parse` must distinguish the host SAYING `null` from a message it cannot read.\n// `undefined` means \"not a value for this channel\" (createPushChannel ignores it and\n// keeps the last known workspace), while `null` is the host reporting \"no session\"\n// and MUST land — otherwise a frame that navigates out of a session would keep\n// reporting the project it used to be in.\n//\n// It REBUILDS the value field by field rather than passing the host's object through.\n// This channel is baseline-readable by every app, so \"it carries only the session's\n// coordinates\" has to be a property of this code, not of the host's good manners: a\n// host that pushed a token or a user id alongside them would otherwise get it into\n// app-visible memory by reference.\nconst channel = createPushChannel<Workspace | null>({\n pushType: WORKSPACE,\n requestType: REQUEST_WORKSPACE,\n initial: DEFAULT_WORKSPACE,\n parse: (msg) => {\n if (msg.workspace === null) return null;\n if (!isWorkspace(msg.workspace)) return undefined;\n const { provider, namespace, repository, ref, label } = msg.workspace;\n return { provider, namespace, repository, ref, label };\n },\n});\n\n/** Returns the current workspace, or `null` outside an editing session. */\nexport const getWorkspace = (): Workspace | null => channel.get();\n\n/**\n * Subscribe to workspace changes. The listener is invoked immediately with the\n * current value, then again on every change. Returns an unsubscribe fn.\n */\nexport const onWorkspaceChange = (listener: (workspace: Workspace | null) => void): (() => void) =>\n channel.onChange(listener);\n\n/** React hook returning the current workspace, re-rendering on change. */\nexport const useWorkspace = (): Workspace | null => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAkC;AAClC,sBAA6C;AAwD7C,MAAM,oBAAsC;AAE5C,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,KACF,OAAO,MAAM,YACb,OAAO,EAAE,aAAa,YACtB,OAAO,EAAE,cAAc,YACvB,OAAO,EAAE,eAAe,YACxB,OAAO,EAAE,QAAQ,YACjB,OAAO,EAAE,UAAU;AAEvB;AAgBA,MAAM,cAAU,sCAAoC;AAAA,EAClD,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QAAQ;AACd,QAAI,IAAI,cAAc,KAAM,QAAO;AACnC,QAAI,CAAC,YAAY,IAAI,SAAS,EAAG,QAAO;AACxC,UAAM,EAAE,UAAU,WAAW,YAAY,KAAK,MAAM,IAAI,IAAI;AAC5D,WAAO,EAAE,UAAU,WAAW,YAAY,KAAK,MAAM;AAAA,EACvD;AACF,CAAC;AAGM,MAAM,eAAe,MAAwB,QAAQ,IAAI;AAMzD,MAAM,oBAAoB,CAAC,aAChC,QAAQ,SAAS,QAAQ;AAGpB,MAAM,eAAe,MAAwB,QAAQ,IAAI;","names":[]}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Which project the immediately.run workbench currently has open — the repository
3
+ * the user is editing, not your app's own repository. `null` when your app is not
4
+ * running inside an editing session at all (a standalone full-tab route, a task
5
+ * overlay): that is a real answer, not a failure, and code here must handle it.
6
+ *
7
+ * ⚠ **`namespace` is not necessarily a GitHub owner.** A `local` session is
8
+ * well-formed but not GitHub-shaped — `/edit/local/my-app-3fa9c2d1/my-app/live`
9
+ * reads back as `{ provider: 'local', namespace: 'my-app-3fa9c2d1', repository:
10
+ * 'my-app', ref: 'live' }`. Branch on `provider` before rendering a GitHub URL.
11
+ *
12
+ * Baseline capability `workspace:read` — every app may read it. It discloses
13
+ * nothing that is not already in the session URL your app can read from
14
+ * {@link onUrlChange} when it drives the host route; this channel exists for the
15
+ * apps that *don't* — a self-routed panel keeps its own route, never receives
16
+ * `urlchange`, and would otherwise have no way to learn which project it is in.
17
+ *
18
+ * There is deliberately no counterpart that lets an app *change* the workspace:
19
+ * navigating the workbench stays a host action under the ordinary consent.
20
+ *
21
+ * ```ts
22
+ * import { useWorkspace } from '@immediately-run/sdk';
23
+ *
24
+ * const workspace = useWorkspace();
25
+ * // Scope your app's stored data to the project it belongs to.
26
+ * const scope = workspace?.label ?? null;
27
+ * ```
28
+ */
29
+ interface Workspace {
30
+ /** Loader provider — `'github'`, `'local'`, … Branch on this, never assume. */
31
+ provider: string;
32
+ /** Owner/namespace segment. See the warning above: NOT always a GitHub owner. */
33
+ namespace: string;
34
+ /** Repository/project segment. */
35
+ repository: string;
36
+ /** The branch, tag, or ref-shaped segment of the session. */
37
+ ref: string;
38
+ /**
39
+ * The session's display identity, `` `${namespace}/${repository}` `` — e.g.
40
+ * `neumark-family/recipes`.
41
+ *
42
+ * This is the string to key durable per-project data on. It is the SAME value the
43
+ * host labels the working-tree mount with, so an app that already scopes by that
44
+ * mount's label needs no migration to move onto this channel.
45
+ */
46
+ label: string;
47
+ }
48
+ /** Returns the current workspace, or `null` outside an editing session. */
49
+ declare const getWorkspace: () => Workspace | null;
50
+ /**
51
+ * Subscribe to workspace changes. The listener is invoked immediately with the
52
+ * current value, then again on every change. Returns an unsubscribe fn.
53
+ */
54
+ declare const onWorkspaceChange: (listener: (workspace: Workspace | null) => void) => (() => void);
55
+ /** React hook returning the current workspace, re-rendering on change. */
56
+ declare const useWorkspace: () => Workspace | null;
57
+
58
+ export { type Workspace, getWorkspace, onWorkspaceChange, useWorkspace };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Which project the immediately.run workbench currently has open — the repository
3
+ * the user is editing, not your app's own repository. `null` when your app is not
4
+ * running inside an editing session at all (a standalone full-tab route, a task
5
+ * overlay): that is a real answer, not a failure, and code here must handle it.
6
+ *
7
+ * ⚠ **`namespace` is not necessarily a GitHub owner.** A `local` session is
8
+ * well-formed but not GitHub-shaped — `/edit/local/my-app-3fa9c2d1/my-app/live`
9
+ * reads back as `{ provider: 'local', namespace: 'my-app-3fa9c2d1', repository:
10
+ * 'my-app', ref: 'live' }`. Branch on `provider` before rendering a GitHub URL.
11
+ *
12
+ * Baseline capability `workspace:read` — every app may read it. It discloses
13
+ * nothing that is not already in the session URL your app can read from
14
+ * {@link onUrlChange} when it drives the host route; this channel exists for the
15
+ * apps that *don't* — a self-routed panel keeps its own route, never receives
16
+ * `urlchange`, and would otherwise have no way to learn which project it is in.
17
+ *
18
+ * There is deliberately no counterpart that lets an app *change* the workspace:
19
+ * navigating the workbench stays a host action under the ordinary consent.
20
+ *
21
+ * ```ts
22
+ * import { useWorkspace } from '@immediately-run/sdk';
23
+ *
24
+ * const workspace = useWorkspace();
25
+ * // Scope your app's stored data to the project it belongs to.
26
+ * const scope = workspace?.label ?? null;
27
+ * ```
28
+ */
29
+ interface Workspace {
30
+ /** Loader provider — `'github'`, `'local'`, … Branch on this, never assume. */
31
+ provider: string;
32
+ /** Owner/namespace segment. See the warning above: NOT always a GitHub owner. */
33
+ namespace: string;
34
+ /** Repository/project segment. */
35
+ repository: string;
36
+ /** The branch, tag, or ref-shaped segment of the session. */
37
+ ref: string;
38
+ /**
39
+ * The session's display identity, `` `${namespace}/${repository}` `` — e.g.
40
+ * `neumark-family/recipes`.
41
+ *
42
+ * This is the string to key durable per-project data on. It is the SAME value the
43
+ * host labels the working-tree mount with, so an app that already scopes by that
44
+ * mount's label needs no migration to move onto this channel.
45
+ */
46
+ label: string;
47
+ }
48
+ /** Returns the current workspace, or `null` outside an editing session. */
49
+ declare const getWorkspace: () => Workspace | null;
50
+ /**
51
+ * Subscribe to workspace changes. The listener is invoked immediately with the
52
+ * current value, then again on every change. Returns an unsubscribe fn.
53
+ */
54
+ declare const onWorkspaceChange: (listener: (workspace: Workspace | null) => void) => (() => void);
55
+ /** React hook returning the current workspace, re-rendering on change. */
56
+ declare const useWorkspace: () => Workspace | null;
57
+
58
+ export { type Workspace, getWorkspace, onWorkspaceChange, useWorkspace };
@@ -0,0 +1,28 @@
1
+ import "./chunk-VHAA22YE.js";
2
+ import { createPushChannel } from "./pushChannel";
3
+ import { WORKSPACE, REQUEST_WORKSPACE } from "./generated/protocol";
4
+ const DEFAULT_WORKSPACE = null;
5
+ const isWorkspace = (v) => {
6
+ const w = v;
7
+ return !!w && typeof w === "object" && typeof w.provider === "string" && typeof w.namespace === "string" && typeof w.repository === "string" && typeof w.ref === "string" && typeof w.label === "string";
8
+ };
9
+ const channel = createPushChannel({
10
+ pushType: WORKSPACE,
11
+ requestType: REQUEST_WORKSPACE,
12
+ initial: DEFAULT_WORKSPACE,
13
+ parse: (msg) => {
14
+ if (msg.workspace === null) return null;
15
+ if (!isWorkspace(msg.workspace)) return void 0;
16
+ const { provider, namespace, repository, ref, label } = msg.workspace;
17
+ return { provider, namespace, repository, ref, label };
18
+ }
19
+ });
20
+ const getWorkspace = () => channel.get();
21
+ const onWorkspaceChange = (listener) => channel.onChange(listener);
22
+ const useWorkspace = () => channel.use();
23
+ export {
24
+ getWorkspace,
25
+ onWorkspaceChange,
26
+ useWorkspace
27
+ };
28
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/workspace.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\nimport { WORKSPACE, REQUEST_WORKSPACE } from './generated/protocol';\n\n/**\n * Which project the immediately.run workbench currently has open — the repository\n * the user is editing, not your app's own repository. `null` when your app is not\n * running inside an editing session at all (a standalone full-tab route, a task\n * overlay): that is a real answer, not a failure, and code here must handle it.\n *\n * ⚠ **`namespace` is not necessarily a GitHub owner.** A `local` session is\n * well-formed but not GitHub-shaped — `/edit/local/my-app-3fa9c2d1/my-app/live`\n * reads back as `{ provider: 'local', namespace: 'my-app-3fa9c2d1', repository:\n * 'my-app', ref: 'live' }`. Branch on `provider` before rendering a GitHub URL.\n *\n * Baseline capability `workspace:read` — every app may read it. It discloses\n * nothing that is not already in the session URL your app can read from\n * {@link onUrlChange} when it drives the host route; this channel exists for the\n * apps that *don't* — a self-routed panel keeps its own route, never receives\n * `urlchange`, and would otherwise have no way to learn which project it is in.\n *\n * There is deliberately no counterpart that lets an app *change* the workspace:\n * navigating the workbench stays a host action under the ordinary consent.\n *\n * ```ts\n * import { useWorkspace } from '@immediately-run/sdk';\n *\n * const workspace = useWorkspace();\n * // Scope your app's stored data to the project it belongs to.\n * const scope = workspace?.label ?? null;\n * ```\n */\nexport interface Workspace {\n /** Loader provider — `'github'`, `'local'`, … Branch on this, never assume. */\n provider: string;\n /** Owner/namespace segment. See the warning above: NOT always a GitHub owner. */\n namespace: string;\n /** Repository/project segment. */\n repository: string;\n /** The branch, tag, or ref-shaped segment of the session. */\n ref: string;\n /**\n * The session's display identity, `` `${namespace}/${repository}` `` — e.g.\n * `neumark-family/recipes`.\n *\n * This is the string to key durable per-project data on. It is the SAME value the\n * host labels the working-tree mount with, so an app that already scopes by that\n * mount's label needs no migration to move onto this channel.\n */\n label: string;\n}\n\n/**\n * Assumed before the host reports — and the value that stands forever on a host too\n * old to push this channel. `null` means \"no project here\", which is also the honest\n * answer outside an editing session, so an app written against it degrades to\n * unscoped rather than to wrong.\n */\nconst DEFAULT_WORKSPACE: Workspace | null = null;\n\nconst isWorkspace = (v: unknown): v is Workspace => {\n const w = v as Partial<Workspace> | null;\n return (\n !!w &&\n typeof w === 'object' &&\n typeof w.provider === 'string' &&\n typeof w.namespace === 'string' &&\n typeof w.repository === 'string' &&\n typeof w.ref === 'string' &&\n typeof w.label === 'string'\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `workspace` and\n// answers `request-workspace` (wire format: site-main channelBridge.ts).\n//\n// `parse` must distinguish the host SAYING `null` from a message it cannot read.\n// `undefined` means \"not a value for this channel\" (createPushChannel ignores it and\n// keeps the last known workspace), while `null` is the host reporting \"no session\"\n// and MUST land — otherwise a frame that navigates out of a session would keep\n// reporting the project it used to be in.\n//\n// It REBUILDS the value field by field rather than passing the host's object through.\n// This channel is baseline-readable by every app, so \"it carries only the session's\n// coordinates\" has to be a property of this code, not of the host's good manners: a\n// host that pushed a token or a user id alongside them would otherwise get it into\n// app-visible memory by reference.\nconst channel = createPushChannel<Workspace | null>({\n pushType: WORKSPACE,\n requestType: REQUEST_WORKSPACE,\n initial: DEFAULT_WORKSPACE,\n parse: (msg) => {\n if (msg.workspace === null) return null;\n if (!isWorkspace(msg.workspace)) return undefined;\n const { provider, namespace, repository, ref, label } = msg.workspace;\n return { provider, namespace, repository, ref, label };\n },\n});\n\n/** Returns the current workspace, or `null` outside an editing session. */\nexport const getWorkspace = (): Workspace | null => channel.get();\n\n/**\n * Subscribe to workspace changes. The listener is invoked immediately with the\n * current value, then again on every change. Returns an unsubscribe fn.\n */\nexport const onWorkspaceChange = (listener: (workspace: Workspace | null) => void): (() => void) =>\n channel.onChange(listener);\n\n/** React hook returning the current workspace, re-rendering on change. */\nexport const useWorkspace = (): Workspace | null => channel.use();\n"],"mappings":";AAAA,SAAS,yBAAyB;AAClC,SAAS,WAAW,yBAAyB;AAwD7C,MAAM,oBAAsC;AAE5C,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,KACF,OAAO,MAAM,YACb,OAAO,EAAE,aAAa,YACtB,OAAO,EAAE,cAAc,YACvB,OAAO,EAAE,eAAe,YACxB,OAAO,EAAE,QAAQ,YACjB,OAAO,EAAE,UAAU;AAEvB;AAgBA,MAAM,UAAU,kBAAoC;AAAA,EAClD,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QAAQ;AACd,QAAI,IAAI,cAAc,KAAM,QAAO;AACnC,QAAI,CAAC,YAAY,IAAI,SAAS,EAAG,QAAO;AACxC,UAAM,EAAE,UAAU,WAAW,YAAY,KAAK,MAAM,IAAI,IAAI;AAC5D,WAAO,EAAE,UAAU,WAAW,YAAY,KAAK,MAAM;AAAA,EACvD;AACF,CAAC;AAGM,MAAM,eAAe,MAAwB,QAAQ,IAAI;AAMzD,MAAM,oBAAoB,CAAC,aAChC,QAAQ,SAAS,QAAQ;AAGpB,MAAM,eAAe,MAAwB,QAAQ,IAAI;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immediately-run/sdk",
3
- "version": "0.57.1",
3
+ "version": "0.58.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",
@@ -39,11 +39,12 @@
39
39
  "api:update": "node scripts/check-api-stability.mjs --update",
40
40
  "check:pins": "node scripts/check-dependency-pins.mjs --self-test && node scripts/check-dependency-pins.mjs",
41
41
  "check:publish-version": "node scripts/check-publish-version.mjs --self-test && node scripts/check-publish-version.mjs",
42
- "verify": "npm run check:pins && npm run check:publish-version && npm run format:check && npm run check:circular && npm run check:bundler:selftest && npm run check:bundler && npm run build && npm test && npm run test:safe-content && npm run test:metadata-e2e && npm run api:selftest && npm run api:check && npm run compat:selftest && npm run compat:previous && npm run protocol:check && npm run protocol:selftest && npm run check:ambient:selftest && npm run check:ambient && npm run check:selfhost:selftest && npm run check:selfhost && npm run verify:codegen-parity",
42
+ "verify": "npm run check:pins && npm run check:publish-version && npm run format:check && npm run check:circular && npm run check:bundler:selftest && npm run check:bundler && npm run build && npm test && npm run test:safe-content && npm run test:metadata-e2e && npm run test:subpath-imports && npm run api:selftest && npm run api:check && npm run compat:selftest && npm run compat:previous && npm run protocol:check && npm run protocol:selftest && npm run check:ambient:selftest && npm run check:ambient && npm run check:selfhost:selftest && npm run check:selfhost && npm run verify:codegen-parity",
43
43
  "docs": "typedoc --json docs/api.json && node scripts/gen-llms.mjs",
44
44
  "prepublishOnly": "npm run check:circular && npm run build && npm run api:selftest && npm run api:check",
45
45
  "test:safe-content": "node scripts/build-safecontent-e2e.mjs && node --test test/safeContent.e2e.mjs",
46
46
  "test:metadata-e2e": "node --test test/metadataHooks.e2e.mjs",
47
+ "test:subpath-imports": "node --test test/subpathImports.e2e.mjs",
47
48
  "protocol:selftest": "node scripts/check-protocol-snapshot.mjs --self-test",
48
49
  "compat:previous": "node scripts/check-previous-sdk-compat.mjs",
49
50
  "compat:selftest": "node scripts/check-previous-sdk-compat.mjs --self-test",
@@ -60,11 +61,11 @@
60
61
  "react-dom": "^19.0.0"
61
62
  },
62
63
  "dependencies": {
64
+ "@immediately-run/mdx-plugins": "0.5.0",
63
65
  "@immediately-run/platform-constants": "0.2.0",
64
- "@immediately-run/sandbox-protocol": "0.7.1",
65
- "react-error-boundary": "^6.0.0",
66
66
  "@immediately-run/safe-content": "0.1.0",
67
- "@immediately-run/mdx-plugins": "0.5.0"
67
+ "@immediately-run/sandbox-protocol": "0.7.7",
68
+ "react-error-boundary": "^6.0.0"
68
69
  },
69
70
  "devDependencies": {
70
71
  "@immediately-run/prettier-config": "0.1.0",