open-multi-agent-kit 0.78.0 → 0.78.1

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 (70) hide show
  1. package/CHANGELOG.md +44 -15
  2. package/MATURITY.md +2 -2
  3. package/README.md +56 -26
  4. package/ROADMAP.md +36 -28
  5. package/dist/cli/register-basic-commands.js +3 -2
  6. package/dist/cli/register-mcp-dag-cron-screenshot-commands.js +2 -0
  7. package/dist/cli/register-tool-commands.js +11 -0
  8. package/dist/cli/register-workflow-commands.js +1 -0
  9. package/dist/cli/registry/tooling.js +3 -2
  10. package/dist/commands/chat/core.js +5 -0
  11. package/dist/commands/chat/native-root-loop.js +60 -0
  12. package/dist/commands/dag-from-spec.d.ts +1 -0
  13. package/dist/commands/dag-from-spec.js +61 -1
  14. package/dist/commands/graph.d.ts +62 -0
  15. package/dist/commands/graph.js +182 -0
  16. package/dist/commands/merge.d.ts +1 -0
  17. package/dist/commands/merge.js +88 -0
  18. package/dist/commands/parallel/core.js +3 -3
  19. package/dist/commands/provider.js +5 -3
  20. package/dist/commands/star.js +6 -1
  21. package/dist/commands/summary.d.ts +4 -1
  22. package/dist/commands/summary.js +103 -1
  23. package/dist/commands/team.d.ts +1 -0
  24. package/dist/commands/team.js +38 -0
  25. package/dist/contracts/provider-health.d.ts +42 -0
  26. package/dist/contracts/provider-health.js +9 -0
  27. package/dist/goal/intent-frame.d.ts +24 -0
  28. package/dist/goal/intent-frame.js +18 -0
  29. package/dist/memory/local-graph-memory-store.d.ts +15 -0
  30. package/dist/memory/local-graph-memory-store.js +176 -0
  31. package/dist/memory/memory-store.d.ts +18 -0
  32. package/dist/memory/memory-store.js +18 -0
  33. package/dist/orchestration/adaptorch-topology.d.ts +59 -0
  34. package/dist/orchestration/adaptorch-topology.js +194 -0
  35. package/dist/orchestration/capability-routing.d.ts +23 -0
  36. package/dist/orchestration/capability-routing.js +56 -0
  37. package/dist/orchestration/dag-compiler-types.d.ts +3 -0
  38. package/dist/orchestration/dag-compiler.js +14 -1
  39. package/dist/orchestration/parallel-orchestrator.d.ts +6 -0
  40. package/dist/orchestration/parallel-orchestrator.js +31 -0
  41. package/dist/providers/provider-health.d.ts +39 -0
  42. package/dist/providers/provider-health.js +161 -0
  43. package/dist/runtime/context-broker.d.ts +13 -4
  44. package/dist/runtime/context-broker.js +14 -1
  45. package/dist/runtime/headroom-policy.d.ts +37 -0
  46. package/dist/runtime/headroom-policy.js +122 -0
  47. package/dist/runtime/ouroboros-policy.d.ts +57 -0
  48. package/dist/runtime/ouroboros-policy.js +134 -0
  49. package/dist/runtime/runtime-backed-task-runner.js +9 -1
  50. package/dist/runtime/tool-dispatch-contracts.d.ts +57 -1
  51. package/dist/runtime/tool-dispatch-contracts.js +79 -3
  52. package/dist/safety/tool-authority-gate.d.ts +62 -0
  53. package/dist/safety/tool-authority-gate.js +108 -0
  54. package/dist/schema/provider.schema.d.ts +4 -4
  55. package/dist/util/first-run-star.d.ts +9 -0
  56. package/dist/util/first-run-star.js +42 -1
  57. package/dist/util/terminal-input.d.ts +20 -0
  58. package/dist/util/terminal-input.js +32 -0
  59. package/dist/util/update-check.d.ts +6 -1
  60. package/dist/util/update-check.js +35 -1
  61. package/docs/2026-06-08/critical-issues.md +20 -0
  62. package/docs/2026-06-08/improvements.md +14 -0
  63. package/docs/2026-06-08/init-checklist.md +25 -0
  64. package/docs/2026-06-08/plan.md +20 -0
  65. package/docs/getting-started.md +31 -3
  66. package/docs/integrations/ouroboros.md +96 -0
  67. package/docs/provider-maturity.md +1 -1
  68. package/docs/versioning.md +3 -3
  69. package/package.json +1 -1
  70. package/dist/native/linux-x64/omk-safety +0 -0
@@ -8,7 +8,45 @@ import { join } from "path";
8
8
  import { refreshRunStateEstimate } from "../orchestration/run-state.js";
9
9
  import { createInteractiveRunState as createParallelRunState } from "./parallel/orchestrator.js";
10
10
  import { ensureCompletionArtifactContract } from "../orchestration/completion-artifacts.js";
11
+ import { createOmkJsonEnvelope } from "../util/json-envelope.js";
12
+ import { emitJson } from "../util/cli-contract.js";
13
+ /**
14
+ * JSON path for `omk team --json`.
15
+ * Read-only describe: reports the expected team layout for the resolved run
16
+ * WITHOUT spawning or attaching tmux, so stdout stays exactly one
17
+ * omk.contract.v1 envelope (no banner, no ANSI) and never blocks/exits.
18
+ */
19
+ async function emitTeamJson(options) {
20
+ const started = Date.now();
21
+ const root = getProjectRoot();
22
+ const resources = await getOmkResourceSettings();
23
+ const workerCount = normalizeWorkerCount(options.workers, resources.maxWorkers);
24
+ const runId = options.runId ?? `team-${new Date().toISOString().replace(/[:.]/g, "-")}`;
25
+ const statePath = getRunPath(runId, "state.json", root);
26
+ const expected = buildExpectedTeamRuntimeStatus("omk-team", statePath, workerCount, "starting");
27
+ const data = {
28
+ teamId: runId,
29
+ members: expected.windows.map((window) => ({
30
+ name: window.name,
31
+ role: window.role,
32
+ nodeId: window.nodeId,
33
+ })),
34
+ statePath,
35
+ };
36
+ emitJson(createOmkJsonEnvelope({
37
+ command: "team",
38
+ status: "passed",
39
+ ok: true,
40
+ runId,
41
+ data,
42
+ durationMs: Date.now() - started,
43
+ }));
44
+ }
11
45
  export async function teamCommand(options = {}) {
46
+ if (options.json === true || process.argv.includes("--json")) {
47
+ await emitTeamJson(options);
48
+ return;
49
+ }
12
50
  const root = getProjectRoot();
13
51
  const resources = await getOmkResourceSettings();
14
52
  const workerCount = normalizeWorkerCount(options.workers, resources.maxWorkers);
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Shared, provider-neutral health shape.
3
+ *
4
+ * This contract is intentionally additive: it is embedded alongside the
5
+ * existing `omk provider doctor <x> --json` payloads without removing or
6
+ * renaming any pre-existing keys. It never carries secret values — only
7
+ * boolean signals (e.g. `authOk`) and non-sensitive remediation hints.
8
+ */
9
+ /** Classified failure category for a provider health check. */
10
+ export type ProviderFailureKind = "none" | "runtime" | "auth" | "model" | "quota" | "policy" | "transient" | "unknown";
11
+ /** Authority level a provider holds for a given capability lane. */
12
+ export type ProviderAuthorityLevel = "none" | "advisory" | "direct" | "full";
13
+ /**
14
+ * Normalized provider health snapshot.
15
+ *
16
+ * All fields are required so consumers can rely on a stable shape regardless
17
+ * of which provider produced it.
18
+ */
19
+ export interface ProviderHealth {
20
+ /** Provider id (e.g. "kimi", "deepseek", "codex"). */
21
+ provider: string;
22
+ /** ISO-8601 timestamp of when the health snapshot was produced. */
23
+ checkedAt: string;
24
+ /** Runtime/transport reachable and enabled. */
25
+ runtimeOk: boolean;
26
+ /** Authentication satisfied (API key present or externally managed auth). */
27
+ authOk: boolean;
28
+ /** A resolvable default model is configured. */
29
+ modelOk: boolean;
30
+ /** No known quota/balance/rate-limit blocker. */
31
+ quotaOk: boolean;
32
+ /** Authority to perform write/merge work. */
33
+ writeAuthority: ProviderAuthorityLevel;
34
+ /** Authority to run shell/CLI work. */
35
+ shellAuthority: ProviderAuthorityLevel;
36
+ /** Authority to drive MCP tools. */
37
+ mcpAuthority: ProviderAuthorityLevel;
38
+ /** Classified failure category ("none" when healthy). */
39
+ failureKind: ProviderFailureKind;
40
+ /** Non-sensitive remediation hints (never includes secret values). */
41
+ remediation: string[];
42
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Shared, provider-neutral health shape.
3
+ *
4
+ * This contract is intentionally additive: it is embedded alongside the
5
+ * existing `omk provider doctor <x> --json` payloads without removing or
6
+ * renaming any pre-existing keys. It never carries secret values — only
7
+ * boolean signals (e.g. `authOk`) and non-sensitive remediation hints.
8
+ */
9
+ export {};
@@ -1,6 +1,7 @@
1
1
  import type { ActionAtom, ActionAtomVerb, GoalEvidence, GoalSpec, IntentFrame, NextActionContract, PromptNoveltyReport } from "../contracts/goal.js";
2
2
  import type { DagNodeRouting } from "../orchestration/dag.js";
3
3
  import type { NextAction, RunState } from "../contracts/orchestration.js";
4
+ import type { OuroborosDecision } from "../runtime/ouroboros-policy.js";
4
5
  export declare function buildIntentFrame(rawPrompt: string, input?: {
5
6
  constraints?: string[];
6
7
  successCriteria?: string[];
@@ -10,6 +11,29 @@ export declare function buildIntentFrame(rawPrompt: string, input?: {
10
11
  }>;
11
12
  }): IntentFrame;
12
13
  export declare function buildIntentFrameFromGoal(goal: GoalSpec): IntentFrame;
14
+ /**
15
+ * IntentFrame enriched with an advisory Ouroboros routing decision.
16
+ * The existing IntentFrame shape is unchanged; the optional ouroboros
17
+ * field is additive and consumed only by callers that understand it.
18
+ */
19
+ export type IntentFrameWithOuroboros = IntentFrame & {
20
+ ouroboros?: OuroborosDecision;
21
+ };
22
+ /**
23
+ * Build an IntentFrame and, for goal/spec/orchestration intents,
24
+ * attach a non-fatal Ouroboros routing decision.
25
+ *
26
+ * The base IntentFrame fields are never modified; the ouroboros hint
27
+ * is purely advisory.
28
+ */
29
+ export declare function buildIntentFrameWithOuroboros(rawPrompt: string, input?: {
30
+ constraints?: string[];
31
+ successCriteria?: string[];
32
+ expectedArtifacts?: Array<{
33
+ name: string;
34
+ path?: string;
35
+ }>;
36
+ }): Promise<IntentFrameWithOuroboros>;
13
37
  export declare function actionAtomRouting(atom: ActionAtom): NonNullable<DagNodeRouting["actionAtom"]>;
14
38
  export declare function renderActionDigest(frame: IntentFrame, options?: {
15
39
  maxAtoms?: number;
@@ -1,4 +1,5 @@
1
1
  import { extractPromptKeywords, promptDigestFingerprint } from "./prompt-digest.js";
2
+ import { resolveOuroborosDecision } from "../runtime/ouroboros-policy.js";
2
3
  const TOKEN_PATTERN = /[\p{L}\p{N}_-]+/gu;
3
4
  const REPLAY_PATTERN = /repeat|replay|verbatim|same prompt|original objective|original goal|raw input|원문|반복|재사용|그대로/u;
4
5
  const REPLAY_THRESHOLD = 0.72;
@@ -51,6 +52,23 @@ export function buildIntentFrameFromGoal(goal) {
51
52
  expectedArtifacts: goal.expectedArtifacts,
52
53
  });
53
54
  }
55
+ /**
56
+ * Build an IntentFrame and, for goal/spec/orchestration intents,
57
+ * attach a non-fatal Ouroboros routing decision.
58
+ *
59
+ * The base IntentFrame fields are never modified; the ouroboros hint
60
+ * is purely advisory.
61
+ */
62
+ export async function buildIntentFrameWithOuroboros(rawPrompt, input = {}) {
63
+ const frame = buildIntentFrame(rawPrompt, input);
64
+ try {
65
+ const decision = await resolveOuroborosDecision({ intent: rawPrompt });
66
+ return { ...frame, ouroboros: decision };
67
+ }
68
+ catch {
69
+ return frame;
70
+ }
71
+ }
54
72
  export function actionAtomRouting(atom) {
55
73
  return {
56
74
  id: atom.id,
@@ -1,4 +1,5 @@
1
1
  import { type MemorySettings, type MemoryStatus } from "./memory-config.js";
2
+ import type { RunManifest } from "../contracts/run.js";
2
3
  export interface MemorySearchResult {
3
4
  path: string;
4
5
  content: string;
@@ -114,6 +115,20 @@ export declare class LocalGraphMemoryStore {
114
115
  search(query: string, limit?: number): Promise<MemorySearchResult[]>;
115
116
  ontology(): Promise<MemoryOntology>;
116
117
  mindmap(query?: string, limit?: number): Promise<MemoryMindmap>;
118
+ /**
119
+ * Idempotent finalizer that links a finalized run manifest into the local
120
+ * graph. Emits run -> providerRoute -> provider, run -> evidence,
121
+ * run -> decision, and run -> artifact nodes/edges.
122
+ *
123
+ * Privacy: only ids, status, paths, sha256, and aggregate counts are stored.
124
+ * Raw evidence/decision message bodies and secrets are never persisted.
125
+ *
126
+ * Idempotency: node/edge ids are deterministic and prior run-generated
127
+ * nodes/edges (tagged `generatedFromRun`) are pruned before re-inserting, so
128
+ * re-running with the same (or a changed) manifest never duplicates state.
129
+ */
130
+ linkRun(runId: string, manifest: RunManifest): Promise<void>;
131
+ private applyRunManifest;
117
132
  graphQuery(query: string): Promise<GraphQueryResult>;
118
133
  private loadState;
119
134
  private emptyState;
@@ -339,6 +339,182 @@ export class LocalGraphMemoryStore {
339
339
  const root = this.buildMindmapTree(rootNode, state, includedIds, new Set());
340
340
  return { root, nodes: flatNodes, edges, ontology: ONTOLOGY };
341
341
  }
342
+ /**
343
+ * Idempotent finalizer that links a finalized run manifest into the local
344
+ * graph. Emits run -> providerRoute -> provider, run -> evidence,
345
+ * run -> decision, and run -> artifact nodes/edges.
346
+ *
347
+ * Privacy: only ids, status, paths, sha256, and aggregate counts are stored.
348
+ * Raw evidence/decision message bodies and secrets are never persisted.
349
+ *
350
+ * Idempotency: node/edge ids are deterministic and prior run-generated
351
+ * nodes/edges (tagged `generatedFromRun`) are pruned before re-inserting, so
352
+ * re-running with the same (or a changed) manifest never duplicates state.
353
+ */
354
+ async linkRun(runId, manifest) {
355
+ const canonicalRunId = manifest.runId || runId;
356
+ if (runId && manifest.runId && runId !== manifest.runId) {
357
+ throw new Error(`linkRun: runId mismatch (param=${runId}, manifest=${manifest.runId})`);
358
+ }
359
+ await this.mutateState((state, now) => {
360
+ this.applyRunManifest(state, canonicalRunId, manifest, now);
361
+ });
362
+ }
363
+ applyRunManifest(state, runId, manifest, now) {
364
+ state.updatedAt = now;
365
+ state.project = { ...this.settings.project };
366
+ state.ontology = ONTOLOGY;
367
+ // Prune prior run-generated child nodes/edges for idempotent re-runs. The
368
+ // Run node and shared Provider nodes are intentionally not pruned (the Run
369
+ // node is re-upserted preserving createdAt; Providers are shared by runs).
370
+ const staleNodeIds = new Set(state.nodes.filter((node) => node.properties.generatedFromRun === runId).map((node) => node.id));
371
+ state.nodes = state.nodes.filter((node) => !staleNodeIds.has(node.id));
372
+ state.edges = state.edges.filter((edge) => edge.properties.generatedFromRun !== runId && !staleNodeIds.has(edge.from) && !staleNodeIds.has(edge.to));
373
+ const tag = { generatedFromRun: runId };
374
+ const projectId = this.nodeId("Project", this.settings.project.key);
375
+ this.upsertNode(state, {
376
+ id: projectId,
377
+ type: "Project",
378
+ labels: ["OmkProject", "Project"],
379
+ label: this.settings.project.name,
380
+ summary: this.settings.project.root,
381
+ tags: ["project"],
382
+ properties: { key: this.settings.project.key, root: this.settings.project.root },
383
+ createdAt: now,
384
+ updatedAt: now,
385
+ });
386
+ const runNodeId = this.nodeId("Run", runId);
387
+ this.upsertNode(state, {
388
+ id: runNodeId,
389
+ type: "Run",
390
+ labels: ["OmkRun", "Run"],
391
+ label: runId,
392
+ summary: `Run ${runId} (${manifest.status})`,
393
+ tags: ["run", manifest.status],
394
+ properties: {
395
+ runId,
396
+ status: manifest.status,
397
+ schemaVersion: manifest.schemaVersion,
398
+ createdAt: manifest.createdAt,
399
+ completedAt: manifest.completedAt ?? null,
400
+ promptHash: manifest.promptHash ?? null,
401
+ decisionTracePath: manifest.decisionTracePath ?? null,
402
+ evidenceRequired: manifest.evidenceSummary.required,
403
+ evidencePassed: manifest.evidenceSummary.passed,
404
+ evidenceFailed: manifest.evidenceSummary.failed,
405
+ evidenceMissing: manifest.evidenceSummary.missing,
406
+ nodeCount: manifest.nodes.length,
407
+ artifactCount: manifest.artifacts.length,
408
+ projectKey: this.settings.project.key,
409
+ source: this.source,
410
+ },
411
+ createdAt: now,
412
+ updatedAt: now,
413
+ });
414
+ this.upsertEdge(state, projectId, runNodeId, "HAS_RUN", now, tag);
415
+ // Provider route -> provider.
416
+ const provider = manifest.providerPolicy.provider;
417
+ const mode = manifest.providerPolicy.mode ?? "auto";
418
+ const routeNodeId = this.nodeId("ProviderRoute", `${runId}:${provider}:${mode}`);
419
+ this.upsertNode(state, {
420
+ id: routeNodeId,
421
+ type: "ProviderRoute",
422
+ labels: ["OmkProviderRoute", "ProviderRoute"],
423
+ label: `${provider} (${mode})`,
424
+ summary: `Provider route for run ${runId}`,
425
+ tags: ["provider-route", provider, mode],
426
+ properties: { runId, provider, mode, projectKey: this.settings.project.key, generatedFromRun: runId },
427
+ createdAt: now,
428
+ updatedAt: now,
429
+ });
430
+ this.upsertEdge(state, runNodeId, routeNodeId, "HAS_PROVIDER_ROUTE", now, tag);
431
+ const providerNodeId = this.nodeId("Provider", provider);
432
+ this.upsertNode(state, {
433
+ id: providerNodeId,
434
+ type: "Provider",
435
+ labels: ["OmkProvider", "Provider"],
436
+ label: provider,
437
+ summary: `Provider ${provider}`,
438
+ tags: ["provider", provider],
439
+ properties: { provider, projectKey: this.settings.project.key },
440
+ createdAt: now,
441
+ updatedAt: now,
442
+ });
443
+ this.upsertEdge(state, routeNodeId, providerNodeId, "ROUTES_TO", now, tag);
444
+ // Evidence summary node (counts/paths/sha only — never raw bodies).
445
+ const evidenceArtifact = manifest.artifacts.find((artifact) => artifact.kind === "evidence" || /(^|\/)evidence\.jsonl$/i.test(artifact.path));
446
+ const evidenceNodeId = this.nodeId("Evidence", `${runId}:evidence`);
447
+ this.upsertNode(state, {
448
+ id: evidenceNodeId,
449
+ type: "Evidence",
450
+ labels: ["OmkEvidence", "Evidence"],
451
+ label: `evidence:${runId}`,
452
+ summary: `Evidence ${manifest.evidenceSummary.passed}/${manifest.evidenceSummary.required} passed`,
453
+ tags: ["evidence", manifest.status],
454
+ properties: {
455
+ runId,
456
+ required: manifest.evidenceSummary.required,
457
+ passed: manifest.evidenceSummary.passed,
458
+ failed: manifest.evidenceSummary.failed,
459
+ missing: manifest.evidenceSummary.missing,
460
+ path: evidenceArtifact?.path ?? null,
461
+ sha256: evidenceArtifact?.sha256 ?? null,
462
+ projectKey: this.settings.project.key,
463
+ generatedFromRun: runId,
464
+ },
465
+ createdAt: now,
466
+ updatedAt: now,
467
+ });
468
+ this.upsertEdge(state, runNodeId, evidenceNodeId, "HAS_EVIDENCE", now, tag);
469
+ // Decision trace node (path/sha only — never raw bodies).
470
+ const decisionArtifact = manifest.artifacts.find((artifact) => artifact.kind === "decision" ||
471
+ artifact.kind === "decisions" ||
472
+ /(^|\/)decisions\.jsonl$/i.test(artifact.path));
473
+ const decisionPath = manifest.decisionTracePath ?? decisionArtifact?.path ?? null;
474
+ const decisionNodeId = this.nodeId("Decision", `${runId}:decision`);
475
+ this.upsertNode(state, {
476
+ id: decisionNodeId,
477
+ type: "Decision",
478
+ labels: ["OmkDecision", "Decision"],
479
+ label: `decisions:${runId}`,
480
+ summary: `Decision trace for run ${runId}`,
481
+ tags: ["decision", manifest.status],
482
+ properties: {
483
+ runId,
484
+ path: decisionPath,
485
+ sha256: decisionArtifact?.sha256 ?? null,
486
+ projectKey: this.settings.project.key,
487
+ generatedFromRun: runId,
488
+ },
489
+ createdAt: now,
490
+ updatedAt: now,
491
+ });
492
+ this.upsertEdge(state, runNodeId, decisionNodeId, "HAS_DECISION", now, tag);
493
+ // Artifacts -> File/Artifact nodes (id/kind/path/sha only).
494
+ for (const artifact of manifest.artifacts) {
495
+ const artifactNodeId = this.nodeId("Artifact", `${runId}:${artifact.kind}:${artifact.path}`);
496
+ this.upsertNode(state, {
497
+ id: artifactNodeId,
498
+ type: "Artifact",
499
+ labels: ["OmkArtifact", "Artifact", "File"],
500
+ label: artifact.path,
501
+ path: artifact.path,
502
+ summary: `${artifact.kind} artifact`,
503
+ tags: ["artifact", artifact.kind, ...pathTags(artifact.path)],
504
+ properties: {
505
+ runId,
506
+ kind: artifact.kind,
507
+ path: artifact.path,
508
+ sha256: artifact.sha256 ?? null,
509
+ projectKey: this.settings.project.key,
510
+ generatedFromRun: runId,
511
+ },
512
+ createdAt: now,
513
+ updatedAt: now,
514
+ });
515
+ this.upsertEdge(state, runNodeId, artifactNodeId, "TOUCHES_FILE", now, tag);
516
+ }
517
+ }
342
518
  async graphQuery(query) {
343
519
  const normalized = query.replace(/\s+/g, " ").trim();
344
520
  const data = {};
@@ -1,6 +1,7 @@
1
1
  import { type MemoryStatus } from "./memory-config.js";
2
2
  import type { GraphQueryResult, MemoryMindmap, MemoryOntology, MemorySearchResult } from "./local-graph-memory-store.js";
3
3
  import type { MemoryBackend } from "./memory-config.js";
4
+ import type { RunManifest } from "../contracts/run.js";
4
5
  export interface MemoryStoreOptions {
5
6
  projectRoot?: string;
6
7
  sessionId?: string;
@@ -19,7 +20,24 @@ export interface UnifiedMemoryStore {
19
20
  ontology?: () => Promise<MemoryOntology | null>;
20
21
  mindmap?: (query?: string, limit?: number) => Promise<MemoryMindmap | null>;
21
22
  graphQuery?: (query: string) => Promise<GraphQueryResult>;
23
+ linkRun?: (runId: string, manifest: RunManifest) => Promise<void>;
22
24
  }
25
+ export interface LinkRunToGraphOptions {
26
+ projectRoot?: string;
27
+ sessionId?: string;
28
+ source?: string;
29
+ env?: NodeJS.ProcessEnv;
30
+ }
31
+ /**
32
+ * Idempotent, non-fatal finalizer: links a finalized run manifest into the
33
+ * local graph memory backend. Returns true when the active backend linked the
34
+ * run, false when no graph backend is available (e.g. file-only backend).
35
+ *
36
+ * Only ids/status/paths/sha256/counts are persisted — never raw evidence or
37
+ * decision bodies, and never secrets. Callers should still wrap this in their
38
+ * own try/catch so a graph write failure can never fail a run.
39
+ */
40
+ export declare function linkRunToGraph(runId: string, manifest: RunManifest, options?: LinkRunToGraphOptions): Promise<boolean>;
23
41
  export declare function createMemoryStore(backend: MemoryBackend, options?: MemoryStoreOptions): Promise<UnifiedMemoryStore | null>;
24
42
  export declare class MemoryStore {
25
43
  private readonly options;
@@ -2,6 +2,24 @@ import { readFile, writeFile, mkdir } from "fs/promises";
2
2
  import { join, dirname, resolve, relative, isAbsolute } from "path";
3
3
  import { getProjectRoot } from "../util/fs.js";
4
4
  import { loadMemorySettings, summarizeMemorySettings, } from "./memory-config.js";
5
+ import { loadMemorySettings as loadMemorySettingsForLink } from "./memory-config.js";
6
+ /**
7
+ * Idempotent, non-fatal finalizer: links a finalized run manifest into the
8
+ * local graph memory backend. Returns true when the active backend linked the
9
+ * run, false when no graph backend is available (e.g. file-only backend).
10
+ *
11
+ * Only ids/status/paths/sha256/counts are persisted — never raw evidence or
12
+ * decision bodies, and never secrets. Callers should still wrap this in their
13
+ * own try/catch so a graph write failure can never fail a run.
14
+ */
15
+ export async function linkRunToGraph(runId, manifest, options = {}) {
16
+ const settings = await loadMemorySettingsForLink(options.projectRoot, options.env);
17
+ const store = await createMemoryStore(settings.backend, options);
18
+ if (!store || typeof store.linkRun !== "function")
19
+ return false;
20
+ await store.linkRun(runId, manifest);
21
+ return true;
22
+ }
5
23
  export async function createMemoryStore(backend, options = {}) {
6
24
  if (backend === "local_graph") {
7
25
  const { LocalGraphMemoryStore } = await import("./local-graph-memory-store.js");
@@ -0,0 +1,59 @@
1
+ /**
2
+ * AdaptOrch-style topology router — pure TS, no IO, no python dependency.
3
+ *
4
+ * Extracts 5 structural features from a DAG and selects an execution topology
5
+ * using threshold-based rules matching the AdaptOrch TopologyRouter spec.
6
+ */
7
+ export type DagTopology = "singleton" | "parallel" | "pipeline" | "map_reduce" | "hierarchical" | "dag" | "hybrid";
8
+ export interface TopologyFeatures {
9
+ nodeCount: number;
10
+ edgeCount: number;
11
+ width: number;
12
+ criticalDepth: number;
13
+ couplingDensity: number;
14
+ parallelRatio: number;
15
+ }
16
+ export interface TopologyDecision {
17
+ topology: DagTopology;
18
+ reason: string;
19
+ features: TopologyFeatures;
20
+ waves: string[][];
21
+ }
22
+ export interface TopologyThresholds {
23
+ /** θ_ω — parallel ratio threshold (default 0.5) */
24
+ parallelRatio: number;
25
+ /** θ_γ — coupling density considered "high" (default 0.6) */
26
+ highCoupling: number;
27
+ /** θ_δ — node count threshold for hierarchical (default 5) */
28
+ hierarchicalSubtasks: number;
29
+ }
30
+ /**
31
+ * Returns `true` unless OMK_ADAPTORCH_ROUTING is explicitly off/0/false.
32
+ */
33
+ export declare function isAdaptorchRoutingEnabled(env?: Record<string, string | undefined>): boolean;
34
+ /**
35
+ * Compute structural features of a DAG via Kahn topological sort.
36
+ * Returns layers (waves) and derived metrics. If a cycle is detected,
37
+ * all nodes collapse into a single wave.
38
+ */
39
+ export declare function computeTopologyFeatures(nodes: string[], edges: Array<{
40
+ from: string;
41
+ to: string;
42
+ }>): TopologyFeatures & {
43
+ layers: string[][];
44
+ };
45
+ /**
46
+ * Select a topology and emit layered execution waves.
47
+ *
48
+ * Selection rules (in priority order):
49
+ * 0 nodes → singleton
50
+ * 1 node → singleton
51
+ * high coupling → dag (if depth==1) or hierarchical (if depth>1)
52
+ * parallelRatio ≥ θ_ω and nodes > 1 → parallel / map_reduce
53
+ * criticalDepth == nodes → pipeline
54
+ * otherwise → hybrid
55
+ */
56
+ export declare function routeTopology(nodeIds: string[], edges: Array<{
57
+ from: string;
58
+ to: string;
59
+ }>, thresholds?: Partial<TopologyThresholds>): TopologyDecision;