@atolis-hq/wake 0.4.47 → 0.4.49
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/README.md +3 -0
- package/dist/src/activities/agent/agent-activity.js +9 -3
- package/dist/src/activities/contracts/activity.d.ts +14 -0
- package/dist/src/artifacts/application/artifact-projection.d.ts +3 -0
- package/dist/src/artifacts/application/artifact-projection.js +15 -0
- package/dist/src/artifacts/application/artifact-repository.d.ts +13 -0
- package/dist/src/artifacts/application/artifact-repository.js +19 -0
- package/dist/src/artifacts/application/artifact-service.d.ts +40 -0
- package/dist/src/artifacts/application/artifact-service.js +128 -0
- package/dist/src/artifacts/contracts/config.d.ts +6 -0
- package/dist/src/artifacts/contracts/config.js +19 -0
- package/dist/src/artifacts/contracts/event-factory.d.ts +6 -0
- package/dist/src/artifacts/contracts/event-factory.js +10 -0
- package/dist/src/artifacts/contracts/events.d.ts +33 -0
- package/dist/src/artifacts/contracts/events.js +59 -0
- package/dist/src/artifacts/contracts/identifiers.d.ts +5 -0
- package/dist/src/artifacts/contracts/identifiers.js +8 -0
- package/dist/src/artifacts/contracts/paths.d.ts +5 -0
- package/dist/src/artifacts/contracts/paths.js +9 -0
- package/dist/src/artifacts/contracts/streams.d.ts +7 -0
- package/dist/src/artifacts/contracts/streams.js +4 -0
- package/dist/src/artifacts/contracts/views.d.ts +20 -0
- package/dist/src/artifacts/contracts/views.js +1 -0
- package/dist/src/artifacts/domain/artifact-work-item.d.ts +4 -0
- package/dist/src/artifacts/domain/artifact-work-item.js +42 -0
- package/dist/src/artifacts/index.d.ts +14 -0
- package/dist/src/artifacts/index.js +14 -0
- package/dist/src/artifacts/infrastructure/file-artifact-store.d.ts +22 -0
- package/dist/src/artifacts/infrastructure/file-artifact-store.js +51 -0
- package/dist/src/artifacts/infrastructure/mcp-server.d.ts +18 -0
- package/dist/src/artifacts/infrastructure/mcp-server.js +135 -0
- package/dist/src/artifacts/infrastructure/mcp-session-store.d.ts +25 -0
- package/dist/src/artifacts/infrastructure/mcp-session-store.js +61 -0
- package/dist/src/bootstrap/artifact-mcp.d.ts +23 -0
- package/dist/src/bootstrap/artifact-mcp.js +119 -0
- package/dist/src/bootstrap/board-projection.d.ts +2 -0
- package/dist/src/bootstrap/board-projection.js +27 -11
- package/dist/src/bootstrap/composition-root.d.ts +2 -0
- package/dist/src/bootstrap/composition-root.js +13 -0
- package/dist/src/bootstrap/config/root-schema.d.ts +6 -0
- package/dist/src/bootstrap/config/root-schema.js +5 -0
- package/dist/src/bootstrap/paths.d.ts +1 -0
- package/dist/src/bootstrap/paths.js +1 -0
- package/dist/src/bootstrap/projection-runtime.js +2 -0
- package/dist/src/bootstrap/runner-registry.js +22 -4
- package/dist/src/bootstrap/surface-api-work-applications.js +39 -1
- package/dist/src/bootstrap/surface-cli-applications.js +12 -1
- package/dist/src/bootstrap/version.js +1 -1
- package/dist/src/execution/application/execution-activity.d.ts +11 -0
- package/dist/src/execution/application/execution-activity.js +26 -10
- package/dist/src/execution/application/execution-service.js +1 -1
- package/dist/src/execution/contracts/runner.d.ts +10 -0
- package/dist/src/execution/infrastructure/runners/claude.d.ts +1 -0
- package/dist/src/execution/infrastructure/runners/claude.js +21 -0
- package/dist/src/execution/infrastructure/runners/codex.js +12 -0
- package/dist/src/execution/infrastructure/runners/fake.d.ts +1 -0
- package/dist/src/execution/infrastructure/runners/fake.js +1 -0
- package/dist/src/execution/infrastructure/runners/registry.d.ts +3 -1
- package/dist/src/execution/infrastructure/runners/registry.js +5 -3
- package/dist/src/surfaces/api/contracts/board.d.ts +2 -0
- package/dist/src/surfaces/api/contracts/work.d.ts +10 -0
- package/dist/src/surfaces/api/routes/applications.d.ts +5 -0
- package/dist/src/surfaces/api/routes/read.js +34 -3
- package/dist/src/surfaces/api/routes/work.d.ts +1 -1
- package/dist/src/surfaces/api/routes/work.js +1 -0
- package/dist/src/surfaces/cli/main.d.ts +8 -0
- package/dist/src/surfaces/cli/main.js +31 -0
- package/dist/src/surfaces/cli/usage.js +1 -0
- package/dist/src/surfaces/index.d.ts +1 -0
- package/dist/src/surfaces/index.js +1 -0
- package/dist/src/surfaces/mcp/stdio.d.ts +3 -0
- package/dist/src/surfaces/mcp/stdio.js +5 -0
- package/dist/src/surfaces/web-assets/assets/{index-aTndh5BE.js → index-CuIiUKuh.js} +3 -3
- package/dist/src/surfaces/web-assets/assets/index-DKpuc-RV.css +1 -0
- package/dist/src/surfaces/web-assets/index.html +2 -2
- package/package.json +3 -1
- package/dist/src/surfaces/web-assets/assets/index-D1NjO4qa.css +0 -1
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
import { createClaudeRunner, createCodexRunner, createCommandRunner, createCursorRunner, emptyFakeScenarios, FakeExecutionRunner, RunnerRegistry, } from '../execution/index.js';
|
|
2
2
|
export function createRunnerRegistry(config, scenarios = emptyFakeScenarios, decorateRunner) {
|
|
3
|
-
const runners = Object.fromEntries(Object.entries(config.agentRunners ?? {}).map(([name, runner]) =>
|
|
4
|
-
name,
|
|
5
|
-
(decorateRunner ?? identity)(
|
|
6
|
-
|
|
3
|
+
const runners = Object.fromEntries(Object.entries(config.agentRunners ?? {}).map(([name, runner]) => {
|
|
4
|
+
const configured = createConfiguredRunner(name, runner, scenarios);
|
|
5
|
+
const decorated = (decorateRunner ?? identity)(configured, name);
|
|
6
|
+
return [name, preserveRunnerCapabilities(configured, decorated)];
|
|
7
|
+
}));
|
|
7
8
|
return new RunnerRegistry(config.runnerPools, runners);
|
|
8
9
|
}
|
|
10
|
+
// Test and operational decorators commonly wrap only `start`; adapter capabilities
|
|
11
|
+
// describe the underlying transport and must survive that wrapper.
|
|
12
|
+
function preserveRunnerCapabilities(configured, decorated) {
|
|
13
|
+
const capabilities = {
|
|
14
|
+
...(decorated.supportsSessionResume === undefined &&
|
|
15
|
+
configured.supportsSessionResume !== undefined
|
|
16
|
+
? { supportsSessionResume: configured.supportsSessionResume }
|
|
17
|
+
: {}),
|
|
18
|
+
...(decorated.supportsEphemeralMcp === undefined &&
|
|
19
|
+
configured.supportsEphemeralMcp !== undefined
|
|
20
|
+
? { supportsEphemeralMcp: configured.supportsEphemeralMcp }
|
|
21
|
+
: {}),
|
|
22
|
+
};
|
|
23
|
+
if (Object.keys(capabilities).length === 0)
|
|
24
|
+
return decorated;
|
|
25
|
+
return Object.assign(Object.create(decorated), capabilities);
|
|
26
|
+
}
|
|
9
27
|
function identity(value) {
|
|
10
28
|
return value;
|
|
11
29
|
}
|
|
@@ -4,7 +4,7 @@ import { conversationIdForWorkItem, ConversationOriginKind, } from '../conversat
|
|
|
4
4
|
import { executionProjection, runsByWorkflowInstanceProjection, } from '../execution/index.js';
|
|
5
5
|
import { ApprovalAuthorityKind, ConversationSurfaceCapability, GroupBudgetExtensionIneligibleError, isGroupBudgetExtensionEligible, OperatorRetryIneligibleError, orchestrationProjection, selectOperatorRetryTarget, workflowsByWorkItemProjection, } from '../orchestration/index.js';
|
|
6
6
|
import { workCorrelationsProjection, } from '../resources/index.js';
|
|
7
|
-
import { ApiCommandStatus, fromWorkItemKey, presentResource, presentRun, presentWorkflowInstance, presentWorkItem, } from '../surfaces/index.js';
|
|
7
|
+
import { ApiCommandStatus, fromWorkItemKey, presentResource, presentRun, presentWorkflowInstance, presentWorkItem, toWorkItemKey, } from '../surfaces/index.js';
|
|
8
8
|
import { workItemId, WorkStatus } from '../work/index.js';
|
|
9
9
|
import { primaryExternalRef } from './external-ref.js';
|
|
10
10
|
import { projectionMeta } from './surface-api-metadata.js';
|
|
@@ -29,6 +29,20 @@ export function createSurfaceWorkApplications(root, now) {
|
|
|
29
29
|
});
|
|
30
30
|
},
|
|
31
31
|
detail: (key) => workDetail(root, key, now),
|
|
32
|
+
async artifact(key, revisionId) {
|
|
33
|
+
const id = decodeWorkItemId(key);
|
|
34
|
+
if (id === undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
const accepted = new Set((await root.orchestration.listForWorkItem(id)).flatMap((workflow) => workflow.acceptedOutcomes));
|
|
37
|
+
const artifact = await root.artifacts.readAcceptedRevision(id, revisionId, async (activationId) => accepted.has(activationId));
|
|
38
|
+
if (artifact === null)
|
|
39
|
+
return undefined;
|
|
40
|
+
return {
|
|
41
|
+
bytes: artifact.bytes,
|
|
42
|
+
mediaType: safeMediaType(artifact.revision.mediaType),
|
|
43
|
+
filename: artifact.revision.path.split('/').at(-1) ?? 'artifact',
|
|
44
|
+
};
|
|
45
|
+
},
|
|
32
46
|
async transcript(key, groupId) {
|
|
33
47
|
const id = decodeWorkItemId(key);
|
|
34
48
|
if (id === undefined)
|
|
@@ -197,6 +211,7 @@ async function workDetail(root, key, now) {
|
|
|
197
211
|
transcriptGroups: await transcriptGroups(root.transcriptStore, id, runs),
|
|
198
212
|
},
|
|
199
213
|
activities: presentPullRequest(pullRequest?.value),
|
|
214
|
+
artifacts: await presentArtifacts(root, id),
|
|
200
215
|
conversation: presentConversation(conversation, root.config?.surfaces.api.conversationMessages.enabled === true),
|
|
201
216
|
};
|
|
202
217
|
const [projections, correlationFacts] = await Promise.all([
|
|
@@ -208,6 +223,29 @@ async function workDetail(root, key, now) {
|
|
|
208
223
|
meta: await projectionMeta(root.journal, [...projections, ...correlationFacts, ...(pullRequest == null ? [] : [pullRequest])], now()),
|
|
209
224
|
};
|
|
210
225
|
}
|
|
226
|
+
async function presentArtifacts(root, id) {
|
|
227
|
+
const partial = root;
|
|
228
|
+
if (partial.artifacts === undefined || partial.orchestration?.listAll === undefined)
|
|
229
|
+
return [];
|
|
230
|
+
const accepted = new Set((await partial.orchestration.listAll())
|
|
231
|
+
.filter((workflow) => workflow.workItemId === id)
|
|
232
|
+
.flatMap((workflow) => workflow.acceptedOutcomes));
|
|
233
|
+
return (await partial.artifacts.latestPublished(id, async (activationId) => accepted.has(activationId))).map((artifact) => ({
|
|
234
|
+
revisionId: artifact.revisionId,
|
|
235
|
+
producer: artifact.producer,
|
|
236
|
+
path: artifact.path,
|
|
237
|
+
occurredAt: artifact.occurredAt,
|
|
238
|
+
...(artifact.mediaType === undefined ? {} : { mediaType: artifact.mediaType }),
|
|
239
|
+
...(artifact.byteLength === undefined ? {} : { byteLength: artifact.byteLength }),
|
|
240
|
+
...(artifact.digest === undefined ? {} : { digest: artifact.digest }),
|
|
241
|
+
href: `/api/v1/work-items/${encodeURIComponent(toWorkItemKey(id))}/artifacts/${encodeURIComponent(artifact.revisionId)}`,
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
function safeMediaType(value) {
|
|
245
|
+
return value !== undefined && /^[^\r\n]+\/[^\r\n]+$/.test(value)
|
|
246
|
+
? value
|
|
247
|
+
: 'application/octet-stream';
|
|
248
|
+
}
|
|
211
249
|
async function conversationForWorkItem(root, id) {
|
|
212
250
|
// Narrow test/application seams may provide a partial composition root while
|
|
213
251
|
// the production root always composes Conversations.
|
|
@@ -6,11 +6,14 @@ import { join } from 'node:path';
|
|
|
6
6
|
import { createInterface } from 'node:readline/promises';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
8
|
import { BuiltInActivityName, agentActivityDefinition } from '../activities/index.js';
|
|
9
|
+
import { FileArtifactMcpSessionStore } from '../artifacts/index.js';
|
|
9
10
|
import { IntakeHost, ResidentHost, TickHost, } from '../control-plane/index.js';
|
|
10
11
|
import { ExecutionFailureCode, RunStatus, isActiveRunStatus, loadPromptTemplate, } from '../execution/index.js';
|
|
12
|
+
import { UlidIdGenerator } from '../kernel/index.js';
|
|
11
13
|
import { ResourceCorrelationRole, resourceId } from '../resources/index.js';
|
|
12
|
-
import { DockerProcessError, createApiDispatcher, createLoggedDockerCli, createPackagedAssetSource, createPairingGrant, createProcessLogSink, createSandboxDockerPort, createSurfaceHttpServer, describeSandboxStartupFailure, drainProcessOutput, loadOrCreateCredentials, promoteSandboxImage, redeemPairingGrant, replaceAccessKey, runDoctor, runSandbox, runSandboxEntrypoint, runSandboxSetup, runSelfUpdateLatestLoop, runTargetSmoke, verifyResidentStart, waitForActiveRuns, waitForever, } from '../surfaces/index.js';
|
|
14
|
+
import { DockerProcessError, createApiDispatcher, createLoggedDockerCli, createPackagedAssetSource, createPairingGrant, createProcessLogSink, createSandboxDockerPort, createSurfaceHttpServer, describeSandboxStartupFailure, drainProcessOutput, loadOrCreateCredentials, promoteSandboxImage, redeemPairingGrant, replaceAccessKey, runDoctor, runSandbox, runSandboxEntrypoint, runSandboxSetup, runSelfUpdateLatestLoop, runTargetSmoke, serveMcpOverStdio, verifyResidentStart, waitForActiveRuns, waitForever, } from '../surfaces/index.js';
|
|
13
15
|
import { WorkStreamKind, workItemId } from '../work/index.js';
|
|
16
|
+
import { createArtifactMcpServerForSession } from './artifact-mcp.js';
|
|
14
17
|
import { loadConfig } from './config/load-config.js';
|
|
15
18
|
import { createNpmUpdatePort } from './npm-update-port.js';
|
|
16
19
|
import { runtimeProjectionDefinitions } from './projection-runtime.js';
|
|
@@ -24,6 +27,8 @@ import { UpdateMaintenancePhase } from './update-maintenance-lease.js';
|
|
|
24
27
|
import { resolveWakeVersion, wakePackageVersion } from './version.js';
|
|
25
28
|
const execFile = promisify(nodeExecFile);
|
|
26
29
|
export function createSurfaceCliApplications(root, api, now) {
|
|
30
|
+
const artifactMcpSessions = new FileArtifactMcpSessionStore(root.paths.artifactsRoot, now);
|
|
31
|
+
const artifactMcpIds = new UlidIdGenerator();
|
|
27
32
|
const runnerTick = new TickHost(createOneShotRunnerAdvance(root));
|
|
28
33
|
const runnerResidentTick = new TickHost(createResidentRunnerAdvance(root));
|
|
29
34
|
const intakeHost = new IntakeHost((signal) => root.intakePipeline.run(signal));
|
|
@@ -84,6 +89,12 @@ export function createSurfaceCliApplications(root, api, now) {
|
|
|
84
89
|
},
|
|
85
90
|
api: { start: (options) => startHttp(options, false) },
|
|
86
91
|
ui: { start: (options) => startHttp(options, true) },
|
|
92
|
+
mcp: {
|
|
93
|
+
async serve(sessionPath, token) {
|
|
94
|
+
const server = await createArtifactMcpServerForSession(root, artifactMcpSessions, sessionPath, token, { now, nextId: () => artifactMcpIds.next('artifact') });
|
|
95
|
+
await serveMcpOverStdio(server);
|
|
96
|
+
},
|
|
97
|
+
},
|
|
87
98
|
auth: {
|
|
88
99
|
async token(accessKey) {
|
|
89
100
|
if (root.config.surfaces.web.auth.disabled)
|
|
@@ -124,6 +124,6 @@ export function resolveWakeVersion(options = {}) {
|
|
|
124
124
|
return `g${headHash.slice(0, 7)}`;
|
|
125
125
|
return '0.1.0-dev';
|
|
126
126
|
}
|
|
127
|
-
export const wakeVersion = "v0.4.
|
|
127
|
+
export const wakeVersion = "v0.4.49";
|
|
128
128
|
/** The registry-installable version used by packaged sandbox images. */
|
|
129
129
|
export const wakePackageVersion = packageVersion(defaultRepoRoot) ?? wakeVersion;
|
|
@@ -4,6 +4,7 @@ import { type Clock, type IdGenerator } from '../../kernel/index.js';
|
|
|
4
4
|
import type { ExecutionActivation, ExecutionAttemptContext } from '../contracts/commands.js';
|
|
5
5
|
import type { ExecutionConfig } from '../contracts/config.js';
|
|
6
6
|
import type { runId } from '../contracts/identifiers.js';
|
|
7
|
+
import { type RunnerMcpServer } from '../contracts/runner.js';
|
|
7
8
|
import type { WorkspaceProvider } from '../contracts/workspace.js';
|
|
8
9
|
import type { RunnerRegistry } from '../infrastructure/runners/registry.js';
|
|
9
10
|
import type { RunRepository } from './run-repository.js';
|
|
@@ -12,6 +13,11 @@ export interface ExecutionDependencies {
|
|
|
12
13
|
readonly ids: IdGenerator;
|
|
13
14
|
readonly workspaces?: WorkspaceProvider;
|
|
14
15
|
readonly runners?: RunnerRegistry;
|
|
16
|
+
readonly mcp?: (input: {
|
|
17
|
+
readonly runId: string;
|
|
18
|
+
readonly activation: ExecutionActivation;
|
|
19
|
+
readonly context: ExecutionAttemptContext;
|
|
20
|
+
}) => Promise<ExecutionMcpConfiguration>;
|
|
15
21
|
readonly transcriptRecorder?: ActivityExecutionContext['transcriptRecorder'];
|
|
16
22
|
readonly logOperationalError?: ActivityExecutionContext['logOperationalError'];
|
|
17
23
|
readonly reportRunnerQuota?: (input: {
|
|
@@ -19,6 +25,11 @@ export interface ExecutionDependencies {
|
|
|
19
25
|
readonly message: string;
|
|
20
26
|
}) => Promise<void>;
|
|
21
27
|
}
|
|
28
|
+
export interface ExecutionMcpConfiguration {
|
|
29
|
+
readonly servers: readonly RunnerMcpServer[];
|
|
30
|
+
readonly prompt?: string;
|
|
31
|
+
release(): Promise<void>;
|
|
32
|
+
}
|
|
22
33
|
export interface ExecutionRuntime {
|
|
23
34
|
readonly activities: ActivityRegistry;
|
|
24
35
|
readonly config: ExecutionConfig;
|
|
@@ -4,10 +4,15 @@ import { ExecutionCancellationReason } from '../contracts/vocabulary.js';
|
|
|
4
4
|
import { parseAgentRunnerResponse } from '../infrastructure/agent-runner-adapter.js';
|
|
5
5
|
import { createRunEvent } from './run-lifecycle.js';
|
|
6
6
|
import { confirmCancellation, requestCancellation } from './run-liveness-service.js';
|
|
7
|
+
// Execution context assembly intentionally keeps every run-bound capability visible at dispatch.
|
|
8
|
+
// eslint-disable-next-line max-lines-per-function, complexity
|
|
7
9
|
export async function executeActivity(runtime, currentRunId, request) {
|
|
8
10
|
const { activation, context, occurredAt, runner } = request;
|
|
9
11
|
const controller = activityController(runtime, currentRunId);
|
|
10
12
|
runtime.active.set(currentRunId, controller);
|
|
13
|
+
const mcp = runner === undefined || runtime.dependencies.mcp === undefined
|
|
14
|
+
? undefined
|
|
15
|
+
: await runtime.dependencies.mcp({ runId: currentRunId, activation, context });
|
|
11
16
|
const executionContext = {
|
|
12
17
|
signal: controller.signal,
|
|
13
18
|
occurredAt,
|
|
@@ -20,6 +25,12 @@ export async function executeActivity(runtime, currentRunId, request) {
|
|
|
20
25
|
: { reportRunnerStarted: request.reportRunnerStarted }),
|
|
21
26
|
...workspaceContext(request.workspace),
|
|
22
27
|
...(runner === undefined ? {} : { runner }),
|
|
28
|
+
...(mcp === undefined
|
|
29
|
+
? {}
|
|
30
|
+
: {
|
|
31
|
+
mcpServers: mcp.servers,
|
|
32
|
+
...(mcp.prompt === undefined ? {} : { mcpPrompt: mcp.prompt }),
|
|
33
|
+
}),
|
|
23
34
|
...(runtime.dependencies.transcriptRecorder === undefined
|
|
24
35
|
? {}
|
|
25
36
|
: { transcriptRecorder: runtime.dependencies.transcriptRecorder }),
|
|
@@ -41,16 +52,21 @@ export async function executeActivity(runtime, currentRunId, request) {
|
|
|
41
52
|
reportRunnerResult: runnerResultReporter(runtime, currentRunId, context, activation, request),
|
|
42
53
|
reportRunnerTimeout: runnerTimeoutReporter(runtime, currentRunId),
|
|
43
54
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
try {
|
|
56
|
+
return await runtime.activities.execute({
|
|
57
|
+
activationId: activation.activationId,
|
|
58
|
+
activity: activation.activity,
|
|
59
|
+
workItemId: context.workItemId,
|
|
60
|
+
workflowInstanceId: context.workflowInstanceId,
|
|
61
|
+
orchestrationGroupId: context.orchestrationGroupId,
|
|
62
|
+
causationId: activation.activationId,
|
|
63
|
+
input: activation.input,
|
|
64
|
+
resources: context.resources,
|
|
65
|
+
}, executionContext);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
await mcp?.release();
|
|
69
|
+
}
|
|
54
70
|
}
|
|
55
71
|
function activityController(runtime, currentRunId) {
|
|
56
72
|
return runtime.active.get(currentRunId) ?? new AbortController();
|
|
@@ -563,7 +563,7 @@ function resolveRunner(runtime, executionKind, activation, context) {
|
|
|
563
563
|
throw new Error(`Unknown execution runner pool: ${runnerPool}`);
|
|
564
564
|
if (executionKind !== ActivityExecutionKind.Agent)
|
|
565
565
|
return { runner: undefined };
|
|
566
|
-
const resolved = runtime.dependencies.runners?.resolve(runnerPool, context.ineligibleRunners ?? new Set());
|
|
566
|
+
const resolved = runtime.dependencies.runners?.resolve(runnerPool, context.ineligibleRunners ?? new Set(), { ephemeralMcp: runtime.dependencies.mcp !== undefined });
|
|
567
567
|
return describeResolvedRunner(runtime, runnerPool, resolved);
|
|
568
568
|
}
|
|
569
569
|
function describeResolvedRunner(runtime, pool, resolved) {
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { ExternalExecutionKind } from '../../activities/index.js';
|
|
2
2
|
import type { FinishedRunStatus, WorkspaceMode } from './vocabulary.js';
|
|
3
3
|
export declare const ProviderQuotaExceededFailureKind = "provider-quota-exceeded";
|
|
4
|
+
/** A trusted MCP process configured only for this runner invocation. */
|
|
5
|
+
export interface RunnerMcpServer {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly command: string;
|
|
8
|
+
readonly args?: readonly string[];
|
|
9
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
10
|
+
}
|
|
4
11
|
export interface RunnerRequest {
|
|
5
12
|
readonly runId: string;
|
|
6
13
|
readonly prompt: string;
|
|
@@ -23,6 +30,7 @@ export interface RunnerRequest {
|
|
|
23
30
|
readonly cacheRead?: number;
|
|
24
31
|
readonly cacheWrite?: number;
|
|
25
32
|
};
|
|
33
|
+
readonly mcpServers?: readonly RunnerMcpServer[];
|
|
26
34
|
}
|
|
27
35
|
export type AgentRunOutcome = 'DONE' | 'REJECTED' | 'BLOCKED' | 'FAILED' | 'NEEDS_CLARIFICATION';
|
|
28
36
|
export interface AgentRunResponse {
|
|
@@ -74,5 +82,7 @@ export interface RunnerExecution {
|
|
|
74
82
|
export interface Runner {
|
|
75
83
|
/** Whether this adapter can continue an earlier session with its opaque session ID. */
|
|
76
84
|
readonly supportsSessionResume?: boolean;
|
|
85
|
+
/** Whether this adapter can inject trusted MCP configuration per invocation. */
|
|
86
|
+
readonly supportsEphemeralMcp?: boolean;
|
|
77
87
|
start(request: RunnerRequest, signal: AbortSignal): Promise<RunnerExecution>;
|
|
78
88
|
}
|
|
@@ -14,6 +14,7 @@ export declare function cliRunner(name: string, command: string, args: (request:
|
|
|
14
14
|
readonly runnerTimeouts?: ProcessTimeouts;
|
|
15
15
|
readonly defaultModel?: string;
|
|
16
16
|
readonly supportsSessionResume?: boolean;
|
|
17
|
+
readonly supportsEphemeralMcp?: boolean;
|
|
17
18
|
readonly parseSuccessfulOutput?: (stdout: string, request: RunnerRequest) => Partial<AgentRunnerResult>;
|
|
18
19
|
readonly classifyFailure?: (input: {
|
|
19
20
|
readonly stdout: string;
|
|
@@ -9,6 +9,7 @@ export function createClaudeRunner(options = {}) {
|
|
|
9
9
|
parseSuccessfulOutput: parseClaudeOutput,
|
|
10
10
|
classifyFailure: classifyClaudeFailure,
|
|
11
11
|
supportsSessionResume: true,
|
|
12
|
+
supportsEphemeralMcp: true,
|
|
12
13
|
});
|
|
13
14
|
}
|
|
14
15
|
export function parseClaudeOutput(stdout, _request) {
|
|
@@ -84,15 +85,35 @@ export function claudeCommandArgs(request, passthroughArgs = [], defaults = {})
|
|
|
84
85
|
...(request.allowedTools.length === 0
|
|
85
86
|
? []
|
|
86
87
|
: ['--allowedTools', request.allowedTools.join(' ')]),
|
|
88
|
+
...(request.mcpServers === undefined || request.mcpServers.length === 0
|
|
89
|
+
? []
|
|
90
|
+
: ['--mcp-config', JSON.stringify(claudeMcpConfig(request)), '--strict-mcp-config']),
|
|
87
91
|
...passthroughArgs,
|
|
88
92
|
'--',
|
|
89
93
|
request.prompt,
|
|
90
94
|
];
|
|
91
95
|
}
|
|
96
|
+
function claudeMcpConfig(request) {
|
|
97
|
+
return {
|
|
98
|
+
mcpServers: Object.fromEntries((request.mcpServers ?? []).map((server) => [
|
|
99
|
+
server.name,
|
|
100
|
+
{
|
|
101
|
+
command: server.command,
|
|
102
|
+
...(server.args === undefined ? {} : { args: server.args }),
|
|
103
|
+
...(server.env === undefined ? {} : { env: server.env }),
|
|
104
|
+
},
|
|
105
|
+
])),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
92
108
|
export function cliRunner(name, command, args, options = {}) {
|
|
93
109
|
return {
|
|
94
110
|
supportsSessionResume: options.supportsSessionResume === true,
|
|
111
|
+
supportsEphemeralMcp: options.supportsEphemeralMcp === true,
|
|
95
112
|
async start(request, signal) {
|
|
113
|
+
if (request.mcpServers !== undefined &&
|
|
114
|
+
request.mcpServers.length > 0 &&
|
|
115
|
+
!options.supportsEphemeralMcp)
|
|
116
|
+
throw new Error(`${name} runner does not support ephemeral MCP configuration`);
|
|
96
117
|
const process = runProcess(command, args(request), request.workspacePath, signal, {
|
|
97
118
|
...options.runnerTimeouts,
|
|
98
119
|
...(request.onTimeout === undefined ? {} : { onTimeout: request.onTimeout }),
|
|
@@ -42,6 +42,7 @@ export function createCodexRunner(options = {}) {
|
|
|
42
42
|
parseSuccessfulOutput: parseCodexOutput,
|
|
43
43
|
classifyFailure: classifyCodexFailure,
|
|
44
44
|
supportsSessionResume: true,
|
|
45
|
+
supportsEphemeralMcp: true,
|
|
45
46
|
});
|
|
46
47
|
return runner;
|
|
47
48
|
}
|
|
@@ -59,11 +60,22 @@ export function codexCommandArgs(request, passthroughArgs = [], defaults = {}) {
|
|
|
59
60
|
...((request.effort ?? defaults.effort) === undefined
|
|
60
61
|
? []
|
|
61
62
|
: ['-c', `model_reasoning_effort=${request.effort ?? defaults.effort}`]),
|
|
63
|
+
...codexMcpConfigArgs(request),
|
|
62
64
|
...(request.resumeSessionId === undefined ? [] : ['resume', request.resumeSessionId]),
|
|
63
65
|
...passthroughArgs,
|
|
64
66
|
`${request.prompt}\n\n${waitBackgroundInstruction}`,
|
|
65
67
|
];
|
|
66
68
|
}
|
|
69
|
+
function codexMcpConfigArgs(request) {
|
|
70
|
+
return (request.mcpServers ?? []).flatMap((server) => [
|
|
71
|
+
'-c',
|
|
72
|
+
`mcp_servers.${JSON.stringify(server.name)}={command=${JSON.stringify(server.command)},args=${JSON.stringify(server.args ?? [])}${server.env === undefined
|
|
73
|
+
? ''
|
|
74
|
+
: `,env={${Object.entries(server.env)
|
|
75
|
+
.map(([key, value]) => `${JSON.stringify(key)}=${JSON.stringify(value)}`)
|
|
76
|
+
.join(',')}}`}}`,
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
67
79
|
// Work around Codex's missing background-command lifecycle contract (https://github.com/openai/codex/issues/32505, https://github.com/openai/codex/issues/34866).
|
|
68
80
|
const waitBackgroundInstruction = `When a command you started is still running or lacks a terminal result, do not report a normal Wake outcome. Make your entire final response exactly WAIT_BACKGROUND. Wake will reject that stop attempt and ask you to continue. After that hook prompt, poll the still-running command and report a normal terminal status only after it has settled. Do not use WAIT_BACKGROUND for a human-input or implementation blocker.`;
|
|
69
81
|
function codexSandboxMode(_workspaceMode) {
|
|
@@ -3,6 +3,7 @@ import { type FakeScenarioResolver } from './fake-scenarios.js';
|
|
|
3
3
|
export declare class FakeExecutionRunner implements Runner {
|
|
4
4
|
readonly name: string;
|
|
5
5
|
private readonly scenarios;
|
|
6
|
+
readonly supportsEphemeralMcp = true;
|
|
6
7
|
constructor(name?: string, scenarios?: FakeScenarioResolver);
|
|
7
8
|
start(request: RunnerRequest, signal: AbortSignal): Promise<RunnerExecution>;
|
|
8
9
|
}
|
|
@@ -7,7 +7,9 @@ export declare class RunnerRegistry {
|
|
|
7
7
|
private readonly runnerPools;
|
|
8
8
|
private readonly runners;
|
|
9
9
|
constructor(runnerPools: Readonly<Record<string, readonly string[]>>, runners: Readonly<Record<string, Runner>>);
|
|
10
|
-
resolve(runnerPool: string, ineligible?: ReadonlySet<string
|
|
10
|
+
resolve(runnerPool: string, ineligible?: ReadonlySet<string>, requirements?: {
|
|
11
|
+
readonly ephemeralMcp?: boolean;
|
|
12
|
+
}): {
|
|
11
13
|
readonly name: string;
|
|
12
14
|
readonly runner: Runner;
|
|
13
15
|
};
|
|
@@ -13,21 +13,23 @@ export class RunnerRegistry {
|
|
|
13
13
|
this.runnerPools = runnerPools;
|
|
14
14
|
this.runners = runners;
|
|
15
15
|
}
|
|
16
|
-
resolve(runnerPool, ineligible = new Set()) {
|
|
16
|
+
resolve(runnerPool, ineligible = new Set(), requirements = {}) {
|
|
17
17
|
const candidates = this.runnerPools[runnerPool];
|
|
18
18
|
if (candidates === undefined)
|
|
19
19
|
throw new Error(`Execution runner pool ${runnerPool} has no runner`);
|
|
20
|
-
return selectEligibleCandidate(runnerPool, candidates, this.runners, ineligible);
|
|
20
|
+
return selectEligibleCandidate(runnerPool, candidates, this.runners, ineligible, requirements);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
// Falls sideways to the next candidate on quota ineligibility; never a different runner pool.
|
|
24
|
-
function selectEligibleCandidate(runnerPool, candidates, runners, ineligible) {
|
|
24
|
+
function selectEligibleCandidate(runnerPool, candidates, runners, ineligible, requirements) {
|
|
25
25
|
for (const name of candidates) {
|
|
26
26
|
if (ineligible.has(name))
|
|
27
27
|
continue;
|
|
28
28
|
const runner = runners[name];
|
|
29
29
|
if (runner === undefined)
|
|
30
30
|
throw new Error(`Runner ${name} is not registered`);
|
|
31
|
+
if (requirements.ephemeralMcp === true && runner.supportsEphemeralMcp !== true)
|
|
32
|
+
continue;
|
|
31
33
|
return { name, runner };
|
|
32
34
|
}
|
|
33
35
|
throw new NoEligibleRunnerError(runnerPool);
|
|
@@ -22,6 +22,8 @@ export interface BoardCardResponse {
|
|
|
22
22
|
readonly condition: BoardCardCondition;
|
|
23
23
|
readonly frozen?: boolean;
|
|
24
24
|
readonly awaitingApproval?: boolean;
|
|
25
|
+
/** Whether the primary workflow needs a gate-budget extension to continue. */
|
|
26
|
+
readonly extendEligible?: boolean;
|
|
25
27
|
readonly workflowName?: string;
|
|
26
28
|
readonly stage?: string;
|
|
27
29
|
/** Why the primary workflow is blocked, when it requires operator intervention. */
|
|
@@ -35,6 +35,16 @@ export interface WorkDetailResponse {
|
|
|
35
35
|
readonly activities: {
|
|
36
36
|
readonly pullRequest?: PullRequestResponse;
|
|
37
37
|
};
|
|
38
|
+
readonly artifacts: readonly {
|
|
39
|
+
readonly revisionId: string;
|
|
40
|
+
readonly producer: string;
|
|
41
|
+
readonly path: string;
|
|
42
|
+
readonly occurredAt: string;
|
|
43
|
+
readonly mediaType?: string;
|
|
44
|
+
readonly byteLength?: number;
|
|
45
|
+
readonly digest?: string;
|
|
46
|
+
readonly href: string;
|
|
47
|
+
}[];
|
|
38
48
|
readonly conversation: {
|
|
39
49
|
/** Whether this authenticated surface may create control-plane entries. */
|
|
40
50
|
readonly canCreateEntries: boolean;
|
|
@@ -56,6 +56,11 @@ export interface ApiApplications {
|
|
|
56
56
|
list(query: CollectionQuery): Promise<ApiCollectionPage<WorkItemResponse>>;
|
|
57
57
|
detail(key: string): Promise<ApiResourceResult<WorkDetailResponse> | undefined>;
|
|
58
58
|
transcript?(key: string, groupId: string): Promise<ApiResourceResult<WorkItemTranscriptResponse> | undefined>;
|
|
59
|
+
artifact?(key: string, revisionId: string): Promise<{
|
|
60
|
+
readonly bytes: Uint8Array;
|
|
61
|
+
readonly mediaType: string;
|
|
62
|
+
readonly filename: string;
|
|
63
|
+
} | undefined>;
|
|
59
64
|
freeze?(key: string, command: ApiCommandRequest): Promise<ApiCommandResult>;
|
|
60
65
|
unfreeze?(key: string, command: ApiCommandRequest): Promise<ApiCommandResult>;
|
|
61
66
|
delete?(key: string, command: ApiCommandRequest): Promise<ApiCommandResult>;
|
|
@@ -108,9 +108,9 @@ async function collectionResult(applications, pathname, query) {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
async function readDetail(applications, pathname) {
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
113
|
-
return
|
|
111
|
+
const workSubresource = await readWorkSubresource(applications, pathname);
|
|
112
|
+
if (workSubresource !== undefined)
|
|
113
|
+
return workSubresource;
|
|
114
114
|
const transcript = /^\/api\/v1\/runs\/([^/]+)\/transcript$/.exec(pathname)?.[1];
|
|
115
115
|
if (transcript !== undefined)
|
|
116
116
|
return readTranscript(applications, transcript);
|
|
@@ -120,6 +120,37 @@ async function readDetail(applications, pathname) {
|
|
|
120
120
|
const run = /^\/api\/v1\/runs\/([^/]+)$/.exec(pathname)?.[1];
|
|
121
121
|
return run === undefined ? undefined : readRun(applications, run);
|
|
122
122
|
}
|
|
123
|
+
async function readWorkSubresource(applications, pathname) {
|
|
124
|
+
const workTranscript = /^\/api\/v1\/work-items\/([^/]+)\/transcripts\/([^/]+)$/.exec(pathname);
|
|
125
|
+
if (workTranscript?.[1] !== undefined && workTranscript[2] !== undefined)
|
|
126
|
+
return readWorkTranscript(applications, workTranscript[1], workTranscript[2]);
|
|
127
|
+
const artifact = /^\/api\/v1\/work-items\/([^/]+)\/artifacts\/([^/]+)$/.exec(pathname);
|
|
128
|
+
if (artifact?.[1] !== undefined && artifact[2] !== undefined)
|
|
129
|
+
return readWorkArtifact(applications, artifact[1], artifact[2]);
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
async function readWorkArtifact(applications, keySegment, revisionSegment) {
|
|
133
|
+
const key = decodePathSegment(keySegment);
|
|
134
|
+
const revisionId = decodePathSegment(revisionSegment);
|
|
135
|
+
if (key instanceof ApiPathError)
|
|
136
|
+
return invalidPath(key.message);
|
|
137
|
+
if (revisionId instanceof ApiPathError)
|
|
138
|
+
return invalidPath(revisionId.message);
|
|
139
|
+
if (applications.work.artifact === undefined)
|
|
140
|
+
return unavailable('artifact');
|
|
141
|
+
const artifact = await applications.work.artifact(key, revisionId);
|
|
142
|
+
if (artifact === undefined)
|
|
143
|
+
return found(undefined);
|
|
144
|
+
return {
|
|
145
|
+
status: 200,
|
|
146
|
+
body: artifact.bytes,
|
|
147
|
+
contentType: artifact.mediaType,
|
|
148
|
+
headers: {
|
|
149
|
+
'content-disposition': `attachment; filename*=UTF-8''${encodeURIComponent(artifact.filename)}`,
|
|
150
|
+
'content-length': String(artifact.bytes.byteLength),
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
123
154
|
async function readWorkTranscript(applications, keySegment, groupSegment) {
|
|
124
155
|
const key = decodePathSegment(keySegment);
|
|
125
156
|
const groupId = decodePathSegment(groupSegment);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const workRoutes: readonly ["/api/v1/work-items", "/api/v1/work-items/:workItemKey", "/api/v1/work-items/:workItemKey/transcripts/:groupId", "/api/v1/work-items/:workItemKey/commands/freeze", "/api/v1/work-items/:workItemKey/commands/unfreeze", "/api/v1/work-items/:workItemKey/commands/delete", "/api/v1/work-items/:workItemKey/commands/retry", "/api/v1/work-items/:workItemKey/commands/extend", "/api/v1/work-items/:workItemKey/commands/message"];
|
|
1
|
+
export declare const workRoutes: readonly ["/api/v1/work-items", "/api/v1/work-items/:workItemKey", "/api/v1/work-items/:workItemKey/transcripts/:groupId", "/api/v1/work-items/:workItemKey/artifacts/:revisionId", "/api/v1/work-items/:workItemKey/commands/freeze", "/api/v1/work-items/:workItemKey/commands/unfreeze", "/api/v1/work-items/:workItemKey/commands/delete", "/api/v1/work-items/:workItemKey/commands/retry", "/api/v1/work-items/:workItemKey/commands/extend", "/api/v1/work-items/:workItemKey/commands/message"];
|
|
@@ -2,6 +2,7 @@ export const workRoutes = [
|
|
|
2
2
|
'/api/v1/work-items',
|
|
3
3
|
'/api/v1/work-items/:workItemKey',
|
|
4
4
|
'/api/v1/work-items/:workItemKey/transcripts/:groupId',
|
|
5
|
+
'/api/v1/work-items/:workItemKey/artifacts/:revisionId',
|
|
5
6
|
'/api/v1/work-items/:workItemKey/commands/freeze',
|
|
6
7
|
'/api/v1/work-items/:workItemKey/commands/unfreeze',
|
|
7
8
|
'/api/v1/work-items/:workItemKey/commands/delete',
|
|
@@ -15,6 +15,11 @@ export type WakeCommand = ({
|
|
|
15
15
|
}) | ({
|
|
16
16
|
readonly kind: 'api' | 'ui';
|
|
17
17
|
} & HostOptions) | ({
|
|
18
|
+
readonly kind: 'mcp';
|
|
19
|
+
} & {
|
|
20
|
+
readonly sessionPath: string;
|
|
21
|
+
readonly wakeRoot?: string;
|
|
22
|
+
}) | ({
|
|
18
23
|
readonly kind: 'ui-token';
|
|
19
24
|
readonly accessKey?: string;
|
|
20
25
|
} & Pick<HostOptions, 'wakeRoot'>) | {
|
|
@@ -84,6 +89,9 @@ export interface WakeCliApplications {
|
|
|
84
89
|
readonly ui: {
|
|
85
90
|
start(options?: HostOptions): Promise<void>;
|
|
86
91
|
};
|
|
92
|
+
readonly mcp?: {
|
|
93
|
+
serve(sessionPath: string, token: string): Promise<void>;
|
|
94
|
+
};
|
|
87
95
|
readonly auth?: {
|
|
88
96
|
token(accessKey?: string): Promise<string>;
|
|
89
97
|
};
|
|
@@ -27,6 +27,8 @@ export function parseWakeCommand(arguments_) {
|
|
|
27
27
|
return parseUiCommand(arguments_.slice(1));
|
|
28
28
|
case 'api':
|
|
29
29
|
return { kind: command, ...parseHostOptions(arguments_.slice(1)) };
|
|
30
|
+
case 'mcp':
|
|
31
|
+
return parseMcpCommand(arguments_.slice(1));
|
|
30
32
|
case 'tick':
|
|
31
33
|
case 'start':
|
|
32
34
|
case 'stop':
|
|
@@ -44,6 +46,27 @@ export function parseWakeCommand(arguments_) {
|
|
|
44
46
|
throw new Error(`Unknown wake command: ${command ?? ''}`);
|
|
45
47
|
}
|
|
46
48
|
}
|
|
49
|
+
function parseMcpCommand(arguments_) {
|
|
50
|
+
if (arguments_[0] !== 'serve')
|
|
51
|
+
throw new Error(`Unknown mcp command: ${arguments_[0] ?? ''}`);
|
|
52
|
+
let sessionPath;
|
|
53
|
+
let wakeRoot;
|
|
54
|
+
for (let index = 1; index < arguments_.length; index += 2) {
|
|
55
|
+
const flag = arguments_[index];
|
|
56
|
+
const value = requiredArgument(arguments_[index + 1], `value for ${flag ?? 'option'}`);
|
|
57
|
+
if (flag === '--session' && sessionPath === undefined)
|
|
58
|
+
sessionPath = value;
|
|
59
|
+
else if (flag === '--wake-root' && wakeRoot === undefined)
|
|
60
|
+
wakeRoot = value;
|
|
61
|
+
else
|
|
62
|
+
throw new Error(`Unknown or duplicate mcp option: ${flag ?? ''}`);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
kind: 'mcp',
|
|
66
|
+
sessionPath: requiredArgument(sessionPath, 'mcp --session'),
|
|
67
|
+
...(wakeRoot === undefined ? {} : { wakeRoot }),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
47
70
|
function parseUiCommand(arguments_) {
|
|
48
71
|
if (arguments_[0] !== 'token')
|
|
49
72
|
return { kind: 'ui', ...parseHostOptions(arguments_) };
|
|
@@ -194,6 +217,9 @@ export async function runWakeCommand(command, applications, output, signal) {
|
|
|
194
217
|
case 'ui-token':
|
|
195
218
|
output.write(await formatUiTokenOutput(auth(applications).token(command.accessKey)));
|
|
196
219
|
return;
|
|
220
|
+
case 'mcp':
|
|
221
|
+
await mcp(applications).serve(command.sessionPath, process.env.WAKE_MCP_TOKEN ?? '');
|
|
222
|
+
return;
|
|
197
223
|
case 'audit':
|
|
198
224
|
for (const record of await applications.audit.read(command.workItemId))
|
|
199
225
|
output.write(`${JSON.stringify(record)}\n`);
|
|
@@ -247,6 +273,11 @@ function auth(applications) {
|
|
|
247
273
|
throw new Error('UI auth CLI application was not composed');
|
|
248
274
|
return applications.auth;
|
|
249
275
|
}
|
|
276
|
+
function mcp(applications) {
|
|
277
|
+
if (applications.mcp === undefined)
|
|
278
|
+
throw new Error('MCP CLI application was not composed');
|
|
279
|
+
return applications.mcp;
|
|
280
|
+
}
|
|
250
281
|
function writeResult(output, value) {
|
|
251
282
|
if (value !== undefined)
|
|
252
283
|
output.write(`${JSON.stringify(value)}\n`);
|
|
@@ -14,6 +14,7 @@ export const usage = [
|
|
|
14
14
|
' wake ui token set <key> Replace the UI login access key',
|
|
15
15
|
' wake audit Show autonomous decision audit history',
|
|
16
16
|
' wake correlate Manually correlate a resource to a work item',
|
|
17
|
+
' wake mcp serve Run the private run-scoped Wake MCP transport',
|
|
17
18
|
' wake run resolve Resolve an escalated ambiguous run',
|
|
18
19
|
' wake doctor Diagnose config/GitHub/Docker/sandbox setup problems',
|
|
19
20
|
' wake maintenance clear Clear an abandoned failed self-update maintenance lease',
|
|
@@ -45,6 +45,7 @@ export * from './cli/infrastructure/process-log.js';
|
|
|
45
45
|
export * from './cli/infrastructure/sandbox-startup-failure.js';
|
|
46
46
|
export * from './cli/main.js';
|
|
47
47
|
export * from './cli/usage.js';
|
|
48
|
+
export * from './mcp/stdio.js';
|
|
48
49
|
export * from './contracts/config.js';
|
|
49
50
|
export * from './web-host/asset-source.js';
|
|
50
51
|
export * from './web-host/packaged-assets.js';
|