@jevitate/cli 0.1.0

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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ai-cli.d.ts +31 -0
  3. package/dist/ai-cli.js +238 -0
  4. package/dist/ai-cli.js.map +1 -0
  5. package/dist/bin.d.ts +2 -0
  6. package/dist/bin.js +50663 -0
  7. package/dist/bin.js.map +1 -0
  8. package/dist/data-dir.d.ts +9 -0
  9. package/dist/data-dir.js +13 -0
  10. package/dist/data-dir.js.map +1 -0
  11. package/dist/envelope.d.ts +11 -0
  12. package/dist/envelope.js +7 -0
  13. package/dist/envelope.js.map +1 -0
  14. package/dist/explore-api.d.ts +187 -0
  15. package/dist/explore-api.js +302 -0
  16. package/dist/explore-api.js.map +1 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +3 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/init-keys.d.ts +14 -0
  21. package/dist/init-keys.js +18 -0
  22. package/dist/init-keys.js.map +1 -0
  23. package/dist/init-mcp.d.ts +87 -0
  24. package/dist/init-mcp.js +198 -0
  25. package/dist/init-mcp.js.map +1 -0
  26. package/dist/init-skills.d.ts +80 -0
  27. package/dist/init-skills.js +256 -0
  28. package/dist/init-skills.js.map +1 -0
  29. package/dist/journey-api.d.ts +37 -0
  30. package/dist/journey-api.js +55 -0
  31. package/dist/journey-api.js.map +1 -0
  32. package/dist/load-api.d.ts +48 -0
  33. package/dist/load-api.js +84 -0
  34. package/dist/load-api.js.map +1 -0
  35. package/dist/mcp-api.d.ts +116 -0
  36. package/dist/mcp-api.js +359 -0
  37. package/dist/mcp-api.js.map +1 -0
  38. package/dist/mission-api.d.ts +56 -0
  39. package/dist/mission-api.js +65 -0
  40. package/dist/mission-api.js.map +1 -0
  41. package/dist/program.d.ts +77 -0
  42. package/dist/program.js +1938 -0
  43. package/dist/program.js.map +1 -0
  44. package/dist/record-api.d.ts +74 -0
  45. package/dist/record-api.js +93 -0
  46. package/dist/record-api.js.map +1 -0
  47. package/dist/regression-api.d.ts +17 -0
  48. package/dist/regression-api.js +16 -0
  49. package/dist/regression-api.js.map +1 -0
  50. package/dist/self-heal-adapter.d.ts +31 -0
  51. package/dist/self-heal-adapter.js +70 -0
  52. package/dist/self-heal-adapter.js.map +1 -0
  53. package/dist/source-api.d.ts +147 -0
  54. package/dist/source-api.js +208 -0
  55. package/dist/source-api.js.map +1 -0
  56. package/dist/source-run-api.d.ts +57 -0
  57. package/dist/source-run-api.js +91 -0
  58. package/dist/source-run-api.js.map +1 -0
  59. package/dist/ui-api.d.ts +35 -0
  60. package/dist/ui-api.js +369 -0
  61. package/dist/ui-api.js.map +1 -0
  62. package/dist/ux-api.d.ts +75 -0
  63. package/dist/ux-api.js +231 -0
  64. package/dist/ux-api.js.map +1 -0
  65. package/dist/version.d.ts +1 -0
  66. package/dist/version.js +37 -0
  67. package/dist/version.js.map +1 -0
  68. package/package.json +85 -0
  69. package/skills/jevitate-explore/SKILL.md +108 -0
  70. package/skills/jevitate-load-test/SKILL.md +42 -0
  71. package/skills/jevitate-mission-scope/SKILL.md +107 -0
  72. package/skills/jevitate-record/SKILL.md +78 -0
  73. package/skills/jevitate-run-journey/SKILL.md +72 -0
  74. package/skills/jevitate-sources/SKILL.md +88 -0
  75. package/skills/jevitate-ux-review/SKILL.md +68 -0
  76. package/ui/app.js +538 -0
  77. package/ui/index.html +546 -0
@@ -0,0 +1,37 @@
1
+ import { type RunPolicy } from "@jevitate/domain";
2
+ import { type JourneyRunResult, type SelfHealer } from "@jevitate/runtime";
3
+ /**
4
+ * Distinct from `@jevitate/journey`'s `ParamValidationError` so CLI/API callers
5
+ * can tell "no such journey" apart from "params didn't match the journey's
6
+ * schema" without string-matching error messages.
7
+ */
8
+ export declare class UnknownJourneyError extends Error {
9
+ }
10
+ export interface RunJourneyProgrammaticallyOptions {
11
+ /** Directory a `FsJourneyStore` reads Journey JSON files from. */
12
+ dir: string;
13
+ id: string;
14
+ params: Record<string, string>;
15
+ /** Defaults to `safeRunPolicy()` (Slice 1: fail-closed secret mode) when omitted. */
16
+ policy?: RunPolicy;
17
+ /**
18
+ * Optional gated self-heal port (Ticket #7). Only relevant when
19
+ * `policy.selfHeal.mode !== "fail-closed"`; wired as the `JourneyRunner`'s
20
+ * 5th constructor arg. When omitted (the default), a divergence quarantines
21
+ * exactly as before — identical to Slice 1's fail-closed behavior. The
22
+ * caller (CLI) is responsible for its credential preflight; this surface
23
+ * never builds AI gateways itself. Writes never auto-heal regardless of
24
+ * this port (enforced by `JourneyRunner`'s write floor).
25
+ */
26
+ selfHealer?: SelfHealer;
27
+ }
28
+ /**
29
+ * The one programmatic surface for "run this published Journey by id" —
30
+ * used by BOTH the CLI `journey run` action and external callers. Builds
31
+ * the real `FsJourneyStore` + `JourneyRegistry`, resolves the journey
32
+ * (unknown id -> `UnknownJourneyError`), validates params UP FRONT with
33
+ * `deriveParamSchema`/`validateParams` (unknown/missing param ->
34
+ * `ParamValidationError`, from `@jevitate/journey`) BEFORE any browser is
35
+ * launched, then builds the real Actor + `JourneyRunner` and runs.
36
+ */
37
+ export declare function runJourneyProgrammatically(opts: RunJourneyProgrammaticallyOptions): Promise<JourneyRunResult>;
@@ -0,0 +1,55 @@
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { FsJourneyStore, JourneyRegistry, deriveParamSchema, validateParams } from "@jevitate/journey";
5
+ import { safeRunPolicy } from "@jevitate/domain";
6
+ import { PlaywrightBrowserPort } from "@jevitate/playwright";
7
+ import { CastActor, BrowseTheWeb } from "@jevitate/screenplay";
8
+ import { RecordingInterpreter } from "@jevitate/interpreter";
9
+ import { JourneyRunner } from "@jevitate/runtime";
10
+ /**
11
+ * Distinct from `@jevitate/journey`'s `ParamValidationError` so CLI/API callers
12
+ * can tell "no such journey" apart from "params didn't match the journey's
13
+ * schema" without string-matching error messages.
14
+ */
15
+ export class UnknownJourneyError extends Error {
16
+ }
17
+ /**
18
+ * The one programmatic surface for "run this published Journey by id" —
19
+ * used by BOTH the CLI `journey run` action and external callers. Builds
20
+ * the real `FsJourneyStore` + `JourneyRegistry`, resolves the journey
21
+ * (unknown id -> `UnknownJourneyError`), validates params UP FRONT with
22
+ * `deriveParamSchema`/`validateParams` (unknown/missing param ->
23
+ * `ParamValidationError`, from `@jevitate/journey`) BEFORE any browser is
24
+ * launched, then builds the real Actor + `JourneyRunner` and runs.
25
+ */
26
+ export async function runJourneyProgrammatically(opts) {
27
+ const store = new FsJourneyStore(opts.dir);
28
+ const registry = new JourneyRegistry(store);
29
+ const journey = await registry.get(opts.id);
30
+ if (!journey) {
31
+ throw new UnknownJourneyError(`unknown journey '${opts.id}'`);
32
+ }
33
+ // Fail fast: validate BEFORE any browser launch, so bad params never pay
34
+ // the cost (or risk) of opening a browser.
35
+ validateParams(deriveParamSchema(journey.recording), opts.params);
36
+ const policy = opts.policy ?? safeRunPolicy();
37
+ const profileDir = await mkdtemp(join(tmpdir(), "doit-journey-run-"));
38
+ const port = new PlaywrightBrowserPort();
39
+ const session = await port.open({
40
+ profileDir,
41
+ headless: true,
42
+ allowedOrigins: [journey.recording.site],
43
+ baseUrl: journey.recording.site,
44
+ });
45
+ try {
46
+ const actor = CastActor.named("cli-runner").whoCan(new BrowseTheWeb(session, [journey.recording.site]));
47
+ const runner = new JourneyRunner(actor, new RecordingInterpreter(), undefined, undefined, opts.selfHealer);
48
+ return await runner.run({ journey, params: opts.params, policy });
49
+ }
50
+ finally {
51
+ await session.close();
52
+ await rm(profileDir, { recursive: true, force: true });
53
+ }
54
+ }
55
+ //# sourceMappingURL=journey-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey-api.js","sourceRoot":"","sources":["../src/journey-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACvG,OAAO,EAAE,aAAa,EAAkB,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAA0C,MAAM,mBAAmB,CAAC;AAE1F;;;;GAIG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;CAAG;AAqBjD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,IAAuC;IAEvC,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC;IAE5C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,mBAAmB,CAAC,oBAAoB,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IAChE,CAAC;IAED,yEAAyE;IACzE,2CAA2C;IAC3C,cAAc,CAAC,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAElE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC;IAE9C,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,IAAI,qBAAqB,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;QAC9B,UAAU;QACV,QAAQ,EAAE,IAAI;QACd,cAAc,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC;QACxC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,IAAI;KAChC,CAAC,CAAC;IACH,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,MAAM,CAChD,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CACpD,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,KAAK,EAAE,IAAI,oBAAoB,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3G,OAAO,MAAM,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACpE,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC"}
@@ -0,0 +1,48 @@
1
+ import { type RunPolicy } from "@jevitate/domain";
2
+ import { type BrowserPort } from "@jevitate/playwright";
3
+ import { type CapacityReport } from "@jevitate/load";
4
+ /** Distinct from `@jevitate/journey`'s ParamValidationError-style "unknown id" cases elsewhere, so CLI callers can branch without string-matching. */
5
+ export declare class UnknownLoadJourneyError extends Error {
6
+ }
7
+ export interface RunJourneyLoadTestOptions {
8
+ dir: string;
9
+ id: string;
10
+ params: Record<string, string>;
11
+ concurrency: number;
12
+ iterationsPerActor: number;
13
+ /**
14
+ * Governs deterministic actor fan-out/scheduling only (see
15
+ * `@jevitate/load`'s `RunLoadTestConfig.seed` doc comment) — the real
16
+ * `JourneyRunner` this function drives per pool member has no pacing
17
+ * hook, so this `seed` does NOT (yet) produce human-speed pacing of the
18
+ * real run the way `modeledCapacityReport`'s `seed` drives
19
+ * `simulateTiming()`. Known gap, deferred to a future slice.
20
+ */
21
+ seed: number;
22
+ authorizedOrigins: readonly string[];
23
+ /** Defaults to `safeRunPolicy()`, same convention as `journey-api.ts`'s `runJourneyProgrammatically`. */
24
+ policy?: RunPolicy;
25
+ /**
26
+ * Testing seam: overrides the real `PlaywrightBrowserPort` used per pool
27
+ * member with a fake, so the per-actor browser-session lifecycle (see
28
+ * `runnerFactory` below) can be unit-tested with no real browser. Defaults
29
+ * to `() => new PlaywrightBrowserPort()`.
30
+ */
31
+ browserPortFactory?: () => BrowserPort;
32
+ }
33
+ /**
34
+ * The programmatic surface behind `jevitate load run` — resolves a published
35
+ * Journey (unknown id -> `UnknownLoadJourneyError`), validates params UP
36
+ * FRONT, then hands `@jevitate/load`'s `runLoadTest` a factory that opens ONE
37
+ * real headless Playwright session + `JourneyRunner` per pool member. The
38
+ * authorized-target check happens inside `runLoadTest` itself — this
39
+ * function does not duplicate or bypass it.
40
+ *
41
+ * NOTE on "seeded ⇒ reproducible / human-paced" (corrected post-review):
42
+ * `opts.seed` makes the ACTOR POOL's composition reproducible (via
43
+ * `deriveActorSeeds` inside `runLoadTest`) — it does not yet make the real
44
+ * run human-paced, because `JourneyRunner` (constructed below) has no
45
+ * pacing hook to seed. Only the offline `modeledCapacityReport` path is
46
+ * genuinely human-paced today, via `@jevitate/domain`'s `simulateTiming()`.
47
+ */
48
+ export declare function runJourneyLoadTest(opts: RunJourneyLoadTestOptions): Promise<CapacityReport>;
@@ -0,0 +1,84 @@
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { FsJourneyStore, JourneyRegistry, deriveParamSchema, validateParams } from "@jevitate/journey";
5
+ import { safeRunPolicy } from "@jevitate/domain";
6
+ import { PlaywrightBrowserPort } from "@jevitate/playwright";
7
+ import { CastActor, BrowseTheWeb } from "@jevitate/screenplay";
8
+ import { RecordingInterpreter } from "@jevitate/interpreter";
9
+ import { JourneyRunner } from "@jevitate/runtime";
10
+ import { runLoadTest } from "@jevitate/load";
11
+ /** Distinct from `@jevitate/journey`'s ParamValidationError-style "unknown id" cases elsewhere, so CLI callers can branch without string-matching. */
12
+ export class UnknownLoadJourneyError extends Error {
13
+ }
14
+ /**
15
+ * The programmatic surface behind `jevitate load run` — resolves a published
16
+ * Journey (unknown id -> `UnknownLoadJourneyError`), validates params UP
17
+ * FRONT, then hands `@jevitate/load`'s `runLoadTest` a factory that opens ONE
18
+ * real headless Playwright session + `JourneyRunner` per pool member. The
19
+ * authorized-target check happens inside `runLoadTest` itself — this
20
+ * function does not duplicate or bypass it.
21
+ *
22
+ * NOTE on "seeded ⇒ reproducible / human-paced" (corrected post-review):
23
+ * `opts.seed` makes the ACTOR POOL's composition reproducible (via
24
+ * `deriveActorSeeds` inside `runLoadTest`) — it does not yet make the real
25
+ * run human-paced, because `JourneyRunner` (constructed below) has no
26
+ * pacing hook to seed. Only the offline `modeledCapacityReport` path is
27
+ * genuinely human-paced today, via `@jevitate/domain`'s `simulateTiming()`.
28
+ */
29
+ export async function runJourneyLoadTest(opts) {
30
+ const store = new FsJourneyStore(opts.dir);
31
+ const registry = new JourneyRegistry(store);
32
+ const journey = await registry.get(opts.id);
33
+ if (!journey) {
34
+ throw new UnknownLoadJourneyError(`unknown journey '${opts.id}'`);
35
+ }
36
+ validateParams(deriveParamSchema(journey.recording), opts.params);
37
+ const policy = opts.policy ?? safeRunPolicy();
38
+ const browserPortFactory = opts.browserPortFactory ?? (() => new PlaywrightBrowserPort());
39
+ return runLoadTest({
40
+ targetOrigin: journey.recording.site,
41
+ authorizedOrigins: opts.authorizedOrigins,
42
+ concurrency: opts.concurrency,
43
+ iterationsPerActor: opts.iterationsPerActor,
44
+ seed: opts.seed,
45
+ runnerFactory: async (actorIndex) => {
46
+ const profileDir = await mkdtemp(join(tmpdir(), `doit-load-actor-${actorIndex}-`));
47
+ const port = browserPortFactory();
48
+ const session = await port.open({
49
+ profileDir,
50
+ headless: true,
51
+ allowedOrigins: [journey.recording.site],
52
+ baseUrl: journey.recording.site,
53
+ });
54
+ const actor = CastActor.named(`load-actor-${actorIndex}`).whoCan(new BrowseTheWeb(session, [journey.recording.site]));
55
+ const runner = new JourneyRunner(actor, new RecordingInterpreter());
56
+ // `runLoadTest` calls `run()` exactly `iterationsPerActor` times for
57
+ // this actor — success or failure, never more, never fewer (see
58
+ // measured-load-runner.ts's per-actor loop). Closing the real browser
59
+ // session on EVERY `run()` would defeat pooling (a fresh browser
60
+ // launch per iteration); never closing it leaks a Chromium process +
61
+ // tmp profile dir per actor per invocation, worse as `--concurrency`
62
+ // scales — the whole point of a load harness. So: reuse one session
63
+ // across this actor's iterations, and close it in a `finally` once
64
+ // the LAST iteration finishes OR throws — mirroring `journey-api.ts`'s
65
+ // `try { ... } finally { await session.close(); }`.
66
+ let remainingIterations = opts.iterationsPerActor;
67
+ return {
68
+ run: async () => {
69
+ try {
70
+ return await runner.run({ journey, params: opts.params, policy });
71
+ }
72
+ finally {
73
+ remainingIterations--;
74
+ if (remainingIterations <= 0) {
75
+ await session.close();
76
+ await rm(profileDir, { recursive: true, force: true });
77
+ }
78
+ }
79
+ },
80
+ };
81
+ },
82
+ });
83
+ }
84
+ //# sourceMappingURL=load-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-api.js","sourceRoot":"","sources":["../src/load-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ import { type AiGenerateTextArgs, type AiGenerateTextResult } from "@jevitate/mcp-facade";
3
+ import { type CredentialStore, type GenerationPort, type SetupRequiredResult } from "@jevitate/ai-core";
4
+ /**
5
+ * The MCP stdio server behind `jevitate mcp`. It exposes ONLY the tools in
6
+ * `@jevitate/mcp-facade`'s `ALLOWED_TOOLS` and structurally CANNOT serve any
7
+ * of `FORBIDDEN_TOOLS` (the raw browser primitives): the served surface is
8
+ * derived from `ALLOWED_TOOLS`, every descriptor is re-checked against the
9
+ * allow/forbid sets at build time (`assertAllowlisted`), and the call
10
+ * dispatcher refuses any name that is forbidden or not in the served map.
11
+ *
12
+ * `buildMcpTools` is the unit-testable seam — a test can enumerate the served
13
+ * tool names (and exercise each handler) WITHOUT opening a stdio transport.
14
+ * `startMcpServer` is the only place a real `StdioServerTransport` is opened.
15
+ */
16
+ export interface McpToolResult {
17
+ content: Array<{
18
+ type: "text";
19
+ text: string;
20
+ }>;
21
+ isError?: boolean;
22
+ }
23
+ export interface McpTool {
24
+ name: string;
25
+ description: string;
26
+ inputSchema: {
27
+ type: "object";
28
+ properties: Record<string, unknown>;
29
+ required?: string[];
30
+ };
31
+ handler: (args: Record<string, unknown>) => Promise<McpToolResult>;
32
+ }
33
+ export interface McpApiDeps {
34
+ /** Journeys store directory (`~/.jevitate/journeys` in production). */
35
+ journeysDir: string;
36
+ /**
37
+ * Test seam. Defaults to the real promoted-only projection over the
38
+ * journeys store (`@jevitate/mcp-facade`'s `findCapabilities`).
39
+ */
40
+ findCapabilities?: (query: string) => Promise<unknown>;
41
+ /**
42
+ * Test seam. Defaults to `runJourneyProgrammatically` with a fail-closed
43
+ * `safeRunPolicy()` (invariant #5: PUBLISHED-id-only, never inline steps).
44
+ */
45
+ runJourney?: (id: string, params: Record<string, string>) => Promise<unknown>;
46
+ /**
47
+ * Promoted mission-target store directory (`~/.jevitate/missions/targets` in
48
+ * production — the SAME store `jevitate mission target` writes). Required for
49
+ * the default `queue_exploration` wiring; the host resolves it.
50
+ */
51
+ missionTargetsDir?: string;
52
+ /** Mission queue directory (`~/.jevitate/missions/queue` in production). */
53
+ missionQueueDir?: string;
54
+ /**
55
+ * Inbox store directory (`~/.jevitate/inbox` in production) backing all 8
56
+ * `list_incoming`/`get_command`/`get_thread`/`queue_action`/
57
+ * `queue_retrieval`/`approve_action`/`cancel_command`/`get_site_health`
58
+ * tools. The `FsInboxStore` is built lazily inside each handler's closure
59
+ * (mirrors `queueExploration`'s lazy store) so constructing the tool set
60
+ * never touches disk. A missing/unconfigured dir is a config error (a
61
+ * refusal), never a silent success.
62
+ */
63
+ inboxDir?: string;
64
+ /**
65
+ * Test seam. Defaults to `@jevitate/mcp-facade`'s `queueExploration` over the
66
+ * fs-backed promoted `MissionTargetRegistry` + `FsMissionQueueStore`. Never
67
+ * runs a mission — only enqueues; refuses unknown/unpromoted targets and
68
+ * over-ceiling budgets (all validation delegated to `enqueueMission`).
69
+ */
70
+ queueExploration?: (args: unknown) => Promise<unknown>;
71
+ /**
72
+ * Credential store for `ai_generate_text`'s preflight gate. Defaults to
73
+ * `envCredentialStore()` (env + gitignored local config). `detect` never
74
+ * returns a value; the key value is read ONLY at the provider call, inside
75
+ * the gateway, and placed only in the Authorization header.
76
+ */
77
+ credentialStore?: CredentialStore;
78
+ /**
79
+ * Generation gateway for `ai_generate_text`. Provided by the host (the real
80
+ * `OpenRouterGenerationGateway`, which routes every outbound payload through
81
+ * the never-to-model credential guard). When ABSENT and no `aiGenerateText`
82
+ * seam is injected, `ai_generate_text` stays a typed `not_implemented`
83
+ * rather than fabricating an answer.
84
+ */
85
+ generationGateway?: GenerationPort;
86
+ /**
87
+ * Test seam. Defaults to `@jevitate/mcp-facade`'s `aiGenerateText`
88
+ * (`credentialStore` + `generationGateway`), preflight-gated so a missing key
89
+ * returns a typed `setup_required` result instead of calling the model.
90
+ */
91
+ aiGenerateText?: (args: AiGenerateTextArgs) => Promise<AiGenerateTextResult | SetupRequiredResult>;
92
+ }
93
+ /**
94
+ * Builds the served tool descriptors — EXACTLY one per `ALLOWED_TOOLS` entry.
95
+ * Wired to their real backing services: `find_capabilities` + `run_journey`
96
+ * (journey store), `queue_exploration` (`@jevitate/missions` enqueue over the
97
+ * promoted `MissionTargetRegistry`), and `ai_generate_text` (`@jevitate/ai-core`
98
+ * generation gateway behind a fail-closed credential preflight). The remaining
99
+ * allowlisted tools — the inbox/command-queue surface — are registered (so the
100
+ * served set equals `ALLOWED_TOOLS`) but respond with a typed `not_implemented`
101
+ * error, since no facade-level backing service exists for them in this slice.
102
+ * No handler ever fabricates a success.
103
+ */
104
+ export declare function buildMcpTools(deps: McpApiDeps): McpTool[];
105
+ /**
106
+ * Constructs the MCP `Server` and registers its `tools/list` and `tools/call`
107
+ * handlers off `buildMcpTools`. Does NOT open a transport — that is
108
+ * `startMcpServer`'s job — so this is safe to construct in a unit test.
109
+ */
110
+ export declare function createMcpServer(deps: McpApiDeps): Server;
111
+ /**
112
+ * Starts the real MCP stdio server. Blocks (the transport owns stdin/stdout)
113
+ * until the client disconnects — callers must NOT write to stdout while it
114
+ * runs, since stdout is the MCP protocol channel.
115
+ */
116
+ export declare function startMcpServer(deps: McpApiDeps): Promise<void>;