@obversa/runtime 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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +192 -0
  3. package/dist/api.d.ts +72 -0
  4. package/dist/api.js +9775 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/artifacts/conformance.d.ts +1 -0
  7. package/dist/artifacts/file-store.d.ts +8 -0
  8. package/dist/artifacts/store.d.ts +1 -0
  9. package/dist/callback/approval.d.ts +8 -0
  10. package/dist/callback/client.d.ts +38 -0
  11. package/dist/callback/gate.d.ts +1 -0
  12. package/dist/callback/stored-client.d.ts +5 -0
  13. package/dist/chunk-3L6YNPN6.js +3 -0
  14. package/dist/chunk-3L6YNPN6.js.map +1 -0
  15. package/dist/chunk-5GLEABOU.js +3 -0
  16. package/dist/chunk-5GLEABOU.js.map +1 -0
  17. package/dist/chunk-DEW5R23M.js +338 -0
  18. package/dist/chunk-DEW5R23M.js.map +1 -0
  19. package/dist/chunk-DV5P4QLI.js +3056 -0
  20. package/dist/chunk-DV5P4QLI.js.map +1 -0
  21. package/dist/chunk-NIBHM5I5.js +34 -0
  22. package/dist/chunk-NIBHM5I5.js.map +1 -0
  23. package/dist/chunk-RZLMX3IA.js +368 -0
  24. package/dist/chunk-RZLMX3IA.js.map +1 -0
  25. package/dist/core/agent-md.d.ts +36 -0
  26. package/dist/core/agent.d.ts +86 -0
  27. package/dist/core/approval-job.d.ts +43 -0
  28. package/dist/core/assert-graph.d.ts +34 -0
  29. package/dist/core/budget.d.ts +49 -0
  30. package/dist/core/concurrency.d.ts +2 -0
  31. package/dist/core/condition.d.ts +209 -0
  32. package/dist/core/context.d.ts +36 -0
  33. package/dist/core/cost.d.ts +59 -0
  34. package/dist/core/dag.d.ts +20 -0
  35. package/dist/core/decision.d.ts +29 -0
  36. package/dist/core/describe.d.ts +56 -0
  37. package/dist/core/engine-meta.d.ts +5 -0
  38. package/dist/core/env-overlay.d.ts +35 -0
  39. package/dist/core/errors.d.ts +46 -0
  40. package/dist/core/feedback.d.ts +68 -0
  41. package/dist/core/git.d.ts +152 -0
  42. package/dist/core/guards.d.ts +70 -0
  43. package/dist/core/isolated.d.ts +40 -0
  44. package/dist/core/job.d.ts +134 -0
  45. package/dist/core/limits.d.ts +22 -0
  46. package/dist/core/loop.d.ts +24 -0
  47. package/dist/core/merge.d.ts +30 -0
  48. package/dist/core/pipeline.d.ts +35 -0
  49. package/dist/core/process.d.ts +11 -0
  50. package/dist/core/progress.d.ts +82 -0
  51. package/dist/core/redact.d.ts +1 -0
  52. package/dist/core/stats.d.ts +63 -0
  53. package/dist/core/team.d.ts +34 -0
  54. package/dist/core/text.d.ts +8 -0
  55. package/dist/core/tournament.d.ts +25 -0
  56. package/dist/core/types.d.ts +650 -0
  57. package/dist/engines/command-runner.d.ts +1 -0
  58. package/dist/engines/conformance.d.ts +1 -0
  59. package/dist/engines/engine.d.ts +4 -0
  60. package/dist/engines/failure.d.ts +1 -0
  61. package/dist/engines/fallback.d.ts +35 -0
  62. package/dist/engines/message-map.d.ts +1 -0
  63. package/dist/engines/mock.d.ts +1 -0
  64. package/dist/engines/preflight.d.ts +36 -0
  65. package/dist/env/command.d.ts +50 -0
  66. package/dist/env/command.js +65 -0
  67. package/dist/env/command.js.map +1 -0
  68. package/dist/env/environment.d.ts +4 -0
  69. package/dist/env/mock.d.ts +24 -0
  70. package/dist/events/conformance.d.ts +1 -0
  71. package/dist/events/envelope.d.ts +1 -0
  72. package/dist/events/jsonl-store.d.ts +8 -0
  73. package/dist/events/store.d.ts +1 -0
  74. package/dist/graph/commands.d.ts +5 -0
  75. package/dist/graph/conformance.d.ts +32 -0
  76. package/dist/graph/kernel.d.ts +5 -0
  77. package/dist/graph/plan.d.ts +1 -0
  78. package/dist/graph/type.d.ts +7 -0
  79. package/dist/graph/value.d.ts +1 -0
  80. package/dist/graph-types/dag.d.ts +137 -0
  81. package/dist/graph-types/loop.d.ts +194 -0
  82. package/dist/graph-types/team.d.ts +68 -0
  83. package/dist/memory.d.ts +82 -0
  84. package/dist/memory.js +397 -0
  85. package/dist/memory.js.map +1 -0
  86. package/dist/proof/acceptance.d.ts +5 -0
  87. package/dist/proof/artifact.d.ts +6 -0
  88. package/dist/proof/cache.d.ts +4 -0
  89. package/dist/runtime/attempt.d.ts +19 -0
  90. package/dist/runtime/budget.d.ts +4 -0
  91. package/dist/runtime/engine-availability.d.ts +18 -0
  92. package/dist/runtime/graph-executor.d.ts +7 -0
  93. package/dist/runtime/monitor.d.ts +80 -0
  94. package/dist/runtime/node-lifecycle.d.ts +84 -0
  95. package/dist/runtime/paths.d.ts +2 -0
  96. package/dist/runtime/persist.d.ts +31 -0
  97. package/dist/runtime/preflight-record.d.ts +149 -0
  98. package/dist/runtime/process-tree.d.ts +1 -0
  99. package/dist/runtime/result-contract.d.ts +4 -0
  100. package/dist/runtime/result-parts.d.ts +1 -0
  101. package/dist/runtime/run-definition.d.ts +24 -0
  102. package/dist/runtime/run-event.d.ts +9 -0
  103. package/dist/runtime/runner.d.ts +139 -0
  104. package/dist/runtime/supervisor.d.ts +111 -0
  105. package/dist/runtime/team-rooms.d.ts +13 -0
  106. package/dist/runtime/workspace-policy.d.ts +25 -0
  107. package/dist/storage/error.d.ts +1 -0
  108. package/dist/storage/id.d.ts +1 -0
  109. package/dist/storage/local.d.ts +12 -0
  110. package/dist/storage/local.js +1492 -0
  111. package/dist/storage/local.js.map +1 -0
  112. package/dist/testing.d.ts +15 -0
  113. package/dist/testing.js +274 -0
  114. package/dist/testing.js.map +1 -0
  115. package/dist/workflow-agent-response.d.ts +3 -0
  116. package/dist/workflow-support.d.ts +36 -0
  117. package/dist/workflow-support.js +5 -0
  118. package/dist/workflow-support.js.map +1 -0
  119. package/dist/workflow.d.ts +73 -0
  120. package/dist/workspace/conformance.d.ts +1 -0
  121. package/dist/workspace/git-provider.d.ts +26 -0
  122. package/dist/workspace/provider.d.ts +2 -0
  123. package/package.json +91 -0
@@ -0,0 +1,35 @@
1
+ /**
2
+ * A fallback chain over engine instances, tried in declared order.
3
+ *
4
+ * Default triggers are auth, billing, missing CLI, unavailable model,
5
+ * invalid configuration and quota. An engine that reports one of these is
6
+ * skipped for the rest of this chain's lifetime. Rate limits and transient
7
+ * failures propagate to the caller. An explicit `on` set replaces the
8
+ * defaults.
9
+ *
10
+ * Aborts never fall back. If every engine fails, the final engine error is
11
+ * thrown. A subsequent call with every engine skipped reports no live engine.
12
+ */
13
+ import type { Engine } from './engine.js';
14
+ import { type EngineFailureKind } from './failure.js';
15
+ export interface FallbackInfo {
16
+ /** The lane that just died. */
17
+ from: string;
18
+ /** The lane the call is moving to, when one is left. */
19
+ to?: string;
20
+ failure: EngineFailureKind;
21
+ error: unknown;
22
+ }
23
+ export interface FallbackOptions {
24
+ /** Failure kinds that trigger fallback. Default: `LANE_DEAD_FAILURES`. */
25
+ on?: Iterable<EngineFailureKind>;
26
+ /** Observe each reroute (log it, count it, surface it). */
27
+ onFallback?: (info: FallbackInfo) => void;
28
+ }
29
+ /**
30
+ * Build a fallback chain over ready-made `Engine`s, tried in order.
31
+ *
32
+ * ```ts
33
+ * await run(job, { engine: fallbackEngine([claude, codex]) });
34
+ */
35
+ export declare function fallbackEngine(engines: readonly [Engine, ...Engine[]], options?: FallbackOptions): Engine;
@@ -0,0 +1 @@
1
+ export { mapMessage, newAccumulator, type Accumulator, } from '@obversa/core/claude-stream-json';
@@ -0,0 +1 @@
1
+ export { MockEngine, mockVerdict, type MockResponder, } from '@obversa/core/testing';
@@ -0,0 +1,36 @@
1
+ /** One bounded live call through the selected engine, with validated evidence. */
2
+ import { type AgentResult, type AttemptMetadata, type Engine, type EngineIncompleteResultEvidence, type EngineSelectionRecord, type UsageReceipt } from './engine.js';
3
+ import { type EngineFailureKind } from './failure.js';
4
+ export interface PreflightResult {
5
+ engine: string;
6
+ model?: string;
7
+ ok: boolean;
8
+ /** Set when the probe failed, using the live engine-failure vocabulary. */
9
+ failure?: EngineFailureKind;
10
+ /** One line of evidence: the reply, or the error message. */
11
+ detail: string;
12
+ latencyMs: number;
13
+ usage?: UsageReceipt;
14
+ effective?: EngineSelectionRecord;
15
+ evidence?: {
16
+ readonly kind: 'complete';
17
+ readonly result: Omit<AgentResult, 'raw'>;
18
+ } | {
19
+ readonly kind: 'incomplete';
20
+ readonly result: Omit<EngineIncompleteResultEvidence, 'raw'>;
21
+ };
22
+ }
23
+ export interface PreflightOptions {
24
+ model?: string;
25
+ /** Hard limit on waiting for the probe. Default 60s. */
26
+ timeoutMs?: number;
27
+ signal?: AbortSignal;
28
+ cwd?: string;
29
+ attempt?: AttemptMetadata;
30
+ }
31
+ /** Probe one engine with a tiny live turn. Failures are returned, not thrown. */
32
+ export declare function preflightEngine(engine: Engine, opts?: PreflightOptions): Promise<PreflightResult>;
33
+ /** Probe several engines concurrently (they are independent lanes). */
34
+ export declare function preflight(engines: readonly Engine[], opts?: PreflightOptions): Promise<PreflightResult[]>;
35
+ /** One line per lane, for terminals and logs. */
36
+ export declare function formatPreflight(result: PreflightResult): string;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * `commandEnvironment` — a generic, CLI-driven Environment. Every IaC tool
3
+ * (sst, terraform, pulumi, cloudformation-via-aws-cli) has the same shape: a
4
+ * command to deploy a stage, a command to read its outputs, a command to tear it
5
+ * down. This factory captures that shape. The consumer supplies the concrete
6
+ * command configuration.
7
+ *
8
+ * It drives the CLIs through the bounded process helper, so it stays in
9
+ * the Obversa package as an opt-in subpath without coupling
10
+ * the core to any deploy tool. An SDK-bound adapter (e.g. @aws-sdk) adds a real
11
+ * dependency and belongs in a separate package or the consumer instead.
12
+ *
13
+ * The consumer supplies the tool-specific bits: how a stage name is derived, the
14
+ * argv for each phase, and `map` (which parsed output is the URL and how the
15
+ * outputs become the env vars the gate reads). The factory stays tool-agnostic.
16
+ */
17
+ import type { Workspace } from '../core/types.js';
18
+ import type { Environment } from './environment.js';
19
+ /** A command to run: a binary and its args. */
20
+ export interface Cmd {
21
+ cmd: string;
22
+ args?: string[];
23
+ }
24
+ export interface CommandEnvConfig {
25
+ /** Adapter name (surfaced in errors). Default 'command'. */
26
+ name?: string;
27
+ /** Working dir for the commands. Default: the workspace dir (the worktree). */
28
+ cwd?: (ws: Workspace) => string;
29
+ /** Stage/stack/workspace identity. Default: a slug of the workspace branch. */
30
+ stage?: (ws: Workspace) => string;
31
+ /** Argv to deploy the stage. */
32
+ deploy: (stage: string, ws: Workspace) => Cmd;
33
+ /** Argv to read outputs as JSON on stdout. Optional (no URL/env if omitted). */
34
+ outputs?: (stage: string, ws: Workspace) => Cmd;
35
+ /** Argv to tear the stage down. */
36
+ destroy: (stage: string, ws: Workspace) => Cmd;
37
+ /**
38
+ * Turn the outputs into the handle's `url` + `env`, normalising the tool's
39
+ * specific shape. `outputs` is the best-effort JSON parse (for
40
+ * terraform/pulumi/sst); `raw` is the verbatim stdout (for tools whose output
41
+ * is not JSON, e.g. `docker compose port` prints `0.0.0.0:49153`).
42
+ */
43
+ map?: (outputs: Record<string, unknown>, stage: string, raw: string) => {
44
+ url?: string;
45
+ env?: Record<string, string>;
46
+ };
47
+ /** Per-command timeout (ms). */
48
+ timeoutMs?: number;
49
+ }
50
+ export declare function commandEnvironment(config: CommandEnvConfig): Environment;
@@ -0,0 +1,65 @@
1
+ import { runRuntimeProcess, processText } from '../chunk-NIBHM5I5.js';
2
+
3
+ // src/env/command.ts
4
+ function stageSlug(s) {
5
+ return (s ?? "main").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/(^-+|-+$)/g, "") || "main";
6
+ }
7
+ function parseJson(s) {
8
+ try {
9
+ const v = JSON.parse(s);
10
+ return v && typeof v === "object" && !Array.isArray(v) ? v : {};
11
+ } catch {
12
+ return {};
13
+ }
14
+ }
15
+ function commandEnvironment(config) {
16
+ const name = config.name ?? "command";
17
+ const stageOf = config.stage ?? ((ws) => stageSlug(ws.branch));
18
+ const cwdOf = config.cwd ?? ((ws) => ws.dir);
19
+ return {
20
+ name,
21
+ async up(ws, signal) {
22
+ const stage = stageOf(ws);
23
+ const cwd = cwdOf(ws);
24
+ const exec = async (c, phase) => {
25
+ const r = await runRuntimeProcess({
26
+ executable: c.cmd,
27
+ args: c.args ?? [],
28
+ cwd,
29
+ timeoutMs: config.timeoutMs,
30
+ signal
31
+ });
32
+ if (r.exitCode !== 0) {
33
+ const stderr = processText(r.stderr);
34
+ const stdout = processText(r.stdout);
35
+ const detail = (stderr || stdout).slice(0, 500);
36
+ throw new Error(
37
+ `${name} ${phase} failed for stage "${stage}" (exit ${r.exitCode}): ${detail}`.trim()
38
+ );
39
+ }
40
+ return processText(r.stdout).replace(/\r?\n$/u, "");
41
+ };
42
+ await exec(config.deploy(stage, ws), "deploy");
43
+ const raw = config.outputs ? await exec(config.outputs(stage, ws), "outputs") : "";
44
+ const mapped = config.map?.(parseJson(raw), stage, raw) ?? {};
45
+ return {
46
+ url: mapped.url,
47
+ env: mapped.env ?? {},
48
+ async down(sig) {
49
+ const d = config.destroy(stage, ws);
50
+ await runRuntimeProcess({
51
+ executable: d.cmd,
52
+ args: d.args ?? [],
53
+ cwd,
54
+ timeoutMs: config.timeoutMs,
55
+ signal: sig
56
+ });
57
+ }
58
+ };
59
+ }
60
+ };
61
+ }
62
+
63
+ export { commandEnvironment };
64
+ //# sourceMappingURL=command.js.map
65
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/env/command.ts"],"names":[],"mappings":";;;AAwDA,SAAS,UAAU,CAAA,EAA+B;AAChD,EAAA,OAAA,CACG,CAAA,IAAK,QAAQ,OAAA,CAAQ,mBAAA,EAAqB,GAAG,CAAA,CAAE,OAAA,CAAQ,YAAA,EAAc,EAAE,CAAA,IACxE,MAAA;AAEJ;AAEA,SAAS,UAAU,CAAA,EAAoC;AACrD,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAa,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA;AAC/B,IAAA,OAAO,CAAA,IAAK,OAAO,CAAA,KAAM,QAAA,IAAY,CAAC,MAAM,OAAA,CAAQ,CAAC,CAAA,GAChD,CAAA,GACD,EAAC;AAAA,EACP,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAEO,SAAS,mBAAmB,MAAA,EAAuC;AACxE,EAAA,MAAM,IAAA,GAAO,OAAO,IAAA,IAAQ,SAAA;AAC5B,EAAA,MAAM,UAAU,MAAA,CAAO,KAAA,KAAU,CAAC,EAAA,KAAkB,SAAA,CAAU,GAAG,MAAM,CAAA,CAAA;AACvE,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,GAAA,KAAQ,CAAC,OAAkB,EAAA,CAAG,GAAA,CAAA;AAEnD,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,MAAM,EAAA,CAAG,EAAA,EAAe,MAAA,EAAyC;AAC/D,MAAA,MAAM,KAAA,GAAQ,QAAQ,EAAE,CAAA;AACxB,MAAA,MAAM,GAAA,GAAM,MAAM,EAAE,CAAA;AAEpB,MAAA,MAAM,IAAA,GAAO,OAAO,CAAA,EAAQ,KAAA,KAAmC;AAC7D,QAAA,MAAM,CAAA,GAAI,MAAM,iBAAA,CAAkB;AAAA,UAChC,YAAY,CAAA,CAAE,GAAA;AAAA,UACd,IAAA,EAAM,CAAA,CAAE,IAAA,IAAQ,EAAC;AAAA,UACjB,GAAA;AAAA,UACA,WAAW,MAAA,CAAO,SAAA;AAAA,UAClB;AAAA,SACD,CAAA;AACD,QAAA,IAAI,CAAA,CAAE,aAAa,CAAA,EAAG;AACpB,UAAA,MAAM,MAAA,GAAS,WAAA,CAAY,CAAA,CAAE,MAAM,CAAA;AACnC,UAAA,MAAM,MAAA,GAAS,WAAA,CAAY,CAAA,CAAE,MAAM,CAAA;AACnC,UAAA,MAAM,MAAA,GAAA,CAAU,MAAA,IAAU,MAAA,EAAQ,KAAA,CAAM,GAAG,GAAG,CAAA;AAC9C,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,KAAK,CAAA,mBAAA,EAAsB,KAAK,CAAA,QAAA,EAAW,CAAA,CAAE,QAAQ,CAAA,GAAA,EAAM,MAAM,CAAA,CAAA,CAAG,IAAA;AAAK,WACtF;AAAA,QACF;AACA,QAAA,OAAO,YAAY,CAAA,CAAE,MAAM,CAAA,CAAE,OAAA,CAAQ,WAAW,EAAE,CAAA;AAAA,MACpD,CAAA;AAEA,MAAA,MAAM,KAAK,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,EAAE,GAAG,QAAQ,CAAA;AAC7C,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,OAAA,GACf,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,EAAG,SAAS,CAAA,GAC/C,EAAA;AACJ,MAAA,MAAM,MAAA,GAAS,OAAO,GAAA,GAAM,SAAA,CAAU,GAAG,CAAA,EAAG,KAAA,EAAO,GAAG,CAAA,IAAK,EAAC;AAE5D,MAAA,OAAO;AAAA,QACL,KAAK,MAAA,CAAO,GAAA;AAAA,QACZ,GAAA,EAAK,MAAA,CAAO,GAAA,IAAO,EAAC;AAAA,QACpB,MAAM,KAAK,GAAA,EAAiC;AAC1C,UAAA,MAAM,CAAA,GAAI,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAClC,UAAA,MAAM,iBAAA,CAAkB;AAAA,YACtB,YAAY,CAAA,CAAE,GAAA;AAAA,YACd,IAAA,EAAM,CAAA,CAAE,IAAA,IAAQ,EAAC;AAAA,YACjB,GAAA;AAAA,YACA,WAAW,MAAA,CAAO,SAAA;AAAA,YAClB,MAAA,EAAQ;AAAA,WACT,CAAA;AAAA,QACH;AAAA,OACF;AAAA,IACF;AAAA,GACF;AACF","file":"command.js","sourcesContent":["/**\n * `commandEnvironment` — a generic, CLI-driven Environment. Every IaC tool\n * (sst, terraform, pulumi, cloudformation-via-aws-cli) has the same shape: a\n * command to deploy a stage, a command to read its outputs, a command to tear it\n * down. This factory captures that shape. The consumer supplies the concrete\n * command configuration.\n *\n * It drives the CLIs through the bounded process helper, so it stays in\n * the Obversa package as an opt-in subpath without coupling\n * the core to any deploy tool. An SDK-bound adapter (e.g. @aws-sdk) adds a real\n * dependency and belongs in a separate package or the consumer instead.\n *\n * The consumer supplies the tool-specific bits: how a stage name is derived, the\n * argv for each phase, and `map` (which parsed output is the URL and how the\n * outputs become the env vars the gate reads). The factory stays tool-agnostic.\n */\n\nimport type { Workspace } from '../core/types.js';\nimport type { Environment, EnvHandle } from './environment.js';\nimport { processText, runRuntimeProcess } from '../core/process.js';\n\n/** A command to run: a binary and its args. */\nexport interface Cmd {\n cmd: string;\n args?: string[];\n}\n\nexport interface CommandEnvConfig {\n /** Adapter name (surfaced in errors). Default 'command'. */\n name?: string;\n /** Working dir for the commands. Default: the workspace dir (the worktree). */\n cwd?: (ws: Workspace) => string;\n /** Stage/stack/workspace identity. Default: a slug of the workspace branch. */\n stage?: (ws: Workspace) => string;\n /** Argv to deploy the stage. */\n deploy: (stage: string, ws: Workspace) => Cmd;\n /** Argv to read outputs as JSON on stdout. Optional (no URL/env if omitted). */\n outputs?: (stage: string, ws: Workspace) => Cmd;\n /** Argv to tear the stage down. */\n destroy: (stage: string, ws: Workspace) => Cmd;\n /**\n * Turn the outputs into the handle's `url` + `env`, normalising the tool's\n * specific shape. `outputs` is the best-effort JSON parse (for\n * terraform/pulumi/sst); `raw` is the verbatim stdout (for tools whose output\n * is not JSON, e.g. `docker compose port` prints `0.0.0.0:49153`).\n */\n map?: (\n outputs: Record<string, unknown>,\n stage: string,\n raw: string,\n ) => { url?: string; env?: Record<string, string> };\n /** Per-command timeout (ms). */\n timeoutMs?: number;\n}\n\n/** Slug a branch into a stage-safe identity. */\nfunction stageSlug(s: string | undefined): string {\n return (\n (s ?? 'main').replace(/[^A-Za-z0-9._-]+/g, '-').replace(/(^-+|-+$)/g, '') ||\n 'main'\n );\n}\n\nfunction parseJson(s: string): Record<string, unknown> {\n try {\n const v: unknown = JSON.parse(s);\n return v && typeof v === 'object' && !Array.isArray(v)\n ? (v as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nexport function commandEnvironment(config: CommandEnvConfig): Environment {\n const name = config.name ?? 'command';\n const stageOf = config.stage ?? ((ws: Workspace) => stageSlug(ws.branch));\n const cwdOf = config.cwd ?? ((ws: Workspace) => ws.dir);\n\n return {\n name,\n async up(ws: Workspace, signal: AbortSignal): Promise<EnvHandle> {\n const stage = stageOf(ws);\n const cwd = cwdOf(ws);\n\n const exec = async (c: Cmd, phase: string): Promise<string> => {\n const r = await runRuntimeProcess({\n executable: c.cmd,\n args: c.args ?? [],\n cwd,\n timeoutMs: config.timeoutMs,\n signal,\n });\n if (r.exitCode !== 0) {\n const stderr = processText(r.stderr);\n const stdout = processText(r.stdout);\n const detail = (stderr || stdout).slice(0, 500);\n throw new Error(\n `${name} ${phase} failed for stage \"${stage}\" (exit ${r.exitCode}): ${detail}`.trim(),\n );\n }\n return processText(r.stdout).replace(/\\r?\\n$/u, '');\n };\n\n await exec(config.deploy(stage, ws), 'deploy');\n const raw = config.outputs\n ? await exec(config.outputs(stage, ws), 'outputs')\n : '';\n const mapped = config.map?.(parseJson(raw), stage, raw) ?? {};\n\n return {\n url: mapped.url,\n env: mapped.env ?? {},\n async down(sig: AbortSignal): Promise<void> {\n const d = config.destroy(stage, ws);\n await runRuntimeProcess({\n executable: d.cmd,\n args: d.args ?? [],\n cwd,\n timeoutMs: config.timeoutMs,\n signal: sig,\n });\n },\n };\n },\n };\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { Environment } from '@obversa/api';
2
+ export type { Environment, EnvHandle, EnvironmentWorkspace } from '@obversa/api';
3
+ /** Duck-type guard: a ready-made `Environment` rather than something else. */
4
+ export declare function isEnvironment(value: unknown): value is Environment;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A scripted, offline environment, mirroring `MockEngine`. It simulates a deploy
3
+ * (hands back a URL + env vars, counts up/down) with no network, so the
4
+ * lifecycle binding and gate integration run the same code paths in tests as a
5
+ * real sst/Vercel adapter would.
6
+ */
7
+ import type { Workspace } from '../core/types.js';
8
+ import type { Environment, EnvHandle } from './environment.js';
9
+ export interface MockEnvOptions {
10
+ /** The URL to hand back. A function derives it from the workspace (branch). */
11
+ url?: string | ((ws: Workspace) => string);
12
+ /** Extra env vars to inject alongside `BASE_URL`. */
13
+ env?: Record<string, string>;
14
+ onUp?: (ws: Workspace) => void;
15
+ onDown?: () => void;
16
+ }
17
+ export declare class MockEnvironment implements Environment {
18
+ private readonly opts;
19
+ readonly name = "mock-env";
20
+ upCount: number;
21
+ downCount: number;
22
+ constructor(opts?: MockEnvOptions);
23
+ up(workspace: Workspace): Promise<EnvHandle>;
24
+ }
@@ -0,0 +1 @@
1
+ export { runEventStoreConformance, assertEventStoreConformance, type EventStoreConformanceOptions, type EventStoreConformanceFactory, type EventStoreConformanceFailure, type EventStoreConformanceReport, } from '@obversa/api/testing';
@@ -0,0 +1 @@
1
+ export { validateDomainEventId, validateNewDomainEvent, validateDomainEventEnvelope, type DomainEventId, type EventStreamId, type StorageNamespace, type StreamRevision, type EventStreamRef, type NewDomainEvent, type DomainEventEnvelope, } from '@obversa/api';
@@ -0,0 +1,8 @@
1
+ import { type EventStore } from './store.js';
2
+ export interface LocalEventStoreOptions {
3
+ readonly root: string;
4
+ readonly maxEventPayloadBytes?: number;
5
+ readonly maxAppendBatchBytes?: number;
6
+ readonly knownSecrets?: readonly string[];
7
+ }
8
+ export declare function createLocalEventStore(options: LocalEventStoreOptions): EventStore;
@@ -0,0 +1 @@
1
+ export { validateStorageId, validateEventStreamRef, validateStreamRevision, validateDomainEventBatch, findKnownSecretInEvents, type DomainEventBatch, type EventStore, } from '@obversa/api';
@@ -0,0 +1,5 @@
1
+ import type { GraphKernel } from './kernel.js';
2
+ import type { GraphCommand } from '@obversa/api';
3
+ export { type DispatchGraphCommand, type PauseGraphCommand, type CompleteGraphCommand, type FailGraphCommand, type GraphCommand } from '@obversa/api';
4
+ /** Validate one pure graph decision before the runtime can act on it. */
5
+ export declare function validateGraphCommands(commands: readonly GraphCommand[], kernel: GraphKernel): readonly GraphCommand[];
@@ -0,0 +1,32 @@
1
+ import type { GraphDefinition } from './kernel.js';
2
+ import type { GraphCommand } from './commands.js';
3
+ import { type GraphBounds, type GraphRequirements, type PlanResolution } from './plan.js';
4
+ import { type GraphEvent, type GraphType } from './type.js';
5
+ import { type JsonValue } from './value.js';
6
+ export interface GraphTypeConformanceFixture<Definition extends GraphDefinition = GraphDefinition, State extends JsonValue = JsonValue, Event extends GraphEvent = GraphEvent, Requirements extends GraphRequirements = GraphRequirements> {
7
+ readonly graphType: GraphType<Definition, State, Event, Requirements>;
8
+ readonly definition: Definition;
9
+ readonly events: readonly Event[];
10
+ readonly invalidDefinitions: readonly [Definition, ...Definition[]];
11
+ readonly planResolution: PlanResolution;
12
+ readonly expected: {
13
+ /** Initial state followed by the state after each event prefix. */
14
+ readonly states: readonly [State, ...State[]];
15
+ /** Ordered decision at the initial state and after each event prefix. */
16
+ readonly commands: readonly [readonly GraphCommand[], ...(readonly GraphCommand[])[]];
17
+ readonly bounds: GraphBounds;
18
+ };
19
+ }
20
+ export interface GraphTypeConformanceFailure {
21
+ readonly case: string;
22
+ readonly message: string;
23
+ }
24
+ export interface GraphTypeConformanceReport {
25
+ readonly ok: boolean;
26
+ readonly cases: number;
27
+ readonly failures: readonly GraphTypeConformanceFailure[];
28
+ }
29
+ /** Run the framework-free behavioral checks for an outside graph type. */
30
+ export declare function runGraphTypeConformance<Definition extends GraphDefinition, State extends JsonValue, Event extends GraphEvent, Requirements extends GraphRequirements>(fixture: GraphTypeConformanceFixture<Definition, State, Event, Requirements>): GraphTypeConformanceReport;
31
+ /** Throw one readable error when an outside graph type breaks the contract. */
32
+ export declare function assertGraphTypeConformance<Definition extends GraphDefinition, State extends JsonValue, Event extends GraphEvent, Requirements extends GraphRequirements>(fixture: GraphTypeConformanceFixture<Definition, State, Event, Requirements>): void;
@@ -0,0 +1,5 @@
1
+ export { GraphValidationError } from './value.js';
2
+ export type { GraphValidationIssue } from './value.js';
3
+ import type { GraphDefinition, GraphKernel } from '@obversa/api';
4
+ export type { GraphId, NodeId, EdgeId, GraphNode, GraphEdge, GraphDefinition, CompiledGraphDefinition, GraphKernel } from '@obversa/api';
5
+ export declare function createGraphKernel<Definition extends GraphDefinition>(definition: Definition): GraphKernel<Definition>;
@@ -0,0 +1 @@
1
+ export { type PermissionDescriptor, type ExecutionLaneDescription, type GraphPhaseDescription, type GraphNodeDescription, type GraphEdgeDescription, type PlanBound, type GraphBounds, type GraphPolicyDescription, type GraphRequirements, type GraphDescriptionInput, type GraphDescription, type GraphPackageIdentity, type GraphPackageAdmission, type ExecutionLaneResolution, type RunPreflightPolicy, type PlanResolution, type ResolvedExecutionLane, type ResolvedPlan, type ResolvedPlanSnapshot, validateResolvedPlan, validateGraphDescription, resolveGraphPlan, type ExecutionTarget, } from '@obversa/api';
@@ -0,0 +1,7 @@
1
+ import { type GraphDefinition } from './kernel.js';
2
+ import { type GraphRequirements } from './plan.js';
3
+ import { type JsonValue } from './value.js';
4
+ import type { GraphEvent, CompiledGraphType, GraphType } from '@obversa/api';
5
+ export { type GraphEvent, type GraphEngineIdentity, type EngineAttemptRecordedPayload, type GraphBindings, type GraphTypeCompilation, type CompiledGraphType, type GraphType } from '@obversa/api';
6
+ /** Validate a definition, then compile its trusted graph-type implementation. */
7
+ export declare function compileGraph<Definition extends GraphDefinition, State extends JsonValue, Event extends GraphEvent, Requirements extends GraphRequirements>(graphType: GraphType<Definition, State, Event, Requirements>, definition: Definition): CompiledGraphType<Definition, State, Event, Requirements>;
@@ -0,0 +1 @@
1
+ export { GraphValidationError, type GraphValidationIssue, type RunBrief, JsonValueError, canonicalJson, cloneFrozenJson, digestJson, type JsonObject, type JsonPrimitive, type JsonValue, type Sha256Digest, } from '@obversa/api';
@@ -0,0 +1,137 @@
1
+ /**
2
+ * The DAG graph type (roadmap D6): dependency graphs, sequences, parallel
3
+ * work, and readable pipelines on the common graph contract.
4
+ *
5
+ * One node is one DAG node; edges are dependencies (source must finish
6
+ * before target runs). Required, optional, and finalizer kinds carry the
7
+ * legacy failure policy: a required node failing blocks its dependents and
8
+ * fails the run; an optional failure neither fails the run nor blocks
9
+ * dependents; a completed node whose result carries `skipped: true` counts
10
+ * as an expected skip, which is neutral and never blocks (the legacy unmet
11
+ * `when` gate). With `stopOnError`, the first required failure stops
12
+ * scheduling anything not already in flight; independent branches continue
13
+ * without it. Finalizers run after everything else settles, on failure as
14
+ * on success, and can never turn a failed run green.
15
+ *
16
+ * This is the pure form only: definition validation (including cycle
17
+ * detection through the pinned `toposort`), state reduction, decisions,
18
+ * bounds, and the plan description. Job execution, isolation, kickbacks,
19
+ * and the sequence/parallel/pipeline authoring helpers are the runtime
20
+ * owner's; this file only folds the standard node events and decides what
21
+ * can run next.
22
+ *
23
+ * Event vocabulary (all JSON, the executor's standard node events, recorded
24
+ * into the durable `graph:` namespace):
25
+ * - `node-dispatched` — a node attempt started at a position
26
+ * - `node-completed` — a node attempt finished; a result carrying
27
+ * `skipped: true` is an expected skip
28
+ * - `node-failed` — a node attempt failed with a typed code
29
+ * - `node-paused` — a node attempt paused; the DAG waits
30
+ * - `node-resumed` — a paused node continues its attempt
31
+ *
32
+ * A dispatch, completion, or failure counts only for the node's attempt
33
+ * currently in flight at that exact position; a stale event from an earlier
34
+ * attempt is ignored. Positions are minted as `dag/${node}/${attempt}` where
35
+ * the attempt number is the count of recorded dispatches for that node, so
36
+ * every retry takes a fresh position. Retry is graph policy, declared as a
37
+ * per-node cap: decide offers a fresh dispatch for a failed required node
38
+ * while the cap allows, stopOnError suppresses retries as it stops
39
+ * scheduling, and the DAG fails a node only once its cap is exhausted. A
40
+ * late result for a superseded attempt is ignored.
41
+ *
42
+ * A paused node pauses the whole DAG, as the legacy scheduler does. The form
43
+ * first waits for every other attempt in the recorded batch to settle, then
44
+ * returns the pause command. A resumed node continues the same attempt.
45
+ *
46
+ * Each decision waits for every recorded attempt to settle, then dispatches
47
+ * one ready batch that fits the global and keyed limits in declaration order.
48
+ * Positions stay unique across the run; the form never re-emits a dispatch
49
+ * for an attempt already recorded in the folded event history. Each dispatch
50
+ * carries the named results of its direct predecessors. Completion returns
51
+ * every completed node result by node name.
52
+ */
53
+ import type { GraphDefinition, NodeId } from '../graph/kernel.js';
54
+ import type { GraphEvent, GraphType } from '../graph/type.js';
55
+ import { type JsonObject, type JsonValue } from '../graph/value.js';
56
+ export type DagNodeKind = 'required' | 'optional' | 'finalizer';
57
+ type DagExecutionTargetData = {
58
+ readonly adapter: string;
59
+ readonly provider: string;
60
+ readonly modelFamily: string;
61
+ readonly model: string;
62
+ readonly tools: readonly string[];
63
+ };
64
+ type DagExecutionLaneData = {
65
+ readonly id: string;
66
+ readonly requested: DagExecutionTargetData;
67
+ readonly knownSubstitutions: readonly DagExecutionTargetData[];
68
+ };
69
+ export type DagNodeData = JsonObject & {
70
+ readonly kind: DagNodeKind;
71
+ /** Concurrency key; nodes sharing a key share its declared limit. */
72
+ readonly key: string | null;
73
+ /** Engine lane for this node; omit it for a data-only node. */
74
+ readonly lane?: DagExecutionLaneData;
75
+ };
76
+ export interface DagEdgeData extends JsonObject {
77
+ }
78
+ export interface DagData extends JsonObject {
79
+ /** Max node attempts running at once. The legacy default is 4. */
80
+ readonly globalConcurrency: number;
81
+ /** Per-key attempt caps; every node key must appear here. */
82
+ readonly keyedConcurrency: Readonly<Record<string, number>>;
83
+ /** When true, the first required failure stops scheduling anything not in flight. */
84
+ readonly stopOnError: boolean;
85
+ /** Retries offered per failed required node before the DAG fails. */
86
+ readonly retryCapPerNode: number;
87
+ }
88
+ export type DagDefinition = GraphDefinition<DagNodeData, DagEdgeData, DagData>;
89
+ export interface NodeDispatchedPayload extends JsonObject {
90
+ readonly nodeId: NodeId;
91
+ readonly position: string;
92
+ }
93
+ export interface NodeCompletedPayload extends JsonObject {
94
+ readonly nodeId: NodeId;
95
+ readonly position: string;
96
+ readonly result: JsonValue;
97
+ }
98
+ export interface NodeFailedPayload extends JsonObject {
99
+ readonly nodeId: NodeId;
100
+ readonly position: string;
101
+ readonly code: string;
102
+ }
103
+ export interface NodePausedPayload extends JsonObject {
104
+ readonly nodeId: NodeId;
105
+ readonly position: string;
106
+ readonly reason: string;
107
+ readonly request: JsonValue;
108
+ }
109
+ export interface NodeResumedPayload extends JsonObject {
110
+ readonly nodeId: NodeId;
111
+ readonly position: string;
112
+ }
113
+ export type DagEvent = GraphEvent<'node-dispatched', NodeDispatchedPayload> | GraphEvent<'node-completed', NodeCompletedPayload> | GraphEvent<'node-failed', NodeFailedPayload> | GraphEvent<'node-paused', NodePausedPayload> | GraphEvent<'node-resumed', NodeResumedPayload>;
114
+ export type DagNodeStatus = 'pending' | 'in-flight' | 'paused' | 'passed' | 'skipped' | 'failed';
115
+ export interface DagNodeState extends JsonObject {
116
+ readonly status: DagNodeStatus;
117
+ /** Recorded dispatch count; the source of fresh attempt positions. */
118
+ readonly attempts: number;
119
+ /** The in-flight attempt position, or null. */
120
+ readonly inFlight: string | null;
121
+ /** The reason supplied by a paused attempt, or null. */
122
+ readonly pauseReason: string | null;
123
+ /** The completed payload, or null before a successful completion. */
124
+ readonly result: JsonValue;
125
+ }
126
+ export interface DagStatus extends JsonObject {
127
+ readonly nodes: Readonly<Record<NodeId, DagNodeState>>;
128
+ }
129
+ export type DagRequirements = {
130
+ readonly memory: 'unused';
131
+ };
132
+ /**
133
+ * The DAG graph type. Pure data operations only: no file, model, process,
134
+ * clock, or storage services (the D2 contract).
135
+ */
136
+ export declare const dag: GraphType<DagDefinition, DagStatus, DagEvent, DagRequirements>;
137
+ export {};