Dashboard › opencode › Distillation
04ab3733-1d7f-4850-bfcb-db426fd9fe8d["lore_tm_v1_W8poJk4gNKuNnTV4aS_pQG9x3YLDYcmAnQk68baVjcw","lore_tm_v1_w-e26XgQcdtR0EkRd8gPPaVnRSTqr_xZJ8zyQsIuX9s","lore_tm_v1_4Zsv5QuwjQ3bgPpe_1OW0WEuAowDfEr-g17pj6e_q_g","lore_tm_v1_MMwbaYmjWsaF1kwuxoBbM5FbLn1S9-7GpXROljmRsL0"]
/home/byk/Code/opencode-lore-v2/packages/opencode/node_modules/@opencode/plugin/dist/promise/adapter.js is a 443-line compiled Promise-to-Effect plugin adapter; source references at commit 2ac698d65aa4690a694307e9cbdf44537ea9a4fb point to packages/plugin/src/promise/adapter.ts.makeStreams() captures the Effect context and tracks each async iteratorβs close() in a Set; plugin-scope finalization closes all subscriptions, iterator completion/error removes the subscription, and an optional AbortSignal closes the iterator immediately or upon "abort".rpcFromEffect(host, streams) adapts Effect RPC domains to Promise clients and registrations. Client methods run with the captured Effect context and optional abort signal; events.subscribe exposes async iteration, while events.on combines a local AbortController with any caller signal via AbortSignal.any, awaits handlers serially, logs loop errors with Effect.logError, and returns an unsubscribe function.Effect.tryPromise. Handler context includes signal and error(type, message, data), which constructs ReturnedRpcError; hostRpcError(context, error) maps that class to context.error(error.type, error.message, error.data) while passing all other errors through unchanged. Registration exposes Promise-based dispose() and events.emit(...).compileEndpoint(endpoint) caches compiled schemas in compiledEndpoints: WeakMap. It rejects more than one payload schema or more than one success schema with Unsupported API schema cardinality: ${endpoint.identifier}; inputs combine endpoint.params, type-form query, endpoint.headers, and payload; absent success uses HttpApiSchema.NoContent.compileEndpoint(endpoint) unwraps a success schema only when its type is an object with no index signatures and exactly one "data" property, using that propertyβs original encoded schema; the compiled result contains input decoders, an output encoder, and noContent.fromPromise(plugin) returns an Effect plugin via define({ id: plugin.id, effect }). It dynamically imports ClientApi from @opencode/protocol/client and OpenCodeEvent from @opencode/protocol/groups/event, captures the Effect context, and runs plugin.setup(context2) with Effect.acquireRelease; an optional setup cleanup function runs when the plugin scope is released.dispose() methods. The adapter documentation states that unloading disposes setup-time registrations and that the captured fiber context preserves boot-time batching so Promise-plugin transforms coalesce into one reload per domain.adaptApiMethod(endpoint, method) JSON-normalizes input ?? {} through Schema.encodeUnknownEffect(JsonInput) then Schema.decodeUnknownEffect(JsonInput), decodes every endpoint input schema against that normalized object, merges decoded values with Object.assign({}, ...decoded), calls the Effect host method, returns undefined for no-content responses, and otherwise encodes the result through the compiled output schema.fromPromise() adapts API endpoint groups from ClientApi.groups: "server.agent", "server.command", "server.experimental", "server.generate", "server.integration", "server.mcp", "server.model", "server.plugin", "server.permission", "server.provider", "server.reference", "server.session", "server.skill", "server.vcs", "server.websearch", and "server.worktree".agent (get, list, transform, reload); aisdk.hook; catalog.provider (list, get), catalog.model (list, default), plus catalog transform/reload; command (list, adapted transform, reload); and event.subscribe, which schema-encodes each event as OpenCodeEvent.experimental.terminal.read; generate.text; mcp (list, transform, reload); permission (hook, list, get, reply); plugin.list; reference (list, transform, reload); adapted rpc; and skill (list, transform, reload).list, get, connect.key, OAuth connect/status/complete/cancel, command connect/status/cancel, transform, reload, and connection active/resolve. During method transformation, Promise authorize results are converted to Effect callbacks for both "auto" and code-driven modes, and optional refresh(credential) is wrapped with Effect.promise.host.storage.get(key), set(key, value), remove(key), and scan(options) through the captured Effect runner.reload, transform, and hook. Transformed list() and get(id) replace Effect executors with Promise executors; add(tool) wraps Promise execution using executePromiseTool; update(id, update) presents a Promise-style mutable tool to the callback, then copies output, options, and a newly wrapped executor back to the Effect tool; remove and namespace pass through.promiseExecutor(execute) and executePromiseTool(tool, input, context) bridge progress reporting between Promise and Effect APIs. Generic promise execution maps progress(update) to Effect.promise(() => context.progress(update)); tool execution maps it to Effect.runPromise(context.progress(update)).get, base, branches, status, diff, reload, and transform. Added Promise VCS definitions preserve id and name; bind optional base to the definition; wrap info, base, branches, status, and diff with abort-aware attempt; and pass through editor.default.providers, query, reload, and transform; added providers preserve id and name, adapt execute(input, { signal }) through attempt, and retain editor.default.list, create, remove, refresh, reload, and transform; added definitions preserve id and adapt create(input, { signal }), remove(input, { signal }), and list(directory, { signal }) through attempt.hook plus endpoint methods create, get, switchAgent, switchModel, prompt, generate, command, synthetic, interrupt, rename, move, wait, and context; shell adaptation exposes shell.hook.attempt(evaluate) is Effect.tryPromise({ try: evaluate, catch: (cause) => cause }), preserving rejected causes as the Effect failure value.2ac698d65aa4690a694307e9cbdf44537ea9a4fb, registration types are split across packages/plugin/src/effect/registration.ts (dispose: Effect.Effect<void>), packages/plugin/src/promise/registration.ts (dispose: () => Promise<void>), and internal adapter aliases in packages/plugin/src/promise/adapter.ts: HostRegistration for Effect disposal and Registration for Promise disposal.packages/core/src/plugin/hooks.ts defines five hook domains in Domains: aisdk, session, permission, shell, and tool. Its failure typing makes every hook infallible through NoFailures<Spec> except tool hooks, which use ToolFailures; the comment specifies that only toolβs execute.before may fail, where a Tool.Error rejects the call before execution.packages/core/src/plugin/hooks.ts defines eventProviderID(event), which returns event.model.providerID only when each containing value is a non-null object and providerID is a string; otherwise it returns undefined.PluginHooks.Interface provides has(domain, name, providerID?), register(domain, name, callback, options?), and trigger(domain, name, event). register returns Effect.Effect<State.Registration, never, Scope.Scope>, while trigger returns the same event with the domain/name-specific failure channel.PluginHooks.Service is declared as Context.Service with key "@opencode/PluginHooks". Its layer stores callbacks in Map<string, Entry[]> keyed as ${domain}.${String(name)}.PluginHooks.register obtains Scope.Scope, appends { callback, options }, and creates an idempotent dispose Effect guarded by active. Disposal removes that exact entry, deletes an empty callback bucket, and is installed with Scope.addFinalizer(scope, dispose) before returning { dispose }.PluginHooks.trigger visits registered callbacks in insertion order, skips an entry when its explicit options.providerID does not equal eventProviderID(event), invokes callbacks with Reflect.apply(entry.callback, undefined, [event]), executes each sequentially with yield*, and finally returns the original event.PluginHooks.has returns true when at least one callback for the domain/name either has no providerID filter or has a filter equal to the requested providerID.packages/core/src/plugin/hooks.ts exports its node as makeLocationNode({ service: Service, layer, deps: [] }); packages/core/src/plugin/index.ts re-exports the module namespace as PluginHooks from "./hooks.js".