fraim-hub 2.0.272 → 2.0.274
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/dist/src/ai-hub/hosts.js +193 -27
- package/dist/src/ai-hub/server.js +189 -7
- package/dist/src/cli/commands/add-ide.js +7 -30
- package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +113 -13
- package/dist/src/cli/mcp/mcp-server-registry.js +14 -8
- package/dist/src/cli/setup/mcp-config-generator.js +42 -1
- package/package.json +2 -2
- package/public/ai-hub/index.html +19 -0
- package/public/ai-hub/script.js +254 -4
- package/public/ai-hub/styles.css +79 -0
package/dist/src/ai-hub/hosts.js
CHANGED
|
@@ -28,6 +28,7 @@ exports.parseHostLine = parseHostLine;
|
|
|
28
28
|
exports.findGeminiSessionIdForPrompt = findGeminiSessionIdForPrompt;
|
|
29
29
|
const crypto_1 = require("crypto");
|
|
30
30
|
const child_process_1 = require("child_process");
|
|
31
|
+
const tree_kill_1 = __importDefault(require("tree-kill"));
|
|
31
32
|
const fs_1 = __importDefault(require("fs"));
|
|
32
33
|
const os_1 = __importDefault(require("os"));
|
|
33
34
|
const path_1 = __importDefault(require("path"));
|
|
@@ -743,20 +744,40 @@ function resolveHostInvocation(plan) {
|
|
|
743
744
|
args: ['/d', '/s', '/c', [command, ...args.map(escapeWindowsArg)].join(' ')],
|
|
744
745
|
};
|
|
745
746
|
}
|
|
747
|
+
function stripProjectLocalNodeBinDirs(basePath) {
|
|
748
|
+
return (basePath ?? '')
|
|
749
|
+
.split(path_1.default.delimiter)
|
|
750
|
+
.filter(Boolean)
|
|
751
|
+
.filter((entry) => {
|
|
752
|
+
const normalized = path_1.default.normalize(entry).toLowerCase();
|
|
753
|
+
return !normalized.endsWith(`${path_1.default.sep}node_modules${path_1.default.sep}.bin`);
|
|
754
|
+
})
|
|
755
|
+
.join(path_1.default.delimiter);
|
|
756
|
+
}
|
|
757
|
+
function buildAgentVersionProbePath(basePath) {
|
|
758
|
+
const withoutManaged = (0, managed_agent_paths_1.stripManagedAgentBinDirsFromPath)(basePath);
|
|
759
|
+
const withoutProjectBins = stripProjectLocalNodeBinDirs(withoutManaged);
|
|
760
|
+
return (0, managed_agent_paths_1.appendBinDirsToPath)(withoutProjectBins, (0, managed_agent_paths_1.getManagedAgentBinDirs)());
|
|
761
|
+
}
|
|
746
762
|
// Single source for the probe environment, shared by the sync and async probes so the two
|
|
747
|
-
// cannot drift on how
|
|
763
|
+
// cannot drift on how agent bin directories are put on PATH. Project-local npm shims are
|
|
764
|
+
// intentionally excluded: the Hub is checking installed user agent CLIs, not devDependency
|
|
765
|
+
// shims from whichever project happened to launch it.
|
|
748
766
|
const versionProbeEnv = () => ({
|
|
749
767
|
...process.env,
|
|
750
|
-
PATH: (
|
|
751
|
-
Path: (
|
|
768
|
+
PATH: buildAgentVersionProbePath(process.env.PATH ?? process.env.Path),
|
|
769
|
+
Path: buildAgentVersionProbePath(process.env.PATH ?? process.env.Path),
|
|
752
770
|
});
|
|
771
|
+
// Returns the version string on success, or null when the CLI is absent.
|
|
753
772
|
const availableByVersionProbe = (command) => {
|
|
754
773
|
const invocation = resolveHostInvocation({ command, args: ['--version'] });
|
|
755
774
|
const result = (0, child_process_1.spawnSync)(invocation.command, invocation.args, {
|
|
756
775
|
encoding: 'utf8',
|
|
757
776
|
env: versionProbeEnv(),
|
|
758
777
|
});
|
|
759
|
-
|
|
778
|
+
if (result.status !== 0 || result.error)
|
|
779
|
+
return null;
|
|
780
|
+
return (result.stdout || result.stderr || '').trim() || null;
|
|
760
781
|
};
|
|
761
782
|
// Issue #1010: the async counterpart of availableByVersionProbe. Same semantics (exit 0
|
|
762
783
|
// from `<agent> --version` means the CLI is installed AND actually runs), but non-blocking
|
|
@@ -769,11 +790,13 @@ const availableByVersionProbe = (command) => {
|
|
|
769
790
|
// print its version within 10s is reported unavailable rather than allowed to hang the Hub.
|
|
770
791
|
// It is recoverable from the UI via the existing per-agent "Check" action.
|
|
771
792
|
const VERSION_PROBE_TIMEOUT_MS = 10_000;
|
|
793
|
+
// Returns the version string on success, or null when the CLI is absent.
|
|
772
794
|
const availableByVersionProbeAsync = (command) => new Promise((resolve) => {
|
|
773
795
|
const invocation = resolveHostInvocation({ command, args: ['--version'] });
|
|
774
796
|
let settled = false;
|
|
775
797
|
let timer;
|
|
776
798
|
let child;
|
|
799
|
+
const chunks = [];
|
|
777
800
|
const finish = (value) => {
|
|
778
801
|
if (settled)
|
|
779
802
|
return;
|
|
@@ -785,23 +808,28 @@ const availableByVersionProbeAsync = (command) => new Promise((resolve) => {
|
|
|
785
808
|
try {
|
|
786
809
|
child = (0, child_process_1.spawn)(invocation.command, invocation.args, {
|
|
787
810
|
env: versionProbeEnv(),
|
|
788
|
-
stdio: 'ignore',
|
|
811
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
789
812
|
});
|
|
813
|
+
child.stdout?.on('data', (chunk) => chunks.push(chunk));
|
|
814
|
+
child.stderr?.on('data', (chunk) => chunks.push(chunk));
|
|
790
815
|
timer = setTimeout(() => {
|
|
791
816
|
console.warn(`[ai-hub] agent version probe timed out after ${VERSION_PROBE_TIMEOUT_MS}ms: ${command}`);
|
|
792
817
|
try {
|
|
793
818
|
child?.kill();
|
|
794
819
|
}
|
|
795
820
|
catch { /* already gone */ }
|
|
796
|
-
finish(
|
|
821
|
+
finish(null);
|
|
797
822
|
}, VERSION_PROBE_TIMEOUT_MS);
|
|
798
823
|
// Do not hold the process open just for a probe.
|
|
799
824
|
timer.unref?.();
|
|
800
|
-
child.on('error', () => finish(
|
|
801
|
-
child.on('close', (code) =>
|
|
825
|
+
child.on('error', () => finish(null));
|
|
826
|
+
child.on('close', (code) => {
|
|
827
|
+
const raw = Buffer.concat(chunks).toString('utf8').trim();
|
|
828
|
+
finish(code === 0 ? (raw || null) : null);
|
|
829
|
+
});
|
|
802
830
|
}
|
|
803
831
|
catch {
|
|
804
|
-
finish(
|
|
832
|
+
finish(null);
|
|
805
833
|
}
|
|
806
834
|
});
|
|
807
835
|
// ─── Issue #1010: employee-detection cache ───────────────────────────────────
|
|
@@ -978,11 +1006,13 @@ function __getEmployeeProbeRoundsForTests() {
|
|
|
978
1006
|
function __resetEmployeeProbeRoundsForTests() {
|
|
979
1007
|
employeeProbeRounds = 0;
|
|
980
1008
|
}
|
|
981
|
-
function buildEmployeeStatus(id,
|
|
1009
|
+
function buildEmployeeStatus(id, version) {
|
|
1010
|
+
const available = version !== null;
|
|
982
1011
|
return {
|
|
983
1012
|
id,
|
|
984
1013
|
label: EMPLOYEE_LABELS[id],
|
|
985
1014
|
available,
|
|
1015
|
+
version,
|
|
986
1016
|
detail: available ? 'Installed and ready on this machine.' : 'CLI not detected on this machine.',
|
|
987
1017
|
supportsRaw: supportsDirectPath(id),
|
|
988
1018
|
};
|
|
@@ -1532,6 +1562,22 @@ function parseHostLine(hostId, line) {
|
|
|
1532
1562
|
if (parsed.type === 'turn_context' && typeof parsed.payload?.model === 'string') {
|
|
1533
1563
|
return withSignal({ raw: trimmed, agentIdentity: { agentName: 'codex', agentModel: parsed.payload.model } });
|
|
1534
1564
|
}
|
|
1565
|
+
// Issue #1221: Codex reports stream failures (session-model mismatch
|
|
1566
|
+
// notices, reconnect/rate-limit retries, and the terminal turn.failed)
|
|
1567
|
+
// as `type: "error"` / `type: "item.completed" item.type: "error"` /
|
|
1568
|
+
// `type: "turn.failed"`. These previously fell through to raw JSON,
|
|
1569
|
+
// which the conversation projector drops as opaque payload — so a
|
|
1570
|
+
// debilitating failure (e.g. an Azure rate limit) never reached the
|
|
1571
|
+
// manager. Surface the message text as hostError instead.
|
|
1572
|
+
if (parsed.type === 'error' && typeof parsed.message === 'string') {
|
|
1573
|
+
return withSignal({ hostError: parsed.message, raw: trimmed });
|
|
1574
|
+
}
|
|
1575
|
+
if (parsed.type === 'item.completed' && parsed.item?.type === 'error' && typeof parsed.item.message === 'string') {
|
|
1576
|
+
return withSignal({ hostError: parsed.item.message, raw: trimmed });
|
|
1577
|
+
}
|
|
1578
|
+
if (parsed.type === 'turn.failed' && typeof parsed.error?.message === 'string') {
|
|
1579
|
+
return withSignal({ hostError: parsed.error.message, raw: trimmed });
|
|
1580
|
+
}
|
|
1535
1581
|
return withSignal({ raw: trimmed });
|
|
1536
1582
|
}
|
|
1537
1583
|
catch {
|
|
@@ -1595,6 +1641,40 @@ function parseHostLine(hostId, line) {
|
|
|
1595
1641
|
}
|
|
1596
1642
|
try {
|
|
1597
1643
|
const parsed = JSON.parse(trimmed);
|
|
1644
|
+
// Issue #1234: a task appearing in `background_tasks_changed.tasks` is a
|
|
1645
|
+
// genuinely backgrounded (still-running) command. `task_started` /
|
|
1646
|
+
// `task_notification` fire for every Bash call, sync or backgrounded, so
|
|
1647
|
+
// they are deliberately NOT parsed here — only this event and
|
|
1648
|
+
// `task_updated` distinguish real background-task state.
|
|
1649
|
+
if (parsed.type === 'system' && parsed.subtype === 'background_tasks_changed' && Array.isArray(parsed.tasks)) {
|
|
1650
|
+
return withSignal({
|
|
1651
|
+
sessionId: parsed.session_id,
|
|
1652
|
+
raw: trimmed,
|
|
1653
|
+
hostLifecycle: {
|
|
1654
|
+
status: 'background_tasks_changed',
|
|
1655
|
+
source: hostId,
|
|
1656
|
+
backgroundTasks: parsed.tasks
|
|
1657
|
+
.filter((t) => typeof t.task_id === 'string')
|
|
1658
|
+
.map((t) => ({ taskId: t.task_id, description: t.description })),
|
|
1659
|
+
},
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
// Issue #1234: the authoritative terminal-status update for a tracked
|
|
1663
|
+
// background task. `patch.status` is `"completed"` or `"killed"` in the
|
|
1664
|
+
// observed schema; treat anything else as `"killed"` conservatively.
|
|
1665
|
+
if (parsed.type === 'system' && parsed.subtype === 'task_updated' && typeof parsed.task_id === 'string') {
|
|
1666
|
+
const backgroundTaskStatus = parsed.patch?.status === 'completed' ? 'completed' : 'killed';
|
|
1667
|
+
return withSignal({
|
|
1668
|
+
sessionId: parsed.session_id,
|
|
1669
|
+
raw: trimmed,
|
|
1670
|
+
hostLifecycle: {
|
|
1671
|
+
status: 'background_task_updated',
|
|
1672
|
+
source: hostId,
|
|
1673
|
+
backgroundTaskId: parsed.task_id,
|
|
1674
|
+
backgroundTaskStatus,
|
|
1675
|
+
},
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1598
1678
|
if (parsed.type === 'system' && parsed.session_id) {
|
|
1599
1679
|
return withSignal({ sessionId: parsed.session_id, raw: trimmed });
|
|
1600
1680
|
}
|
|
@@ -1776,6 +1856,16 @@ function normalizeGeminiPromptForMatch(value) {
|
|
|
1776
1856
|
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
1777
1857
|
}
|
|
1778
1858
|
class CliHostRuntime {
|
|
1859
|
+
constructor(spawn = spawnHostProcess, killTree = (pid, signal) => (0, tree_kill_1.default)(pid, signal)) {
|
|
1860
|
+
this.spawn = spawn;
|
|
1861
|
+
this.killTree = killTree;
|
|
1862
|
+
// Issue #1176: active continuation runs keyed by `${hostId}::${sessionId}` (R1,
|
|
1863
|
+
// R13, R14). Lives on this long-lived singleton instance, in-memory only (R15).
|
|
1864
|
+
// `spawn`/`killTree` are constructor-injectable so tests can prove queueing,
|
|
1865
|
+
// batching, and course-correction deterministically without launching a real
|
|
1866
|
+
// host CLI or touching a real OS process (R20).
|
|
1867
|
+
this.activeContinueRuns = new Map();
|
|
1868
|
+
}
|
|
1779
1869
|
detectEmployees() {
|
|
1780
1870
|
return detectEmployees();
|
|
1781
1871
|
}
|
|
@@ -1784,16 +1874,91 @@ class CliHostRuntime {
|
|
|
1784
1874
|
return detectEmployeesAsync();
|
|
1785
1875
|
}
|
|
1786
1876
|
startRun(hostId, projectPath, message, handlers, sessionId, launchContext) {
|
|
1787
|
-
|
|
1877
|
+
// R11: start/startDirect mint sessions rather than resuming one, so they
|
|
1878
|
+
// stay outside the continuation queue entirely.
|
|
1879
|
+
return this.spawn(hostId, (0, configured_agents_1.decorateHostPlanWithConfiguredAgent)(buildStartPlan(hostId, message, sessionId), launchContext?.agent, launchContext?.env), projectPath, handlers);
|
|
1788
1880
|
}
|
|
1789
|
-
continueRun(hostId, projectPath, sessionId, message, handlers, launchContext) {
|
|
1790
|
-
return
|
|
1881
|
+
continueRun(hostId, projectPath, sessionId, message, handlers, launchContext, deliveryIntent) {
|
|
1882
|
+
return this.guardedContinue(hostId, sessionId, { projectPath, message, handlers, launchContext, deliveryIntent });
|
|
1791
1883
|
}
|
|
1792
1884
|
startDirectRun(hostId, message, projectPath, handlers, sessionId, launchContext) {
|
|
1793
|
-
return
|
|
1885
|
+
return this.spawn(hostId, (0, configured_agents_1.decorateHostPlanWithConfiguredAgent)(buildDirectStartPlan(hostId, message, sessionId), launchContext?.agent, launchContext?.env), projectPath, handlers);
|
|
1886
|
+
}
|
|
1887
|
+
continueDirectRun(hostId, sessionId, message, projectPath, handlers, launchContext, deliveryIntent) {
|
|
1888
|
+
// R10: continueDirectRun is protected by the same per-hostId::sessionId
|
|
1889
|
+
// serialization as continueRun (same map, same key shape).
|
|
1890
|
+
return this.guardedContinue(hostId, sessionId, { projectPath, message, handlers, launchContext, deliveryIntent, direct: true });
|
|
1891
|
+
}
|
|
1892
|
+
// Issue #1176 (R34/R35/AC22): 1-based position the NEXT continuation would
|
|
1893
|
+
// take if queued right now, or null if it would spawn immediately.
|
|
1894
|
+
getQueuePosition(hostId, sessionId) {
|
|
1895
|
+
const active = this.activeContinueRuns.get(`${hostId}::${sessionId}`);
|
|
1896
|
+
if (!active)
|
|
1897
|
+
return null;
|
|
1898
|
+
return active.pending.length + 1;
|
|
1794
1899
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1900
|
+
guardedContinue(hostId, sessionId, entry) {
|
|
1901
|
+
const key = `${hostId}::${sessionId}`;
|
|
1902
|
+
const active = this.activeContinueRuns.get(key);
|
|
1903
|
+
if (!active) {
|
|
1904
|
+
return this.spawnAndRegister(key, hostId, sessionId, entry);
|
|
1905
|
+
}
|
|
1906
|
+
// R3/R31: an ordinary follow-up queues behind existing work; a course
|
|
1907
|
+
// correction (deliveryIntent 'stop') jumps to the front of the queue and
|
|
1908
|
+
// stops the active child so the correction is what runs next.
|
|
1909
|
+
if (entry.deliveryIntent === 'stop') {
|
|
1910
|
+
active.pending.unshift(entry);
|
|
1911
|
+
if (active.child.pid != null) {
|
|
1912
|
+
try {
|
|
1913
|
+
this.killTree(active.child.pid, 'SIGTERM');
|
|
1914
|
+
}
|
|
1915
|
+
catch {
|
|
1916
|
+
// Best-effort: `close` still fires on normal process exit either way.
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
else {
|
|
1921
|
+
active.pending.push(entry);
|
|
1922
|
+
}
|
|
1923
|
+
// R17: the caller receives the ALREADY-active child, not a process of its
|
|
1924
|
+
// own — callers must not treat this return value as proof the queued
|
|
1925
|
+
// message has its own process; event routing for the queued message goes
|
|
1926
|
+
// through `entry.handlers` once it is actually dequeued and spawned.
|
|
1927
|
+
return active.child;
|
|
1928
|
+
}
|
|
1929
|
+
spawnAndRegister(key, hostId, sessionId, entry) {
|
|
1930
|
+
const plan = (0, configured_agents_1.decorateHostPlanWithConfiguredAgent)(entry.direct ? buildDirectContinuePlan(hostId, sessionId, entry.message) : buildContinuePlan(hostId, sessionId, entry.message), entry.launchContext?.agent, entry.launchContext?.env);
|
|
1931
|
+
const child = this.spawn(hostId, plan, entry.projectPath, entry.handlers);
|
|
1932
|
+
const runEntry = { child, pending: [] };
|
|
1933
|
+
this.activeContinueRuns.set(key, runEntry);
|
|
1934
|
+
child.once('close', () => this.dequeueNext(key, hostId, sessionId, runEntry));
|
|
1935
|
+
return child;
|
|
1936
|
+
}
|
|
1937
|
+
// R4-R6, R9, R16 (batch-delivery per issue #1176 Round 2 design feedback):
|
|
1938
|
+
// when the active child closes, drain every pending continuation at once
|
|
1939
|
+
// (FIFO), join their messages into a single combined turn, and spawn exactly
|
|
1940
|
+
// one continuation for the whole batch. A course-correction entry (if any)
|
|
1941
|
+
// leads the combined message (R31); any "after" messages queued before or
|
|
1942
|
+
// during the redirect follow it, preserved rather than discarded (R32). The
|
|
1943
|
+
// batch uses the LAST entry's projectPath/handlers/launchContext unless a
|
|
1944
|
+
// 'stop' entry is present, in which case that correction's context wins —
|
|
1945
|
+
// it is the manager's most recent, most urgent instruction.
|
|
1946
|
+
dequeueNext(key, hostId, sessionId, runEntry) {
|
|
1947
|
+
this.activeContinueRuns.delete(key);
|
|
1948
|
+
const batch = runEntry.pending.splice(0);
|
|
1949
|
+
if (!batch.length)
|
|
1950
|
+
return;
|
|
1951
|
+
const combinedMessage = batch.map((e) => e.message).join('\n\n');
|
|
1952
|
+
const primary = batch.find((e) => e.deliveryIntent === 'stop') ?? batch[batch.length - 1];
|
|
1953
|
+
try {
|
|
1954
|
+
this.guardedContinue(hostId, sessionId, { ...primary, message: combinedMessage });
|
|
1955
|
+
}
|
|
1956
|
+
catch {
|
|
1957
|
+
// R19: a synchronous spawn failure must not leave the queue permanently
|
|
1958
|
+
// stuck behind this key — clear it so the next continueRun call spawns
|
|
1959
|
+
// fresh instead of queueing behind a dead entry.
|
|
1960
|
+
this.activeContinueRuns.delete(key);
|
|
1961
|
+
}
|
|
1797
1962
|
}
|
|
1798
1963
|
}
|
|
1799
1964
|
exports.CliHostRuntime = CliHostRuntime;
|
|
@@ -1801,11 +1966,11 @@ class FakeHostRuntime {
|
|
|
1801
1966
|
constructor() {
|
|
1802
1967
|
this.isTestDouble = true;
|
|
1803
1968
|
this.employees = [
|
|
1804
|
-
{ id: 'codex', label: 'Codex', available: true, detail: 'Test double employee.', supportsRaw: true },
|
|
1805
|
-
{ id: 'claude', label: 'Claude Code', available: true, detail: 'Test double employee.', supportsRaw: true },
|
|
1806
|
-
{ id: 'gemini', label: 'Gemini CLI', available: true, detail: 'Test double employee.', supportsRaw: true },
|
|
1807
|
-
{ id: 'copilot', label: 'GitHub Copilot CLI', available: true, detail: 'Test double agent tool.', supportsRaw: true },
|
|
1808
|
-
{ id: 'antigravity', label: 'Antigravity CLI', available: true, detail: 'Test double agent tool.', supportsRaw: false },
|
|
1969
|
+
{ id: 'codex', label: 'Codex', available: true, detail: 'Test double employee.', supportsRaw: true, version: '1.0.0-test' },
|
|
1970
|
+
{ id: 'claude', label: 'Claude Code', available: true, detail: 'Test double employee.', supportsRaw: true, version: '1.0.0-test' },
|
|
1971
|
+
{ id: 'gemini', label: 'Gemini CLI', available: true, detail: 'Test double employee.', supportsRaw: true, version: '1.0.0-test' },
|
|
1972
|
+
{ id: 'copilot', label: 'GitHub Copilot CLI', available: true, detail: 'Test double agent tool.', supportsRaw: true, version: '1.0.0-test' },
|
|
1973
|
+
{ id: 'antigravity', label: 'Antigravity CLI', available: true, detail: 'Test double agent tool.', supportsRaw: false, version: '1.0.0-test' },
|
|
1809
1974
|
];
|
|
1810
1975
|
// Remembered across turns like a resumed agent session: the job label from the
|
|
1811
1976
|
// start turn. Issue #732 — a same-job continue no longer carries a /fraim <job>
|
|
@@ -1893,11 +2058,11 @@ class ScriptedHostRuntime {
|
|
|
1893
2058
|
constructor() {
|
|
1894
2059
|
this.isTestDouble = true;
|
|
1895
2060
|
this.employees = [
|
|
1896
|
-
{ id: 'codex', label: 'Codex', available: true, detail: 'Scripted test double.', supportsRaw: true },
|
|
1897
|
-
{ id: 'claude', label: 'Claude Code', available: true, detail: 'Scripted test double.', supportsRaw: true },
|
|
1898
|
-
{ id: 'gemini', label: 'Gemini CLI', available: true, detail: 'Scripted test double.', supportsRaw: true },
|
|
1899
|
-
{ id: 'copilot', label: 'GitHub Copilot CLI', available: true, detail: 'Scripted test double.', supportsRaw: true },
|
|
1900
|
-
{ id: 'antigravity', label: 'Antigravity CLI', available: true, detail: 'Scripted test double.', supportsRaw: false },
|
|
2061
|
+
{ id: 'codex', label: 'Codex', available: true, detail: 'Scripted test double.', supportsRaw: true, version: '1.0.0-test' },
|
|
2062
|
+
{ id: 'claude', label: 'Claude Code', available: true, detail: 'Scripted test double.', supportsRaw: true, version: '1.0.0-test' },
|
|
2063
|
+
{ id: 'gemini', label: 'Gemini CLI', available: true, detail: 'Scripted test double.', supportsRaw: true, version: '1.0.0-test' },
|
|
2064
|
+
{ id: 'copilot', label: 'GitHub Copilot CLI', available: true, detail: 'Scripted test double.', supportsRaw: true, version: '1.0.0-test' },
|
|
2065
|
+
{ id: 'antigravity', label: 'Antigravity CLI', available: true, detail: 'Scripted test double.', supportsRaw: false, version: '1.0.0-test' },
|
|
1901
2066
|
];
|
|
1902
2067
|
// Track each active run so the test can emit signals at it. The Hub
|
|
1903
2068
|
// passes the run id as the requested start session id in test/demo paths;
|
|
@@ -2105,11 +2270,12 @@ class ScriptedHostRuntime {
|
|
|
2105
2270
|
}
|
|
2106
2271
|
}
|
|
2107
2272
|
exports.ScriptedHostRuntime = ScriptedHostRuntime;
|
|
2108
|
-
const createHubMessage = (role, text) => ({
|
|
2273
|
+
const createHubMessage = (role, text, deliveryStatus) => ({
|
|
2109
2274
|
id: (0, crypto_1.randomUUID)(),
|
|
2110
2275
|
role,
|
|
2111
2276
|
text,
|
|
2112
2277
|
createdAt: new Date().toISOString(),
|
|
2278
|
+
...(deliveryStatus ? { deliveryStatus } : {}),
|
|
2113
2279
|
});
|
|
2114
2280
|
exports.createHubMessage = createHubMessage;
|
|
2115
2281
|
const createHubEvent = (channel, text) => ({
|