@fungi.computer/mycelium 0.0.0-stage → 0.0.2
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/LICENSE +21 -0
- package/README.md +303 -2
- package/THIRD_PARTY_NOTICES +2 -0
- package/dist/access.d.ts +129 -0
- package/dist/access.js +166 -0
- package/dist/capability-source.d.ts +44 -0
- package/dist/capability-source.js +1 -0
- package/dist/compiler.d.ts +28 -0
- package/dist/compiler.js +221 -0
- package/dist/define.d.ts +8 -0
- package/dist/define.js +7 -0
- package/dist/effect.d.ts +89 -0
- package/dist/effect.js +42 -0
- package/dist/errors.d.ts +48 -0
- package/dist/errors.js +62 -0
- package/dist/execute-tool.d.ts +22 -0
- package/dist/execute-tool.js +163 -0
- package/dist/image-collector.d.ts +47 -0
- package/dist/image-collector.js +39 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +22 -0
- package/dist/internal/build-identity.d.ts +1 -0
- package/dist/internal/build-identity.js +15 -0
- package/dist/internal/build-identity.node.d.ts +1 -0
- package/dist/internal/build-identity.node.js +42 -0
- package/dist/internal/execution-lifecycle.d.ts +44 -0
- package/dist/internal/execution-lifecycle.js +328 -0
- package/dist/internal/mcp-principal.d.ts +14 -0
- package/dist/internal/mcp-principal.js +17 -0
- package/dist/mcp-catalog.d.ts +23 -0
- package/dist/mcp-catalog.js +110 -0
- package/dist/mcp.d.ts +92 -0
- package/dist/mcp.js +250 -0
- package/dist/operation-schema.d.ts +47 -0
- package/dist/operation-schema.js +50 -0
- package/dist/promise-facade.d.ts +3 -0
- package/dist/promise-facade.js +82 -0
- package/dist/registration.d.ts +74 -0
- package/dist/registration.js +157 -0
- package/dist/remote.d.ts +33 -0
- package/dist/remote.js +126 -0
- package/dist/runtime.d.ts +51 -0
- package/dist/runtime.js +159 -0
- package/dist/sandbox-boundary.d.ts +44 -0
- package/dist/sandbox-boundary.js +325 -0
- package/dist/sandbox-failure-event.d.ts +15 -0
- package/dist/sandbox-failure-event.js +34 -0
- package/dist/sandbox-failure.d.ts +128 -0
- package/dist/sandbox-failure.js +161 -0
- package/dist/sandbox-guest.d.ts +34 -0
- package/dist/sandbox-guest.js +131 -0
- package/dist/sandbox-wire.d.ts +76 -0
- package/dist/sandbox-wire.js +190 -0
- package/dist/sandbox.d.ts +41 -0
- package/dist/sandbox.js +1 -0
- package/dist/semaphore-transition.d.ts +7 -0
- package/dist/semaphore-transition.js +9 -0
- package/dist/source-runtime.d.ts +28 -0
- package/dist/source-runtime.js +162 -0
- package/dist/testing.d.ts +11 -0
- package/dist/testing.js +19 -0
- package/dist/workers/index.d.ts +9 -0
- package/dist/workers/index.js +90 -0
- package/docs/api/README.md +25 -0
- package/docs/api/define.md +20 -0
- package/docs/api/effect.md +245 -0
- package/docs/api/index.md +1081 -0
- package/docs/api/mcp.md +239 -0
- package/docs/api/remote.md +108 -0
- package/docs/api/sandbox-boundary.md +325 -0
- package/docs/api/sandbox-failure.md +1133 -0
- package/docs/api/sandbox-guest.md +191 -0
- package/docs/api/sandbox-wire.md +117 -0
- package/docs/api/sandbox.md +81 -0
- package/docs/api/semaphore-transition.md +40 -0
- package/docs/api/testing.md +72 -0
- package/docs/api/workers.md +55 -0
- package/package.json +132 -3
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ForageCatalog } from "@fungi.computer/forage";
|
|
2
|
+
import { type ModuleRegistration, type StoredOperation } from "./registration.js";
|
|
3
|
+
/** One deterministically named user operation in an installed capability surface. */
|
|
4
|
+
export type CompiledOperation = Readonly<{
|
|
5
|
+
/** Grant key of the top-level module whose grant decides this operation. */
|
|
6
|
+
readonly grantKey: string;
|
|
7
|
+
readonly path: string;
|
|
8
|
+
readonly namespace: string;
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly moduleDescription: string | undefined;
|
|
11
|
+
readonly operation: StoredOperation;
|
|
12
|
+
}>;
|
|
13
|
+
/** Immutable catalog and user-operation graph installed as one revision. */
|
|
14
|
+
export type CompiledSurface = Readonly<{
|
|
15
|
+
readonly revision: string;
|
|
16
|
+
readonly catalog: ForageCatalog;
|
|
17
|
+
readonly operations: readonly CompiledOperation[];
|
|
18
|
+
/** Every allocated execution alias mapped to its stable module id. */
|
|
19
|
+
readonly moduleAliases: Readonly<Record<string, string>>;
|
|
20
|
+
readonly namespacePaths: Readonly<Record<string, readonly string[]>>;
|
|
21
|
+
}>;
|
|
22
|
+
/** Secret-free source identity included only in the opaque installed revision. */
|
|
23
|
+
export type SourceRevisionFrame = Readonly<{
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly version: string;
|
|
26
|
+
}>;
|
|
27
|
+
/** Compile opaque declarations and Forage intrinsics into one immutable surface. */
|
|
28
|
+
export declare function compileSurface(registrations: readonly ModuleRegistration[], sourceFrames?: readonly SourceRevisionFrame[]): CompiledSurface;
|
package/dist/compiler.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { createCatalog, forageDescribeDefinition, forageSearchDefinition, forageListDefinition, } from "@fungi.computer/forage";
|
|
2
|
+
import { sandboxNamespaceSegmentSchema, sandboxTransportAliasSchema, } from "./sandbox-guest.js";
|
|
3
|
+
import { MyceliumDefinitionError } from "./errors.js";
|
|
4
|
+
import { readModule, isModuleRegistration, readOperation, } from "./registration.js";
|
|
5
|
+
function compareCodeUnits(left, right) {
|
|
6
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
7
|
+
}
|
|
8
|
+
function definitionMessage(cause) {
|
|
9
|
+
return cause instanceof Error
|
|
10
|
+
? cause.message
|
|
11
|
+
: "Capability declarations could not be compiled";
|
|
12
|
+
}
|
|
13
|
+
const maximumModuleIdLength = 160;
|
|
14
|
+
function assertModuleId(value) {
|
|
15
|
+
if (value.length === 0 || value.length > maximumModuleIdLength) {
|
|
16
|
+
throw new MyceliumDefinitionError("Module id must be non-empty and bounded");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function assertOperationName(value) {
|
|
20
|
+
if (!value
|
|
21
|
+
.split(".")
|
|
22
|
+
.every((segment) => sandboxNamespaceSegmentSchema.safeParse(segment).success)) {
|
|
23
|
+
throw new MyceliumDefinitionError("Operation key must be a path segment or a dotted relative path");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function namespaceName(name) {
|
|
27
|
+
const sanitized = name.replace(/[^A-Za-z0-9_]/g, "_");
|
|
28
|
+
return /^[A-Za-z]/.test(sanitized) ? sanitized : `module_${sanitized}`;
|
|
29
|
+
}
|
|
30
|
+
function executionAlias(name) {
|
|
31
|
+
const segment = namespaceName(name);
|
|
32
|
+
return sandboxTransportAliasSchema.safeParse(segment).success
|
|
33
|
+
? segment
|
|
34
|
+
: `module_${segment}`;
|
|
35
|
+
}
|
|
36
|
+
function allocateAliases(modules, scope) {
|
|
37
|
+
const used = new Set(["forage", "env"]);
|
|
38
|
+
const aliases = new Map();
|
|
39
|
+
for (const module of modules) {
|
|
40
|
+
const prefix = scope === "logical" ? module.namespace : [];
|
|
41
|
+
const base = scope === "logical"
|
|
42
|
+
? namespaceName(module.name)
|
|
43
|
+
: executionAlias(module.name);
|
|
44
|
+
const fullPath = (name) => [...prefix, name].join(".");
|
|
45
|
+
let alias = base;
|
|
46
|
+
let suffix = 2;
|
|
47
|
+
while (used.has(fullPath(alias))) {
|
|
48
|
+
alias = `${base}_${String(suffix)}`;
|
|
49
|
+
suffix += 1;
|
|
50
|
+
}
|
|
51
|
+
used.add(fullPath(alias));
|
|
52
|
+
aliases.set(module.id, alias);
|
|
53
|
+
}
|
|
54
|
+
return aliases;
|
|
55
|
+
}
|
|
56
|
+
const fnvOffsetBasis = 0xcbf29ce484222325n;
|
|
57
|
+
const fnvPrime = 0x100000001b3n;
|
|
58
|
+
const unsigned64Mask = 0xffffffffffffffffn;
|
|
59
|
+
/** Hash one length-framed UTF-16 string into the bounded installed identity. */
|
|
60
|
+
function hashFrame(hash, value) {
|
|
61
|
+
let result = hash;
|
|
62
|
+
let length = BigInt(value.length);
|
|
63
|
+
for (let index = 0; index < 8; index += 1) {
|
|
64
|
+
result = ((result ^ (length & 0xffn)) * fnvPrime) & unsigned64Mask;
|
|
65
|
+
length >>= 8n;
|
|
66
|
+
}
|
|
67
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
68
|
+
const codeUnit = value.charCodeAt(index);
|
|
69
|
+
result = ((result ^ BigInt(codeUnit & 0xff)) * fnvPrime) & unsigned64Mask;
|
|
70
|
+
result =
|
|
71
|
+
((result ^ BigInt((codeUnit >>> 8) & 0xff)) * fnvPrime) & unsigned64Mask;
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
/** Keep content identity and installed stable-id alias identity distinct. */
|
|
76
|
+
function installedRevision(contentStamp, aliases, sourceFrames) {
|
|
77
|
+
let hash = hashFrame(fnvOffsetBasis, contentStamp);
|
|
78
|
+
const mapping = [...aliases.entries()].sort(([left], [right]) => compareCodeUnits(left, right));
|
|
79
|
+
for (const [stableId, alias] of mapping) {
|
|
80
|
+
hash = hashFrame(hash, stableId);
|
|
81
|
+
hash = hashFrame(hash, alias);
|
|
82
|
+
}
|
|
83
|
+
const frames = [...sourceFrames].sort((left, right) => compareCodeUnits(left.id, right.id));
|
|
84
|
+
for (const frame of frames) {
|
|
85
|
+
hash = hashFrame(hash, frame.id);
|
|
86
|
+
hash = hashFrame(hash, frame.version);
|
|
87
|
+
}
|
|
88
|
+
return `installed-${hash.toString(16).padStart(16, "0")}`;
|
|
89
|
+
}
|
|
90
|
+
function intrinsicOperations() {
|
|
91
|
+
return [
|
|
92
|
+
forageDescribeDefinition,
|
|
93
|
+
forageSearchDefinition,
|
|
94
|
+
forageListDefinition,
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
/** Flatten child modules beneath their parent's single authority identity. */
|
|
98
|
+
function flattenOperations(grantKey, module, namespace, path, description, names = []) {
|
|
99
|
+
return Object.entries(module.operations).flatMap(([name, registration]) => {
|
|
100
|
+
assertOperationName(name);
|
|
101
|
+
if (isModuleRegistration(registration)) {
|
|
102
|
+
const child = readModule(registration);
|
|
103
|
+
if (child === undefined)
|
|
104
|
+
throw new MyceliumDefinitionError("Unknown child module registration");
|
|
105
|
+
return flattenOperations(grantKey, child, namespace, [...path, name, ...child.namespace], child.description ?? description, [...names, name, ...child.namespace]);
|
|
106
|
+
}
|
|
107
|
+
const operation = readOperation(registration);
|
|
108
|
+
if (operation === undefined)
|
|
109
|
+
throw new MyceliumDefinitionError(`Unknown operation registration: ${[...path, name].join(".")}`);
|
|
110
|
+
return [
|
|
111
|
+
Object.freeze({
|
|
112
|
+
grantKey,
|
|
113
|
+
path: [...path, name].join("."),
|
|
114
|
+
namespace,
|
|
115
|
+
name: [...names, name].join("."),
|
|
116
|
+
moduleDescription: description,
|
|
117
|
+
operation,
|
|
118
|
+
}),
|
|
119
|
+
];
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/** Compile opaque declarations and Forage intrinsics into one immutable surface. */
|
|
123
|
+
export function compileSurface(registrations, sourceFrames = []) {
|
|
124
|
+
const modules = registrations
|
|
125
|
+
.map((registration) => {
|
|
126
|
+
const module = readModule(registration);
|
|
127
|
+
if (module === undefined) {
|
|
128
|
+
throw new MyceliumDefinitionError("Unknown module registration");
|
|
129
|
+
}
|
|
130
|
+
assertModuleId(module.id);
|
|
131
|
+
return module;
|
|
132
|
+
})
|
|
133
|
+
.sort((left, right) => compareCodeUnits(left.id, right.id));
|
|
134
|
+
const ids = new Set();
|
|
135
|
+
for (const module of modules) {
|
|
136
|
+
if (ids.has(module.id)) {
|
|
137
|
+
throw new MyceliumDefinitionError(`Duplicate module id: ${module.id}`);
|
|
138
|
+
}
|
|
139
|
+
ids.add(module.id);
|
|
140
|
+
}
|
|
141
|
+
const aliases = allocateAliases(modules, "transport");
|
|
142
|
+
const logicalAliases = allocateAliases(modules, "logical");
|
|
143
|
+
const namespacePaths = Object.freeze(Object.fromEntries(modules.map((module) => {
|
|
144
|
+
const alias = aliases.get(module.id);
|
|
145
|
+
if (alias === undefined)
|
|
146
|
+
throw new MyceliumDefinitionError(`Missing module alias: ${module.id}`);
|
|
147
|
+
const logical = logicalAliases.get(module.id);
|
|
148
|
+
if (logical === undefined)
|
|
149
|
+
throw new MyceliumDefinitionError(`Missing logical alias: ${module.id}`);
|
|
150
|
+
return [alias, Object.freeze([...module.namespace, logical])];
|
|
151
|
+
})));
|
|
152
|
+
const operations = modules
|
|
153
|
+
.flatMap((module) => {
|
|
154
|
+
const alias = aliases.get(module.id);
|
|
155
|
+
if (alias === undefined)
|
|
156
|
+
throw new MyceliumDefinitionError(`Missing module alias: ${module.id}`);
|
|
157
|
+
const path = namespacePaths[alias];
|
|
158
|
+
if (path === undefined)
|
|
159
|
+
throw new MyceliumDefinitionError(`Missing namespace path: ${module.id}`);
|
|
160
|
+
return flattenOperations(module.grantKey, module, alias, path, module.description);
|
|
161
|
+
})
|
|
162
|
+
.sort((left, right) => compareCodeUnits(left.path, right.path));
|
|
163
|
+
const operationPaths = new Set(operations.map((operation) => operation.path));
|
|
164
|
+
if (operationPaths.size !== operations.length)
|
|
165
|
+
throw new MyceliumDefinitionError("Duplicate operation path");
|
|
166
|
+
for (const intrinsic of intrinsicOperations()) {
|
|
167
|
+
if (operationPaths.has(intrinsic.path))
|
|
168
|
+
throw new MyceliumDefinitionError(`Reserved intrinsic path: ${intrinsic.path}`);
|
|
169
|
+
operationPaths.add(intrinsic.path);
|
|
170
|
+
}
|
|
171
|
+
for (const path of operationPaths) {
|
|
172
|
+
const segments = path.split(".");
|
|
173
|
+
for (let length = 1; length < segments.length; length += 1)
|
|
174
|
+
if (operationPaths.has(segments.slice(0, length).join(".")))
|
|
175
|
+
throw new MyceliumDefinitionError(`Operation conflicts with namespace: ${path}`);
|
|
176
|
+
}
|
|
177
|
+
let prepared;
|
|
178
|
+
try {
|
|
179
|
+
prepared = createCatalog({
|
|
180
|
+
revision: "prepared-content-stamp",
|
|
181
|
+
operations: [
|
|
182
|
+
...operations.map(({ path, moduleDescription, operation }) => {
|
|
183
|
+
const catalogOperation = {
|
|
184
|
+
path,
|
|
185
|
+
input: operation.input,
|
|
186
|
+
};
|
|
187
|
+
if (operation.description !== undefined) {
|
|
188
|
+
catalogOperation.description = operation.description;
|
|
189
|
+
}
|
|
190
|
+
if (operation.output !== undefined) {
|
|
191
|
+
catalogOperation.output = operation.output;
|
|
192
|
+
}
|
|
193
|
+
if (moduleDescription !== undefined) {
|
|
194
|
+
catalogOperation.instructions = moduleDescription;
|
|
195
|
+
}
|
|
196
|
+
return catalogOperation;
|
|
197
|
+
}),
|
|
198
|
+
...intrinsicOperations(),
|
|
199
|
+
],
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
catch (cause) {
|
|
203
|
+
throw new MyceliumDefinitionError(definitionMessage(cause));
|
|
204
|
+
}
|
|
205
|
+
const revision = installedRevision(prepared.stamp(), aliases, sourceFrames);
|
|
206
|
+
const catalog = Object.freeze({
|
|
207
|
+
revision,
|
|
208
|
+
search: prepared.search,
|
|
209
|
+
describe: prepared.describe,
|
|
210
|
+
list: prepared.list,
|
|
211
|
+
stamp: prepared.stamp,
|
|
212
|
+
});
|
|
213
|
+
const moduleAliases = Object.freeze(Object.fromEntries([...aliases.entries()].map(([moduleId, alias]) => [alias, moduleId])));
|
|
214
|
+
return Object.freeze({
|
|
215
|
+
revision,
|
|
216
|
+
catalog,
|
|
217
|
+
operations: Object.freeze(operations),
|
|
218
|
+
moduleAliases,
|
|
219
|
+
namespacePaths,
|
|
220
|
+
});
|
|
221
|
+
}
|
package/dist/define.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime-free authoring: declare modules and operations without loading the
|
|
3
|
+
* execute runtime, so a definition can be bundled into an isolate that only
|
|
4
|
+
* describes and invokes it (for example a hosted Agent extension).
|
|
5
|
+
*/
|
|
6
|
+
import type { MyceliumApi } from "./index.js";
|
|
7
|
+
/** The authoring half of {@link MyceliumApi}; registrations are the same handles. */
|
|
8
|
+
export declare const Mycelium: Pick<MyceliumApi, "module" | "operation" | "gate">;
|
package/dist/define.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { defineModule, defineOperation, gateModule } from "./registration.js";
|
|
2
|
+
/** The authoring half of {@link MyceliumApi}; registrations are the same handles. */
|
|
3
|
+
export const Mycelium = Object.freeze({
|
|
4
|
+
module: defineModule,
|
|
5
|
+
operation: defineOperation,
|
|
6
|
+
gate: gateModule,
|
|
7
|
+
});
|
package/dist/effect.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { SandboxFailureEvent } from "./sandbox-failure-event.js";
|
|
2
|
+
import type { ForageCatalog } from "@fungi.computer/forage";
|
|
3
|
+
import type { MuleTool } from "@fungi.computer/mule/effect";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Scope from "effect/Scope";
|
|
6
|
+
import { type SandboxFailure } from "./sandbox-failure.js";
|
|
7
|
+
import type { MyceliumError } from "./errors.js";
|
|
8
|
+
import type { ExecutionOptions } from "./execute-tool.js";
|
|
9
|
+
import type { CapabilitySession, CapabilitySource, ModuleRegistration, Sandbox as PromiseSandbox, SandboxBinding, SandboxRequest, SandboxValue } from "./index.js";
|
|
10
|
+
import type { AccessOptions, LabelledTool } from "./access.js";
|
|
11
|
+
export type { CapabilitySession, CapabilitySource, ModuleRegistration, SandboxBinding, SandboxRequest, };
|
|
12
|
+
export type { ExecutionOptions } from "./execute-tool.js";
|
|
13
|
+
export type { Access, AccessContext, AccessLabel, AccessOptions, AccessRefusal, AccessRefusalReason, ConfirmRequest, Grant, Grants, LabelledTool, ToolPolicy, } from "./access.js";
|
|
14
|
+
/**
|
|
15
|
+
* Effect-native host seam for sandbox execution.
|
|
16
|
+
*
|
|
17
|
+
* Interrupting `execute` must not complete until the backing work has actually
|
|
18
|
+
* settled. Mycelium bounds that wait by the acknowledgement grace and reports
|
|
19
|
+
* `abort_unacknowledged` when it expires, keeping the work's source revision
|
|
20
|
+
* alive until it exits. A host with Promise-shaped work adapts it with
|
|
21
|
+
* {@link fromTypedSandbox}; `Effect.tryPromise` alone completes interruption
|
|
22
|
+
* without awaiting the Promise and so breaks that settlement law.
|
|
23
|
+
*/
|
|
24
|
+
export type Sandbox = Readonly<{
|
|
25
|
+
/**
|
|
26
|
+
* Evaluate one bounded program within the caller's Effect scope. `signal` is
|
|
27
|
+
* Mycelium's invocation abort projection for Promise-shaped host work.
|
|
28
|
+
*/
|
|
29
|
+
execute: (request: SandboxRequest, signal: AbortSignal) => Effect.Effect<SandboxValue, SandboxFailure>;
|
|
30
|
+
/** Operator identity supplied by the host. */
|
|
31
|
+
adapter?: string;
|
|
32
|
+
/** Source revision embedded by the build, absent in an unpacked source run. */
|
|
33
|
+
buildSha?: string | undefined;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Adapt a Promise-shaped sandbox to the Effect seam. This is Mycelium's one
|
|
37
|
+
* Promise sandbox conversion: interruption waits for the host Promise to
|
|
38
|
+
* settle, while cancellation reaches the host through the abort signal.
|
|
39
|
+
*/
|
|
40
|
+
export declare function fromTypedSandbox(sandbox: PromiseSandbox): Sandbox;
|
|
41
|
+
/** Inputs used to construct the canonical Effect runtime. */
|
|
42
|
+
export type MyceliumOptions = Readonly<{
|
|
43
|
+
/** Unordered module registrations compiled into one immutable revision. */
|
|
44
|
+
modules?: readonly ModuleRegistration[];
|
|
45
|
+
/** Versioned host projections reconciled into immutable revisions. */
|
|
46
|
+
sources?: readonly CapabilitySource[];
|
|
47
|
+
/** Session every source acquisition is made for; absent for session-free hosts. */
|
|
48
|
+
session?: CapabilitySession;
|
|
49
|
+
/** Bounded Mycelium-owned deadline and cancellation acknowledgement policy. */
|
|
50
|
+
execution?: ExecutionOptions;
|
|
51
|
+
/** Host-owned Effect boundary for model-authored programs. */
|
|
52
|
+
sandbox: Sandbox;
|
|
53
|
+
/** Operator-only failure sink; never contributes a model message. */
|
|
54
|
+
onSandboxFailure?: (event: SandboxFailureEvent) => Effect.Effect<void>;
|
|
55
|
+
/** Labelled direct tools offered beside execute under the same access check. */
|
|
56
|
+
tools?: readonly LabelledTool[];
|
|
57
|
+
/** Host grants and approval, checked against each call's labels. */
|
|
58
|
+
access?: AccessOptions;
|
|
59
|
+
}>;
|
|
60
|
+
/** One immutable Effect-facing capability revision. */
|
|
61
|
+
export type Lease = Readonly<{
|
|
62
|
+
/** Installed capability revision captured by this immutable lease. */
|
|
63
|
+
revision: string;
|
|
64
|
+
/** Bounded discovery catalog for the captured revision. */
|
|
65
|
+
catalog: ForageCatalog;
|
|
66
|
+
/** Single Effect-native Mule tool for discovery and scoped execution. */
|
|
67
|
+
executeTool: MuleTool;
|
|
68
|
+
/** The runtime's labelled direct tools, each behind the access check. */
|
|
69
|
+
tools: readonly MuleTool[];
|
|
70
|
+
/**
|
|
71
|
+
* Release explicitly with a typed failure. The owning scope also awaits this
|
|
72
|
+
* cleanup; automatic-finalizer failure terminates that scope as the same
|
|
73
|
+
* identifiable defect because Effect finalizers are infallible.
|
|
74
|
+
*/
|
|
75
|
+
release: () => Effect.Effect<void, MyceliumError>;
|
|
76
|
+
}>;
|
|
77
|
+
/** Warm Effect lifecycle for acquiring scope-owned run leases. */
|
|
78
|
+
export type Runtime = Readonly<{
|
|
79
|
+
/** Acquire one immutable lease owned by the caller's Effect scope. */
|
|
80
|
+
acquire: () => Effect.Effect<Lease, MyceliumError, Scope.Scope>;
|
|
81
|
+
/**
|
|
82
|
+
* Stop acquisition and await cleanup with a typed failure when called
|
|
83
|
+
* explicitly. Automatic scope cleanup retains a failure as an identifiable
|
|
84
|
+
* defect under Effect's infallible-finalizer contract.
|
|
85
|
+
*/
|
|
86
|
+
close: () => Effect.Effect<void, MyceliumError>;
|
|
87
|
+
}>;
|
|
88
|
+
/** Construct the canonical scoped Effect runtime. */
|
|
89
|
+
export declare function make(options: MyceliumOptions): Effect.Effect<Runtime, MyceliumError, Scope.Scope>;
|
package/dist/effect.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { sandboxFailure } from "./sandbox-boundary.js";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Scope from "effect/Scope";
|
|
4
|
+
import {} from "./sandbox-failure.js";
|
|
5
|
+
import { openCoreRuntime } from "./runtime.js";
|
|
6
|
+
/**
|
|
7
|
+
* Adapt a Promise-shaped sandbox to the Effect seam. This is Mycelium's one
|
|
8
|
+
* Promise sandbox conversion: interruption waits for the host Promise to
|
|
9
|
+
* settle, while cancellation reaches the host through the abort signal.
|
|
10
|
+
*/
|
|
11
|
+
export function fromTypedSandbox(sandbox) {
|
|
12
|
+
return Object.freeze({
|
|
13
|
+
adapter: sandbox.adapter ?? "promise",
|
|
14
|
+
buildSha: sandbox.buildSha,
|
|
15
|
+
execute: (request, signal) => Effect.uninterruptible(Effect.tryPromise({
|
|
16
|
+
try: () => sandbox.execute(request, signal),
|
|
17
|
+
catch: sandboxFailure,
|
|
18
|
+
})),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** Construct the canonical scoped Effect runtime. */
|
|
22
|
+
export function make(options) {
|
|
23
|
+
return openCoreRuntime({
|
|
24
|
+
modules: options.modules ?? [],
|
|
25
|
+
sources: options.sources ?? [],
|
|
26
|
+
session: options.session,
|
|
27
|
+
execution: options.execution,
|
|
28
|
+
sandbox: options.sandbox,
|
|
29
|
+
onSandboxFailure: options.onSandboxFailure,
|
|
30
|
+
tools: options.tools ?? [],
|
|
31
|
+
access: options.access ?? {},
|
|
32
|
+
}).pipe(Effect.map((runtime) => Object.freeze({
|
|
33
|
+
acquire: () => runtime.acquire().pipe(Effect.map((lease) => Object.freeze({
|
|
34
|
+
revision: lease.revision,
|
|
35
|
+
catalog: lease.catalog,
|
|
36
|
+
executeTool: lease.effectExecuteTool,
|
|
37
|
+
tools: lease.tools,
|
|
38
|
+
release: lease.release,
|
|
39
|
+
}))),
|
|
40
|
+
close: runtime.close,
|
|
41
|
+
})));
|
|
42
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { OperationSchemaIssue } from "./operation-schema.js";
|
|
2
|
+
import type { SandboxFailure } from "./sandbox-failure.js";
|
|
3
|
+
/** Lifecycle phase in which a capability source failed. */
|
|
4
|
+
export type MyceliumSourcePhase = "acquire" | "release" | "stale";
|
|
5
|
+
/** A capability declaration could not form one coherent installed surface. */
|
|
6
|
+
export declare class MyceliumDefinitionError extends Error {
|
|
7
|
+
/** Stable machine-readable failure code. */
|
|
8
|
+
readonly code = "invalid_definition";
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
/** An operation attempted to use a closed Mycelium runtime. */
|
|
12
|
+
export declare class MyceliumClosedError extends Error {
|
|
13
|
+
/** Stable machine-readable failure code. */
|
|
14
|
+
readonly code = "runtime_closed";
|
|
15
|
+
constructor(message?: string);
|
|
16
|
+
}
|
|
17
|
+
/** A capability source failed at its bounded host boundary. */
|
|
18
|
+
export declare class MyceliumSourceError extends Error {
|
|
19
|
+
/** Stable identity of the source, absent for whole-candidate failures. */
|
|
20
|
+
readonly sourceId: string | undefined;
|
|
21
|
+
/** Lifecycle phase that could not settle. */
|
|
22
|
+
readonly phase: MyceliumSourcePhase;
|
|
23
|
+
/** Stable machine-readable failure code. */
|
|
24
|
+
readonly code = "source_failure";
|
|
25
|
+
constructor(
|
|
26
|
+
/** Stable identity of the source, absent for whole-candidate failures. */
|
|
27
|
+
sourceId: string | undefined,
|
|
28
|
+
/** Lifecycle phase that could not settle. */
|
|
29
|
+
phase: MyceliumSourcePhase);
|
|
30
|
+
}
|
|
31
|
+
/** Closed failure vocabulary for the first Mycelium runtime slice. */
|
|
32
|
+
export type MyceliumError = MyceliumClosedError | MyceliumDefinitionError | MyceliumSourceError | SandboxFailure;
|
|
33
|
+
/** Untrusted operation input failed the operation's declared input schema. */
|
|
34
|
+
export declare class InvalidOperationInput extends Error {
|
|
35
|
+
/** Schema issues, formatted for the model by the execute door. */
|
|
36
|
+
readonly issues: readonly OperationSchemaIssue[];
|
|
37
|
+
constructor(
|
|
38
|
+
/** Schema issues, formatted for the model by the execute door. */
|
|
39
|
+
issues: readonly OperationSchemaIssue[]);
|
|
40
|
+
}
|
|
41
|
+
/** An operation's result failed its declared output schema. */
|
|
42
|
+
export declare class InvalidOperationOutput extends Error {
|
|
43
|
+
/** Schema issues, kept for operators; never shown to the model. */
|
|
44
|
+
readonly issues: readonly OperationSchemaIssue[];
|
|
45
|
+
constructor(
|
|
46
|
+
/** Schema issues, kept for operators; never shown to the model. */
|
|
47
|
+
issues: readonly OperationSchemaIssue[]);
|
|
48
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const invalidDefinitionCode = "invalid_definition";
|
|
2
|
+
const runtimeClosedCode = "runtime_closed";
|
|
3
|
+
const sourceFailureCode = "source_failure";
|
|
4
|
+
/** A capability declaration could not form one coherent installed surface. */
|
|
5
|
+
export class MyceliumDefinitionError extends Error {
|
|
6
|
+
/** Stable machine-readable failure code. */
|
|
7
|
+
code = invalidDefinitionCode;
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "MyceliumDefinitionError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** An operation attempted to use a closed Mycelium runtime. */
|
|
14
|
+
export class MyceliumClosedError extends Error {
|
|
15
|
+
/** Stable machine-readable failure code. */
|
|
16
|
+
code = runtimeClosedCode;
|
|
17
|
+
constructor(message = "Mycelium runtime is closed") {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "MyceliumClosedError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** A capability source failed at its bounded host boundary. */
|
|
23
|
+
export class MyceliumSourceError extends Error {
|
|
24
|
+
sourceId;
|
|
25
|
+
phase;
|
|
26
|
+
/** Stable machine-readable failure code. */
|
|
27
|
+
code = sourceFailureCode;
|
|
28
|
+
constructor(
|
|
29
|
+
/** Stable identity of the source, absent for whole-candidate failures. */
|
|
30
|
+
sourceId,
|
|
31
|
+
/** Lifecycle phase that could not settle. */
|
|
32
|
+
phase) {
|
|
33
|
+
super(sourceId === undefined
|
|
34
|
+
? `Capability source ${phase} failed`
|
|
35
|
+
: `Capability source ${phase} failed: ${sourceId}`);
|
|
36
|
+
this.sourceId = sourceId;
|
|
37
|
+
this.phase = phase;
|
|
38
|
+
this.name = "MyceliumSourceError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Untrusted operation input failed the operation's declared input schema. */
|
|
42
|
+
export class InvalidOperationInput extends Error {
|
|
43
|
+
issues;
|
|
44
|
+
constructor(
|
|
45
|
+
/** Schema issues, formatted for the model by the execute door. */
|
|
46
|
+
issues) {
|
|
47
|
+
super("Invalid operation input");
|
|
48
|
+
this.issues = issues;
|
|
49
|
+
this.name = "InvalidOperationInput";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** An operation's result failed its declared output schema. */
|
|
53
|
+
export class InvalidOperationOutput extends Error {
|
|
54
|
+
issues;
|
|
55
|
+
constructor(
|
|
56
|
+
/** Schema issues, kept for operators; never shown to the model. */
|
|
57
|
+
issues) {
|
|
58
|
+
super("Operation output failed its declared schema");
|
|
59
|
+
this.issues = issues;
|
|
60
|
+
this.name = "InvalidOperationOutput";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ForageCatalog } from "@fungi.computer/forage";
|
|
2
|
+
import { type MuleTool } from "@fungi.computer/mule/effect";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import { type AccessOptions } from "./access.js";
|
|
5
|
+
import type { CompiledOperation } from "./compiler.js";
|
|
6
|
+
import { type BackingWorkGroup, type SerializedSandboxValue } from "./internal/execution-lifecycle.js";
|
|
7
|
+
import { type MyceliumError } from "./errors.js";
|
|
8
|
+
import type { EffectSandbox } from "./runtime.js";
|
|
9
|
+
/** Host-selectable execution limits; neither budget can be disabled. */
|
|
10
|
+
export type ExecutionOptions = Readonly<{
|
|
11
|
+
/** Total execution deadline in milliseconds. */
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
/** Maximum acknowledgement wait after cancellation in milliseconds. */
|
|
14
|
+
abortGraceMs?: number;
|
|
15
|
+
}>;
|
|
16
|
+
/** The Effect-owned execute implementation shared by both public projections. */
|
|
17
|
+
export type ExecuteDoor = Readonly<{
|
|
18
|
+
readonly effectTool: MuleTool;
|
|
19
|
+
readonly execute: (code: string, signal?: AbortSignal) => Effect.Effect<SerializedSandboxValue, MyceliumError>;
|
|
20
|
+
}>;
|
|
21
|
+
/** Construct one execute door over a stable compiled surface and sandbox adapter. */
|
|
22
|
+
export declare function createExecuteDoor(operations: readonly CompiledOperation[], catalog: ForageCatalog, sandbox: EffectSandbox, backingWork: BackingWorkGroup, modules: Readonly<Record<string, string>>, namespacePaths: Readonly<Record<string, readonly string[]>>, sessionId: string | null, access: AccessOptions): ExecuteDoor;
|