@immediately-run/sdk 0.57.1 → 0.57.4

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 (125) 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 +19 -1
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +11 -3
  72. package/dist/index.d.ts +11 -3
  73. package/dist/index.js +9 -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/package.json +6 -5
@@ -1,3 +1,10 @@
1
+ /** One server-side change in a mount batch: the path (mount-relative, leading
2
+ * slash) and its kind — the Node `watch` event mapping is `add`/`remove` →
3
+ * `rename`, `change` → `change`. (R3-409.) */
4
+ interface MountChange {
5
+ path: string;
6
+ kind: 'add' | 'change' | 'remove';
7
+ }
1
8
  /** One working-tree change batch the host pushes: the changed paths plus an epoch. */
2
9
  interface FsChange {
3
10
  /** Repo-relative paths (leading slash, e.g. `/src/App.tsx`) that just changed. */
@@ -8,6 +15,19 @@ interface FsChange {
8
15
  * deduplicated away). `0` is the pre-first-event initial.
9
16
  */
10
17
  epoch: number;
18
+ /**
19
+ * R3-409 — a SPACE mount's server-side change batch (another tab's/member's
20
+ * writes), anchored at the mount path the frame holds. The `fs.watch` events
21
+ * themselves are delivered by the sandbox's fs shim (no SDK surface needed);
22
+ * this field is DECLARED so the wire shape has one written-down type on this
23
+ * side too (the R3-274e lesson), and so an SDK consumer that wants the raw
24
+ * per-mount batches can read them. Absent on the working-tree leg.
25
+ */
26
+ mount?: {
27
+ /** The sandbox mount root the `changes[].path`s are relative to (e.g. `/mnt/{hash}`). */
28
+ path: string;
29
+ changes: MountChange[];
30
+ };
11
31
  }
12
32
  /** The most recent working-tree change batch (the empty initial until the first). */
13
33
  declare const getFsChange: () => FsChange;
@@ -20,4 +40,4 @@ declare const onFsChange: (listener: (change: FsChange) => void) => (() => void)
20
40
  /** React hook: the current working-tree change batch, re-rendering on every push. */
21
41
  declare const useFsChange: () => FsChange;
22
42
 
23
- export { type FsChange, getFsChange, onFsChange, useFsChange };
43
+ export { type FsChange, type MountChange, getFsChange, onFsChange, useFsChange };
@@ -2,10 +2,22 @@ import "./chunk-VHAA22YE.js";
2
2
  import { createPushChannel } from "./pushChannel";
3
3
  import { FS_CHANGE } from "./generated/protocol";
4
4
  const isStringArray = (v) => Array.isArray(v) && v.every((p) => typeof p === "string");
5
+ const isMountBatch = (v) => {
6
+ if (typeof v !== "object" || v === null) return false;
7
+ const m = v;
8
+ if (typeof m.path !== "string" || !Array.isArray(m.changes)) return false;
9
+ return m.changes.every(
10
+ (c) => typeof c === "object" && c !== null && typeof c.path === "string" && ["add", "change", "remove"].includes(c.kind)
11
+ );
12
+ };
5
13
  const channel = createPushChannel({
6
14
  pushType: FS_CHANGE,
7
15
  initial: { paths: [], epoch: 0 },
8
- parse: (msg) => isStringArray(msg.paths) && typeof msg.epoch === "number" ? { paths: msg.paths, epoch: msg.epoch } : void 0
16
+ parse: (msg) => isStringArray(msg.paths) && typeof msg.epoch === "number" ? {
17
+ paths: msg.paths,
18
+ epoch: msg.epoch,
19
+ ...isMountBatch(msg.mount) ? { mount: msg.mount } : {}
20
+ } : void 0
9
21
  });
10
22
  const getFsChange = () => channel.get();
11
23
  const onFsChange = (listener) => channel.onChange(listener);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/onFsChange.ts"],"sourcesContent":["// Working-tree change stream (EDITOR_AS_APP_SPEC §4.2). The host pushes the\n// repo-relative paths that just changed in the working tree — from ANY writer: an\n// agent's port write, a host `editor:write` action (create/delete/rename), or the\n// preview's own copy-on-write. A working-tree observer (the editor app, the file\n// explorer) reacts by re-reading the affected files instead of polling.\n//\n// Elevated `editor:read` — a previewed app holds no `editor:read`, so it never sees\n// the stream (the host channel ACL withholds it). Push-only: there is no past-event\n// state worth polling, so the empty initial stands until the first write.\n//\n// Origin-exclusion is the CONSUMER's responsibility: the editor must ignore the\n// echo of its OWN write (a debounced write lags the buffer, so re-reading it as\n// \"external\" would surface a false conflict). Compare the changed file's bytes to\n// what you last wrote; if they match, it is your echo, not an external change.\nimport { createPushChannel } from './pushChannel';\nimport { FS_CHANGE } from './generated/protocol';\n\n/** One working-tree change batch the host pushes: the changed paths plus an epoch. */\nexport interface FsChange {\n /** Repo-relative paths (leading slash, e.g. `/src/App.tsx`) that just changed. */\n paths: string[];\n /**\n * Monotonic batch id — bumps on every change even if the path set repeats, so a\n * subscriber re-fires for a second edit to the same file (the value is never\n * deduplicated away). `0` is the pre-first-event initial.\n */\n epoch: number;\n}\n\nconst isStringArray = (v: unknown): v is string[] => Array.isArray(v) && v.every((p) => typeof p === 'string');\n\nconst channel = createPushChannel<FsChange>({\n pushType: FS_CHANGE,\n initial: { paths: [], epoch: 0 },\n parse: (msg) =>\n isStringArray(msg.paths) && typeof msg.epoch === 'number' ? { paths: msg.paths, epoch: msg.epoch } : undefined,\n});\n\n/** The most recent working-tree change batch (the empty initial until the first). */\nexport const getFsChange = (): FsChange => channel.get();\n\n/**\n * Subscribe to working-tree changes. The listener fires immediately with the\n * current batch, then on every host push. Returns an unsubscribe fn. The common\n * use: re-read an open file when its path appears in `change.paths`.\n */\nexport const onFsChange = (listener: (change: FsChange) => void): (() => void) => channel.onChange(listener);\n\n/** React hook: the current working-tree change batch, re-rendering on every push. */\nexport const useFsChange = (): FsChange => channel.use();\n"],"mappings":";AAcA,SAAS,yBAAyB;AAClC,SAAS,iBAAiB;AAc1B,MAAM,gBAAgB,CAAC,MAA8B,MAAM,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ;AAE7G,MAAM,UAAU,kBAA4B;AAAA,EAC1C,UAAU;AAAA,EACV,SAAS,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE;AAAA,EAC/B,OAAO,CAAC,QACN,cAAc,IAAI,KAAK,KAAK,OAAO,IAAI,UAAU,WAAW,EAAE,OAAO,IAAI,OAAO,OAAO,IAAI,MAAM,IAAI;AACzG,CAAC;AAGM,MAAM,cAAc,MAAgB,QAAQ,IAAI;AAOhD,MAAM,aAAa,CAAC,aAAuD,QAAQ,SAAS,QAAQ;AAGpG,MAAM,cAAc,MAAgB,QAAQ,IAAI;","names":[]}
1
+ {"version":3,"sources":["../src/onFsChange.ts"],"sourcesContent":["// Working-tree change stream (EDITOR_AS_APP_SPEC §4.2). The host pushes the\n// repo-relative paths that just changed in the working tree — from ANY writer: an\n// agent's port write, a host `editor:write` action (create/delete/rename), or the\n// preview's own copy-on-write. A working-tree observer (the editor app, the file\n// explorer) reacts by re-reading the affected files instead of polling.\n//\n// Elevated `editor:read` — a previewed app holds no `editor:read`, so it never sees\n// the stream (the host channel ACL withholds it). Push-only: there is no past-event\n// state worth polling, so the empty initial stands until the first write.\n//\n// Origin-exclusion is the CONSUMER's responsibility: the editor must ignore the\n// echo of its OWN write (a debounced write lags the buffer, so re-reading it as\n// \"external\" would surface a false conflict). Compare the changed file's bytes to\n// what you last wrote; if they match, it is your echo, not an external change.\nimport { createPushChannel } from './pushChannel';\nimport { FS_CHANGE } from './generated/protocol';\n\n/** One server-side change in a mount batch: the path (mount-relative, leading\n * slash) and its kind — the Node `watch` event mapping is `add`/`remove` →\n * `rename`, `change` → `change`. (R3-409.) */\nexport interface MountChange {\n path: string;\n kind: 'add' | 'change' | 'remove';\n}\n\n/** One working-tree change batch the host pushes: the changed paths plus an epoch. */\nexport interface FsChange {\n /** Repo-relative paths (leading slash, e.g. `/src/App.tsx`) that just changed. */\n paths: string[];\n /**\n * Monotonic batch id — bumps on every change even if the path set repeats, so a\n * subscriber re-fires for a second edit to the same file (the value is never\n * deduplicated away). `0` is the pre-first-event initial.\n */\n epoch: number;\n /**\n * R3-409 — a SPACE mount's server-side change batch (another tab's/member's\n * writes), anchored at the mount path the frame holds. The `fs.watch` events\n * themselves are delivered by the sandbox's fs shim (no SDK surface needed);\n * this field is DECLARED so the wire shape has one written-down type on this\n * side too (the R3-274e lesson), and so an SDK consumer that wants the raw\n * per-mount batches can read them. Absent on the working-tree leg.\n */\n mount?: {\n /** The sandbox mount root the `changes[].path`s are relative to (e.g. `/mnt/{hash}`). */\n path: string;\n changes: MountChange[];\n };\n}\n\nconst isStringArray = (v: unknown): v is string[] => Array.isArray(v) && v.every((p) => typeof p === 'string');\n\nconst isMountBatch = (v: unknown): v is FsChange['mount'] => {\n if (typeof v !== 'object' || v === null) return false;\n const m = v as { path?: unknown; changes?: unknown };\n if (typeof m.path !== 'string' || !Array.isArray(m.changes)) return false;\n return m.changes.every(\n (c) =>\n typeof c === 'object' &&\n c !== null &&\n typeof (c as { path?: unknown }).path === 'string' &&\n ['add', 'change', 'remove'].includes((c as { kind?: unknown }).kind as string),\n );\n};\n\nconst channel = createPushChannel<FsChange>({\n pushType: FS_CHANGE,\n initial: { paths: [], epoch: 0 },\n parse: (msg) =>\n isStringArray(msg.paths) && typeof msg.epoch === 'number'\n ? {\n paths: msg.paths,\n epoch: msg.epoch,\n ...(isMountBatch(msg.mount) ? { mount: msg.mount } : {}),\n }\n : undefined,\n});\n\n/** The most recent working-tree change batch (the empty initial until the first). */\nexport const getFsChange = (): FsChange => channel.get();\n\n/**\n * Subscribe to working-tree changes. The listener fires immediately with the\n * current batch, then on every host push. Returns an unsubscribe fn. The common\n * use: re-read an open file when its path appears in `change.paths`.\n */\nexport const onFsChange = (listener: (change: FsChange) => void): (() => void) => channel.onChange(listener);\n\n/** React hook: the current working-tree change batch, re-rendering on every push. */\nexport const useFsChange = (): FsChange => channel.use();\n"],"mappings":";AAcA,SAAS,yBAAyB;AAClC,SAAS,iBAAiB;AAmC1B,MAAM,gBAAgB,CAAC,MAA8B,MAAM,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ;AAE7G,MAAM,eAAe,CAAC,MAAuC;AAC3D,MAAI,OAAO,MAAM,YAAY,MAAM,KAAM,QAAO;AAChD,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,SAAS,YAAY,CAAC,MAAM,QAAQ,EAAE,OAAO,EAAG,QAAO;AACpE,SAAO,EAAE,QAAQ;AAAA,IACf,CAAC,MACC,OAAO,MAAM,YACb,MAAM,QACN,OAAQ,EAAyB,SAAS,YAC1C,CAAC,OAAO,UAAU,QAAQ,EAAE,SAAU,EAAyB,IAAc;AAAA,EACjF;AACF;AAEA,MAAM,UAAU,kBAA4B;AAAA,EAC1C,UAAU;AAAA,EACV,SAAS,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE;AAAA,EAC/B,OAAO,CAAC,QACN,cAAc,IAAI,KAAK,KAAK,OAAO,IAAI,UAAU,WAC7C;AAAA,IACE,OAAO,IAAI;AAAA,IACX,OAAO,IAAI;AAAA,IACX,GAAI,aAAa,IAAI,KAAK,IAAI,EAAE,OAAO,IAAI,MAAM,IAAI,CAAC;AAAA,EACxD,IACA;AACR,CAAC;AAGM,MAAM,cAAc,MAAgB,QAAQ,IAAI;AAOhD,MAAM,aAAa,CAAC,aAAuD,QAAQ,SAAS,QAAQ;AAGpG,MAAM,cAAc,MAAgB,QAAQ,IAAI;","names":[]}
@@ -34,6 +34,7 @@ const SCHEMES = {
34
34
  [import_protocol.PROTOCOL_IPC]: schemeOf(import_protocol.PROTOCOL_IPC),
35
35
  [import_protocol.PROTOCOL_LAUNCH]: schemeOf(import_protocol.PROTOCOL_LAUNCH),
36
36
  [import_protocol.PROTOCOL_LLM]: schemeOf(import_protocol.PROTOCOL_LLM),
37
+ [import_protocol.PROTOCOL_RECENTS]: schemeOf(import_protocol.PROTOCOL_RECENTS),
37
38
  [import_protocol.PROTOCOL_SECRETS]: schemeOf(import_protocol.PROTOCOL_SECRETS),
38
39
  [import_protocol.PROTOCOL_SETTINGS]: schemeOf(import_protocol.PROTOCOL_SETTINGS),
39
40
  [import_protocol.PROTOCOL_SPACES]: schemeOf(import_protocol.PROTOCOL_SPACES),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/protocolSchemes.ts"],"sourcesContent":["// The `protocol-<scheme>` SCHEMES, derived from the wire names.\n//\n// `protocolRequest(scheme, method, params)` takes the scheme — `'theme'` — while the\n// wire name the frame dispatches on is `'protocol-theme'`; the frame adds the prefix.\n// So the typed service wrappers cannot pass the published `PROTOCOL_*` constant\n// directly, and spelling the scheme inline would put a second, unguarded copy of the\n// name back in the tree — exactly what R3-274c removes.\n//\n// Instead the schemes are *derived* from the wire names, keyed BY the wire name:\n//\n// protocolRequest(SCHEMES[PROTOCOL_THEME], 'set', [{ theme }])\n//\n// Keying by the constant is what makes the derivation unfalsifiable — there is no\n// second place to name the family, so there is no pair to get wrong. `schemeOf`\n// returns a template-literal conditional, so each value has a literal type (`'theme'`),\n// which buys two more things:\n//\n// - a wire name that stops matching `protocol-*` stops compiling here, rather than\n// silently producing an empty scheme at runtime;\n// - `check-protocol-snapshot.mjs` resolves `SCHEMES[PROTOCOL_THEME]` through the type\n// checker exactly like a plain literal, so the call sites stay visible to the gate.\n//\n// One export, deliberately: `./*` is a public subpath, so every name added here is\n// public API forever (ways_of_working §6, additive-only).\n//\n// This module is NOT generated — the derivation is the content — so it lives outside\n// `src/generated/`.\nimport {\n PROTOCOL_ANALYTICS,\n PROTOCOL_CONTRIBUTE,\n PROTOCOL_DND,\n PROTOCOL_EDITOR,\n PROTOCOL_FEED,\n PROTOCOL_FETCH,\n PROTOCOL_IPC,\n PROTOCOL_LAUNCH,\n PROTOCOL_LLM,\n PROTOCOL_SECRETS,\n PROTOCOL_SETTINGS,\n PROTOCOL_SPACES,\n PROTOCOL_TASK,\n PROTOCOL_THEME,\n PROTOCOL_VCS,\n} from './generated/protocol';\n\nconst PREFIX = 'protocol-';\n\n/** The scheme half of a `protocol-<scheme>` wire name. */\ntype SchemeOf<N extends string> = N extends `${typeof PREFIX}${infer S}` ? S : never;\n\n/**\n * `'protocol-theme'` → `'theme'`, as a literal type.\n *\n * The cast is the only place the derivation is asserted rather than computed; the\n * `N extends \\`protocol-${string}\\`` bound is what makes it sound — a wire name that is\n * not scheme-shaped is a compile error at the call, not a `never` at runtime.\n */\nconst schemeOf = <N extends `${typeof PREFIX}${string}`>(name: N): SchemeOf<N> =>\n name.slice(PREFIX.length) as SchemeOf<N>;\n\n/** Every `protocol-*` scheme the SDK speaks, keyed by its wire name. */\nexport const SCHEMES = {\n [PROTOCOL_ANALYTICS]: schemeOf(PROTOCOL_ANALYTICS),\n [PROTOCOL_CONTRIBUTE]: schemeOf(PROTOCOL_CONTRIBUTE),\n [PROTOCOL_DND]: schemeOf(PROTOCOL_DND),\n [PROTOCOL_EDITOR]: schemeOf(PROTOCOL_EDITOR),\n [PROTOCOL_FEED]: schemeOf(PROTOCOL_FEED),\n [PROTOCOL_FETCH]: schemeOf(PROTOCOL_FETCH),\n [PROTOCOL_IPC]: schemeOf(PROTOCOL_IPC),\n [PROTOCOL_LAUNCH]: schemeOf(PROTOCOL_LAUNCH),\n [PROTOCOL_LLM]: schemeOf(PROTOCOL_LLM),\n [PROTOCOL_SECRETS]: schemeOf(PROTOCOL_SECRETS),\n [PROTOCOL_SETTINGS]: schemeOf(PROTOCOL_SETTINGS),\n [PROTOCOL_SPACES]: schemeOf(PROTOCOL_SPACES),\n [PROTOCOL_TASK]: schemeOf(PROTOCOL_TASK),\n [PROTOCOL_THEME]: schemeOf(PROTOCOL_THEME),\n [PROTOCOL_VCS]: schemeOf(PROTOCOL_VCS),\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BA,sBAgBO;AAEP,MAAM,SAAS;AAYf,MAAM,WAAW,CAAwC,SACvD,KAAK,MAAM,OAAO,MAAM;AAGnB,MAAM,UAAU;AAAA,EACrB,CAAC,kCAAkB,GAAG,SAAS,kCAAkB;AAAA,EACjD,CAAC,mCAAmB,GAAG,SAAS,mCAAmB;AAAA,EACnD,CAAC,4BAAY,GAAG,SAAS,4BAAY;AAAA,EACrC,CAAC,+BAAe,GAAG,SAAS,+BAAe;AAAA,EAC3C,CAAC,6BAAa,GAAG,SAAS,6BAAa;AAAA,EACvC,CAAC,8BAAc,GAAG,SAAS,8BAAc;AAAA,EACzC,CAAC,4BAAY,GAAG,SAAS,4BAAY;AAAA,EACrC,CAAC,+BAAe,GAAG,SAAS,+BAAe;AAAA,EAC3C,CAAC,4BAAY,GAAG,SAAS,4BAAY;AAAA,EACrC,CAAC,gCAAgB,GAAG,SAAS,gCAAgB;AAAA,EAC7C,CAAC,iCAAiB,GAAG,SAAS,iCAAiB;AAAA,EAC/C,CAAC,+BAAe,GAAG,SAAS,+BAAe;AAAA,EAC3C,CAAC,6BAAa,GAAG,SAAS,6BAAa;AAAA,EACvC,CAAC,8BAAc,GAAG,SAAS,8BAAc;AAAA,EACzC,CAAC,4BAAY,GAAG,SAAS,4BAAY;AACvC;","names":[]}
1
+ {"version":3,"sources":["../src/protocolSchemes.ts"],"sourcesContent":["// The `protocol-<scheme>` SCHEMES, derived from the wire names.\n//\n// `protocolRequest(scheme, method, params)` takes the scheme — `'theme'` — while the\n// wire name the frame dispatches on is `'protocol-theme'`; the frame adds the prefix.\n// So the typed service wrappers cannot pass the published `PROTOCOL_*` constant\n// directly, and spelling the scheme inline would put a second, unguarded copy of the\n// name back in the tree — exactly what R3-274c removes.\n//\n// Instead the schemes are *derived* from the wire names, keyed BY the wire name:\n//\n// protocolRequest(SCHEMES[PROTOCOL_THEME], 'set', [{ theme }])\n//\n// Keying by the constant is what makes the derivation unfalsifiable — there is no\n// second place to name the family, so there is no pair to get wrong. `schemeOf`\n// returns a template-literal conditional, so each value has a literal type (`'theme'`),\n// which buys two more things:\n//\n// - a wire name that stops matching `protocol-*` stops compiling here, rather than\n// silently producing an empty scheme at runtime;\n// - `check-protocol-snapshot.mjs` resolves `SCHEMES[PROTOCOL_THEME]` through the type\n// checker exactly like a plain literal, so the call sites stay visible to the gate.\n//\n// One export, deliberately: `./*` is a public subpath, so every name added here is\n// public API forever (ways_of_working §6, additive-only).\n//\n// This module is NOT generated — the derivation is the content — so it lives outside\n// `src/generated/`.\nimport {\n PROTOCOL_ANALYTICS,\n PROTOCOL_CONTRIBUTE,\n PROTOCOL_DND,\n PROTOCOL_EDITOR,\n PROTOCOL_FEED,\n PROTOCOL_FETCH,\n PROTOCOL_IPC,\n PROTOCOL_LAUNCH,\n PROTOCOL_LLM,\n PROTOCOL_RECENTS,\n PROTOCOL_SECRETS,\n PROTOCOL_SETTINGS,\n PROTOCOL_SPACES,\n PROTOCOL_TASK,\n PROTOCOL_THEME,\n PROTOCOL_VCS,\n} from './generated/protocol';\n\nconst PREFIX = 'protocol-';\n\n/** The scheme half of a `protocol-<scheme>` wire name. */\ntype SchemeOf<N extends string> = N extends `${typeof PREFIX}${infer S}` ? S : never;\n\n/**\n * `'protocol-theme'` → `'theme'`, as a literal type.\n *\n * The cast is the only place the derivation is asserted rather than computed; the\n * `N extends \\`protocol-${string}\\`` bound is what makes it sound — a wire name that is\n * not scheme-shaped is a compile error at the call, not a `never` at runtime.\n */\nconst schemeOf = <N extends `${typeof PREFIX}${string}`>(name: N): SchemeOf<N> =>\n name.slice(PREFIX.length) as SchemeOf<N>;\n\n/** Every `protocol-*` scheme the SDK speaks, keyed by its wire name. */\nexport const SCHEMES = {\n [PROTOCOL_ANALYTICS]: schemeOf(PROTOCOL_ANALYTICS),\n [PROTOCOL_CONTRIBUTE]: schemeOf(PROTOCOL_CONTRIBUTE),\n [PROTOCOL_DND]: schemeOf(PROTOCOL_DND),\n [PROTOCOL_EDITOR]: schemeOf(PROTOCOL_EDITOR),\n [PROTOCOL_FEED]: schemeOf(PROTOCOL_FEED),\n [PROTOCOL_FETCH]: schemeOf(PROTOCOL_FETCH),\n [PROTOCOL_IPC]: schemeOf(PROTOCOL_IPC),\n [PROTOCOL_LAUNCH]: schemeOf(PROTOCOL_LAUNCH),\n [PROTOCOL_LLM]: schemeOf(PROTOCOL_LLM),\n [PROTOCOL_RECENTS]: schemeOf(PROTOCOL_RECENTS),\n [PROTOCOL_SECRETS]: schemeOf(PROTOCOL_SECRETS),\n [PROTOCOL_SETTINGS]: schemeOf(PROTOCOL_SETTINGS),\n [PROTOCOL_SPACES]: schemeOf(PROTOCOL_SPACES),\n [PROTOCOL_TASK]: schemeOf(PROTOCOL_TASK),\n [PROTOCOL_THEME]: schemeOf(PROTOCOL_THEME),\n [PROTOCOL_VCS]: schemeOf(PROTOCOL_VCS),\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BA,sBAiBO;AAEP,MAAM,SAAS;AAYf,MAAM,WAAW,CAAwC,SACvD,KAAK,MAAM,OAAO,MAAM;AAGnB,MAAM,UAAU;AAAA,EACrB,CAAC,kCAAkB,GAAG,SAAS,kCAAkB;AAAA,EACjD,CAAC,mCAAmB,GAAG,SAAS,mCAAmB;AAAA,EACnD,CAAC,4BAAY,GAAG,SAAS,4BAAY;AAAA,EACrC,CAAC,+BAAe,GAAG,SAAS,+BAAe;AAAA,EAC3C,CAAC,6BAAa,GAAG,SAAS,6BAAa;AAAA,EACvC,CAAC,8BAAc,GAAG,SAAS,8BAAc;AAAA,EACzC,CAAC,4BAAY,GAAG,SAAS,4BAAY;AAAA,EACrC,CAAC,+BAAe,GAAG,SAAS,+BAAe;AAAA,EAC3C,CAAC,4BAAY,GAAG,SAAS,4BAAY;AAAA,EACrC,CAAC,gCAAgB,GAAG,SAAS,gCAAgB;AAAA,EAC7C,CAAC,gCAAgB,GAAG,SAAS,gCAAgB;AAAA,EAC7C,CAAC,iCAAiB,GAAG,SAAS,iCAAiB;AAAA,EAC/C,CAAC,+BAAe,GAAG,SAAS,+BAAe;AAAA,EAC3C,CAAC,6BAAa,GAAG,SAAS,6BAAa;AAAA,EACvC,CAAC,8BAAc,GAAG,SAAS,8BAAc;AAAA,EACzC,CAAC,4BAAY,GAAG,SAAS,4BAAY;AACvC;","names":[]}
@@ -9,6 +9,7 @@ declare const SCHEMES: {
9
9
  readonly "protocol-ipc": "ipc";
10
10
  readonly "protocol-launch": "launch";
11
11
  readonly "protocol-llm": "llm";
12
+ readonly "protocol-recents": "recents";
12
13
  readonly "protocol-secrets": "secrets";
13
14
  readonly "protocol-settings": "settings";
14
15
  readonly "protocol-spaces": "spaces";
@@ -9,6 +9,7 @@ declare const SCHEMES: {
9
9
  readonly "protocol-ipc": "ipc";
10
10
  readonly "protocol-launch": "launch";
11
11
  readonly "protocol-llm": "llm";
12
+ readonly "protocol-recents": "recents";
12
13
  readonly "protocol-secrets": "secrets";
13
14
  readonly "protocol-settings": "settings";
14
15
  readonly "protocol-spaces": "spaces";
@@ -9,6 +9,7 @@ import {
9
9
  PROTOCOL_IPC,
10
10
  PROTOCOL_LAUNCH,
11
11
  PROTOCOL_LLM,
12
+ PROTOCOL_RECENTS,
12
13
  PROTOCOL_SECRETS,
13
14
  PROTOCOL_SETTINGS,
14
15
  PROTOCOL_SPACES,
@@ -28,6 +29,7 @@ const SCHEMES = {
28
29
  [PROTOCOL_IPC]: schemeOf(PROTOCOL_IPC),
29
30
  [PROTOCOL_LAUNCH]: schemeOf(PROTOCOL_LAUNCH),
30
31
  [PROTOCOL_LLM]: schemeOf(PROTOCOL_LLM),
32
+ [PROTOCOL_RECENTS]: schemeOf(PROTOCOL_RECENTS),
31
33
  [PROTOCOL_SECRETS]: schemeOf(PROTOCOL_SECRETS),
32
34
  [PROTOCOL_SETTINGS]: schemeOf(PROTOCOL_SETTINGS),
33
35
  [PROTOCOL_SPACES]: schemeOf(PROTOCOL_SPACES),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/protocolSchemes.ts"],"sourcesContent":["// The `protocol-<scheme>` SCHEMES, derived from the wire names.\n//\n// `protocolRequest(scheme, method, params)` takes the scheme — `'theme'` — while the\n// wire name the frame dispatches on is `'protocol-theme'`; the frame adds the prefix.\n// So the typed service wrappers cannot pass the published `PROTOCOL_*` constant\n// directly, and spelling the scheme inline would put a second, unguarded copy of the\n// name back in the tree — exactly what R3-274c removes.\n//\n// Instead the schemes are *derived* from the wire names, keyed BY the wire name:\n//\n// protocolRequest(SCHEMES[PROTOCOL_THEME], 'set', [{ theme }])\n//\n// Keying by the constant is what makes the derivation unfalsifiable — there is no\n// second place to name the family, so there is no pair to get wrong. `schemeOf`\n// returns a template-literal conditional, so each value has a literal type (`'theme'`),\n// which buys two more things:\n//\n// - a wire name that stops matching `protocol-*` stops compiling here, rather than\n// silently producing an empty scheme at runtime;\n// - `check-protocol-snapshot.mjs` resolves `SCHEMES[PROTOCOL_THEME]` through the type\n// checker exactly like a plain literal, so the call sites stay visible to the gate.\n//\n// One export, deliberately: `./*` is a public subpath, so every name added here is\n// public API forever (ways_of_working §6, additive-only).\n//\n// This module is NOT generated — the derivation is the content — so it lives outside\n// `src/generated/`.\nimport {\n PROTOCOL_ANALYTICS,\n PROTOCOL_CONTRIBUTE,\n PROTOCOL_DND,\n PROTOCOL_EDITOR,\n PROTOCOL_FEED,\n PROTOCOL_FETCH,\n PROTOCOL_IPC,\n PROTOCOL_LAUNCH,\n PROTOCOL_LLM,\n PROTOCOL_SECRETS,\n PROTOCOL_SETTINGS,\n PROTOCOL_SPACES,\n PROTOCOL_TASK,\n PROTOCOL_THEME,\n PROTOCOL_VCS,\n} from './generated/protocol';\n\nconst PREFIX = 'protocol-';\n\n/** The scheme half of a `protocol-<scheme>` wire name. */\ntype SchemeOf<N extends string> = N extends `${typeof PREFIX}${infer S}` ? S : never;\n\n/**\n * `'protocol-theme'` → `'theme'`, as a literal type.\n *\n * The cast is the only place the derivation is asserted rather than computed; the\n * `N extends \\`protocol-${string}\\`` bound is what makes it sound — a wire name that is\n * not scheme-shaped is a compile error at the call, not a `never` at runtime.\n */\nconst schemeOf = <N extends `${typeof PREFIX}${string}`>(name: N): SchemeOf<N> =>\n name.slice(PREFIX.length) as SchemeOf<N>;\n\n/** Every `protocol-*` scheme the SDK speaks, keyed by its wire name. */\nexport const SCHEMES = {\n [PROTOCOL_ANALYTICS]: schemeOf(PROTOCOL_ANALYTICS),\n [PROTOCOL_CONTRIBUTE]: schemeOf(PROTOCOL_CONTRIBUTE),\n [PROTOCOL_DND]: schemeOf(PROTOCOL_DND),\n [PROTOCOL_EDITOR]: schemeOf(PROTOCOL_EDITOR),\n [PROTOCOL_FEED]: schemeOf(PROTOCOL_FEED),\n [PROTOCOL_FETCH]: schemeOf(PROTOCOL_FETCH),\n [PROTOCOL_IPC]: schemeOf(PROTOCOL_IPC),\n [PROTOCOL_LAUNCH]: schemeOf(PROTOCOL_LAUNCH),\n [PROTOCOL_LLM]: schemeOf(PROTOCOL_LLM),\n [PROTOCOL_SECRETS]: schemeOf(PROTOCOL_SECRETS),\n [PROTOCOL_SETTINGS]: schemeOf(PROTOCOL_SETTINGS),\n [PROTOCOL_SPACES]: schemeOf(PROTOCOL_SPACES),\n [PROTOCOL_TASK]: schemeOf(PROTOCOL_TASK),\n [PROTOCOL_THEME]: schemeOf(PROTOCOL_THEME),\n [PROTOCOL_VCS]: schemeOf(PROTOCOL_VCS),\n} as const;\n"],"mappings":";AA2BA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,MAAM,SAAS;AAYf,MAAM,WAAW,CAAwC,SACvD,KAAK,MAAM,OAAO,MAAM;AAGnB,MAAM,UAAU;AAAA,EACrB,CAAC,kBAAkB,GAAG,SAAS,kBAAkB;AAAA,EACjD,CAAC,mBAAmB,GAAG,SAAS,mBAAmB;AAAA,EACnD,CAAC,YAAY,GAAG,SAAS,YAAY;AAAA,EACrC,CAAC,eAAe,GAAG,SAAS,eAAe;AAAA,EAC3C,CAAC,aAAa,GAAG,SAAS,aAAa;AAAA,EACvC,CAAC,cAAc,GAAG,SAAS,cAAc;AAAA,EACzC,CAAC,YAAY,GAAG,SAAS,YAAY;AAAA,EACrC,CAAC,eAAe,GAAG,SAAS,eAAe;AAAA,EAC3C,CAAC,YAAY,GAAG,SAAS,YAAY;AAAA,EACrC,CAAC,gBAAgB,GAAG,SAAS,gBAAgB;AAAA,EAC7C,CAAC,iBAAiB,GAAG,SAAS,iBAAiB;AAAA,EAC/C,CAAC,eAAe,GAAG,SAAS,eAAe;AAAA,EAC3C,CAAC,aAAa,GAAG,SAAS,aAAa;AAAA,EACvC,CAAC,cAAc,GAAG,SAAS,cAAc;AAAA,EACzC,CAAC,YAAY,GAAG,SAAS,YAAY;AACvC;","names":[]}
1
+ {"version":3,"sources":["../src/protocolSchemes.ts"],"sourcesContent":["// The `protocol-<scheme>` SCHEMES, derived from the wire names.\n//\n// `protocolRequest(scheme, method, params)` takes the scheme — `'theme'` — while the\n// wire name the frame dispatches on is `'protocol-theme'`; the frame adds the prefix.\n// So the typed service wrappers cannot pass the published `PROTOCOL_*` constant\n// directly, and spelling the scheme inline would put a second, unguarded copy of the\n// name back in the tree — exactly what R3-274c removes.\n//\n// Instead the schemes are *derived* from the wire names, keyed BY the wire name:\n//\n// protocolRequest(SCHEMES[PROTOCOL_THEME], 'set', [{ theme }])\n//\n// Keying by the constant is what makes the derivation unfalsifiable — there is no\n// second place to name the family, so there is no pair to get wrong. `schemeOf`\n// returns a template-literal conditional, so each value has a literal type (`'theme'`),\n// which buys two more things:\n//\n// - a wire name that stops matching `protocol-*` stops compiling here, rather than\n// silently producing an empty scheme at runtime;\n// - `check-protocol-snapshot.mjs` resolves `SCHEMES[PROTOCOL_THEME]` through the type\n// checker exactly like a plain literal, so the call sites stay visible to the gate.\n//\n// One export, deliberately: `./*` is a public subpath, so every name added here is\n// public API forever (ways_of_working §6, additive-only).\n//\n// This module is NOT generated — the derivation is the content — so it lives outside\n// `src/generated/`.\nimport {\n PROTOCOL_ANALYTICS,\n PROTOCOL_CONTRIBUTE,\n PROTOCOL_DND,\n PROTOCOL_EDITOR,\n PROTOCOL_FEED,\n PROTOCOL_FETCH,\n PROTOCOL_IPC,\n PROTOCOL_LAUNCH,\n PROTOCOL_LLM,\n PROTOCOL_RECENTS,\n PROTOCOL_SECRETS,\n PROTOCOL_SETTINGS,\n PROTOCOL_SPACES,\n PROTOCOL_TASK,\n PROTOCOL_THEME,\n PROTOCOL_VCS,\n} from './generated/protocol';\n\nconst PREFIX = 'protocol-';\n\n/** The scheme half of a `protocol-<scheme>` wire name. */\ntype SchemeOf<N extends string> = N extends `${typeof PREFIX}${infer S}` ? S : never;\n\n/**\n * `'protocol-theme'` → `'theme'`, as a literal type.\n *\n * The cast is the only place the derivation is asserted rather than computed; the\n * `N extends \\`protocol-${string}\\`` bound is what makes it sound — a wire name that is\n * not scheme-shaped is a compile error at the call, not a `never` at runtime.\n */\nconst schemeOf = <N extends `${typeof PREFIX}${string}`>(name: N): SchemeOf<N> =>\n name.slice(PREFIX.length) as SchemeOf<N>;\n\n/** Every `protocol-*` scheme the SDK speaks, keyed by its wire name. */\nexport const SCHEMES = {\n [PROTOCOL_ANALYTICS]: schemeOf(PROTOCOL_ANALYTICS),\n [PROTOCOL_CONTRIBUTE]: schemeOf(PROTOCOL_CONTRIBUTE),\n [PROTOCOL_DND]: schemeOf(PROTOCOL_DND),\n [PROTOCOL_EDITOR]: schemeOf(PROTOCOL_EDITOR),\n [PROTOCOL_FEED]: schemeOf(PROTOCOL_FEED),\n [PROTOCOL_FETCH]: schemeOf(PROTOCOL_FETCH),\n [PROTOCOL_IPC]: schemeOf(PROTOCOL_IPC),\n [PROTOCOL_LAUNCH]: schemeOf(PROTOCOL_LAUNCH),\n [PROTOCOL_LLM]: schemeOf(PROTOCOL_LLM),\n [PROTOCOL_RECENTS]: schemeOf(PROTOCOL_RECENTS),\n [PROTOCOL_SECRETS]: schemeOf(PROTOCOL_SECRETS),\n [PROTOCOL_SETTINGS]: schemeOf(PROTOCOL_SETTINGS),\n [PROTOCOL_SPACES]: schemeOf(PROTOCOL_SPACES),\n [PROTOCOL_TASK]: schemeOf(PROTOCOL_TASK),\n [PROTOCOL_THEME]: schemeOf(PROTOCOL_THEME),\n [PROTOCOL_VCS]: schemeOf(PROTOCOL_VCS),\n} as const;\n"],"mappings":";AA2BA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,MAAM,SAAS;AAYf,MAAM,WAAW,CAAwC,SACvD,KAAK,MAAM,OAAO,MAAM;AAGnB,MAAM,UAAU;AAAA,EACrB,CAAC,kBAAkB,GAAG,SAAS,kBAAkB;AAAA,EACjD,CAAC,mBAAmB,GAAG,SAAS,mBAAmB;AAAA,EACnD,CAAC,YAAY,GAAG,SAAS,YAAY;AAAA,EACrC,CAAC,eAAe,GAAG,SAAS,eAAe;AAAA,EAC3C,CAAC,aAAa,GAAG,SAAS,aAAa;AAAA,EACvC,CAAC,cAAc,GAAG,SAAS,cAAc;AAAA,EACzC,CAAC,YAAY,GAAG,SAAS,YAAY;AAAA,EACrC,CAAC,eAAe,GAAG,SAAS,eAAe;AAAA,EAC3C,CAAC,YAAY,GAAG,SAAS,YAAY;AAAA,EACrC,CAAC,gBAAgB,GAAG,SAAS,gBAAgB;AAAA,EAC7C,CAAC,gBAAgB,GAAG,SAAS,gBAAgB;AAAA,EAC7C,CAAC,iBAAiB,GAAG,SAAS,iBAAiB;AAAA,EAC/C,CAAC,eAAe,GAAG,SAAS,eAAe;AAAA,EAC3C,CAAC,aAAa,GAAG,SAAS,aAAa;AAAA,EACvC,CAAC,cAAc,GAAG,SAAS,cAAc;AAAA,EACzC,CAAC,YAAY,GAAG,SAAS,YAAY;AACvC;","names":[]}
@@ -0,0 +1,41 @@
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 recents_exports = {};
20
+ __export(recents_exports, {
21
+ clearRecentProjects: () => clearRecentProjects,
22
+ listRecentProjects: () => listRecentProjects
23
+ });
24
+ module.exports = __toCommonJS(recents_exports);
25
+ var import_sandboxUtils = require("./sandboxUtils");
26
+ var import_protocol = require("./generated/protocol");
27
+ var import_protocolSchemes = require("./protocolSchemes");
28
+ const recentsRequest = (params) => (0, import_sandboxUtils.protocolRequest)(import_protocolSchemes.SCHEMES[import_protocol.PROTOCOL_RECENTS], "list", [params]);
29
+ async function listRecentProjects() {
30
+ const res = await recentsRequest({});
31
+ return res.projects ?? null;
32
+ }
33
+ async function clearRecentProjects() {
34
+ await recentsRequest({ clear: true });
35
+ }
36
+ // Annotate the CommonJS export names for ESM import in node:
37
+ 0 && (module.exports = {
38
+ clearRecentProjects,
39
+ listRecentProjects
40
+ });
41
+ //# sourceMappingURL=recents.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/recents.ts"],"sourcesContent":["// The recent-projects record — app-facing surface (R3-485, OSO §4.3).\n//\n// `listRecentProjects()` reads the HOST-OWNED record through the one elevated,\n// app-scoped `recents:read` capability. Two gates stand between an app and the\n// record, and both must pass: the §8.4 capability grant (declared as\n// `requests: { 'recents:read': {} }` and consented per (app, principal)), and the\n// handler's structural binding — the read is granted only to the app bound at\n// `page.home`. For every other app the call resolves to a refusal, whatever it\n// declared.\n//\n// A project entry is COORDINATES (provider/namespace/repository/ref + when) — a\n// location the user already navigated to under their own authority, never an\n// in-repo path. Opening one runs the ordinary load path with the ordinary\n// consent: the record confers nothing (R-OSO-22).\nimport { protocolRequest } from './sandboxUtils';\nimport { PROTOCOL_RECENTS } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** One recently opened project — repository coordinates, never an in-repo path. */\nexport interface RecentProject {\n provider: string;\n namespace: string;\n repository: string;\n /** The ref the user opened (`main`, a sha). */\n ref: string;\n /** When the user last opened it (epoch ms). */\n ts: number;\n}\n\ninterface RecentsReply {\n /** Newest-first; `null` when the record is empty or the user cleared it. */\n projects: RecentProject[] | null;\n}\n\n/**\n * The one call shape the SDK speaks under `protocol-recents` — read, or clear\n * (same gated surface). A single typed call site keeps the wire contract exact:\n * the snapshot gate reads THIS shape, and the host handler accepts the same.\n */\nconst recentsRequest = (params: { clear?: boolean }): Promise<RecentsReply> =>\n protocolRequest(SCHEMES[PROTOCOL_RECENTS], 'list', [params]) as Promise<RecentsReply>;\n\n/**\n * The user's recently opened projects, newest-first, or `null` when the record is\n * absent (R-OSO-22: cleared is absent, never an empty list). Refuses for any app\n * that is not the `page.home` binding.\n */\nexport async function listRecentProjects(): Promise<RecentProject[] | null> {\n const res = await recentsRequest({});\n return res.projects ?? null;\n}\n\n/**\n * Clear the user's recent-projects record (the surface afterwards is absent, not\n * empty). Rides the same page.home binding gate as the read.\n */\nexport async function clearRecentProjects(): Promise<void> {\n await recentsRequest({ clear: true });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcA,0BAAgC;AAChC,sBAAiC;AACjC,6BAAwB;AAuBxB,MAAM,iBAAiB,CAAC,eACtB,qCAAgB,+BAAQ,gCAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC;AAO7D,eAAsB,qBAAsD;AAC1E,QAAM,MAAM,MAAM,eAAe,CAAC,CAAC;AACnC,SAAO,IAAI,YAAY;AACzB;AAMA,eAAsB,sBAAqC;AACzD,QAAM,eAAe,EAAE,OAAO,KAAK,CAAC;AACtC;","names":[]}
@@ -0,0 +1,23 @@
1
+ /** One recently opened project — repository coordinates, never an in-repo path. */
2
+ interface RecentProject {
3
+ provider: string;
4
+ namespace: string;
5
+ repository: string;
6
+ /** The ref the user opened (`main`, a sha). */
7
+ ref: string;
8
+ /** When the user last opened it (epoch ms). */
9
+ ts: number;
10
+ }
11
+ /**
12
+ * The user's recently opened projects, newest-first, or `null` when the record is
13
+ * absent (R-OSO-22: cleared is absent, never an empty list). Refuses for any app
14
+ * that is not the `page.home` binding.
15
+ */
16
+ declare function listRecentProjects(): Promise<RecentProject[] | null>;
17
+ /**
18
+ * Clear the user's recent-projects record (the surface afterwards is absent, not
19
+ * empty). Rides the same page.home binding gate as the read.
20
+ */
21
+ declare function clearRecentProjects(): Promise<void>;
22
+
23
+ export { type RecentProject, clearRecentProjects, listRecentProjects };
@@ -0,0 +1,23 @@
1
+ /** One recently opened project — repository coordinates, never an in-repo path. */
2
+ interface RecentProject {
3
+ provider: string;
4
+ namespace: string;
5
+ repository: string;
6
+ /** The ref the user opened (`main`, a sha). */
7
+ ref: string;
8
+ /** When the user last opened it (epoch ms). */
9
+ ts: number;
10
+ }
11
+ /**
12
+ * The user's recently opened projects, newest-first, or `null` when the record is
13
+ * absent (R-OSO-22: cleared is absent, never an empty list). Refuses for any app
14
+ * that is not the `page.home` binding.
15
+ */
16
+ declare function listRecentProjects(): Promise<RecentProject[] | null>;
17
+ /**
18
+ * Clear the user's recent-projects record (the surface afterwards is absent, not
19
+ * empty). Rides the same page.home binding gate as the read.
20
+ */
21
+ declare function clearRecentProjects(): Promise<void>;
22
+
23
+ export { type RecentProject, clearRecentProjects, listRecentProjects };
@@ -0,0 +1,17 @@
1
+ import "./chunk-VHAA22YE.js";
2
+ import { protocolRequest } from "./sandboxUtils";
3
+ import { PROTOCOL_RECENTS } from "./generated/protocol";
4
+ import { SCHEMES } from "./protocolSchemes";
5
+ const recentsRequest = (params) => protocolRequest(SCHEMES[PROTOCOL_RECENTS], "list", [params]);
6
+ async function listRecentProjects() {
7
+ const res = await recentsRequest({});
8
+ return res.projects ?? null;
9
+ }
10
+ async function clearRecentProjects() {
11
+ await recentsRequest({ clear: true });
12
+ }
13
+ export {
14
+ clearRecentProjects,
15
+ listRecentProjects
16
+ };
17
+ //# sourceMappingURL=recents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/recents.ts"],"sourcesContent":["// The recent-projects record — app-facing surface (R3-485, OSO §4.3).\n//\n// `listRecentProjects()` reads the HOST-OWNED record through the one elevated,\n// app-scoped `recents:read` capability. Two gates stand between an app and the\n// record, and both must pass: the §8.4 capability grant (declared as\n// `requests: { 'recents:read': {} }` and consented per (app, principal)), and the\n// handler's structural binding — the read is granted only to the app bound at\n// `page.home`. For every other app the call resolves to a refusal, whatever it\n// declared.\n//\n// A project entry is COORDINATES (provider/namespace/repository/ref + when) — a\n// location the user already navigated to under their own authority, never an\n// in-repo path. Opening one runs the ordinary load path with the ordinary\n// consent: the record confers nothing (R-OSO-22).\nimport { protocolRequest } from './sandboxUtils';\nimport { PROTOCOL_RECENTS } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** One recently opened project — repository coordinates, never an in-repo path. */\nexport interface RecentProject {\n provider: string;\n namespace: string;\n repository: string;\n /** The ref the user opened (`main`, a sha). */\n ref: string;\n /** When the user last opened it (epoch ms). */\n ts: number;\n}\n\ninterface RecentsReply {\n /** Newest-first; `null` when the record is empty or the user cleared it. */\n projects: RecentProject[] | null;\n}\n\n/**\n * The one call shape the SDK speaks under `protocol-recents` — read, or clear\n * (same gated surface). A single typed call site keeps the wire contract exact:\n * the snapshot gate reads THIS shape, and the host handler accepts the same.\n */\nconst recentsRequest = (params: { clear?: boolean }): Promise<RecentsReply> =>\n protocolRequest(SCHEMES[PROTOCOL_RECENTS], 'list', [params]) as Promise<RecentsReply>;\n\n/**\n * The user's recently opened projects, newest-first, or `null` when the record is\n * absent (R-OSO-22: cleared is absent, never an empty list). Refuses for any app\n * that is not the `page.home` binding.\n */\nexport async function listRecentProjects(): Promise<RecentProject[] | null> {\n const res = await recentsRequest({});\n return res.projects ?? null;\n}\n\n/**\n * Clear the user's recent-projects record (the surface afterwards is absent, not\n * empty). Rides the same page.home binding gate as the read.\n */\nexport async function clearRecentProjects(): Promise<void> {\n await recentsRequest({ clear: true });\n}\n"],"mappings":";AAcA,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,eAAe;AAuBxB,MAAM,iBAAiB,CAAC,WACtB,gBAAgB,QAAQ,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC;AAO7D,eAAsB,qBAAsD;AAC1E,QAAM,MAAM,MAAM,eAAe,CAAC,CAAC;AACnC,SAAO,IAAI,YAAY;AACzB;AAMA,eAAsB,sBAAqC;AACzD,QAAM,eAAe,EAAE,OAAO,KAAK,CAAC;AACtC;","names":[]}
package/dist/tasks.cjs CHANGED
@@ -21,6 +21,8 @@ __export(tasks_exports, {
21
21
  cancelTask: () => cancelTask,
22
22
  capDir: () => capDir,
23
23
  capFile: () => capFile,
24
+ captureAudio: () => captureAudio,
25
+ capturePhoto: () => capturePhoto,
24
26
  completeTask: () => completeTask,
25
27
  getTaskInput: () => getTaskInput,
26
28
  invokeTask: () => invokeTask,
@@ -29,6 +31,7 @@ __export(tasks_exports, {
29
31
  module.exports = __toCommonJS(tasks_exports);
30
32
  var import_react = require("react");
31
33
  var import_sandboxUtils = require("./sandboxUtils");
34
+ var import_hostTransport = require("./hostTransport");
32
35
  var import_protocol = require("./generated/protocol");
33
36
  var import_protocolSchemes = require("./protocolSchemes");
34
37
  const capFile = (ref, opts) => ({
@@ -52,15 +55,44 @@ const invokeTask = async (task, params = {}) => {
52
55
  }
53
56
  return res.data;
54
57
  };
58
+ const capturePhoto = (options = {}) => invokeTask("capture-photo", { ...options });
59
+ const captureAudio = (options = {}) => invokeTask("capture-audio", { ...options });
55
60
  let latestInput = null;
56
61
  const inputListeners = /* @__PURE__ */ new Set();
57
- (0, import_sandboxUtils.addListener)(import_protocol.TASK_INPUT, (m) => {
58
- latestInput = { task: m.task, params: m.params ?? {} };
59
- inputListeners.forEach((l) => l(latestInput));
60
- });
61
- const getTaskInput = () => latestInput;
62
- const completeTask = (result) => (0, import_sandboxUtils.sendMessage)(import_protocol.TASK_COMPLETE, { result });
63
- const cancelTask = () => (0, import_sandboxUtils.sendMessage)(import_protocol.TASK_CANCEL, {});
62
+ let inputListenerRegistered = false;
63
+ const ensureInputListener = () => {
64
+ if (inputListenerRegistered) return;
65
+ try {
66
+ (0, import_sandboxUtils.addListener)(import_protocol.TASK_INPUT, (m) => {
67
+ latestInput = { task: m.task, params: m.params ?? {} };
68
+ inputListeners.forEach((l) => l(latestInput));
69
+ });
70
+ } catch {
71
+ return;
72
+ }
73
+ inputListenerRegistered = true;
74
+ };
75
+ ensureInputListener();
76
+ const getTaskInput = () => {
77
+ ensureInputListener();
78
+ return latestInput;
79
+ };
80
+ const hostReachable = () => {
81
+ try {
82
+ (0, import_hostTransport.transport)();
83
+ return true;
84
+ } catch {
85
+ return false;
86
+ }
87
+ };
88
+ const completeTask = (result) => {
89
+ if (!hostReachable()) return;
90
+ (0, import_sandboxUtils.sendMessage)(import_protocol.TASK_COMPLETE, { result });
91
+ };
92
+ const cancelTask = () => {
93
+ if (!hostReachable()) return;
94
+ (0, import_sandboxUtils.sendMessage)(import_protocol.TASK_CANCEL, {});
95
+ };
64
96
  const useTaskInput = () => {
65
97
  const [input, setInput] = (0, import_react.useState)(getTaskInput);
66
98
  (0, import_react.useEffect)(() => {
@@ -78,6 +110,8 @@ const useTaskInput = () => {
78
110
  cancelTask,
79
111
  capDir,
80
112
  capFile,
113
+ captureAudio,
114
+ capturePhoto,
81
115
  completeTask,
82
116
  getTaskInput,
83
117
  invokeTask,
@@ -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":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,mBAAoC;AACpC,0BAA0D;AAC1D,sBAAsE;AACtE,6BAAwB;AAmBjB,MAAM,UAAU,CAAC,KAA2C,UAA0C;AAAA,EAC3G,MAAM;AAAA,EACN,SAAS,IAAI;AAAA,EACb,SAAS,IAAI;AAAA,EACb,MAAM,KAAK;AACb;AAoBO,MAAM,SAAS,CAAC,KAA2C,UAAyC;AAAA,EACzG,MAAM;AAAA,EACN,SAAS,IAAI;AAAA,EACb,SAAS,IAAI;AAAA,EACb,MAAM,KAAK;AACb;AASO,MAAM,aAAa,OAAoB,MAAc,SAAkC,CAAC,MAAkB;AAC/G,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,6BAAa,GAAG,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,CAAC;AAIvF,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,SAAS,IAAI,UAAU;AAC7D,QAAI,OAAO,KAAK,QAAQ;AACxB,UAAM;AAAA,EACR;AACA,SAAO,IAAI;AACb;AAUA,IAAI,cAAgC;AACpC,MAAM,iBAAiB,oBAAI,IAA4B;AAAA,IAIvD,iCAAY,4BAAY,CAAC,MAA0D;AACjF,gBAAc,EAAE,MAAM,EAAE,MAAM,QAAQ,EAAE,UAAU,CAAC,EAAE;AACrD,iBAAe,QAAQ,CAAC,MAAM,EAAE,WAAY,CAAC;AAC/C,CAAC;AAGM,MAAM,eAAe,MAAwB;AAO7C,MAAM,eAAe,CAAC,eAA0B,iCAAY,+BAAe,EAAE,OAAO,CAAC;AAGrF,MAAM,aAAa,UAAY,iCAAY,6BAAa,CAAC,CAAC;AAG1D,MAAM,eAAe,MAAwB;AAClD,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAA2B,YAAY;AACjE,8BAAU,MAAM;AACd,UAAM,IAAI,CAAC,MAAiB,SAAS,CAAC;AACtC,mBAAe,IAAI,CAAC;AACpB,QAAI,YAAa,UAAS,WAAW;AACrC,WAAO,MAAM;AACX,qBAAe,OAAO,CAAC;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,CAAC;AACL,SAAO;AACT;","names":[]}
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":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBA,mBAAoC;AACpC,0BAA0D;AAC1D,2BAA0B;AAC1B,sBAAsE;AACtE,6BAAwB;AAmBjB,MAAM,UAAU,CAAC,KAA2C,UAA0C;AAAA,EAC3G,MAAM;AAAA,EACN,SAAS,IAAI;AAAA,EACb,SAAS,IAAI;AAAA,EACb,MAAM,KAAK;AACb;AAoBO,MAAM,SAAS,CAAC,KAA2C,UAAyC;AAAA,EACzG,MAAM;AAAA,EACN,SAAS,IAAI;AAAA,EACb,SAAS,IAAI;AAAA,EACb,MAAM,KAAK;AACb;AAiDO,MAAM,aAAa,OAAoB,MAAc,SAAkC,CAAC,MAAkB;AAC/G,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,6BAAa,GAAG,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,CAAC;AAIvF,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,SAAS,IAAI,UAAU;AAC7D,QAAI,OAAO,KAAK,QAAQ;AACxB,UAAM;AAAA,EACR;AACA,SAAO,IAAI;AACb;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,yCAAY,4BAAY,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,wCAAU;AACV,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAeO,MAAM,eAAe,CAAC,WAA0B;AACrD,MAAI,CAAC,cAAc,EAAG;AAEtB,uCAAY,+BAAe,EAAE,OAAO,CAAC;AACvC;AAKO,MAAM,aAAa,MAAY;AACpC,MAAI,CAAC,cAAc,EAAG;AACtB,uCAAY,6BAAa,CAAC,CAAC;AAC7B;AAKO,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
@@ -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 };