fraim-framework 2.0.161 → 2.0.163
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/conversation-store.js +164 -0
- package/dist/src/ai-hub/desktop-main.js +24 -1
- package/dist/src/ai-hub/hosts.js +474 -26
- package/dist/src/ai-hub/managed-browser.js +269 -0
- package/dist/src/ai-hub/manager-turns.js +13 -0
- package/dist/src/ai-hub/preferences.js +11 -2
- package/dist/src/ai-hub/server.js +1228 -65
- package/dist/src/cli/commands/init-project.js +7 -1
- package/dist/src/cli/doctor/check-runner.js +3 -1
- package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +261 -2
- package/dist/src/cli/utils/agent-adapters.js +1 -1
- package/dist/src/core/fraim-config-schema.generated.js +50 -13
- package/dist/src/first-run/types.js +8 -0
- package/dist/src/local-mcp-server/agent-token-prices.js +53 -0
- package/dist/src/local-mcp-server/learning-context-builder.js +438 -2
- package/dist/src/local-mcp-server/stdio-server.js +45 -6
- package/package.json +5 -4
- package/public/ai-hub/index.html +459 -10
- package/public/ai-hub/review.css +354 -0
- package/public/ai-hub/script.js +6007 -1274
- package/public/ai-hub/styles.css +1838 -16
|
@@ -235,7 +235,13 @@ const runInitProject = async (options = {}) => {
|
|
|
235
235
|
}
|
|
236
236
|
result.warnings.push(`${configDisplayPath} was not created by init-project. The manager \`project-onboarding\` job is the only supported config-writing path.`);
|
|
237
237
|
}
|
|
238
|
-
[
|
|
238
|
+
[
|
|
239
|
+
'jobs',
|
|
240
|
+
'ai-employee/jobs',
|
|
241
|
+
'ai-employee/skills',
|
|
242
|
+
'ai-manager/jobs',
|
|
243
|
+
'personalized-employee',
|
|
244
|
+
].forEach((dir) => {
|
|
239
245
|
const dirPath = path_1.default.join(fraimDir, dir);
|
|
240
246
|
if (!fs_1.default.existsSync(dirPath)) {
|
|
241
247
|
fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.runChecks = runChecks;
|
|
9
9
|
const CHECK_TIMEOUT = 2000; // 2 seconds per check
|
|
10
|
-
|
|
10
|
+
// Issue #532: stdio MCP servers need up to 15s for handshake + npm version resolution
|
|
11
|
+
// on first call via fraim-mcp-latest-launcher; 20s gives a 5s buffer.
|
|
12
|
+
const MCP_CHECK_TIMEOUT = 20000; // 20 seconds for MCP connectivity checks
|
|
11
13
|
const TOTAL_TIMEOUT = 30000; // 30 seconds total
|
|
12
14
|
// Simple logger for doctor command (optional, falls back to no-op)
|
|
13
15
|
const logger = {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* MCP connectivity checks for FRAIM doctor command
|
|
4
4
|
* Tests FRAIM server connectivity and validates IDE MCP configurations
|
|
5
5
|
* Issue #144: Enhanced doctor command
|
|
6
|
+
* Issue #532: Stdio MCP runtime connectivity check
|
|
6
7
|
*/
|
|
7
8
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
9
|
if (k2 === undefined) k2 = k;
|
|
@@ -43,6 +44,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
43
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
45
|
exports.getNpmMajorVersion = getNpmMajorVersion;
|
|
45
46
|
exports.diagnoseFraimMcpLaunchPlan = diagnoseFraimMcpLaunchPlan;
|
|
47
|
+
exports.spawnAndHandshake = spawnAndHandshake;
|
|
48
|
+
exports.getStdioMCPRuntimeChecks = getStdioMCPRuntimeChecks;
|
|
46
49
|
exports.getMCPConnectivityChecks = getMCPConnectivityChecks;
|
|
47
50
|
const fs_1 = __importDefault(require("fs"));
|
|
48
51
|
const path_1 = __importDefault(require("path"));
|
|
@@ -52,7 +55,16 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
52
55
|
const toml = __importStar(require("toml"));
|
|
53
56
|
const ide_detector_1 = require("../../setup/ide-detector");
|
|
54
57
|
const fraim_mcp_latest_launcher_1 = require("../../mcp/fraim-mcp-latest-launcher");
|
|
58
|
+
const command_resolution_1 = require("../../mcp/command-resolution");
|
|
59
|
+
// Cache the npm major version so execFileSync is called at most once per process.
|
|
60
|
+
// Without caching, each IDE config check calls diagnoseFraimMcpLaunchPlan which calls
|
|
61
|
+
// getNpmMajorVersion, and execFileSync blocks the event loop for ~2s on Windows.
|
|
62
|
+
// With 8+ IDE checks running via Promise.all the blocking stacks sequentially.
|
|
63
|
+
let _npmMajorVersionCache = undefined;
|
|
55
64
|
function getNpmMajorVersion() {
|
|
65
|
+
if (_npmMajorVersionCache !== undefined) {
|
|
66
|
+
return _npmMajorVersionCache;
|
|
67
|
+
}
|
|
56
68
|
try {
|
|
57
69
|
const command = process.platform === 'win32' ? (process.env.ComSpec || 'cmd.exe') : 'npm';
|
|
58
70
|
const args = process.platform === 'win32' ? ['/d', '/s', '/c', 'npm --version'] : ['--version'];
|
|
@@ -61,11 +73,12 @@ function getNpmMajorVersion() {
|
|
|
61
73
|
stdio: ['ignore', 'pipe', 'ignore']
|
|
62
74
|
});
|
|
63
75
|
const major = Number.parseInt(output.trim().split('.')[0], 10);
|
|
64
|
-
|
|
76
|
+
_npmMajorVersionCache = Number.isFinite(major) ? major : null;
|
|
65
77
|
}
|
|
66
78
|
catch {
|
|
67
|
-
|
|
79
|
+
_npmMajorVersionCache = null;
|
|
68
80
|
}
|
|
81
|
+
return _npmMajorVersionCache;
|
|
69
82
|
}
|
|
70
83
|
function diagnoseFraimMcpLaunchPlan(fraimServer, platform = process.platform, npmMajorVersion = getNpmMajorVersion()) {
|
|
71
84
|
const command = String(fraimServer?.command || '');
|
|
@@ -419,6 +432,248 @@ async function validateIDEMCPConfig(ide) {
|
|
|
419
432
|
};
|
|
420
433
|
}
|
|
421
434
|
}
|
|
435
|
+
// ============================================================
|
|
436
|
+
// Issue #532: Stdio MCP Runtime Connectivity Check
|
|
437
|
+
// ============================================================
|
|
438
|
+
const STDIO_HANDSHAKE_TIMEOUT_MS = 15000;
|
|
439
|
+
/**
|
|
440
|
+
* Spawn a stdio MCP server, perform a two-phase JSON-RPC handshake
|
|
441
|
+
* (initialize then tools/list), and return a structured result.
|
|
442
|
+
*
|
|
443
|
+
* On Windows, wraps the command in cmd.exe /d /s /c so that .cmd wrappers
|
|
444
|
+
* resolve correctly. The same pattern is used by resolveManagedCommand and
|
|
445
|
+
* fraim-mcp-latest-launcher.
|
|
446
|
+
*/
|
|
447
|
+
async function spawnAndHandshake(command, args, timeoutMs = STDIO_HANDSHAKE_TIMEOUT_MS) {
|
|
448
|
+
return new Promise((resolve) => {
|
|
449
|
+
const startTime = Date.now();
|
|
450
|
+
let spawnCommand;
|
|
451
|
+
let spawnArgs;
|
|
452
|
+
// spawnOptions is typed loosely so we can set windowsVerbatimArguments below.
|
|
453
|
+
let spawnOptions = {
|
|
454
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
455
|
+
env: process.env
|
|
456
|
+
};
|
|
457
|
+
if (process.platform === 'win32') {
|
|
458
|
+
// On Windows, wrap in cmd.exe so .cmd wrappers are resolved.
|
|
459
|
+
// cmd.exe /d /s /c requires special handling when the command path contains spaces:
|
|
460
|
+
// cmd /c ""path with spaces" arg1 arg2"
|
|
461
|
+
// The outer pair of double-quotes is mandatory when the first token is quoted.
|
|
462
|
+
// We must also pass windowsVerbatimArguments: true so Node's CreateProcess call
|
|
463
|
+
// does not re-escape our already-quoted command string.
|
|
464
|
+
const comSpec = process.env.ComSpec || 'cmd.exe';
|
|
465
|
+
const quotedTokens = [command, ...args].map((a) => {
|
|
466
|
+
const v = String(a);
|
|
467
|
+
return /[\s"&|<>^]/.test(v) ? `"${v.replace(/"/g, '""')}"` : v;
|
|
468
|
+
});
|
|
469
|
+
const innerCmd = quotedTokens.join(' ');
|
|
470
|
+
// Wrap in outer quotes only when the command contains spaces (i.e. was itself quoted).
|
|
471
|
+
const needsOuterQuote = /[\s"&|<>^]/.test(String(command));
|
|
472
|
+
const cmdString = needsOuterQuote ? `"${innerCmd}"` : innerCmd;
|
|
473
|
+
spawnCommand = comSpec;
|
|
474
|
+
spawnArgs = ['/d', '/s', '/c', cmdString];
|
|
475
|
+
spawnOptions = { ...spawnOptions, windowsVerbatimArguments: true };
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
spawnCommand = command;
|
|
479
|
+
spawnArgs = args;
|
|
480
|
+
}
|
|
481
|
+
let child;
|
|
482
|
+
try {
|
|
483
|
+
child = (0, child_process_1.spawn)(spawnCommand, spawnArgs, spawnOptions);
|
|
484
|
+
}
|
|
485
|
+
catch (spawnErr) {
|
|
486
|
+
resolve({
|
|
487
|
+
status: 'error',
|
|
488
|
+
phase: 'spawn',
|
|
489
|
+
message: `Failed to spawn MCP server: ${spawnErr.message}`,
|
|
490
|
+
suggestion: 'Verify that npx is installed and accessible on PATH.',
|
|
491
|
+
details: { command, args, error: spawnErr.message }
|
|
492
|
+
});
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
const timer = setTimeout(() => {
|
|
496
|
+
try {
|
|
497
|
+
child.kill('SIGKILL');
|
|
498
|
+
}
|
|
499
|
+
catch { /* best effort */ }
|
|
500
|
+
resolve({
|
|
501
|
+
status: 'error',
|
|
502
|
+
phase: 'initialize',
|
|
503
|
+
message: `MCP server did not respond within ${timeoutMs}ms`,
|
|
504
|
+
suggestion: 'The server may be slow to start. Check that the package is installed and npx cache is warm.',
|
|
505
|
+
details: { command, args, timeoutMs, elapsed: Date.now() - startTime }
|
|
506
|
+
});
|
|
507
|
+
}, timeoutMs);
|
|
508
|
+
let stderrBuffer = '';
|
|
509
|
+
let stdoutBuffer = '';
|
|
510
|
+
let spawnError = null;
|
|
511
|
+
child.stderr?.on('data', (d) => {
|
|
512
|
+
stderrBuffer += d.toString();
|
|
513
|
+
});
|
|
514
|
+
child.on('error', (err) => {
|
|
515
|
+
spawnError = err;
|
|
516
|
+
});
|
|
517
|
+
child.stdout?.on('data', (d) => {
|
|
518
|
+
stdoutBuffer += d.toString();
|
|
519
|
+
});
|
|
520
|
+
child.on('close', (code) => {
|
|
521
|
+
clearTimeout(timer);
|
|
522
|
+
if (spawnError) {
|
|
523
|
+
const msg = spawnError.message || '';
|
|
524
|
+
const isEnoent = msg.includes('ENOENT') || msg.includes('not found');
|
|
525
|
+
resolve({
|
|
526
|
+
status: 'error',
|
|
527
|
+
phase: 'spawn',
|
|
528
|
+
message: `Failed to start MCP server: ${msg}`,
|
|
529
|
+
suggestion: isEnoent
|
|
530
|
+
? 'Verify that npx is installed. Run: npm install -g npm to update npm/npx.'
|
|
531
|
+
: 'Check that the required package can be installed via npx.',
|
|
532
|
+
details: { command, args, error: msg }
|
|
533
|
+
});
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
if (code !== 0 && stdoutBuffer.trim() === '') {
|
|
537
|
+
resolve({
|
|
538
|
+
status: 'error',
|
|
539
|
+
phase: 'spawn',
|
|
540
|
+
message: `MCP server exited with code ${code} before responding`,
|
|
541
|
+
suggestion: 'Run: npx -y <package> manually to check for installation errors.',
|
|
542
|
+
details: { command, args, exitCode: code, stderr: stderrBuffer.slice(0, 500) }
|
|
543
|
+
});
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
// Parse all JSON-RPC messages received from stdout
|
|
547
|
+
const lines = stdoutBuffer.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
548
|
+
let initializeResponse = null;
|
|
549
|
+
let toolsListResponse = null;
|
|
550
|
+
for (const line of lines) {
|
|
551
|
+
try {
|
|
552
|
+
const msg = JSON.parse(line);
|
|
553
|
+
if (msg?.id === 1 && msg?.result !== undefined) {
|
|
554
|
+
initializeResponse = msg;
|
|
555
|
+
}
|
|
556
|
+
if (msg?.id === 2 && msg?.result !== undefined) {
|
|
557
|
+
toolsListResponse = msg;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
catch {
|
|
561
|
+
// Ignore non-JSON lines (e.g., startup log output)
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (!initializeResponse) {
|
|
565
|
+
resolve({
|
|
566
|
+
status: 'error',
|
|
567
|
+
phase: 'initialize',
|
|
568
|
+
message: 'MCP server did not return a valid initialize response',
|
|
569
|
+
suggestion: 'The server may have crashed during startup. Check stderr for error details.',
|
|
570
|
+
details: { command, args, exitCode: code, stderr: stderrBuffer.slice(0, 500) }
|
|
571
|
+
});
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
if (!toolsListResponse) {
|
|
575
|
+
resolve({
|
|
576
|
+
status: 'error',
|
|
577
|
+
phase: 'tools-list',
|
|
578
|
+
message: 'MCP server did not respond to tools/list',
|
|
579
|
+
suggestion: 'The server initialized but did not expose any tools. Check the package documentation.',
|
|
580
|
+
details: { command, args, exitCode: code }
|
|
581
|
+
});
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
const toolCount = Array.isArray(toolsListResponse.result?.tools)
|
|
585
|
+
? toolsListResponse.result.tools.length
|
|
586
|
+
: 0;
|
|
587
|
+
const elapsed = Date.now() - startTime;
|
|
588
|
+
resolve({
|
|
589
|
+
status: 'passed',
|
|
590
|
+
message: `MCP server responded with ${toolCount} tool(s) in ${elapsed}ms`,
|
|
591
|
+
details: { command, args, toolCount, elapsed }
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
// Send two-phase JSON-RPC handshake
|
|
595
|
+
try {
|
|
596
|
+
const initMsg = JSON.stringify({
|
|
597
|
+
jsonrpc: '2.0',
|
|
598
|
+
id: 1,
|
|
599
|
+
method: 'initialize',
|
|
600
|
+
params: {
|
|
601
|
+
protocolVersion: '2024-11-05',
|
|
602
|
+
capabilities: {},
|
|
603
|
+
clientInfo: { name: 'fraim-doctor', version: '1.0' }
|
|
604
|
+
}
|
|
605
|
+
}) + '\n';
|
|
606
|
+
child.stdin?.write(initMsg);
|
|
607
|
+
const toolsMsg = JSON.stringify({
|
|
608
|
+
jsonrpc: '2.0',
|
|
609
|
+
id: 2,
|
|
610
|
+
method: 'tools/list',
|
|
611
|
+
params: {}
|
|
612
|
+
}) + '\n';
|
|
613
|
+
child.stdin?.write(toolsMsg);
|
|
614
|
+
child.stdin?.end();
|
|
615
|
+
}
|
|
616
|
+
catch {
|
|
617
|
+
// stdin may not be writable if the process exited immediately
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Run a connectivity check for a single named stdio MCP server.
|
|
623
|
+
*/
|
|
624
|
+
async function testStdioMCPServer(serverName, command, args) {
|
|
625
|
+
if (process.env.NODE_ENV === 'test') {
|
|
626
|
+
return {
|
|
627
|
+
status: 'warning',
|
|
628
|
+
message: `Stdio MCP runtime check for "${serverName}" skipped (test mode)`,
|
|
629
|
+
details: { testMode: true, skipped: true, serverName }
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
const result = await spawnAndHandshake(command, args, STDIO_HANDSHAKE_TIMEOUT_MS);
|
|
633
|
+
if (result.status === 'passed') {
|
|
634
|
+
return {
|
|
635
|
+
status: 'passed',
|
|
636
|
+
message: `${serverName} stdio MCP: ${result.message}`,
|
|
637
|
+
details: result.details
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
return {
|
|
641
|
+
status: 'error',
|
|
642
|
+
message: `${serverName} stdio MCP connectivity failed (phase: ${result.phase}): ${result.message}`,
|
|
643
|
+
suggestion: result.suggestion,
|
|
644
|
+
details: { ...result.details, phase: result.phase }
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Build the 3 stdio MCP runtime checks (git, playwright, fraim).
|
|
649
|
+
* Exported separately so callers can obtain just the runtime checks if needed.
|
|
650
|
+
*/
|
|
651
|
+
function getStdioMCPRuntimeChecks() {
|
|
652
|
+
const npx = (0, command_resolution_1.resolveManagedCommand)('npx');
|
|
653
|
+
// For the fraim MCP server we use the latest launcher so the test
|
|
654
|
+
// exercises the same path that IDE configs use.
|
|
655
|
+
const fraimLauncherPath = (0, fraim_mcp_latest_launcher_1.getFraimMcpLatestLauncherPath)();
|
|
656
|
+
return [
|
|
657
|
+
{
|
|
658
|
+
name: 'git stdio runtime check',
|
|
659
|
+
category: 'mcpConnectivity',
|
|
660
|
+
critical: false,
|
|
661
|
+
run: () => testStdioMCPServer('git', npx, ['-y', '@cyanheads/git-mcp-server'])
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
name: 'playwright stdio runtime check',
|
|
665
|
+
category: 'mcpConnectivity',
|
|
666
|
+
critical: false,
|
|
667
|
+
run: () => testStdioMCPServer('playwright', npx, ['-y', '@playwright/mcp'])
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
name: 'fraim stdio runtime check',
|
|
671
|
+
category: 'mcpConnectivity',
|
|
672
|
+
critical: false,
|
|
673
|
+
run: () => testStdioMCPServer('fraim', process.execPath, [fraimLauncherPath])
|
|
674
|
+
}
|
|
675
|
+
];
|
|
676
|
+
}
|
|
422
677
|
/**
|
|
423
678
|
* Get all MCP connectivity checks
|
|
424
679
|
*/
|
|
@@ -441,5 +696,9 @@ function getMCPConnectivityChecks() {
|
|
|
441
696
|
run: async () => validateIDEMCPConfig(ide)
|
|
442
697
|
});
|
|
443
698
|
}
|
|
699
|
+
// Check 3 (Issue #532): Confirm stdio MCP servers respond at runtime.
|
|
700
|
+
// "Disconnected" in mcp list is expected on-demand-spawn behavior. These checks
|
|
701
|
+
// verify that each server can actually start and complete a JSON-RPC handshake.
|
|
702
|
+
checks.push(...getStdioMCPRuntimeChecks());
|
|
444
703
|
return checks;
|
|
445
704
|
}
|
|
@@ -71,7 +71,7 @@ This repository uses FRAIM.
|
|
|
71
71
|
- When users ask for next step recommendations, use recommend-next-job skill under \`${employeeSkillsPath}/\` to gather context before suggesting jobs.
|
|
72
72
|
|
|
73
73
|
> [!IMPORTANT]
|
|
74
|
-
> **Job stubs are for discovery only.** When a user
|
|
74
|
+
> **Job stubs are for discovery only.** When a user mentions or references any file under \`${employeeJobsPath}/\` or \`${managerJobsPath}/\`, do NOT attempt to execute the job from the stub content. The stub only shows intent and phase names. Always call \`get_fraim_job({ job: "<job-name>" })\` first to get the full phased instructions before doing any work.
|
|
75
75
|
`);
|
|
76
76
|
const cursorManagedBody = buildManagedSection(`
|
|
77
77
|
# FRAIM
|
|
@@ -349,6 +349,51 @@ exports.FRAIM_CONFIG_SCHEMA = {
|
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
351
|
},
|
|
352
|
+
"playbooks": {
|
|
353
|
+
"kind": "object",
|
|
354
|
+
"properties": {
|
|
355
|
+
"directory": {
|
|
356
|
+
"kind": "string"
|
|
357
|
+
},
|
|
358
|
+
"decisionCommand": {
|
|
359
|
+
"kind": "string"
|
|
360
|
+
},
|
|
361
|
+
"decisionTimeoutMs": {
|
|
362
|
+
"kind": "number"
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
"actionAdapters": {
|
|
367
|
+
"kind": "record",
|
|
368
|
+
"value": {
|
|
369
|
+
"kind": "object",
|
|
370
|
+
"properties": {
|
|
371
|
+
"type": {
|
|
372
|
+
"kind": "enum",
|
|
373
|
+
"values": [
|
|
374
|
+
"command",
|
|
375
|
+
"manual",
|
|
376
|
+
"handoff"
|
|
377
|
+
]
|
|
378
|
+
},
|
|
379
|
+
"command": {
|
|
380
|
+
"kind": "string"
|
|
381
|
+
},
|
|
382
|
+
"timeoutMs": {
|
|
383
|
+
"kind": "number"
|
|
384
|
+
},
|
|
385
|
+
"targetSystem": {
|
|
386
|
+
"kind": "string"
|
|
387
|
+
},
|
|
388
|
+
"targetIdentityProvider": {
|
|
389
|
+
"kind": "string"
|
|
390
|
+
},
|
|
391
|
+
"instructions": {
|
|
392
|
+
"kind": "string"
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
},
|
|
352
397
|
"queue": {
|
|
353
398
|
"kind": "object",
|
|
354
399
|
"properties": {
|
|
@@ -407,17 +452,6 @@ exports.FRAIM_CONFIG_SCHEMA = {
|
|
|
407
452
|
"requireApprovalState": {
|
|
408
453
|
"kind": "string"
|
|
409
454
|
},
|
|
410
|
-
"selector": {
|
|
411
|
-
"kind": "object",
|
|
412
|
-
"properties": {
|
|
413
|
-
"shortDescriptionContains": {
|
|
414
|
-
"kind": "array",
|
|
415
|
-
"element": {
|
|
416
|
-
"kind": "string"
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
},
|
|
421
455
|
"microsoftPasswordReset": {
|
|
422
456
|
"kind": "object",
|
|
423
457
|
"properties": {
|
|
@@ -564,6 +598,11 @@ exports.SUPPORTED_FRAIM_CONFIG_PATHS = [
|
|
|
564
598
|
"automation.support.contextResolver.scriptPath",
|
|
565
599
|
"automation.support.contextResolver.arguments",
|
|
566
600
|
"automation.support.contextResolver.timeoutMs",
|
|
601
|
+
"automation.support.playbooks",
|
|
602
|
+
"automation.support.playbooks.directory",
|
|
603
|
+
"automation.support.playbooks.decisionCommand",
|
|
604
|
+
"automation.support.playbooks.decisionTimeoutMs",
|
|
605
|
+
"automation.support.actionAdapters",
|
|
567
606
|
"automation.support.queue",
|
|
568
607
|
"automation.support.queue.provider",
|
|
569
608
|
"automation.support.queue.table",
|
|
@@ -579,8 +618,6 @@ exports.SUPPORTED_FRAIM_CONFIG_PATHS = [
|
|
|
579
618
|
"automation.support.requestTypes.password_reset",
|
|
580
619
|
"automation.support.requestTypes.password_reset.mode",
|
|
581
620
|
"automation.support.requestTypes.password_reset.requireApprovalState",
|
|
582
|
-
"automation.support.requestTypes.password_reset.selector",
|
|
583
|
-
"automation.support.requestTypes.password_reset.selector.shortDescriptionContains",
|
|
584
621
|
"automation.support.requestTypes.password_reset.microsoftPasswordReset",
|
|
585
622
|
"automation.support.requestTypes.password_reset.microsoftPasswordReset.resetType",
|
|
586
623
|
"automation.support.requestTypes.password_reset.microsoftPasswordReset.providedPasswordEnvVar",
|
|
@@ -50,6 +50,14 @@ exports.FIRST_RUN_AGENT_OPTIONS = [
|
|
|
50
50
|
launchCommand: 'gemini',
|
|
51
51
|
installPackage: '@google/gemini-cli',
|
|
52
52
|
},
|
|
53
|
+
{
|
|
54
|
+
id: 'copilot-cli',
|
|
55
|
+
label: 'GitHub Copilot',
|
|
56
|
+
detectAliases: ['copilot'],
|
|
57
|
+
loginCommand: 'copilot login',
|
|
58
|
+
launchCommand: 'copilot',
|
|
59
|
+
installPackage: '@github/copilot',
|
|
60
|
+
},
|
|
53
61
|
];
|
|
54
62
|
/**
|
|
55
63
|
* The canonical row set, in display order. Each row starts in `pending`;
|
|
@@ -86,6 +86,59 @@ exports.AGENT_TOKEN_PRICES = [
|
|
|
86
86
|
source: 'https://openai.com/api/pricing/ (gpt-5 row, applied to gpt-5.5 as nearest published rate)',
|
|
87
87
|
verifiedOn: '2026-04-30',
|
|
88
88
|
},
|
|
89
|
+
// Google — Gemini CLI. Standard paid-tier text/image/video token rates.
|
|
90
|
+
// Context caching maps to cache reads; the published storage-hour price
|
|
91
|
+
// cannot be derived from the CLI token stats and is intentionally omitted.
|
|
92
|
+
{
|
|
93
|
+
agent: 'gemini',
|
|
94
|
+
model: 'gemini-3.1-flash-lite',
|
|
95
|
+
inputPerMTok: 0.25,
|
|
96
|
+
outputPerMTok: 1.50,
|
|
97
|
+
cacheReadPerMTok: 0.025,
|
|
98
|
+
cacheCreationPerMTok: 0,
|
|
99
|
+
source: 'https://ai.google.dev/gemini-api/docs/pricing',
|
|
100
|
+
verifiedOn: '2026-06-02',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
agent: 'gemini',
|
|
104
|
+
model: 'gemini-3-flash-preview',
|
|
105
|
+
inputPerMTok: 0.50,
|
|
106
|
+
outputPerMTok: 3.00,
|
|
107
|
+
cacheReadPerMTok: 0.05,
|
|
108
|
+
cacheCreationPerMTok: 0,
|
|
109
|
+
source: 'https://ai.google.dev/gemini-api/docs/pricing',
|
|
110
|
+
verifiedOn: '2026-06-02',
|
|
111
|
+
},
|
|
112
|
+
// GitHub Copilot CLI. The CLI's agentic mode uses GPT-4.1 (also branded
|
|
113
|
+
// "gpt-4.1" in the Copilot API stream). GitHub charges for Copilot via
|
|
114
|
+
// premium-request quota rather than per-token billing on most plans; the
|
|
115
|
+
// per-token rates below apply to the Copilot API / enterprise API billing
|
|
116
|
+
// path (pay-per-token, not included-requests) as published at
|
|
117
|
+
// https://docs.github.com/en/copilot/about-github-copilot/plans-for-github-copilot
|
|
118
|
+
// and the underlying OpenAI model pricing for gpt-4.1 at openai.com/api/pricing/.
|
|
119
|
+
// Coverage is 'partial' for Copilot runs where the CLI does not emit a cost
|
|
120
|
+
// field — the table backstops cost computation when a token count is present
|
|
121
|
+
// but no direct costUsd is in the stream.
|
|
122
|
+
{
|
|
123
|
+
agent: 'copilot',
|
|
124
|
+
model: 'gpt-4.1',
|
|
125
|
+
inputPerMTok: 2.00,
|
|
126
|
+
outputPerMTok: 8.00,
|
|
127
|
+
cacheReadPerMTok: 0.50,
|
|
128
|
+
cacheCreationPerMTok: 0,
|
|
129
|
+
source: 'https://openai.com/api/pricing/ (gpt-4.1 row; GitHub Copilot CLI agentic mode)',
|
|
130
|
+
verifiedOn: '2026-06-09',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
agent: 'copilot',
|
|
134
|
+
model: 'gpt-4o',
|
|
135
|
+
inputPerMTok: 2.50,
|
|
136
|
+
outputPerMTok: 10.00,
|
|
137
|
+
cacheReadPerMTok: 1.25,
|
|
138
|
+
cacheCreationPerMTok: 0,
|
|
139
|
+
source: 'https://openai.com/api/pricing/ (gpt-4o row; GitHub Copilot CLI fallback model)',
|
|
140
|
+
verifiedOn: '2026-06-09',
|
|
141
|
+
},
|
|
89
142
|
];
|
|
90
143
|
/**
|
|
91
144
|
* Look up the price entry for an agent + model. Agent is matched
|