@immediately-run/sdk 0.57.0 → 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.
- package/dist/agentChatClient.cjs +112 -0
- package/dist/agentChatClient.cjs.map +1 -0
- package/dist/agentChatClient.d.cts +9 -0
- package/dist/agentChatClient.d.ts +9 -0
- package/dist/agentChatClient.js +89 -0
- package/dist/agentChatClient.js.map +1 -0
- package/dist/agentContext.cjs +69 -0
- package/dist/agentContext.cjs.map +1 -0
- package/dist/agentContext.d.cts +45 -0
- package/dist/agentContext.d.ts +45 -0
- package/dist/agentContext.js +45 -0
- package/dist/agentContext.js.map +1 -0
- package/dist/agentLoop.cjs +301 -0
- package/dist/agentLoop.cjs.map +1 -0
- package/dist/agentLoop.d.cts +267 -0
- package/dist/agentLoop.d.ts +267 -0
- package/dist/agentLoop.js +271 -0
- package/dist/agentLoop.js.map +1 -0
- package/dist/agentSteering.cjs +127 -0
- package/dist/agentSteering.cjs.map +1 -0
- package/dist/agentSteering.d.cts +80 -0
- package/dist/agentSteering.d.ts +80 -0
- package/dist/agentSteering.js +98 -0
- package/dist/agentSteering.js.map +1 -0
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +33 -2
- package/dist/auth.d.ts +33 -2
- package/dist/auth.js.map +1 -1
- package/dist/bundle.cjs +91 -0
- package/dist/bundle.cjs.map +1 -0
- package/dist/bundle.d.cts +63 -0
- package/dist/bundle.d.ts +63 -0
- package/dist/bundle.js +62 -0
- package/dist/bundle.js.map +1 -0
- package/dist/collectHeadings.cjs +59 -0
- package/dist/collectHeadings.cjs.map +1 -0
- package/dist/collectHeadings.d.cts +16 -0
- package/dist/collectHeadings.d.ts +16 -0
- package/dist/collectHeadings.js +36 -0
- package/dist/collectHeadings.js.map +1 -0
- package/dist/components/MainContent.cjs +3 -1
- package/dist/components/MainContent.cjs.map +1 -1
- package/dist/components/MainContent.js +4 -2
- package/dist/components/MainContent.js.map +1 -1
- package/dist/corpus.cjs +9 -58
- package/dist/corpus.cjs.map +1 -1
- package/dist/corpus.d.cts +3 -63
- package/dist/corpus.d.ts +3 -63
- package/dist/corpus.js +15 -57
- package/dist/corpus.js.map +1 -1
- package/dist/debug.cjs +115 -0
- package/dist/debug.cjs.map +1 -1
- package/dist/debug.js +115 -0
- package/dist/debug.js.map +1 -1
- package/dist/fence.cjs +49 -0
- package/dist/fence.cjs.map +1 -0
- package/dist/fence.d.cts +4 -0
- package/dist/fence.d.ts +4 -0
- package/dist/fence.js +26 -0
- package/dist/fence.js.map +1 -0
- package/dist/formFactor.cjs.map +1 -1
- package/dist/formFactor.d.cts +11 -2
- package/dist/formFactor.d.ts +11 -2
- package/dist/formFactor.js.map +1 -1
- package/dist/fs.cjs.map +1 -1
- package/dist/fs.d.cts +16 -6
- package/dist/fs.d.ts +16 -6
- package/dist/fs.js.map +1 -1
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +7 -0
- package/dist/hooks.d.ts +7 -0
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +19 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/launch.cjs +45 -6
- package/dist/launch.cjs.map +1 -1
- package/dist/launch.d.cts +3 -0
- package/dist/launch.d.ts +3 -0
- package/dist/launch.js +45 -6
- package/dist/launch.js.map +1 -1
- package/dist/llm.cjs.map +1 -1
- package/dist/llm.d.cts +20 -3
- package/dist/llm.d.ts +20 -3
- package/dist/llm.js.map +1 -1
- package/dist/metadataQueryTool.cjs +271 -0
- package/dist/metadataQueryTool.cjs.map +1 -0
- package/dist/metadataQueryTool.d.cts +159 -0
- package/dist/metadataQueryTool.d.ts +159 -0
- package/dist/metadataQueryTool.js +241 -0
- package/dist/metadataQueryTool.js.map +1 -0
- package/dist/mounts.cjs.map +1 -1
- package/dist/mounts.d.cts +22 -3
- package/dist/mounts.d.ts +22 -3
- package/dist/mounts.js.map +1 -1
- package/dist/onFsChange.cjs +13 -1
- package/dist/onFsChange.cjs.map +1 -1
- package/dist/onFsChange.d.cts +21 -1
- package/dist/onFsChange.d.ts +21 -1
- package/dist/onFsChange.js +13 -1
- package/dist/onFsChange.js.map +1 -1
- package/dist/protocolSchemes.cjs +1 -0
- package/dist/protocolSchemes.cjs.map +1 -1
- package/dist/protocolSchemes.d.cts +1 -0
- package/dist/protocolSchemes.d.ts +1 -0
- package/dist/protocolSchemes.js +2 -0
- package/dist/protocolSchemes.js.map +1 -1
- package/dist/recents.cjs +41 -0
- package/dist/recents.cjs.map +1 -0
- package/dist/recents.d.cts +23 -0
- package/dist/recents.d.ts +23 -0
- package/dist/recents.js +17 -0
- package/dist/recents.js.map +1 -0
- package/dist/tasks.cjs +41 -7
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.d.cts +136 -7
- package/dist/tasks.d.ts +136 -7
- package/dist/tasks.js +39 -7
- package/dist/tasks.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +6 -5
package/dist/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks.ts"],"sourcesContent":["import { APP_ROOT, underAppRoot } from '@immediately-run/platform-constants';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { openFs, type FsError } from './fs';\nimport { useMetadataStore } from './metadataSource';\nimport type { SandboxMount } from './mounts';\nimport {\n FilesMetadata,\n Metadata,\n MetadataQueryEntry,\n MetadataQueryFunction,\n MetadataQueryResult,\n} from './sandboxTypes';\n\nconst entriesEqual = <T>(\n a: MetadataQueryEntry<T, Record<string, unknown>>[],\n b: MetadataQueryEntry<T, Record<string, unknown>>[],\n): boolean => {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n // Unchanged frontmatter keeps its object identity across `metadata-update`\n // merges, so an identity check on `meta` is a sound \"did this match change?\".\n if (a[i].path !== b[i].path || a[i].meta !== b[i].meta) {\n return false;\n }\n // Extra fields a record-returning query computed (R3-276) are part of the\n // result, so a change in them is a change in the result — compared by value,\n // one level, because they are derived scalars, not the store's shared objects.\n const ax = a[i] as Record<string, unknown>;\n const bx = b[i] as Record<string, unknown>;\n const keys = new Set([...Object.keys(ax), ...Object.keys(bx)]);\n for (const k of keys) {\n if (k === 'path' || k === 'meta') continue;\n if (ax[k] !== bx[k]) return false;\n }\n }\n return true;\n};\n\n/**\n * Query the file metadata store (MDX frontmatter) with a plain JS function.\n *\n * The query receives every file's frontmatter keyed by path and returns the paths\n * that match; the hook resolves each path back to its frontmatter and returns an\n * array of `{ path, meta }` entries — so a single call gives you everything you\n * filtered on, with no second lookup. A throwing query is reported as `{ error }`\n * rather than crashing the render.\n *\n * The query runs synchronously during render (no empty first frame), and the\n * returned array keeps its identity while the matches are unchanged, so it is safe\n * to use directly in downstream `useMemo`/`useEffect` dependency arrays.\n *\n * A query may return RECORDS instead of bare paths (R3-276) — `{ path, ...extra }`\n * — and the extra fields ride along on each entry, so something derived while\n * selecting does not have to be recomputed downstream:\n * ```ts\n * const posts = useMetadataQuery<PostMeta, { year: string }>((files) =>\n * Object.entries(files).map(([path, m]) => ({ path, year: m.date.slice(0, 4) })),\n * );\n * ```\n * The store it queries is the nearest {@link MetadataSource}, else the host's.\n *\n * Pass a type parameter to get typed frontmatter throughout:\n * ```ts\n * interface PostMeta { title: string; date: string; draft?: boolean }\n * const posts = useMetadataQuery<PostMeta>((files) =>\n * Object.entries(files)\n * .filter(([, m]) => !m.draft)\n * .sort(([, a], [, b]) => b.date.localeCompare(a.date))\n * .map(([path]) => path),\n * );\n * ```\n */\nexport const useMetadataQuery = <T = Metadata, E extends object = {}>(\n queryFunction: MetadataQueryFunction<T>,\n): MetadataQueryResult<T, E> => {\n const files = useMetadataStore<T>();\n const previous = useRef<MetadataQueryEntry<T, Record<string, unknown>>[]>([]);\n return useMemo<MetadataQueryResult<T, E>>(() => {\n let entries: MetadataQueryEntry<T, Record<string, unknown>>[];\n try {\n // `path` and `meta` are applied AFTER the record's own fields: a query cannot\n // shadow the two the hook is responsible for, whatever it happened to name.\n entries = queryFunction(files).map((selected) =>\n typeof selected === 'string'\n ? { path: selected, meta: files[selected] }\n : { ...selected, path: selected.path, meta: files[selected.path] },\n );\n } catch (error) {\n return { error };\n }\n // Preserve the prior array reference when nothing matched differently.\n if (entriesEqual(entries, previous.current)) {\n return previous.current as MetadataQueryEntry<T, E>[];\n }\n previous.current = entries;\n return entries as MetadataQueryEntry<T, E>[];\n }, [files, queryFunction]);\n};\n\n/**\n * Read one file's metadata (MDX frontmatter) by path. Returns `undefined` when the\n * path has no metadata. Pass a type parameter for typed field access.\n *\n * **The store is keyed by ABSOLUTE module path** — `/app/content/post.mdx`, the same\n * identifier `fs`, `module.dynamicImport` and `<Include>` use — not by the\n * repo-relative path this doc claimed until R3-276. Keeping metadata in the file\n * space is what lets an app read a file's metadata and render that same file by the\n * same path (`sandbox/src/bundler/metadataKey.test.ts` pins it).\n *\n * A repo-relative path (`/content/post.mdx`) is accepted as a fallback: if the path\n * is not a key, it is retried under the app root via the shared\n * `underAppRoot` helper (R3-275). That is additive — it only turns a previous\n * `undefined` into a value — and it exists because the old doc told people to pass\n * exactly that form. A path a {@link MetadataSource} provided in some other key\n * space is looked up as given, unchanged.\n */\nexport const useFileMetadata = <T = Metadata>(path: string): T | undefined => {\n const files = useMetadataStore<T>();\n return useMemo(() => {\n const direct = files[path];\n if (direct !== undefined) return direct;\n // The fallback is for a path in the REPO-RELATIVE space. An already-app-rooted\n // path that missed is simply a miss: retrying it would consult `/app/app/…`,\n // which is not a key space anything writes — it would only ever hit by accident.\n if (path === APP_ROOT || path.startsWith(`${APP_ROOT}/`)) return undefined;\n return files[underAppRoot(path)];\n }, [path, files]);\n};\n\n/**\n * The raw, reactive metadata store: a map from file path to frontmatter. The\n * escape hatch for apps that want to render their own index rather than express it\n * as a path-returning query. Pass a type parameter for typed frontmatter values.\n */\nexport const useAllMetadata = <T = Metadata>(): FilesMetadata<T> => useMetadataStore<T>();\n\n/** The reactive state returned by {@link useObjectUrl}. */\nexport interface ObjectUrlState {\n /** The object URL once the bytes have loaded; `null` while loading or on error. */\n url: string | null;\n /** True while the file is being read. */\n loading: boolean;\n /** The {@link FsError} if the read failed (`not-found`, `unavailable`, …), else `null`. */\n error: FsError | null;\n}\n\n/**\n * Read a file from a mount into an **object URL** for `<img src>`, revoking it\n * automatically on unmount or when `mount`/`relPath` changes. This is the React\n * answer to \"an opaque-origin iframe can't fetch a mount path\": it reads the bytes\n * off the sandbox ZenFS ({@link openFs}) and hands you a URL to drop into an\n * `<img>`, and it owns the create/revoke lifecycle so you never leak a URL.\n *\n * Pass `null`/`undefined` for `mount` or `relPath` to mean \"nothing to load yet\"\n * (idle state, no read). For a ready-made element use `MountImage`.\n *\n * ```tsx\n * const { url, loading, error } = useObjectUrl(mount, 'photos/cat.png');\n * if (loading) return <Spinner />;\n * if (error || !url) return <span>missing</span>;\n * return <img src={url} alt=\"cat\" />;\n * ```\n */\nexport const useObjectUrl = (\n mount: SandboxMount | null | undefined,\n relPath: string | null | undefined,\n opts?: { type?: string },\n): ObjectUrlState => {\n const [state, setState] = useState<ObjectUrlState>({\n url: null,\n loading: Boolean(mount && relPath),\n error: null,\n });\n const type = opts?.type;\n // Key the effect on the mount's stable `path` (its object identity churns as the\n // mount set re-announces) plus the relPath and the optional type override.\n const mountPath = mount?.path;\n useEffect(() => {\n if (!mount || !relPath) {\n setState({ url: null, loading: false, error: null });\n return;\n }\n let alive = true;\n let revoke: (() => void) | null = null;\n setState({ url: null, loading: true, error: null });\n openFs(mount)\n .readObjectUrl(relPath, type ? { type } : undefined)\n .then((res) => {\n // Lost the race (unmounted / prop changed): revoke immediately, don't set.\n if (!alive) {\n res.revoke();\n return;\n }\n revoke = res.revoke;\n setState({ url: res.url, loading: false, error: null });\n })\n .catch((e) => {\n if (alive) setState({ url: null, loading: false, error: e as FsError });\n });\n return () => {\n alive = false;\n if (revoke) revoke();\n };\n // `mount` is intentionally tracked via its stable `mountPath`.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [mountPath, relPath, type]);\n return state;\n};\n"],"mappings":";AAAA,SAAS,UAAU,oBAAoB;AACvC,SAAS,WAAW,SAAS,QAAQ,gBAAgB;AACrD,SAAS,cAA4B;AACrC,SAAS,wBAAwB;AAUjC,MAAM,eAAe,CACnB,GACA,MACY;AACZ,MAAI,EAAE,WAAW,EAAE,QAAQ;AACzB,WAAO;AAAA,EACT;AACA,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AAGjC,QAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM;AACtD,aAAO;AAAA,IACT;AAIA,UAAM,KAAK,EAAE,CAAC;AACd,UAAM,KAAK,EAAE,CAAC;AACd,UAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,EAAE,GAAG,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC;AAC7D,eAAW,KAAK,MAAM;AACpB,UAAI,MAAM,UAAU,MAAM,OAAQ;AAClC,UAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAG,QAAO;AAAA,IAC9B;AAAA,EACF;AACA,SAAO;AACT;AAoCO,MAAM,mBAAmB,CAC9B,kBAC8B;AAC9B,QAAM,QAAQ,iBAAoB;AAClC,QAAM,WAAW,OAAyD,CAAC,CAAC;AAC5E,SAAO,QAAmC,MAAM;AAC9C,QAAI;AACJ,QAAI;AAGF,gBAAU,cAAc,KAAK,EAAE;AAAA,QAAI,CAAC,aAClC,OAAO,aAAa,WAChB,EAAE,MAAM,UAAU,MAAM,MAAM,QAAQ,EAAE,IACxC,EAAE,GAAG,UAAU,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,IAAI,EAAE;AAAA,MACrE;AAAA,IACF,SAAS,OAAO;AACd,aAAO,EAAE,MAAM;AAAA,IACjB;AAEA,QAAI,aAAa,SAAS,SAAS,OAAO,GAAG;AAC3C,aAAO,SAAS;AAAA,IAClB;AACA,aAAS,UAAU;AACnB,WAAO;AAAA,EACT,GAAG,CAAC,OAAO,aAAa,CAAC;AAC3B;AAmBO,MAAM,kBAAkB,CAAe,SAAgC;AAC5E,QAAM,QAAQ,iBAAoB;AAClC,SAAO,QAAQ,MAAM;AACnB,UAAM,SAAS,MAAM,IAAI;AACzB,QAAI,WAAW,OAAW,QAAO;AAIjC,QAAI,SAAS,YAAY,KAAK,WAAW,GAAG,QAAQ,GAAG,EAAG,QAAO;AACjE,WAAO,MAAM,aAAa,IAAI,CAAC;AAAA,EACjC,GAAG,CAAC,MAAM,KAAK,CAAC;AAClB;AAOO,MAAM,iBAAiB,MAAsC,iBAAoB;AA6BjF,MAAM,eAAe,CAC1B,OACA,SACA,SACmB;AACnB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAyB;AAAA,IACjD,KAAK;AAAA,IACL,SAAS,QAAQ,SAAS,OAAO;AAAA,IACjC,OAAO;AAAA,EACT,CAAC;AACD,QAAM,OAAO,MAAM;AAGnB,QAAM,YAAY,OAAO;AACzB,YAAU,MAAM;AACd,QAAI,CAAC,SAAS,CAAC,SAAS;AACtB,eAAS,EAAE,KAAK,MAAM,SAAS,OAAO,OAAO,KAAK,CAAC;AACnD;AAAA,IACF;AACA,QAAI,QAAQ;AACZ,QAAI,SAA8B;AAClC,aAAS,EAAE,KAAK,MAAM,SAAS,MAAM,OAAO,KAAK,CAAC;AAClD,WAAO,KAAK,EACT,cAAc,SAAS,OAAO,EAAE,KAAK,IAAI,MAAS,EAClD,KAAK,CAAC,QAAQ;AAEb,UAAI,CAAC,OAAO;AACV,YAAI,OAAO;AACX;AAAA,MACF;AACA,eAAS,IAAI;AACb,eAAS,EAAE,KAAK,IAAI,KAAK,SAAS,OAAO,OAAO,KAAK,CAAC;AAAA,IACxD,CAAC,EACA,MAAM,CAAC,MAAM;AACZ,UAAI,MAAO,UAAS,EAAE,KAAK,MAAM,SAAS,OAAO,OAAO,EAAa,CAAC;AAAA,IACxE,CAAC;AACH,WAAO,MAAM;AACX,cAAQ;AACR,UAAI,OAAQ,QAAO;AAAA,IACrB;AAAA,EAGF,GAAG,CAAC,WAAW,SAAS,IAAI,CAAC;AAC7B,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks.ts"],"sourcesContent":["import { APP_ROOT, underAppRoot } from '@immediately-run/platform-constants';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { openFs, type FsError } from './fs';\nimport { useMetadataStore } from './metadataSource';\nimport type { SandboxMount } from './mounts';\nimport {\n FilesMetadata,\n Metadata,\n MetadataQueryEntry,\n MetadataQueryFunction,\n MetadataQueryResult,\n} from './sandboxTypes';\n\nconst entriesEqual = <T>(\n a: MetadataQueryEntry<T, Record<string, unknown>>[],\n b: MetadataQueryEntry<T, Record<string, unknown>>[],\n): boolean => {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n // Unchanged frontmatter keeps its object identity across `metadata-update`\n // merges, so an identity check on `meta` is a sound \"did this match change?\".\n if (a[i].path !== b[i].path || a[i].meta !== b[i].meta) {\n return false;\n }\n // Extra fields a record-returning query computed (R3-276) are part of the\n // result, so a change in them is a change in the result — compared by value,\n // one level, because they are derived scalars, not the store's shared objects.\n const ax = a[i] as Record<string, unknown>;\n const bx = b[i] as Record<string, unknown>;\n const keys = new Set([...Object.keys(ax), ...Object.keys(bx)]);\n for (const k of keys) {\n if (k === 'path' || k === 'meta') continue;\n if (ax[k] !== bx[k]) return false;\n }\n }\n return true;\n};\n\n/**\n * Query the file metadata store (MDX frontmatter) with a plain JS function.\n *\n * The query receives every file's frontmatter keyed by path and returns the paths\n * that match; the hook resolves each path back to its frontmatter and returns an\n * array of `{ path, meta }` entries — so a single call gives you everything you\n * filtered on, with no second lookup. A throwing query is reported as `{ error }`\n * rather than crashing the render.\n *\n * The query runs synchronously during render (no empty first frame), and the\n * returned array keeps its identity while the matches are unchanged, so it is safe\n * to use directly in downstream `useMemo`/`useEffect` dependency arrays.\n *\n * A query may return RECORDS instead of bare paths (R3-276) — `{ path, ...extra }`\n * — and the extra fields ride along on each entry, so something derived while\n * selecting does not have to be recomputed downstream:\n * ```ts\n * const posts = useMetadataQuery<PostMeta, { year: string }>((files) =>\n * Object.entries(files).map(([path, m]) => ({ path, year: m.date.slice(0, 4) })),\n * );\n * ```\n * The store it queries is the nearest {@link MetadataSource}, else the host's.\n *\n * Pass a type parameter to get typed frontmatter throughout:\n * ```ts\n * interface PostMeta { title: string; date: string; draft?: boolean }\n * const posts = useMetadataQuery<PostMeta>((files) =>\n * Object.entries(files)\n * .filter(([, m]) => !m.draft)\n * .sort(([, a], [, b]) => b.date.localeCompare(a.date))\n * .map(([path]) => path),\n * );\n * ```\n */\nexport const useMetadataQuery = <T = Metadata, E extends object = {}>(\n queryFunction: MetadataQueryFunction<T>,\n): MetadataQueryResult<T, E> => {\n const files = useMetadataStore<T>();\n const previous = useRef<MetadataQueryEntry<T, Record<string, unknown>>[]>([]);\n return useMemo<MetadataQueryResult<T, E>>(() => {\n let entries: MetadataQueryEntry<T, Record<string, unknown>>[];\n try {\n // `path` and `meta` are applied AFTER the record's own fields: a query cannot\n // shadow the two the hook is responsible for, whatever it happened to name.\n entries = queryFunction(files).map((selected) =>\n typeof selected === 'string'\n ? { path: selected, meta: files[selected] }\n : { ...selected, path: selected.path, meta: files[selected.path] },\n );\n } catch (error) {\n return { error };\n }\n // Preserve the prior array reference when nothing matched differently.\n if (entriesEqual(entries, previous.current)) {\n return previous.current as MetadataQueryEntry<T, E>[];\n }\n previous.current = entries;\n return entries as MetadataQueryEntry<T, E>[];\n }, [files, queryFunction]);\n};\n\n/**\n * Read one file's metadata (MDX frontmatter) by path. Returns `undefined` when the\n * path has no metadata. Pass a type parameter for typed field access.\n *\n * **The store is keyed by ABSOLUTE module path** — `/app/content/post.mdx`, the same\n * identifier `fs`, `module.dynamicImport` and `<Include>` use — not by the\n * repo-relative path this doc claimed until R3-276. Keeping metadata in the file\n * space is what lets an app read a file's metadata and render that same file by the\n * same path (`sandbox/src/bundler/metadataKey.test.ts` pins it).\n *\n * A repo-relative path (`/content/post.mdx`) is accepted as a fallback: if the path\n * is not a key, it is retried under the app root via the shared\n * `underAppRoot` helper (R3-275). That is additive — it only turns a previous\n * `undefined` into a value — and it exists because the old doc told people to pass\n * exactly that form. A path a {@link MetadataSource} provided in some other key\n * space is looked up as given, unchanged.\n */\nexport const useFileMetadata = <T = Metadata>(path: string): T | undefined => {\n const files = useMetadataStore<T>();\n return useMemo(() => {\n const direct = files[path];\n if (direct !== undefined) return direct;\n // The fallback is for a path in the REPO-RELATIVE space. An already-app-rooted\n // path that missed is simply a miss: retrying it would consult `/app/app/…`,\n // which is not a key space anything writes — it would only ever hit by accident.\n if (path === APP_ROOT || path.startsWith(`${APP_ROOT}/`)) return undefined;\n return files[underAppRoot(path)];\n }, [path, files]);\n};\n\n/**\n * The raw, reactive metadata store: a map from file path to frontmatter. The\n * escape hatch for apps that want to render their own index rather than express it\n * as a path-returning query. Pass a type parameter for typed frontmatter values.\n */\nexport const useAllMetadata = <T = Metadata>(): FilesMetadata<T> => useMetadataStore<T>();\n\n/** The reactive state returned by {@link useObjectUrl}. */\nexport interface ObjectUrlState {\n /** The object URL once the bytes have loaded; `null` while loading or on error. */\n url: string | null;\n /** True while the file is being read. */\n loading: boolean;\n /** The {@link FsError} if the read failed (`not-found`, `unavailable`, …), else `null`. */\n error: FsError | null;\n}\n\n/**\n * Read a file from a mount into an **object URL** for `<img src>`, revoking it\n * automatically on unmount or when `mount`/`relPath` changes. This is the React\n * answer to \"an opaque-origin iframe can't fetch a mount path\": it reads the bytes\n * off the sandbox ZenFS ({@link openFs}) and hands you a URL to drop into an\n * `<img>`, and it owns the create/revoke lifecycle so you never leak a URL.\n *\n * Pass `null`/`undefined` for `mount` or `relPath` to mean \"nothing to load yet\"\n * (idle state, no read). For a ready-made element use `MountImage`.\n *\n * ```tsx\n * const { url, loading, error } = useObjectUrl(mount, 'photos/cat.png');\n * if (loading) return <Spinner />;\n * if (error || !url) return <span>missing</span>;\n * return <img src={url} alt=\"cat\" />;\n * ```\n *\n * Off-host: under `vite dev` with the `@immediately-run/dev-fs` plugin (>= 0.5.0)\n * this works against your local disk — the plugin publishes its fs bridge where\n * the SDK discovers the sandbox fs (see `sandboxFs` in `fs.ts`). Under plain\n * `vite dev` (no plugin) there is no filesystem at all, and the hook settles to\n * `{ url: null, loading: false, error }` with `error.code === 'unavailable'` —\n * render the error/absent state, don't treat it as forever-loading.\n */\nexport const useObjectUrl = (\n mount: SandboxMount | null | undefined,\n relPath: string | null | undefined,\n opts?: { type?: string },\n): ObjectUrlState => {\n const [state, setState] = useState<ObjectUrlState>({\n url: null,\n loading: Boolean(mount && relPath),\n error: null,\n });\n const type = opts?.type;\n // Key the effect on the mount's stable `path` (its object identity churns as the\n // mount set re-announces) plus the relPath and the optional type override.\n const mountPath = mount?.path;\n useEffect(() => {\n if (!mount || !relPath) {\n setState({ url: null, loading: false, error: null });\n return;\n }\n let alive = true;\n let revoke: (() => void) | null = null;\n setState({ url: null, loading: true, error: null });\n openFs(mount)\n .readObjectUrl(relPath, type ? { type } : undefined)\n .then((res) => {\n // Lost the race (unmounted / prop changed): revoke immediately, don't set.\n if (!alive) {\n res.revoke();\n return;\n }\n revoke = res.revoke;\n setState({ url: res.url, loading: false, error: null });\n })\n .catch((e) => {\n if (alive) setState({ url: null, loading: false, error: e as FsError });\n });\n return () => {\n alive = false;\n if (revoke) revoke();\n };\n // `mount` is intentionally tracked via its stable `mountPath`.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [mountPath, relPath, type]);\n return state;\n};\n"],"mappings":";AAAA,SAAS,UAAU,oBAAoB;AACvC,SAAS,WAAW,SAAS,QAAQ,gBAAgB;AACrD,SAAS,cAA4B;AACrC,SAAS,wBAAwB;AAUjC,MAAM,eAAe,CACnB,GACA,MACY;AACZ,MAAI,EAAE,WAAW,EAAE,QAAQ;AACzB,WAAO;AAAA,EACT;AACA,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AAGjC,QAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM;AACtD,aAAO;AAAA,IACT;AAIA,UAAM,KAAK,EAAE,CAAC;AACd,UAAM,KAAK,EAAE,CAAC;AACd,UAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,EAAE,GAAG,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC;AAC7D,eAAW,KAAK,MAAM;AACpB,UAAI,MAAM,UAAU,MAAM,OAAQ;AAClC,UAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAG,QAAO;AAAA,IAC9B;AAAA,EACF;AACA,SAAO;AACT;AAoCO,MAAM,mBAAmB,CAC9B,kBAC8B;AAC9B,QAAM,QAAQ,iBAAoB;AAClC,QAAM,WAAW,OAAyD,CAAC,CAAC;AAC5E,SAAO,QAAmC,MAAM;AAC9C,QAAI;AACJ,QAAI;AAGF,gBAAU,cAAc,KAAK,EAAE;AAAA,QAAI,CAAC,aAClC,OAAO,aAAa,WAChB,EAAE,MAAM,UAAU,MAAM,MAAM,QAAQ,EAAE,IACxC,EAAE,GAAG,UAAU,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,IAAI,EAAE;AAAA,MACrE;AAAA,IACF,SAAS,OAAO;AACd,aAAO,EAAE,MAAM;AAAA,IACjB;AAEA,QAAI,aAAa,SAAS,SAAS,OAAO,GAAG;AAC3C,aAAO,SAAS;AAAA,IAClB;AACA,aAAS,UAAU;AACnB,WAAO;AAAA,EACT,GAAG,CAAC,OAAO,aAAa,CAAC;AAC3B;AAmBO,MAAM,kBAAkB,CAAe,SAAgC;AAC5E,QAAM,QAAQ,iBAAoB;AAClC,SAAO,QAAQ,MAAM;AACnB,UAAM,SAAS,MAAM,IAAI;AACzB,QAAI,WAAW,OAAW,QAAO;AAIjC,QAAI,SAAS,YAAY,KAAK,WAAW,GAAG,QAAQ,GAAG,EAAG,QAAO;AACjE,WAAO,MAAM,aAAa,IAAI,CAAC;AAAA,EACjC,GAAG,CAAC,MAAM,KAAK,CAAC;AAClB;AAOO,MAAM,iBAAiB,MAAsC,iBAAoB;AAoCjF,MAAM,eAAe,CAC1B,OACA,SACA,SACmB;AACnB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAyB;AAAA,IACjD,KAAK;AAAA,IACL,SAAS,QAAQ,SAAS,OAAO;AAAA,IACjC,OAAO;AAAA,EACT,CAAC;AACD,QAAM,OAAO,MAAM;AAGnB,QAAM,YAAY,OAAO;AACzB,YAAU,MAAM;AACd,QAAI,CAAC,SAAS,CAAC,SAAS;AACtB,eAAS,EAAE,KAAK,MAAM,SAAS,OAAO,OAAO,KAAK,CAAC;AACnD;AAAA,IACF;AACA,QAAI,QAAQ;AACZ,QAAI,SAA8B;AAClC,aAAS,EAAE,KAAK,MAAM,SAAS,MAAM,OAAO,KAAK,CAAC;AAClD,WAAO,KAAK,EACT,cAAc,SAAS,OAAO,EAAE,KAAK,IAAI,MAAS,EAClD,KAAK,CAAC,QAAQ;AAEb,UAAI,CAAC,OAAO;AACV,YAAI,OAAO;AACX;AAAA,MACF;AACA,eAAS,IAAI;AACb,eAAS,EAAE,KAAK,IAAI,KAAK,SAAS,OAAO,OAAO,KAAK,CAAC;AAAA,IACxD,CAAC,EACA,MAAM,CAAC,MAAM;AACZ,UAAI,MAAO,UAAS,EAAE,KAAK,MAAM,SAAS,OAAO,OAAO,EAAa,CAAC;AAAA,IACxE,CAAC;AACH,WAAO,MAAM;AACX,cAAQ;AACR,UAAI,OAAQ,QAAO;AAAA,IACrB;AAAA,EAGF,GAAG,CAAC,WAAW,SAAS,IAAI,CAAC;AAC7B,SAAO;AACT;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var import_SafeInclude = require("./components/SafeInclude");
|
|
|
32
32
|
__reExport(index_exports, require("./sourceCache"), module.exports);
|
|
33
33
|
__reExport(index_exports, require("./components/MDXComponents"), module.exports);
|
|
34
34
|
__reExport(index_exports, require("./linkSpace"), module.exports);
|
|
35
|
+
__reExport(index_exports, require("./bundle"), module.exports);
|
|
35
36
|
__reExport(index_exports, require("./corpus"), module.exports);
|
|
36
37
|
__reExport(index_exports, require("./components/MountImage"), module.exports);
|
|
37
38
|
__reExport(index_exports, require("./components/Routes"), module.exports);
|
|
@@ -55,6 +56,7 @@ __reExport(index_exports, require("./dnd"), module.exports);
|
|
|
55
56
|
__reExport(index_exports, require("./netFetch"), module.exports);
|
|
56
57
|
__reExport(index_exports, require("./feed"), module.exports);
|
|
57
58
|
__reExport(index_exports, require("./secrets"), module.exports);
|
|
59
|
+
__reExport(index_exports, require("./recents"), module.exports);
|
|
58
60
|
__reExport(index_exports, require("./llm"), module.exports);
|
|
59
61
|
__reExport(index_exports, require("./diagnostics"), module.exports);
|
|
60
62
|
__reExport(index_exports, require("./vcs"), module.exports);
|
|
@@ -71,6 +73,13 @@ __reExport(index_exports, require("./protocolStream"), module.exports);
|
|
|
71
73
|
__reExport(index_exports, require("./protocolDeadline"), module.exports);
|
|
72
74
|
__reExport(index_exports, require("./sandboxTypes"), module.exports);
|
|
73
75
|
__reExport(index_exports, require("./safeContent"), module.exports);
|
|
76
|
+
__reExport(index_exports, require("./agentLoop"), module.exports);
|
|
77
|
+
__reExport(index_exports, require("./agentSteering"), module.exports);
|
|
78
|
+
__reExport(index_exports, require("./agentChatClient"), module.exports);
|
|
79
|
+
__reExport(index_exports, require("./metadataQueryTool"), module.exports);
|
|
80
|
+
__reExport(index_exports, require("./collectHeadings"), module.exports);
|
|
81
|
+
__reExport(index_exports, require("./agentContext"), module.exports);
|
|
82
|
+
__reExport(index_exports, require("./fence"), module.exports);
|
|
74
83
|
// Annotate the CommonJS export names for ESM import in node:
|
|
75
84
|
0 && (module.exports = {
|
|
76
85
|
SafeInclude,
|
|
@@ -83,6 +92,7 @@ __reExport(index_exports, require("./safeContent"), module.exports);
|
|
|
83
92
|
...require("./sourceCache"),
|
|
84
93
|
...require("./components/MDXComponents"),
|
|
85
94
|
...require("./linkSpace"),
|
|
95
|
+
...require("./bundle"),
|
|
86
96
|
...require("./corpus"),
|
|
87
97
|
...require("./components/MountImage"),
|
|
88
98
|
...require("./components/Routes"),
|
|
@@ -105,6 +115,7 @@ __reExport(index_exports, require("./safeContent"), module.exports);
|
|
|
105
115
|
...require("./netFetch"),
|
|
106
116
|
...require("./feed"),
|
|
107
117
|
...require("./secrets"),
|
|
118
|
+
...require("./recents"),
|
|
108
119
|
...require("./llm"),
|
|
109
120
|
...require("./diagnostics"),
|
|
110
121
|
...require("./vcs"),
|
|
@@ -120,6 +131,13 @@ __reExport(index_exports, require("./safeContent"), module.exports);
|
|
|
120
131
|
...require("./protocolStream"),
|
|
121
132
|
...require("./protocolDeadline"),
|
|
122
133
|
...require("./sandboxTypes"),
|
|
123
|
-
...require("./safeContent")
|
|
134
|
+
...require("./safeContent"),
|
|
135
|
+
...require("./agentLoop"),
|
|
136
|
+
...require("./agentSteering"),
|
|
137
|
+
...require("./agentChatClient"),
|
|
138
|
+
...require("./metadataQueryTool"),
|
|
139
|
+
...require("./collectHeadings"),
|
|
140
|
+
...require("./agentContext"),
|
|
141
|
+
...require("./fence")
|
|
124
142
|
});
|
|
125
143
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './MDXProvider';\nexport * from './routing';\nexport * from './boot';\nexport * from './components/Include';\n// Only the component is public. `stripFrontmatter`/`appMountRelative` are module-level\n// exports so they can be unit-tested directly, NOT public API — the SDK's surface is\n// backwards-compatible forever, so an internal helper exported for a test's convenience is a\n// permanent commitment made for the wrong reason.\nexport { SafeInclude } from './components/SafeInclude';\nexport * from './sourceCache';\nexport * from './components/MDXComponents';\nexport * from './linkSpace';\nexport * from './corpus';\nexport * from './components/MountImage';\nexport * from './components/Routes';\nexport * from './hooks';\n// R3-276: the supported way for a viewer app to provide its own metadata store,\n// replacing a wholesale re-provision of `TinkerableContext` in app code.\nexport * from './metadataSource';\n// The deprecated injected-bundler adapters, re-exported so their deprecation notices\n// are visible in the published docs (R3-278; the window only narrows).\nexport { getInjectedMetadataEmitter, getInjectedMetadataSnapshot } from './injectedBundler';\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './chromeState';\nexport * from './hostAttention';\nexport * from './region';\nexport * from './mounts';\nexport * from './analytics';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './feed';\nexport * from './secrets';\nexport * from './llm';\nexport * from './diagnostics';\nexport * from './vcs';\nexport * from './onFsChange';\nexport * from './fs';\nexport * from './debug';\nexport * from './tasks';\nexport * from './launch';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './loading';\nexport * from './protocolStream';\nexport * from './protocolDeadline';\nexport * from './sandboxTypes';\nexport * from './safeContent';\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAc,0BAAd;AACA,0BAAc,sBADd;AAEA,0BAAc,mBAFd;AAGA,0BAAc,iCAHd;AAQA,yBAA4B;AAC5B,0BAAc,0BATd;AAUA,0BAAc,uCAVd;AAWA,0BAAc,wBAXd;AAYA,0BAAc,qBAZd;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './MDXProvider';\nexport * from './routing';\nexport * from './boot';\nexport * from './components/Include';\n// Only the component is public. `stripFrontmatter`/`appMountRelative` are module-level\n// exports so they can be unit-tested directly, NOT public API — the SDK's surface is\n// backwards-compatible forever, so an internal helper exported for a test's convenience is a\n// permanent commitment made for the wrong reason.\nexport { SafeInclude } from './components/SafeInclude';\nexport * from './sourceCache';\nexport * from './components/MDXComponents';\nexport * from './linkSpace';\nexport * from './bundle';\n// Deprecated `Corpus*` spellings of the above (R3-482); see `src/corpus.ts`.\nexport * from './corpus';\nexport * from './components/MountImage';\nexport * from './components/Routes';\nexport * from './hooks';\n// R3-276: the supported way for a viewer app to provide its own metadata store,\n// replacing a wholesale re-provision of `TinkerableContext` in app code.\nexport * from './metadataSource';\n// The deprecated injected-bundler adapters, re-exported so their deprecation notices\n// are visible in the published docs (R3-278; the window only narrows).\nexport { getInjectedMetadataEmitter, getInjectedMetadataSnapshot } from './injectedBundler';\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './chromeState';\nexport * from './hostAttention';\nexport * from './region';\nexport * from './mounts';\nexport * from './analytics';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './feed';\nexport * from './secrets';\nexport * from './recents'; // R3-485: the gated recent-projects read (page.home)\nexport * from './llm';\nexport * from './diagnostics';\nexport * from './vcs';\nexport * from './onFsChange';\nexport * from './fs';\nexport * from './debug';\nexport * from './tasks';\nexport * from './launch';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './loading';\nexport * from './protocolStream';\nexport * from './protocolDeadline';\nexport * from './sandboxTypes';\nexport * from './safeContent';\n// R3-489 (GROVE_AGENT_SPEC): the embedded-agent seam every app shares — the tool-use\n// loop ported from agent-demo (`runAgent` over the host chat slot), the MDX metadata\n// query tool, the headings index collector, the deixis context block, and the fence\n// for corpus-derived bytes entering a loop.\nexport * from './agentLoop';\nexport * from './agentSteering';\nexport * from './agentChatClient';\nexport * from './metadataQueryTool';\nexport * from './collectHeadings';\nexport * from './agentContext';\nexport * from './fence';\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAc,0BAAd;AACA,0BAAc,sBADd;AAEA,0BAAc,mBAFd;AAGA,0BAAc,iCAHd;AAQA,yBAA4B;AAC5B,0BAAc,0BATd;AAUA,0BAAc,uCAVd;AAWA,0BAAc,wBAXd;AAYA,0BAAc,qBAZd;AAcA,0BAAc,qBAdd;AAeA,0BAAc,oCAfd;AAgBA,0BAAc,gCAhBd;AAiBA,0BAAc,oBAjBd;AAoBA,0BAAc,6BApBd;AAuBA,6BAAwE;AACxE,0BAAc,mBAxBd;AAyBA,0BAAc,oBAzBd;AA0BA,0BAAc,4BA1Bd;AA2BA,0BAAc,qBA3Bd;AA4BA,0BAAc,yBA5Bd;AA6BA,0BAAc,0BA7Bd;AA8BA,0BAAc,4BA9Bd;AA+BA,0BAAc,qBA/Bd;AAgCA,0BAAc,qBAhCd;AAiCA,0BAAc,wBAjCd;AAkCA,0BAAc,yBAlCd;AAmCA,0BAAc,sBAnCd;AAoCA,0BAAc,kBApCd;AAqCA,0BAAc,kBArCd;AAsCA,0BAAc,uBAtCd;AAuCA,0BAAc,mBAvCd;AAwCA,0BAAc,sBAxCd;AAyCA,0BAAc,sBAzCd;AA0CA,0BAAc,kBA1Cd;AA2CA,0BAAc,0BA3Cd;AA4CA,0BAAc,kBA5Cd;AA6CA,0BAAc,yBA7Cd;AA8CA,0BAAc,iBA9Cd;AA+CA,0BAAc,oBA/Cd;AAgDA,0BAAc,oBAhDd;AAiDA,0BAAc,qBAjDd;AAkDA,0BAAc,sBAlDd;AAmDA,0BAAc,wBAnDd;AAoDA,0BAAc,oBApDd;AAqDA,0BAAc,sBArDd;AAsDA,0BAAc,6BAtDd;AAuDA,0BAAc,+BAvDd;AAwDA,0BAAc,2BAxDd;AAyDA,0BAAc,0BAzDd;AA8DA,0BAAc,wBA9Dd;AA+DA,0BAAc,4BA/Dd;AAgEA,0BAAc,8BAhEd;AAiEA,0BAAc,gCAjEd;AAkEA,0BAAc,8BAlEd;AAmEA,0BAAc,2BAnEd;AAoEA,0BAAc,oBApEd;","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -6,7 +6,7 @@ export { SafeInclude } from './components/SafeInclude.cjs';
|
|
|
6
6
|
export { SourceCache, SourceReader, createSourceCache } from './sourceCache.cjs';
|
|
7
7
|
export { DEFAULT_MDX_COMPONENTS } from './components/MDXComponents.cjs';
|
|
8
8
|
export { LinkSpaceContext } from './linkSpace.cjs';
|
|
9
|
-
export { CorpusContext, CorpusEntry, CorpusScope, fromCorpusPath, toCorpusPath, useCorpus, useCorpusEntries, useCorpusEntry, useCurrentEntry } from './
|
|
9
|
+
export { BundleContext, BundleEntry, BundleScope, BundleContext as CorpusContext, BundleEntry as CorpusEntry, BundleScope as CorpusScope, fromBundlePath, fromBundlePath as fromCorpusPath, toBundlePath, toBundlePath as toCorpusPath, useBundle, useBundleEntries, useBundleEntry, useBundle as useCorpus, useBundleEntries as useCorpusEntries, useBundleEntry as useCorpusEntry, useCurrentEntry } from './bundle.cjs';
|
|
10
10
|
export { MountImage, MountImageProps } from './components/MountImage.cjs';
|
|
11
11
|
export { Route, RouteProps, Routes } from './components/Routes.cjs';
|
|
12
12
|
export { ObjectUrlState, useAllMetadata, useFileMetadata, useMetadataQuery, useObjectUrl } from './hooks.cjs';
|
|
@@ -29,13 +29,14 @@ export { DraggableItem, DroppedItem, ItemDragError, cancelItemDrag, onItemDrop,
|
|
|
29
29
|
export { HostFetchInit, HostFetchResponse, HostFetchStreamEvent, HostFetchStreamResult, hostFetch, hostFetchStream } from './netFetch.cjs';
|
|
30
30
|
export { FeedFetchResponse, FeedParams, feedFetch } from './feed.cjs';
|
|
31
31
|
export { SecretError, SecretGrant, SecretHints, SecretQuery, SecretType, SecretView, getSecrets, onSecretsChange, requestAddSecret, requestSecret, revokeSecret, useSecrets } from './secrets.cjs';
|
|
32
|
+
export { RecentProject, clearRecentProjects, listRecentProjects } from './recents.cjs';
|
|
32
33
|
export { ChatDelta, ChatFeatures, ChatMessage, ChatProviderInfo, ChatProviderState, ChatRequest, ChatResult, ChatRole, ChatStopReason, ContentPart, ToolDef, chat, describeChat, describeChatState, normalizeProviderInfo, onChatProviderChange, onChatProviderStateChange, useChatProvider, useChatProviderState } from './llm.cjs';
|
|
33
34
|
export { BuildError, ConsoleEntry, ConsoleLevel, Diagnostics, DiagnosticsProvenance, getDiagnostics, onDiagnosticsChange, useDiagnostics } from './diagnostics.cjs';
|
|
34
35
|
export { VcsActionError, VcsBranch, VcsChange, VcsPR, VcsState, getVcsState, onVcsStateChange, refreshDiff, refreshPRs, resetWorkingTree, useVcsState } from './vcs.cjs';
|
|
35
|
-
export { FsChange, getFsChange, onFsChange, useFsChange } from './onFsChange.cjs';
|
|
36
|
+
export { FsChange, MountChange, getFsChange, onFsChange, useFsChange } from './onFsChange.cjs';
|
|
36
37
|
export { DirEntry, FileStat, FsError, MountFs, SandboxFsPort, fsAvailable, mimeTypeFor, openAppFs, openFs, sandboxFs } from './fs.cjs';
|
|
37
38
|
export { DebugLevel, debug, isDebugEnabled, log, useDebugEnabled } from './debug.cjs';
|
|
38
|
-
export { DirCap, FileCap, TaskInput, cancelTask, capDir, capFile, completeTask, getTaskInput, invokeTask, useTaskInput } from './tasks.cjs';
|
|
39
|
+
export { CaptureOptions, CaptureResult, DirCap, FileCap, TaskInput, cancelTask, capDir, capFile, captureAudio, capturePhoto, completeTask, getTaskInput, invokeTask, useTaskInput } from './tasks.cjs';
|
|
39
40
|
export { LaunchErrorCode, LaunchHandle, LaunchOptions, LaunchRegion, LaunchStatus, LaunchTarget, launch } from './launch.cjs';
|
|
40
41
|
export { SDK_PROTOCOL_VERSION, SdkHandshake, announceHandshake, sdkHandshake } from './runtime.cjs';
|
|
41
42
|
export { ForwardedMarker, IR_MARKERS, IrMarkerName, isAllowedMarkerName, isIrMarkerName, resolveInteractive, validateMarker } from './irMarkers.cjs';
|
|
@@ -45,6 +46,13 @@ export { StreamError, StreamFrame, StreamTransport, consumeStream, protocolStrea
|
|
|
45
46
|
export { ATTENDED_FIRST_FRAME_MS, ATTENDED_TIMEOUT_MS, Attendance, BoundedCallOptions, CallBounds, DeadlineBound, NETWORK_TIMEOUT_MS, PENDING_NOTICE_MS, PendingAttention, PendingState, ProtocolCancelledError, ProtocolTimeoutError, STREAM_IDLE_TIMEOUT_MS, SuspendableDeadline, UNATTENDED_TIMEOUT_MS, attendanceOf, attendanceReason, boundsFor, createSuspendableDeadline, firstFrameBoundsFor, firstFrameTimeoutFor, timeoutFor } from './protocolDeadline.cjs';
|
|
46
47
|
export { EvaluationContext, FileQueryResult, FilesMetadata, Metadata, MetadataQueryEntry, MetadataQueryFunction, MetadataQueryRecord, MetadataQueryResult, ModuleExports } from './sandboxTypes.cjs';
|
|
47
48
|
export * from '@immediately-run/safe-content';
|
|
49
|
+
export { AgentEvents, AgentMessage, AgentRole, AgentTool, COMPACTION_MARKER, ContentBlock, ImageBlock, ModelClient, ModelResponse, NUDGE_TEXT, ReasoningBlock, RunAgentOptions, StallReason, TextBlock, TokenUsage, ToolExecutor, ToolOutcome, ToolResultBlock, ToolUseBlock, compactTranscript, detectStall, estimateTokens, isContextOverflow, runAgent, shouldCompact } from './agentLoop.cjs';
|
|
50
|
+
export { INTERRUPTED_TURN_TEXT, STEER_INTERRUPT_MARKER, STEER_MARKER, SteerController, SteerMessage, SteerMode, SteerSource, anySignal, parseSteer, steerWireText } from './agentSteering.cjs';
|
|
51
|
+
export { createChatModelClient } from './agentChatClient.cjs';
|
|
52
|
+
export { HeadingSummary, METADATA_HEADINGS_KEY, METADATA_QUERY_TOOL_DESCRIPTOR, METADATA_QUERY_TOOL_NAME, MetadataQueryError, MetadataQueryInput, MetadataQueryRow, createMetadataQueryTool, executeMetadataQuery, globToRegExp, runMetadataQuery } from './metadataQueryTool.cjs';
|
|
53
|
+
export { collectHeadings } from './collectHeadings.cjs';
|
|
54
|
+
export { AgentContextAppFields, AgentContextBlock, renderAgentContext, useAgentContext } from './agentContext.cjs';
|
|
55
|
+
export { fenceUntrusted } from './fence.cjs';
|
|
48
56
|
export { Admonition, AdmonitionType } from './components/Admonition.cjs';
|
|
49
57
|
export { FS_PREFIX, LinkSpace, ResolvedLinkTarget, normalizeAbsolute, resolveLinkTarget } from '@immediately-run/mdx-plugins';
|
|
50
58
|
export { GrantRecord, Member, ResolvedUser, Role, SpaceInfo, getSpaceMembers, inviteToSpace, listAllSpaces, listGrants, listSpaces, lookupUser, revokeGrant, setSpaceRole, unshareSpace } from './generated/spaces.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { SafeInclude } from './components/SafeInclude.js';
|
|
|
6
6
|
export { SourceCache, SourceReader, createSourceCache } from './sourceCache.js';
|
|
7
7
|
export { DEFAULT_MDX_COMPONENTS } from './components/MDXComponents.js';
|
|
8
8
|
export { LinkSpaceContext } from './linkSpace.js';
|
|
9
|
-
export { CorpusContext, CorpusEntry, CorpusScope, fromCorpusPath, toCorpusPath, useCorpus, useCorpusEntries, useCorpusEntry, useCurrentEntry } from './
|
|
9
|
+
export { BundleContext, BundleEntry, BundleScope, BundleContext as CorpusContext, BundleEntry as CorpusEntry, BundleScope as CorpusScope, fromBundlePath, fromBundlePath as fromCorpusPath, toBundlePath, toBundlePath as toCorpusPath, useBundle, useBundleEntries, useBundleEntry, useBundle as useCorpus, useBundleEntries as useCorpusEntries, useBundleEntry as useCorpusEntry, useCurrentEntry } from './bundle.js';
|
|
10
10
|
export { MountImage, MountImageProps } from './components/MountImage.js';
|
|
11
11
|
export { Route, RouteProps, Routes } from './components/Routes.js';
|
|
12
12
|
export { ObjectUrlState, useAllMetadata, useFileMetadata, useMetadataQuery, useObjectUrl } from './hooks.js';
|
|
@@ -29,13 +29,14 @@ export { DraggableItem, DroppedItem, ItemDragError, cancelItemDrag, onItemDrop,
|
|
|
29
29
|
export { HostFetchInit, HostFetchResponse, HostFetchStreamEvent, HostFetchStreamResult, hostFetch, hostFetchStream } from './netFetch.js';
|
|
30
30
|
export { FeedFetchResponse, FeedParams, feedFetch } from './feed.js';
|
|
31
31
|
export { SecretError, SecretGrant, SecretHints, SecretQuery, SecretType, SecretView, getSecrets, onSecretsChange, requestAddSecret, requestSecret, revokeSecret, useSecrets } from './secrets.js';
|
|
32
|
+
export { RecentProject, clearRecentProjects, listRecentProjects } from './recents.js';
|
|
32
33
|
export { ChatDelta, ChatFeatures, ChatMessage, ChatProviderInfo, ChatProviderState, ChatRequest, ChatResult, ChatRole, ChatStopReason, ContentPart, ToolDef, chat, describeChat, describeChatState, normalizeProviderInfo, onChatProviderChange, onChatProviderStateChange, useChatProvider, useChatProviderState } from './llm.js';
|
|
33
34
|
export { BuildError, ConsoleEntry, ConsoleLevel, Diagnostics, DiagnosticsProvenance, getDiagnostics, onDiagnosticsChange, useDiagnostics } from './diagnostics.js';
|
|
34
35
|
export { VcsActionError, VcsBranch, VcsChange, VcsPR, VcsState, getVcsState, onVcsStateChange, refreshDiff, refreshPRs, resetWorkingTree, useVcsState } from './vcs.js';
|
|
35
|
-
export { FsChange, getFsChange, onFsChange, useFsChange } from './onFsChange.js';
|
|
36
|
+
export { FsChange, MountChange, getFsChange, onFsChange, useFsChange } from './onFsChange.js';
|
|
36
37
|
export { DirEntry, FileStat, FsError, MountFs, SandboxFsPort, fsAvailable, mimeTypeFor, openAppFs, openFs, sandboxFs } from './fs.js';
|
|
37
38
|
export { DebugLevel, debug, isDebugEnabled, log, useDebugEnabled } from './debug.js';
|
|
38
|
-
export { DirCap, FileCap, TaskInput, cancelTask, capDir, capFile, completeTask, getTaskInput, invokeTask, useTaskInput } from './tasks.js';
|
|
39
|
+
export { CaptureOptions, CaptureResult, DirCap, FileCap, TaskInput, cancelTask, capDir, capFile, captureAudio, capturePhoto, completeTask, getTaskInput, invokeTask, useTaskInput } from './tasks.js';
|
|
39
40
|
export { LaunchErrorCode, LaunchHandle, LaunchOptions, LaunchRegion, LaunchStatus, LaunchTarget, launch } from './launch.js';
|
|
40
41
|
export { SDK_PROTOCOL_VERSION, SdkHandshake, announceHandshake, sdkHandshake } from './runtime.js';
|
|
41
42
|
export { ForwardedMarker, IR_MARKERS, IrMarkerName, isAllowedMarkerName, isIrMarkerName, resolveInteractive, validateMarker } from './irMarkers.js';
|
|
@@ -45,6 +46,13 @@ export { StreamError, StreamFrame, StreamTransport, consumeStream, protocolStrea
|
|
|
45
46
|
export { ATTENDED_FIRST_FRAME_MS, ATTENDED_TIMEOUT_MS, Attendance, BoundedCallOptions, CallBounds, DeadlineBound, NETWORK_TIMEOUT_MS, PENDING_NOTICE_MS, PendingAttention, PendingState, ProtocolCancelledError, ProtocolTimeoutError, STREAM_IDLE_TIMEOUT_MS, SuspendableDeadline, UNATTENDED_TIMEOUT_MS, attendanceOf, attendanceReason, boundsFor, createSuspendableDeadline, firstFrameBoundsFor, firstFrameTimeoutFor, timeoutFor } from './protocolDeadline.js';
|
|
46
47
|
export { EvaluationContext, FileQueryResult, FilesMetadata, Metadata, MetadataQueryEntry, MetadataQueryFunction, MetadataQueryRecord, MetadataQueryResult, ModuleExports } from './sandboxTypes.js';
|
|
47
48
|
export * from '@immediately-run/safe-content';
|
|
49
|
+
export { AgentEvents, AgentMessage, AgentRole, AgentTool, COMPACTION_MARKER, ContentBlock, ImageBlock, ModelClient, ModelResponse, NUDGE_TEXT, ReasoningBlock, RunAgentOptions, StallReason, TextBlock, TokenUsage, ToolExecutor, ToolOutcome, ToolResultBlock, ToolUseBlock, compactTranscript, detectStall, estimateTokens, isContextOverflow, runAgent, shouldCompact } from './agentLoop.js';
|
|
50
|
+
export { INTERRUPTED_TURN_TEXT, STEER_INTERRUPT_MARKER, STEER_MARKER, SteerController, SteerMessage, SteerMode, SteerSource, anySignal, parseSteer, steerWireText } from './agentSteering.js';
|
|
51
|
+
export { createChatModelClient } from './agentChatClient.js';
|
|
52
|
+
export { HeadingSummary, METADATA_HEADINGS_KEY, METADATA_QUERY_TOOL_DESCRIPTOR, METADATA_QUERY_TOOL_NAME, MetadataQueryError, MetadataQueryInput, MetadataQueryRow, createMetadataQueryTool, executeMetadataQuery, globToRegExp, runMetadataQuery } from './metadataQueryTool.js';
|
|
53
|
+
export { collectHeadings } from './collectHeadings.js';
|
|
54
|
+
export { AgentContextAppFields, AgentContextBlock, renderAgentContext, useAgentContext } from './agentContext.js';
|
|
55
|
+
export { fenceUntrusted } from './fence.js';
|
|
48
56
|
export { Admonition, AdmonitionType } from './components/Admonition.js';
|
|
49
57
|
export { FS_PREFIX, LinkSpace, ResolvedLinkTarget, normalizeAbsolute, resolveLinkTarget } from '@immediately-run/mdx-plugins';
|
|
50
58
|
export { GrantRecord, Member, ResolvedUser, Role, SpaceInfo, getSpaceMembers, inviteToSpace, listAllSpaces, listGrants, listSpaces, lookupUser, revokeGrant, setSpaceRole, unshareSpace } from './generated/spaces.js';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { SafeInclude } from "./components/SafeInclude";
|
|
|
7
7
|
export * from "./sourceCache";
|
|
8
8
|
export * from "./components/MDXComponents";
|
|
9
9
|
export * from "./linkSpace";
|
|
10
|
+
export * from "./bundle";
|
|
10
11
|
export * from "./corpus";
|
|
11
12
|
export * from "./components/MountImage";
|
|
12
13
|
export * from "./components/Routes";
|
|
@@ -30,6 +31,7 @@ export * from "./dnd";
|
|
|
30
31
|
export * from "./netFetch";
|
|
31
32
|
export * from "./feed";
|
|
32
33
|
export * from "./secrets";
|
|
34
|
+
export * from "./recents";
|
|
33
35
|
export * from "./llm";
|
|
34
36
|
export * from "./diagnostics";
|
|
35
37
|
export * from "./vcs";
|
|
@@ -46,6 +48,13 @@ export * from "./protocolStream";
|
|
|
46
48
|
export * from "./protocolDeadline";
|
|
47
49
|
export * from "./sandboxTypes";
|
|
48
50
|
export * from "./safeContent";
|
|
51
|
+
export * from "./agentLoop";
|
|
52
|
+
export * from "./agentSteering";
|
|
53
|
+
export * from "./agentChatClient";
|
|
54
|
+
export * from "./metadataQueryTool";
|
|
55
|
+
export * from "./collectHeadings";
|
|
56
|
+
export * from "./agentContext";
|
|
57
|
+
export * from "./fence";
|
|
49
58
|
export {
|
|
50
59
|
SafeInclude,
|
|
51
60
|
getInjectedMetadataEmitter,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './MDXProvider';\nexport * from './routing';\nexport * from './boot';\nexport * from './components/Include';\n// Only the component is public. `stripFrontmatter`/`appMountRelative` are module-level\n// exports so they can be unit-tested directly, NOT public API — the SDK's surface is\n// backwards-compatible forever, so an internal helper exported for a test's convenience is a\n// permanent commitment made for the wrong reason.\nexport { SafeInclude } from './components/SafeInclude';\nexport * from './sourceCache';\nexport * from './components/MDXComponents';\nexport * from './linkSpace';\nexport * from './corpus';\nexport * from './components/MountImage';\nexport * from './components/Routes';\nexport * from './hooks';\n// R3-276: the supported way for a viewer app to provide its own metadata store,\n// replacing a wholesale re-provision of `TinkerableContext` in app code.\nexport * from './metadataSource';\n// The deprecated injected-bundler adapters, re-exported so their deprecation notices\n// are visible in the published docs (R3-278; the window only narrows).\nexport { getInjectedMetadataEmitter, getInjectedMetadataSnapshot } from './injectedBundler';\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './chromeState';\nexport * from './hostAttention';\nexport * from './region';\nexport * from './mounts';\nexport * from './analytics';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './feed';\nexport * from './secrets';\nexport * from './llm';\nexport * from './diagnostics';\nexport * from './vcs';\nexport * from './onFsChange';\nexport * from './fs';\nexport * from './debug';\nexport * from './tasks';\nexport * from './launch';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './loading';\nexport * from './protocolStream';\nexport * from './protocolDeadline';\nexport * from './sandboxTypes';\nexport * from './safeContent';\n"],"mappings":";AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAKd,SAAS,mBAAmB;AAC5B,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAGd,SAAS,4BAA4B,mCAAmC;AACxE,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './MDXProvider';\nexport * from './routing';\nexport * from './boot';\nexport * from './components/Include';\n// Only the component is public. `stripFrontmatter`/`appMountRelative` are module-level\n// exports so they can be unit-tested directly, NOT public API — the SDK's surface is\n// backwards-compatible forever, so an internal helper exported for a test's convenience is a\n// permanent commitment made for the wrong reason.\nexport { SafeInclude } from './components/SafeInclude';\nexport * from './sourceCache';\nexport * from './components/MDXComponents';\nexport * from './linkSpace';\nexport * from './bundle';\n// Deprecated `Corpus*` spellings of the above (R3-482); see `src/corpus.ts`.\nexport * from './corpus';\nexport * from './components/MountImage';\nexport * from './components/Routes';\nexport * from './hooks';\n// R3-276: the supported way for a viewer app to provide its own metadata store,\n// replacing a wholesale re-provision of `TinkerableContext` in app code.\nexport * from './metadataSource';\n// The deprecated injected-bundler adapters, re-exported so their deprecation notices\n// are visible in the published docs (R3-278; the window only narrows).\nexport { getInjectedMetadataEmitter, getInjectedMetadataSnapshot } from './injectedBundler';\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './chromeState';\nexport * from './hostAttention';\nexport * from './region';\nexport * from './mounts';\nexport * from './analytics';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './feed';\nexport * from './secrets';\nexport * from './recents'; // R3-485: the gated recent-projects read (page.home)\nexport * from './llm';\nexport * from './diagnostics';\nexport * from './vcs';\nexport * from './onFsChange';\nexport * from './fs';\nexport * from './debug';\nexport * from './tasks';\nexport * from './launch';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './loading';\nexport * from './protocolStream';\nexport * from './protocolDeadline';\nexport * from './sandboxTypes';\nexport * from './safeContent';\n// R3-489 (GROVE_AGENT_SPEC): the embedded-agent seam every app shares — the tool-use\n// loop ported from agent-demo (`runAgent` over the host chat slot), the MDX metadata\n// query tool, the headings index collector, the deixis context block, and the fence\n// for corpus-derived bytes entering a loop.\nexport * from './agentLoop';\nexport * from './agentSteering';\nexport * from './agentChatClient';\nexport * from './metadataQueryTool';\nexport * from './collectHeadings';\nexport * from './agentContext';\nexport * from './fence';\n"],"mappings":";AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAKd,SAAS,mBAAmB;AAC5B,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAGd,SAAS,4BAA4B,mCAAmC;AACxE,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAKd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
|
package/dist/launch.cjs
CHANGED
|
@@ -33,10 +33,31 @@ var import_protocol = require("./generated/protocol");
|
|
|
33
33
|
var import_protocolSchemes = require("./protocolSchemes");
|
|
34
34
|
var _status, _dismissListeners, _ended;
|
|
35
35
|
const liveHandles = /* @__PURE__ */ new Map();
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const pendingEnded = /* @__PURE__ */ new Map();
|
|
37
|
+
const MAX_PENDING_ENDED = 16;
|
|
38
|
+
let createsInFlight = 0;
|
|
39
|
+
let endedListenerRegistered = false;
|
|
40
|
+
const ensureEndedListener = () => {
|
|
41
|
+
if (endedListenerRegistered) return;
|
|
42
|
+
try {
|
|
43
|
+
(0, import_sandboxUtils.addListener)(import_protocol.LAUNCH_ENDED, (m) => {
|
|
44
|
+
const h = liveHandles.get(m.launchId);
|
|
45
|
+
if (h) {
|
|
46
|
+
h._end(m.status);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (createsInFlight === 0) return;
|
|
50
|
+
if (pendingEnded.size >= MAX_PENDING_ENDED) {
|
|
51
|
+
const oldest = pendingEnded.keys().next();
|
|
52
|
+
if (!oldest.done) pendingEnded.delete(oldest.value);
|
|
53
|
+
}
|
|
54
|
+
pendingEnded.set(m.launchId, m.status);
|
|
55
|
+
});
|
|
56
|
+
} catch {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
endedListenerRegistered = true;
|
|
60
|
+
};
|
|
40
61
|
class LaunchHandleImpl {
|
|
41
62
|
constructor(launchId) {
|
|
42
63
|
this.launchId = launchId;
|
|
@@ -44,6 +65,11 @@ class LaunchHandleImpl {
|
|
|
44
65
|
__privateAdd(this, _dismissListeners, /* @__PURE__ */ new Set());
|
|
45
66
|
__privateAdd(this, _ended, false);
|
|
46
67
|
liveHandles.set(launchId, this);
|
|
68
|
+
const early = pendingEnded.get(launchId);
|
|
69
|
+
if (early !== void 0) {
|
|
70
|
+
pendingEnded.delete(launchId);
|
|
71
|
+
this._end(early);
|
|
72
|
+
}
|
|
47
73
|
}
|
|
48
74
|
get status() {
|
|
49
75
|
return __privateGet(this, _status);
|
|
@@ -83,12 +109,25 @@ _status = new WeakMap();
|
|
|
83
109
|
_dismissListeners = new WeakMap();
|
|
84
110
|
_ended = new WeakMap();
|
|
85
111
|
const launch = async (target, opts) => {
|
|
86
|
-
|
|
112
|
+
ensureEndedListener();
|
|
113
|
+
createsInFlight += 1;
|
|
114
|
+
let res;
|
|
115
|
+
try {
|
|
116
|
+
res = await (0, import_sandboxUtils.protocolRequest)(import_protocolSchemes.SCHEMES[import_protocol.PROTOCOL_LAUNCH], "create", [{ target, opts }]);
|
|
117
|
+
} finally {
|
|
118
|
+
createsInFlight -= 1;
|
|
119
|
+
}
|
|
120
|
+
const sweep = () => {
|
|
121
|
+
if (createsInFlight === 0) pendingEnded.clear();
|
|
122
|
+
};
|
|
87
123
|
if (!res || res.ok !== true || !res.data?.launchId) {
|
|
124
|
+
sweep();
|
|
88
125
|
const code = res && res.ok === false ? res.code ?? "unknown" : "unknown";
|
|
89
126
|
return { ok: false, code };
|
|
90
127
|
}
|
|
91
|
-
|
|
128
|
+
const handle = new LaunchHandleImpl(res.data.launchId);
|
|
129
|
+
sweep();
|
|
130
|
+
return handle;
|
|
92
131
|
};
|
|
93
132
|
// Annotate the CommonJS export names for ESM import in node:
|
|
94
133
|
0 && (module.exports = {
|
package/dist/launch.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle.\naddListener(LAUNCH_ENDED, (m: LaunchEndedMessage) => {\n const h = liveHandles.get(m.launchId);\n if (h) h._end(m.status);\n});\n\nclass LaunchHandleImpl implements LaunchHandle {\n #status: LaunchStatus = 'running';\n #dismissListeners = new Set<() => void>();\n #ended = false;\n\n constructor(readonly launchId: string) {\n liveHandles.set(launchId, this);\n }\n\n get status(): LaunchStatus {\n return this.#status;\n }\n\n dismiss(): void {\n if (this.#ended) return;\n // Fire-and-forget: the host owns teardown and answers with `launch-ended`,\n // which drives `_end` (so status/onDismiss are host-authoritative, never\n // optimistically local — a dismiss the host refuses would otherwise desync).\n sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });\n }\n\n onDismiss(cb: () => void): () => void {\n if (this.#ended) {\n // Already ended: fire on the next tick so the contract (\"returns an\n // unsubscribe\") holds and the callback never runs synchronously mid-register.\n queueMicrotask(cb);\n return () => {};\n }\n this.#dismissListeners.add(cb);\n return () => {\n this.#dismissListeners.delete(cb);\n };\n }\n\n /** Host-driven terminal transition — the only writer of `status`. Idempotent. */\n _end(status: Exclude<LaunchStatus, 'running'>): void {\n if (this.#ended) return;\n this.#ended = true;\n this.#status = status;\n liveHandles.delete(this.launchId);\n const listeners = [...this.#dismissListeners];\n this.#dismissListeners.clear();\n for (const l of listeners) {\n try {\n l();\n } catch {\n /* a launcher's own callback must never wedge teardown */\n }\n }\n }\n}\n\n/**\n * Launch a bound program to RUN in a region (§2). Non-blocking: resolves once the\n * frame is created and bound, with a {@link LaunchHandle} — or a typed\n * `{ ok:false, code }` on refusal (§8), NEVER a throw for an ordinary refusal (so\n * a launcher branches on `code` without a try/catch). The launched app runs under\n * its OWN grants; the launcher's authority does not flow to it (R-SAL-4).\n *\n * const h = await launch({ task: 'open-project' }, {\n * region: 'stage',\n * input: { dir: capDir({ mountId: 'space:abc', relPath: 'proj' }, { mode: 'ro' }) },\n * });\n * if ('ok' in h && h.ok === false) { ...handle h.code... }\n * else { h.onDismiss(() => ...); }\n */\nexport const launch = async (\n target: LaunchTarget,\n opts: LaunchOptions,\n): Promise<LaunchHandle | { ok: false; code: LaunchErrorCode }> => {\n // The host wraps a successful handler return as `{ ok:true, data }` (the same\n // Recipe-B framing `invokeTask` uses); a refusal is `{ ok:false, code }`.\n const res = (await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], 'create', [{ target, opts }])) as\n | { ok: true; data: { launchId: string } }\n | { ok: false; code?: LaunchErrorCode }\n | undefined;\n if (!res || res.ok !== true || !res.data?.launchId) {\n const code = res && res.ok === false ? res.code ?? 'unknown' : 'unknown';\n return { ok: false, code };\n }\n return new LaunchHandleImpl(res.data.launchId);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,0BAA0D;AAC1D,sBAA8D;AAC9D,6BAAwB;AAbxB;AA0FA,MAAM,cAAc,oBAAI,IAA8B;AAAA,IAKtD,iCAAY,8BAAc,CAAC,MAA0B;AACnD,QAAM,IAAI,YAAY,IAAI,EAAE,QAAQ;AACpC,MAAI,EAAG,GAAE,KAAK,EAAE,MAAM;AACxB,CAAC;AAED,MAAM,iBAAyC;AAAA,EAK7C,YAAqB,UAAkB;AAAlB;AAJrB,gCAAwB;AACxB,0CAAoB,oBAAI,IAAgB;AACxC,+BAAS;AAGP,gBAAY,IAAI,UAAU,IAAI;AAAA,EAChC;AAAA,EAEA,IAAI,SAAuB;AACzB,WAAO,mBAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,QAAI,mBAAK,QAAQ;AAIjB,yCAAY,gCAAgB,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACzD;AAAA,EAEA,UAAU,IAA4B;AACpC,QAAI,mBAAK,SAAQ;AAGf,qBAAe,EAAE;AACjB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AACA,uBAAK,mBAAkB,IAAI,EAAE;AAC7B,WAAO,MAAM;AACX,yBAAK,mBAAkB,OAAO,EAAE;AAAA,IAClC;AAAA,EACF;AAAA;AAAA,EAGA,KAAK,QAAgD;AACnD,QAAI,mBAAK,QAAQ;AACjB,uBAAK,QAAS;AACd,uBAAK,SAAU;AACf,gBAAY,OAAO,KAAK,QAAQ;AAChC,UAAM,YAAY,CAAC,GAAG,mBAAK,kBAAiB;AAC5C,uBAAK,mBAAkB,MAAM;AAC7B,eAAW,KAAK,WAAW;AACzB,UAAI;AACF,UAAE;AAAA,MACJ,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAjDE;AACA;AACA;AA+DK,MAAM,SAAS,OACpB,QACA,SACiE;AAGjE,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,+BAAe,GAAG,UAAU,CAAC,EAAE,QAAQ,KAAK,CAAC,CAAC;AAIzF,MAAI,CAAC,OAAO,IAAI,OAAO,QAAQ,CAAC,IAAI,MAAM,UAAU;AAClD,UAAM,OAAO,OAAO,IAAI,OAAO,QAAQ,IAAI,QAAQ,YAAY;AAC/D,WAAO,EAAE,IAAI,OAAO,KAAK;AAAA,EAC3B;AACA,SAAO,IAAI,iBAAiB,IAAI,KAAK,QAAQ;AAC/C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n/**\n * Terminal statuses that arrived BEFORE their handle existed, keyed by launchId.\n *\n * The window is real: `launch()` registers the listener before it sends `create`,\n * but the handle only enters `liveHandles` once the create RESPONSE resolves. A\n * short-lived launch (or a revoke during binding) can end inside that gap, and a\n * dropped terminal message leaves the handle `running` for ever with `onDismiss`\n * never firing — the one failure a launcher cannot detect or recover from. So the\n * message is BUFFERED and applied when the handle lands.\n *\n * Bounded, because the host is not this module's to trust: entries are only kept\n * while a create is actually in flight, and no more than {@link MAX_PENDING_ENDED}\n * of them (oldest evicted). A `launch-ended` for an id we never created is still\n * ignored, exactly as before.\n */\nconst pendingEnded = new Map<string, Exclude<LaunchStatus, 'running'>>();\nconst MAX_PENDING_ENDED = 16;\n/** How many `launch()` calls are between their create request and its response. */\nlet createsInFlight = 0;\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle, or\n// buffer it for a handle that has not landed yet (above).\n//\n// Registered LAZILY, on the first `launch()` call, not at module evaluation\n// (R3-421 — no subpath may throw at import time off-host). Unlike `task-input`\n// (tasks.ts), first-use registration loses nothing here: a `launch-ended` can only\n// ever follow a launch THIS module created, and `launch()` registers the listener\n// before it sends the create request — so the listener always exists before any\n// launchId it must match. That is a claim about the LISTENER only; the handle it\n// must reach can still be a response away, which is what `pendingEnded` covers.\nlet endedListenerRegistered = false;\nconst ensureEndedListener = (): void => {\n if (endedListenerRegistered) return;\n try {\n addListener(LAUNCH_ENDED, (m: LaunchEndedMessage) => {\n const h = liveHandles.get(m.launchId);\n if (h) {\n h._end(m.status);\n return;\n }\n // No handle yet. Buffer only while a create could still produce one; anything\n // else is a stale or unknown id and is ignored.\n if (createsInFlight === 0) return;\n if (pendingEnded.size >= MAX_PENDING_ENDED) {\n const oldest = pendingEnded.keys().next();\n if (!oldest.done) pendingEnded.delete(oldest.value);\n }\n pendingEnded.set(m.launchId, m.status);\n });\n } catch {\n return; // off-host: no transport — the create request below will fail anyway\n }\n endedListenerRegistered = true;\n};\n\nclass LaunchHandleImpl implements LaunchHandle {\n #status: LaunchStatus = 'running';\n #dismissListeners = new Set<() => void>();\n #ended = false;\n\n constructor(readonly launchId: string) {\n liveHandles.set(launchId, this);\n // A terminal message that beat this handle into existence (see `pendingEnded`):\n // apply it now, so the handle is born ended rather than stuck `running`.\n const early = pendingEnded.get(launchId);\n if (early !== undefined) {\n pendingEnded.delete(launchId);\n this._end(early);\n }\n }\n\n get status(): LaunchStatus {\n return this.#status;\n }\n\n dismiss(): void {\n if (this.#ended) return;\n // Fire-and-forget: the host owns teardown and answers with `launch-ended`,\n // which drives `_end` (so status/onDismiss are host-authoritative, never\n // optimistically local — a dismiss the host refuses would otherwise desync).\n sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });\n }\n\n onDismiss(cb: () => void): () => void {\n if (this.#ended) {\n // Already ended: fire on the next tick so the contract (\"returns an\n // unsubscribe\") holds and the callback never runs synchronously mid-register.\n queueMicrotask(cb);\n return () => {};\n }\n this.#dismissListeners.add(cb);\n return () => {\n this.#dismissListeners.delete(cb);\n };\n }\n\n /** Host-driven terminal transition — the only writer of `status`. Idempotent. */\n _end(status: Exclude<LaunchStatus, 'running'>): void {\n if (this.#ended) return;\n this.#ended = true;\n this.#status = status;\n liveHandles.delete(this.launchId);\n const listeners = [...this.#dismissListeners];\n this.#dismissListeners.clear();\n for (const l of listeners) {\n try {\n l();\n } catch {\n /* a launcher's own callback must never wedge teardown */\n }\n }\n }\n}\n\n/**\n * Launch a bound program to RUN in a region (§2). Non-blocking: resolves once the\n * frame is created and bound, with a {@link LaunchHandle} — or a typed\n * `{ ok:false, code }` on refusal (§8), NEVER a throw for an ordinary refusal (so\n * a launcher branches on `code` without a try/catch). The launched app runs under\n * its OWN grants; the launcher's authority does not flow to it (R-SAL-4).\n *\n * const h = await launch({ task: 'open-project' }, {\n * region: 'stage',\n * input: { dir: capDir({ mountId: 'space:abc', relPath: 'proj' }, { mode: 'ro' }) },\n * });\n * if ('ok' in h && h.ok === false) { ...handle h.code... }\n * else { h.onDismiss(() => ...); }\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 run a launch in.\n */\nexport const launch = async (\n target: LaunchTarget,\n opts: LaunchOptions,\n): Promise<LaunchHandle | { ok: false; code: LaunchErrorCode }> => {\n // Before the create request, so the terminal message can never beat the listener.\n ensureEndedListener();\n // The host wraps a successful handler return as `{ ok:true, data }` (the same\n // Recipe-B framing `invokeTask` uses); a refusal is `{ ok:false, code }`.\n createsInFlight += 1;\n let res: { ok: true; data: { launchId: string } } | { ok: false; code?: LaunchErrorCode } | undefined;\n try {\n res = (await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], 'create', [{ target, opts }])) as typeof res;\n } finally {\n createsInFlight -= 1;\n }\n // Once nothing is in flight, anything still buffered belongs to a launch that will\n // never produce a handle — drop it rather than keep it for ever. Done AFTER the\n // handle below is constructed (its constructor drains its own entry first), so the\n // sweep can never eat the message this very call was waiting for.\n const sweep = (): void => {\n if (createsInFlight === 0) pendingEnded.clear();\n };\n if (!res || res.ok !== true || !res.data?.launchId) {\n sweep();\n const code = res && res.ok === false ? res.code ?? 'unknown' : 'unknown';\n return { ok: false, code };\n }\n // The constructor drains a terminal message that arrived while `create` was in\n // flight, so this handle can be returned already-ended rather than stuck `running`.\n const handle = new LaunchHandleImpl(res.data.launchId);\n sweep();\n return handle;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,0BAA0D;AAC1D,sBAA8D;AAC9D,6BAAwB;AAbxB;AA0FA,MAAM,cAAc,oBAAI,IAA8B;AAiBtD,MAAM,eAAe,oBAAI,IAA8C;AACvE,MAAM,oBAAoB;AAE1B,IAAI,kBAAkB;AActB,IAAI,0BAA0B;AAC9B,MAAM,sBAAsB,MAAY;AACtC,MAAI,wBAAyB;AAC7B,MAAI;AACF,yCAAY,8BAAc,CAAC,MAA0B;AACnD,YAAM,IAAI,YAAY,IAAI,EAAE,QAAQ;AACpC,UAAI,GAAG;AACL,UAAE,KAAK,EAAE,MAAM;AACf;AAAA,MACF;AAGA,UAAI,oBAAoB,EAAG;AAC3B,UAAI,aAAa,QAAQ,mBAAmB;AAC1C,cAAM,SAAS,aAAa,KAAK,EAAE,KAAK;AACxC,YAAI,CAAC,OAAO,KAAM,cAAa,OAAO,OAAO,KAAK;AAAA,MACpD;AACA,mBAAa,IAAI,EAAE,UAAU,EAAE,MAAM;AAAA,IACvC,CAAC;AAAA,EACH,QAAQ;AACN;AAAA,EACF;AACA,4BAA0B;AAC5B;AAEA,MAAM,iBAAyC;AAAA,EAK7C,YAAqB,UAAkB;AAAlB;AAJrB,gCAAwB;AACxB,0CAAoB,oBAAI,IAAgB;AACxC,+BAAS;AAGP,gBAAY,IAAI,UAAU,IAAI;AAG9B,UAAM,QAAQ,aAAa,IAAI,QAAQ;AACvC,QAAI,UAAU,QAAW;AACvB,mBAAa,OAAO,QAAQ;AAC5B,WAAK,KAAK,KAAK;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,IAAI,SAAuB;AACzB,WAAO,mBAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,QAAI,mBAAK,QAAQ;AAIjB,yCAAY,gCAAgB,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACzD;AAAA,EAEA,UAAU,IAA4B;AACpC,QAAI,mBAAK,SAAQ;AAGf,qBAAe,EAAE;AACjB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AACA,uBAAK,mBAAkB,IAAI,EAAE;AAC7B,WAAO,MAAM;AACX,yBAAK,mBAAkB,OAAO,EAAE;AAAA,IAClC;AAAA,EACF;AAAA;AAAA,EAGA,KAAK,QAAgD;AACnD,QAAI,mBAAK,QAAQ;AACjB,uBAAK,QAAS;AACd,uBAAK,SAAU;AACf,gBAAY,OAAO,KAAK,QAAQ;AAChC,UAAM,YAAY,CAAC,GAAG,mBAAK,kBAAiB;AAC5C,uBAAK,mBAAkB,MAAM;AAC7B,eAAW,KAAK,WAAW;AACzB,UAAI;AACF,UAAE;AAAA,MACJ,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAxDE;AACA;AACA;AAyEK,MAAM,SAAS,OACpB,QACA,SACiE;AAEjE,sBAAoB;AAGpB,qBAAmB;AACnB,MAAI;AACJ,MAAI;AACF,UAAO,UAAM,qCAAgB,+BAAQ,+BAAe,GAAG,UAAU,CAAC,EAAE,QAAQ,KAAK,CAAC,CAAC;AAAA,EACrF,UAAE;AACA,uBAAmB;AAAA,EACrB;AAKA,QAAM,QAAQ,MAAY;AACxB,QAAI,oBAAoB,EAAG,cAAa,MAAM;AAAA,EAChD;AACA,MAAI,CAAC,OAAO,IAAI,OAAO,QAAQ,CAAC,IAAI,MAAM,UAAU;AAClD,UAAM;AACN,UAAM,OAAO,OAAO,IAAI,OAAO,QAAQ,IAAI,QAAQ,YAAY;AAC/D,WAAO,EAAE,IAAI,OAAO,KAAK;AAAA,EAC3B;AAGA,QAAM,SAAS,IAAI,iBAAiB,IAAI,KAAK,QAAQ;AACrD,QAAM;AACN,SAAO;AACT;","names":[]}
|
package/dist/launch.d.cts
CHANGED
|
@@ -67,6 +67,9 @@ interface LaunchHandle {
|
|
|
67
67
|
* });
|
|
68
68
|
* if ('ok' in h && h.ok === false) { ...handle h.code... }
|
|
69
69
|
* else { h.onDismiss(() => ...); }
|
|
70
|
+
*
|
|
71
|
+
* Off-host (plain `vite dev` — no host transport) it rejects with a plain
|
|
72
|
+
* "no host transport" error: there is no host to run a launch in.
|
|
70
73
|
*/
|
|
71
74
|
declare const launch: (target: LaunchTarget, opts: LaunchOptions) => Promise<LaunchHandle | {
|
|
72
75
|
ok: false;
|
package/dist/launch.d.ts
CHANGED
|
@@ -67,6 +67,9 @@ interface LaunchHandle {
|
|
|
67
67
|
* });
|
|
68
68
|
* if ('ok' in h && h.ok === false) { ...handle h.code... }
|
|
69
69
|
* else { h.onDismiss(() => ...); }
|
|
70
|
+
*
|
|
71
|
+
* Off-host (plain `vite dev` — no host transport) it rejects with a plain
|
|
72
|
+
* "no host transport" error: there is no host to run a launch in.
|
|
70
73
|
*/
|
|
71
74
|
declare const launch: (target: LaunchTarget, opts: LaunchOptions) => Promise<LaunchHandle | {
|
|
72
75
|
ok: false;
|
package/dist/launch.js
CHANGED
|
@@ -8,10 +8,31 @@ import { protocolRequest, sendMessage, addListener } from "./sandboxUtils";
|
|
|
8
8
|
import { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from "./generated/protocol";
|
|
9
9
|
import { SCHEMES } from "./protocolSchemes";
|
|
10
10
|
const liveHandles = /* @__PURE__ */ new Map();
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const pendingEnded = /* @__PURE__ */ new Map();
|
|
12
|
+
const MAX_PENDING_ENDED = 16;
|
|
13
|
+
let createsInFlight = 0;
|
|
14
|
+
let endedListenerRegistered = false;
|
|
15
|
+
const ensureEndedListener = () => {
|
|
16
|
+
if (endedListenerRegistered) return;
|
|
17
|
+
try {
|
|
18
|
+
addListener(LAUNCH_ENDED, (m) => {
|
|
19
|
+
const h = liveHandles.get(m.launchId);
|
|
20
|
+
if (h) {
|
|
21
|
+
h._end(m.status);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (createsInFlight === 0) return;
|
|
25
|
+
if (pendingEnded.size >= MAX_PENDING_ENDED) {
|
|
26
|
+
const oldest = pendingEnded.keys().next();
|
|
27
|
+
if (!oldest.done) pendingEnded.delete(oldest.value);
|
|
28
|
+
}
|
|
29
|
+
pendingEnded.set(m.launchId, m.status);
|
|
30
|
+
});
|
|
31
|
+
} catch {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
endedListenerRegistered = true;
|
|
35
|
+
};
|
|
15
36
|
class LaunchHandleImpl {
|
|
16
37
|
constructor(launchId) {
|
|
17
38
|
this.launchId = launchId;
|
|
@@ -19,6 +40,11 @@ class LaunchHandleImpl {
|
|
|
19
40
|
__privateAdd(this, _dismissListeners, /* @__PURE__ */ new Set());
|
|
20
41
|
__privateAdd(this, _ended, false);
|
|
21
42
|
liveHandles.set(launchId, this);
|
|
43
|
+
const early = pendingEnded.get(launchId);
|
|
44
|
+
if (early !== void 0) {
|
|
45
|
+
pendingEnded.delete(launchId);
|
|
46
|
+
this._end(early);
|
|
47
|
+
}
|
|
22
48
|
}
|
|
23
49
|
get status() {
|
|
24
50
|
return __privateGet(this, _status);
|
|
@@ -58,12 +84,25 @@ _status = new WeakMap();
|
|
|
58
84
|
_dismissListeners = new WeakMap();
|
|
59
85
|
_ended = new WeakMap();
|
|
60
86
|
const launch = async (target, opts) => {
|
|
61
|
-
|
|
87
|
+
ensureEndedListener();
|
|
88
|
+
createsInFlight += 1;
|
|
89
|
+
let res;
|
|
90
|
+
try {
|
|
91
|
+
res = await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], "create", [{ target, opts }]);
|
|
92
|
+
} finally {
|
|
93
|
+
createsInFlight -= 1;
|
|
94
|
+
}
|
|
95
|
+
const sweep = () => {
|
|
96
|
+
if (createsInFlight === 0) pendingEnded.clear();
|
|
97
|
+
};
|
|
62
98
|
if (!res || res.ok !== true || !res.data?.launchId) {
|
|
99
|
+
sweep();
|
|
63
100
|
const code = res && res.ok === false ? res.code ?? "unknown" : "unknown";
|
|
64
101
|
return { ok: false, code };
|
|
65
102
|
}
|
|
66
|
-
|
|
103
|
+
const handle = new LaunchHandleImpl(res.data.launchId);
|
|
104
|
+
sweep();
|
|
105
|
+
return handle;
|
|
67
106
|
};
|
|
68
107
|
export {
|
|
69
108
|
launch
|
package/dist/launch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle.\naddListener(LAUNCH_ENDED, (m: LaunchEndedMessage) => {\n const h = liveHandles.get(m.launchId);\n if (h) h._end(m.status);\n});\n\nclass LaunchHandleImpl implements LaunchHandle {\n #status: LaunchStatus = 'running';\n #dismissListeners = new Set<() => void>();\n #ended = false;\n\n constructor(readonly launchId: string) {\n liveHandles.set(launchId, this);\n }\n\n get status(): LaunchStatus {\n return this.#status;\n }\n\n dismiss(): void {\n if (this.#ended) return;\n // Fire-and-forget: the host owns teardown and answers with `launch-ended`,\n // which drives `_end` (so status/onDismiss are host-authoritative, never\n // optimistically local — a dismiss the host refuses would otherwise desync).\n sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });\n }\n\n onDismiss(cb: () => void): () => void {\n if (this.#ended) {\n // Already ended: fire on the next tick so the contract (\"returns an\n // unsubscribe\") holds and the callback never runs synchronously mid-register.\n queueMicrotask(cb);\n return () => {};\n }\n this.#dismissListeners.add(cb);\n return () => {\n this.#dismissListeners.delete(cb);\n };\n }\n\n /** Host-driven terminal transition — the only writer of `status`. Idempotent. */\n _end(status: Exclude<LaunchStatus, 'running'>): void {\n if (this.#ended) return;\n this.#ended = true;\n this.#status = status;\n liveHandles.delete(this.launchId);\n const listeners = [...this.#dismissListeners];\n this.#dismissListeners.clear();\n for (const l of listeners) {\n try {\n l();\n } catch {\n /* a launcher's own callback must never wedge teardown */\n }\n }\n }\n}\n\n/**\n * Launch a bound program to RUN in a region (§2). Non-blocking: resolves once the\n * frame is created and bound, with a {@link LaunchHandle} — or a typed\n * `{ ok:false, code }` on refusal (§8), NEVER a throw for an ordinary refusal (so\n * a launcher branches on `code` without a try/catch). The launched app runs under\n * its OWN grants; the launcher's authority does not flow to it (R-SAL-4).\n *\n * const h = await launch({ task: 'open-project' }, {\n * region: 'stage',\n * input: { dir: capDir({ mountId: 'space:abc', relPath: 'proj' }, { mode: 'ro' }) },\n * });\n * if ('ok' in h && h.ok === false) { ...handle h.code... }\n * else { h.onDismiss(() => ...); }\n */\nexport const launch = async (\n target: LaunchTarget,\n opts: LaunchOptions,\n): Promise<LaunchHandle | { ok: false; code: LaunchErrorCode }> => {\n // The host wraps a successful handler return as `{ ok:true, data }` (the same\n // Recipe-B framing `invokeTask` uses); a refusal is `{ ok:false, code }`.\n const res = (await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], 'create', [{ target, opts }])) as\n | { ok: true; data: { launchId: string } }\n | { ok: false; code?: LaunchErrorCode }\n | undefined;\n if (!res || res.ok !== true || !res.data?.launchId) {\n const code = res && res.ok === false ? res.code ?? 'unknown' : 'unknown';\n return { ok: false, code };\n }\n return new LaunchHandleImpl(res.data.launchId);\n};\n"],"mappings":";;;;;AAAA;AAWA,SAAS,iBAAiB,aAAa,mBAAmB;AAC1D,SAAS,gBAAgB,cAAc,uBAAuB;AAC9D,SAAS,eAAe;AA6ExB,MAAM,cAAc,oBAAI,IAA8B;AAKtD,YAAY,cAAc,CAAC,MAA0B;AACnD,QAAM,IAAI,YAAY,IAAI,EAAE,QAAQ;AACpC,MAAI,EAAG,GAAE,KAAK,EAAE,MAAM;AACxB,CAAC;AAED,MAAM,iBAAyC;AAAA,EAK7C,YAAqB,UAAkB;AAAlB;AAJrB,gCAAwB;AACxB,0CAAoB,oBAAI,IAAgB;AACxC,+BAAS;AAGP,gBAAY,IAAI,UAAU,IAAI;AAAA,EAChC;AAAA,EAEA,IAAI,SAAuB;AACzB,WAAO,mBAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,QAAI,mBAAK,QAAQ;AAIjB,gBAAY,gBAAgB,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACzD;AAAA,EAEA,UAAU,IAA4B;AACpC,QAAI,mBAAK,SAAQ;AAGf,qBAAe,EAAE;AACjB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AACA,uBAAK,mBAAkB,IAAI,EAAE;AAC7B,WAAO,MAAM;AACX,yBAAK,mBAAkB,OAAO,EAAE;AAAA,IAClC;AAAA,EACF;AAAA;AAAA,EAGA,KAAK,QAAgD;AACnD,QAAI,mBAAK,QAAQ;AACjB,uBAAK,QAAS;AACd,uBAAK,SAAU;AACf,gBAAY,OAAO,KAAK,QAAQ;AAChC,UAAM,YAAY,CAAC,GAAG,mBAAK,kBAAiB;AAC5C,uBAAK,mBAAkB,MAAM;AAC7B,eAAW,KAAK,WAAW;AACzB,UAAI;AACF,UAAE;AAAA,MACJ,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAjDE;AACA;AACA;AA+DK,MAAM,SAAS,OACpB,QACA,SACiE;AAGjE,QAAM,MAAO,MAAM,gBAAgB,QAAQ,eAAe,GAAG,UAAU,CAAC,EAAE,QAAQ,KAAK,CAAC,CAAC;AAIzF,MAAI,CAAC,OAAO,IAAI,OAAO,QAAQ,CAAC,IAAI,MAAM,UAAU;AAClD,UAAM,OAAO,OAAO,IAAI,OAAO,QAAQ,IAAI,QAAQ,YAAY;AAC/D,WAAO,EAAE,IAAI,OAAO,KAAK;AAAA,EAC3B;AACA,SAAO,IAAI,iBAAiB,IAAI,KAAK,QAAQ;AAC/C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n/**\n * Terminal statuses that arrived BEFORE their handle existed, keyed by launchId.\n *\n * The window is real: `launch()` registers the listener before it sends `create`,\n * but the handle only enters `liveHandles` once the create RESPONSE resolves. A\n * short-lived launch (or a revoke during binding) can end inside that gap, and a\n * dropped terminal message leaves the handle `running` for ever with `onDismiss`\n * never firing — the one failure a launcher cannot detect or recover from. So the\n * message is BUFFERED and applied when the handle lands.\n *\n * Bounded, because the host is not this module's to trust: entries are only kept\n * while a create is actually in flight, and no more than {@link MAX_PENDING_ENDED}\n * of them (oldest evicted). A `launch-ended` for an id we never created is still\n * ignored, exactly as before.\n */\nconst pendingEnded = new Map<string, Exclude<LaunchStatus, 'running'>>();\nconst MAX_PENDING_ENDED = 16;\n/** How many `launch()` calls are between their create request and its response. */\nlet createsInFlight = 0;\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle, or\n// buffer it for a handle that has not landed yet (above).\n//\n// Registered LAZILY, on the first `launch()` call, not at module evaluation\n// (R3-421 — no subpath may throw at import time off-host). Unlike `task-input`\n// (tasks.ts), first-use registration loses nothing here: a `launch-ended` can only\n// ever follow a launch THIS module created, and `launch()` registers the listener\n// before it sends the create request — so the listener always exists before any\n// launchId it must match. That is a claim about the LISTENER only; the handle it\n// must reach can still be a response away, which is what `pendingEnded` covers.\nlet endedListenerRegistered = false;\nconst ensureEndedListener = (): void => {\n if (endedListenerRegistered) return;\n try {\n addListener(LAUNCH_ENDED, (m: LaunchEndedMessage) => {\n const h = liveHandles.get(m.launchId);\n if (h) {\n h._end(m.status);\n return;\n }\n // No handle yet. Buffer only while a create could still produce one; anything\n // else is a stale or unknown id and is ignored.\n if (createsInFlight === 0) return;\n if (pendingEnded.size >= MAX_PENDING_ENDED) {\n const oldest = pendingEnded.keys().next();\n if (!oldest.done) pendingEnded.delete(oldest.value);\n }\n pendingEnded.set(m.launchId, m.status);\n });\n } catch {\n return; // off-host: no transport — the create request below will fail anyway\n }\n endedListenerRegistered = true;\n};\n\nclass LaunchHandleImpl implements LaunchHandle {\n #status: LaunchStatus = 'running';\n #dismissListeners = new Set<() => void>();\n #ended = false;\n\n constructor(readonly launchId: string) {\n liveHandles.set(launchId, this);\n // A terminal message that beat this handle into existence (see `pendingEnded`):\n // apply it now, so the handle is born ended rather than stuck `running`.\n const early = pendingEnded.get(launchId);\n if (early !== undefined) {\n pendingEnded.delete(launchId);\n this._end(early);\n }\n }\n\n get status(): LaunchStatus {\n return this.#status;\n }\n\n dismiss(): void {\n if (this.#ended) return;\n // Fire-and-forget: the host owns teardown and answers with `launch-ended`,\n // which drives `_end` (so status/onDismiss are host-authoritative, never\n // optimistically local — a dismiss the host refuses would otherwise desync).\n sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });\n }\n\n onDismiss(cb: () => void): () => void {\n if (this.#ended) {\n // Already ended: fire on the next tick so the contract (\"returns an\n // unsubscribe\") holds and the callback never runs synchronously mid-register.\n queueMicrotask(cb);\n return () => {};\n }\n this.#dismissListeners.add(cb);\n return () => {\n this.#dismissListeners.delete(cb);\n };\n }\n\n /** Host-driven terminal transition — the only writer of `status`. Idempotent. */\n _end(status: Exclude<LaunchStatus, 'running'>): void {\n if (this.#ended) return;\n this.#ended = true;\n this.#status = status;\n liveHandles.delete(this.launchId);\n const listeners = [...this.#dismissListeners];\n this.#dismissListeners.clear();\n for (const l of listeners) {\n try {\n l();\n } catch {\n /* a launcher's own callback must never wedge teardown */\n }\n }\n }\n}\n\n/**\n * Launch a bound program to RUN in a region (§2). Non-blocking: resolves once the\n * frame is created and bound, with a {@link LaunchHandle} — or a typed\n * `{ ok:false, code }` on refusal (§8), NEVER a throw for an ordinary refusal (so\n * a launcher branches on `code` without a try/catch). The launched app runs under\n * its OWN grants; the launcher's authority does not flow to it (R-SAL-4).\n *\n * const h = await launch({ task: 'open-project' }, {\n * region: 'stage',\n * input: { dir: capDir({ mountId: 'space:abc', relPath: 'proj' }, { mode: 'ro' }) },\n * });\n * if ('ok' in h && h.ok === false) { ...handle h.code... }\n * else { h.onDismiss(() => ...); }\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 run a launch in.\n */\nexport const launch = async (\n target: LaunchTarget,\n opts: LaunchOptions,\n): Promise<LaunchHandle | { ok: false; code: LaunchErrorCode }> => {\n // Before the create request, so the terminal message can never beat the listener.\n ensureEndedListener();\n // The host wraps a successful handler return as `{ ok:true, data }` (the same\n // Recipe-B framing `invokeTask` uses); a refusal is `{ ok:false, code }`.\n createsInFlight += 1;\n let res: { ok: true; data: { launchId: string } } | { ok: false; code?: LaunchErrorCode } | undefined;\n try {\n res = (await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], 'create', [{ target, opts }])) as typeof res;\n } finally {\n createsInFlight -= 1;\n }\n // Once nothing is in flight, anything still buffered belongs to a launch that will\n // never produce a handle — drop it rather than keep it for ever. Done AFTER the\n // handle below is constructed (its constructor drains its own entry first), so the\n // sweep can never eat the message this very call was waiting for.\n const sweep = (): void => {\n if (createsInFlight === 0) pendingEnded.clear();\n };\n if (!res || res.ok !== true || !res.data?.launchId) {\n sweep();\n const code = res && res.ok === false ? res.code ?? 'unknown' : 'unknown';\n return { ok: false, code };\n }\n // The constructor drains a terminal message that arrived while `create` was in\n // flight, so this handle can be returned already-ended rather than stuck `running`.\n const handle = new LaunchHandleImpl(res.data.launchId);\n sweep();\n return handle;\n};\n"],"mappings":";;;;;AAAA;AAWA,SAAS,iBAAiB,aAAa,mBAAmB;AAC1D,SAAS,gBAAgB,cAAc,uBAAuB;AAC9D,SAAS,eAAe;AA6ExB,MAAM,cAAc,oBAAI,IAA8B;AAiBtD,MAAM,eAAe,oBAAI,IAA8C;AACvE,MAAM,oBAAoB;AAE1B,IAAI,kBAAkB;AActB,IAAI,0BAA0B;AAC9B,MAAM,sBAAsB,MAAY;AACtC,MAAI,wBAAyB;AAC7B,MAAI;AACF,gBAAY,cAAc,CAAC,MAA0B;AACnD,YAAM,IAAI,YAAY,IAAI,EAAE,QAAQ;AACpC,UAAI,GAAG;AACL,UAAE,KAAK,EAAE,MAAM;AACf;AAAA,MACF;AAGA,UAAI,oBAAoB,EAAG;AAC3B,UAAI,aAAa,QAAQ,mBAAmB;AAC1C,cAAM,SAAS,aAAa,KAAK,EAAE,KAAK;AACxC,YAAI,CAAC,OAAO,KAAM,cAAa,OAAO,OAAO,KAAK;AAAA,MACpD;AACA,mBAAa,IAAI,EAAE,UAAU,EAAE,MAAM;AAAA,IACvC,CAAC;AAAA,EACH,QAAQ;AACN;AAAA,EACF;AACA,4BAA0B;AAC5B;AAEA,MAAM,iBAAyC;AAAA,EAK7C,YAAqB,UAAkB;AAAlB;AAJrB,gCAAwB;AACxB,0CAAoB,oBAAI,IAAgB;AACxC,+BAAS;AAGP,gBAAY,IAAI,UAAU,IAAI;AAG9B,UAAM,QAAQ,aAAa,IAAI,QAAQ;AACvC,QAAI,UAAU,QAAW;AACvB,mBAAa,OAAO,QAAQ;AAC5B,WAAK,KAAK,KAAK;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,IAAI,SAAuB;AACzB,WAAO,mBAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,QAAI,mBAAK,QAAQ;AAIjB,gBAAY,gBAAgB,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACzD;AAAA,EAEA,UAAU,IAA4B;AACpC,QAAI,mBAAK,SAAQ;AAGf,qBAAe,EAAE;AACjB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AACA,uBAAK,mBAAkB,IAAI,EAAE;AAC7B,WAAO,MAAM;AACX,yBAAK,mBAAkB,OAAO,EAAE;AAAA,IAClC;AAAA,EACF;AAAA;AAAA,EAGA,KAAK,QAAgD;AACnD,QAAI,mBAAK,QAAQ;AACjB,uBAAK,QAAS;AACd,uBAAK,SAAU;AACf,gBAAY,OAAO,KAAK,QAAQ;AAChC,UAAM,YAAY,CAAC,GAAG,mBAAK,kBAAiB;AAC5C,uBAAK,mBAAkB,MAAM;AAC7B,eAAW,KAAK,WAAW;AACzB,UAAI;AACF,UAAE;AAAA,MACJ,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAxDE;AACA;AACA;AAyEK,MAAM,SAAS,OACpB,QACA,SACiE;AAEjE,sBAAoB;AAGpB,qBAAmB;AACnB,MAAI;AACJ,MAAI;AACF,UAAO,MAAM,gBAAgB,QAAQ,eAAe,GAAG,UAAU,CAAC,EAAE,QAAQ,KAAK,CAAC,CAAC;AAAA,EACrF,UAAE;AACA,uBAAmB;AAAA,EACrB;AAKA,QAAM,QAAQ,MAAY;AACxB,QAAI,oBAAoB,EAAG,cAAa,MAAM;AAAA,EAChD;AACA,MAAI,CAAC,OAAO,IAAI,OAAO,QAAQ,CAAC,IAAI,MAAM,UAAU;AAClD,UAAM;AACN,UAAM,OAAO,OAAO,IAAI,OAAO,QAAQ,IAAI,QAAQ,YAAY;AAC/D,WAAO,EAAE,IAAI,OAAO,KAAK;AAAA,EAC3B;AAGA,QAAM,SAAS,IAAI,iBAAiB,IAAI,KAAK,QAAQ;AACrD,QAAM;AACN,SAAO;AACT;","names":[]}
|