| 1813 | }; |
| 1814 | |
| 1815 | export const createExecutor = <const TPlugins extends readonly AnyPlugin[] = readonly []>( |
| 1816 | config: ExecutorConfig<TPlugins>, |
| 1817 | ): Effect.Effect<Executor<TPlugins>, StorageFailure> => |
| 1818 | Effect.gen(function* () { |
| 1819 | const defaultPlugins = (): TPlugins => { |
| 1820 | const empty: readonly AnyPlugin[] = []; |
| 1821 | return empty as TPlugins; |
| 1822 | }; |
| 1823 | const { plugins: userPlugins = defaultPlugins() } = config; |
| 1824 | |
| 1825 | const tenant = String(config.tenant); |
| 1826 | const subject = config.subject != null ? String(config.subject) : null; |
| 1827 | |
| 1828 | const ownerBinding: OwnerBinding = { |
| 1829 | tenant: config.tenant, |
| 1830 | subject: config.subject ?? null, |
| 1831 | }; |
| 1832 | |
| 1833 | const ownedKeys = (owner: Owner): OwnedKeys => { |
| 1834 | if (owner === "org") return { tenant, owner, subject: ORG_SUBJECT }; |
| 1835 | if (subject == null) { |
| 1836 | // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: programmer error caught and surfaced as StorageError below by callers |
| 1837 | throw new StorageError({ |
| 1838 | message: `Cannot target owner "user": executor has no subject.`, |
| 1839 | cause: undefined, |
| 1840 | }); |
| 1841 | } |
| 1842 | return { tenant, owner, subject }; |
| 1843 | }; |
| 1844 | |
| 1845 | const requireUserSubject = (owner: Owner): Effect.Effect<void, StorageFailure> => |
| 1846 | owner === "user" && subject == null |
| 1847 | ? Effect.fail( |
| 1848 | new StorageError({ |
| 1849 | message: `Cannot target owner "user": executor has no subject.`, |
| 1850 | cause: undefined, |
| 1851 | }), |
| 1852 | ) |
| 1853 | : Effect.void; |
| 1854 | |
| 1855 | // Built-in core-tools plugin: agent-facing static tools over the v2 surface. |
| 1856 | const plugins: readonly AnyPlugin[] = config.coreTools |
| 1857 | ? ([ |
| 1858 | coreToolsPlugin({ |
| 1859 | webBaseUrl: config.coreTools.webBaseUrl, |
| 1860 | orgSlug: config.coreTools.orgSlug, |
| 1861 | includeProviders: config.coreTools.includeProviders, |
| 1862 | }), |
| 1863 | ...userPlugins, |
| 1864 | ] as readonly AnyPlugin[]) |
| 1865 | : (userPlugins as readonly AnyPlugin[]); |
| 1866 | |
| 1867 | const tables = yield* Effect.try({ |
| 1868 | try: () => collectTables(), |
| 1869 | catch: (cause) => storageFailureFromUnknown("Failed to collect executor tables", cause), |
| 1870 | }); |
| 1871 | const dbInput = yield* Effect.suspend(() => { |
| 1872 | if (!config.db) return Effect.succeed(createDefaultMemoryDb(tables)); |