mirror of
https://github.com/morluto/rea.git
synced 2026-11-03 21:36:13 +00:00
Bind typed service creation, observed availability and retryable cleanup in one composition owner. Reuse selected CLI/MCP factories and canonical contract groups without copying provider operation inventories. Closes #8429. Refs #740. Validated paired real application workflows, actual local Codex CLI Terra/high use, complete Evidence and Unknown checkpoint parity, compiler-backed import boundaries, SDK schemas and final real mixed routing. Automated review findings were fixed at their producing owners. Existing exact-base CodeQL alerts are documented on the PR under the owner-authorized unrelated-check exception; no global alerts or security rules were changed. Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
41 lines
1.4 KiB
TypeScript
41 lines
1.4 KiB
TypeScript
/** One host owns these exact close capabilities; a released peer stays released. */
|
|
export class CapabilityScope {
|
|
readonly #pending = new Map<string, () => Promise<void>>();
|
|
#accepting = true;
|
|
#closing: Promise<void> | undefined;
|
|
|
|
constructor(readonly failureMessage = "REA provider cleanup failed") {}
|
|
|
|
own<Value>(id: string, value: Value, close: () => Promise<void>): Value {
|
|
if (!this.#accepting)
|
|
throw new Error(`Cannot acquire capability ${id} after host shutdown`);
|
|
if (this.#pending.has(id))
|
|
throw new Error(`Capability ${id} already belongs to this host`);
|
|
this.#pending.set(id, close);
|
|
return value;
|
|
}
|
|
|
|
/** Join concurrent closes, retaining only failed owners for the next retry. */
|
|
close(): Promise<void> {
|
|
this.#accepting = false;
|
|
this.#closing ??= Promise.allSettled(
|
|
[...this.#pending].map(async ([id, close]) => {
|
|
// A synchronous adapter throw must not prevent other owners' cleanup.
|
|
await Promise.resolve().then(close);
|
|
this.#pending.delete(id);
|
|
}),
|
|
)
|
|
.then((results) => {
|
|
const failures = results.flatMap((result) =>
|
|
result.status === "rejected" ? [result.reason] : [],
|
|
);
|
|
if (failures.length > 0)
|
|
throw new AggregateError(failures, this.failureMessage);
|
|
})
|
|
.finally(() => {
|
|
this.#closing = undefined;
|
|
});
|
|
return this.#closing;
|
|
}
|
|
}
|