File size: 4,410 Bytes
76289e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Context-engine host compatibility checks prevent engines from running on unsupported harnesses.
import type {
  ContextEngine,
  ContextEngineHostCapability,
  ContextEngineHostRequirements,
  ContextEngineInfo,
  ContextEngineOperation,
} from "./types.js";

export type ContextEngineHostSupport = {
  id: string;
  label: string;
  capabilities: readonly ContextEngineHostCapability[];
};

/** Return whether an engine implements the durable method matching its declared contract. */
export function supportsContextEngineDurableTurnAdvancement(engine: ContextEngine): boolean {
  return (
    engine.info.transcriptSemantics?.turnAdvancementIdempotency === "atomic-idempotent-v1" &&
    typeof engine.commitTurn === "function"
  );
}

const GENERIC_CLI_CONTEXT_ENGINE_HOST_CAPABILITIES = [
  "bootstrap",
  "after-turn",
  "maintain",
] as const satisfies readonly ContextEngineHostCapability[];

export const OPENCLAW_EMBEDDED_CONTEXT_ENGINE_HOST = {
  id: "openclaw-embedded",
  label: "OpenClaw embedded runner",
  capabilities: [
    "bootstrap",
    "assemble-before-prompt",
    "after-turn",
    "maintain",
    "compact",
    "runtime-llm-complete",
  ],
} as const satisfies ContextEngineHostSupport;

export const CODEX_APP_SERVER_CONTEXT_ENGINE_HOST = {
  id: "codex-app-server",
  label: "Codex app-server harness",
  capabilities: [
    "bootstrap",
    "assemble-before-prompt",
    "after-turn",
    "maintain",
    "compact",
    "runtime-llm-complete",
    "thread-bootstrap-projection",
  ],
} as const satisfies ContextEngineHostSupport;

type ContextEngineHostSupportEvaluation =
  | {
      ok: true;
      requirements?: ContextEngineHostRequirements;
      missingCapabilities: [];
    }
  | {
      ok: false;
      requirements: ContextEngineHostRequirements;
      missingCapabilities: ContextEngineHostCapability[];
    };

/** Build the default host support advertised by the generic CLI runner. */
export function buildGenericCliContextEngineHostSupport(params: {
  backendId: string;
  capabilities?: readonly ContextEngineHostCapability[];
}): ContextEngineHostSupport {
  return {
    id: `cli:${params.backendId}`,
    label: `CLI backend "${params.backendId}"`,
    capabilities: params.capabilities ?? GENERIC_CLI_CONTEXT_ENGINE_HOST_CAPABILITIES,
  };
}

/** Evaluate whether a context-engine host can safely run the requested operation. */
export function evaluateContextEngineHostSupport(params: {
  contextEngineInfo: ContextEngineInfo;
  operation: ContextEngineOperation;
  host: ContextEngineHostSupport;
}): ContextEngineHostSupportEvaluation {
  const requirements = params.contextEngineInfo.hostRequirements?.[params.operation];
  if (!requirements || requirements.requiredCapabilities.length === 0) {
    return { ok: true, requirements, missingCapabilities: [] };
  }

  const supported = new Set(params.host.capabilities);
  // Capabilities are host-advertised facts; requirements stay engine-authored per operation.
  const missingCapabilities = requirements.requiredCapabilities.filter(
    (capability) => !supported.has(capability),
  );
  if (missingCapabilities.length === 0) {
    return { ok: true, requirements, missingCapabilities: [] };
  }

  return {
    ok: false,
    requirements,
    missingCapabilities,
  };
}

/** Assert that a context engine can safely run under the supplied host. */
export function assertContextEngineHostSupport(params: {
  contextEngine: ContextEngine;
  operation: ContextEngineOperation;
  host: ContextEngineHostSupport;
}): void {
  const evaluation = evaluateContextEngineHostSupport({
    contextEngineInfo: params.contextEngine.info,
    operation: params.operation,
    host: params.host,
  });
  if (evaluation.ok) {
    return;
  }

  const engineId = params.contextEngine.info.id;
  const required = evaluation.requirements.requiredCapabilities.join(", ");
  const actual =
    params.host.capabilities.length > 0 ? params.host.capabilities.join(", ") : "(none)";
  const guidance = evaluation.requirements.unsupportedMessage
    ? ` ${evaluation.requirements.unsupportedMessage}`
    : "";
  throw new Error(
    `Context engine "${engineId}" cannot run operation "${params.operation}" on ${params.host.label}. ` +
      `Missing host capabilities: ${evaluation.missingCapabilities.join(", ")}. ` +
      `Required capabilities: ${required}. ` +
      `Host capabilities: ${actual}.${guidance}`,
  );
}