mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 16:02:29 +00:00
* feat(agents): add Prime Agent as a supported TUI agent with session history Wire Prime Intellect's prime-agent CLI (a Pi fork) into the desktop and mobile agent catalogs following the Trae registration pattern, and into the Agent Session History browser following the OMP pattern: - types.ts, tui-agent-config.ts: register 'prime-agent' with argv prompt injection behind a `--` separator (its own help documents `--` as "treat all following arguments as messages"; without it, prompts starting with `help`/`agents`/`-…` dispatch as subcommands or flags), plus csi-u Shift+Enter encoding matching the Pi TUI it embeds. - agent-kind.ts, telemetry-events.ts, agent-status-types.ts, agent-type-label.ts, tui-agent-display-names.ts, tui-agent-selection.ts, skills-cli-agent-keys.ts: standard per-agent registrations. - agent-headless-command.ts: `-p/--print` one-shot runs share the print-mode matcher with Claude/Trae so they are not mistaken for live interactive panes. - agent-process-recognition: the npm shim launches a generic bundled cli.js, so only the exact package path is an authoritative identity (same as Pi and cursor-agent). The three per-agent regex branches are now one table in agent-node-entrypoint-identities.ts — the module was at its max-lines budget and a table makes the next agent one entry. - AI Vault: sessions are Pi's message-graph JSONL under ~/.prime/agent/sessions (override PRIME_AGENT_CODING_AGENT_DIR — Prime Agent brands Pi's env contract instead of sharing PI_CODING_AGENT_DIR); parsed by the shared message-graph parser with incremental append-resume; discovered locally, in WSL homes, and over remote SSH; resumes by absolute transcript path (`prime-agent --resume <path>`) like OMP, with session-id fallback. - skill-discovery-sources.ts: ~/.prime/agent/skills home source. - Catalog, i18n (en/es/ja/ko/zh), mobile registries, and a bundled 64x64 favicon (required by mobile's offline-icon invariant). Scanner-test fixtures for OMP and Prime Agent move into session-scanner-test-fixtures.ts and the incremental fixture into its own module, keeping every touched file inside its max-lines budget without ratchet bumps. * fix(ai-vault): map custom Prime Agent roots to their sessions child PRIME_AGENT_CODING_AGENT_DIR is consumed verbatim by the CLI as its agent config dir, with transcripts always in <agentDir>/sessions — unlike PI_CODING_AGENT_DIR's <home>/agent/sessions shape the shared normalizer models. A custom root with a non-special basename (or a `.prime` leaf) was therefore scanned as-is instead of its sessions child. Dedicated normalizePrimeAgentSessionsDir appends `sessions` to every configured root, taking only an explicit `.../sessions` path as-is; the shared Pi/OMP normalizer drops the `.prime` widening it no longer needs. Raised in review on #12935. * fix(ai-vault): guard degenerate Prime Agent roots and cover the remote source normalizePrimeAgentSessionsDir stripped a filesystem-root value ('/' or '//') to '', which then joined into the relative root 'sessions' and would walk the main-process cwd. session-scanner-roots.ts already carries this guard for the OMP variant; apply the same fallback here. The remote SSH source had no test: deleting jsonlSource('prime-agent', ...) left the suite green, unlike the local path which is pinned by the AI_VAULT_AGENTS exhaustiveness assertion in session-scanner.test.ts. Add a case that fixes the .prime/agent/sessions root segments, the .jsonl extension, and parser routing. Raised in review on #12935. * fix(ai-vault): honor Prime Agent's sessions-root env and non-interactive modes Verified against upstream PrimeIntellect-ai/prime-agent source rather than inferred from the CLI's help text. config.ts getSessionsDir() reads PRIME_AGENT_SESSION_DIR (and its legacy PRIME_AGENT_CODING_AGENT_SESSION_DIR alias) ahead of the agent dir and uses it verbatim; setting either left the vault silently empty. It also appends `sessions` to the agent dir unconditionally, with no basename escape hatch, so PRIME_AGENT_CODING_AGENT_DIR=/data/sessions writes to /data/sessions/sessions while Orca scanned /data/sessions. getAgentDir() and the session-dir override both run through expandTildePath, so a `~` value set outside a shell resolves. cli/args.ts also spells the non-interactive runs `--mode json|rpc|acp|daemon`, which the shared print-mode matcher does not know, so those panes were counted as live interactive agents and the paste-submit path would write user text into a JSON-RPC/ACP stream. Match upstream exactly: only the space-separated form, since `--mode=json` is not parsed by the CLI and does start the TUI. Raised in review on #12935. * fix(ai-vault): keep Prime Agent roots absolute and remote segments posix Two holes in the previous commit. The degenerate-root guard only rejected pure-separator values, so a relative env value still resolved against the main-process cwd: PRIME_AGENT_CODING_AGENT_DIR='.' scanned '<cwd>/sessions' and, worse, PRIME_AGENT_SESSION_DIR='.' scanned the cwd itself. Require an absolute path in both branches and fall back to the default otherwise. remotePrimeAgentSessionsSegments() built its segments with the local-platform join, so on a Windows client scanning a posix SSH host it produced '\.prime\agent\sessions' and split('/') collapsed it to one bogus segment — remote discovery would have found nothing. Remote roots are posix regardless of client platform, so keep them literal. Pi and OMP are unaffected: their normalizer returns a '.../sessions' input unchanged and never joins. Raised in review on #12935. * test(ai-vault): pin Windows drive roots to the Prime Agent default fallback 'C:\' and 'C:/' strip to the drive-relative 'C:', which isAbsolute rejects on every platform — assert they land in the default fallback so a looser truthiness check can't reintroduce a 'C:sessions' scan root. Raised in review on #12935. * test(ai-vault): pin the drive-relative root form and state what the posix runner can assert --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
370 lines
15 KiB
TypeScript
370 lines
15 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
isAgentForegroundWrapperProcess,
|
|
isExpectedAgentProcess,
|
|
isRecognizedAgentType,
|
|
recognizeAgentProcess,
|
|
recognizeAgentProcessFromCommandLine
|
|
} from './agent-process-recognition'
|
|
|
|
describe('agent process recognition', () => {
|
|
it('recognizes packaged Codex foreground process names', () => {
|
|
expect(recognizeAgentProcess('codex-aarch64-ap')).toEqual({
|
|
agent: 'codex',
|
|
processName: 'codex-aarch64-ap'
|
|
})
|
|
expect(isRecognizedAgentType('codex-aarch64-ap')).toBe(true)
|
|
})
|
|
|
|
it('recognizes the OpenClaude foreground process', () => {
|
|
expect(recognizeAgentProcess('/usr/local/bin/openclaude')).toEqual({
|
|
agent: 'openclaude',
|
|
processName: 'openclaude'
|
|
})
|
|
expect(isRecognizedAgentType('openclaude')).toBe(true)
|
|
expect(isExpectedAgentProcess('/usr/local/bin/openclaude', 'claude')).toBe(false)
|
|
})
|
|
|
|
it('recognizes the Droid foreground process on Windows', () => {
|
|
expect(recognizeAgentProcess(String.raw`C:\Users\dev\AppData\Roaming\npm\droid.cmd`)).toEqual({
|
|
agent: 'droid',
|
|
processName: 'droid'
|
|
})
|
|
})
|
|
|
|
it('matches expected agents from platform-specific foreground process paths', () => {
|
|
expect(recognizeAgentProcess('claude')).toEqual({
|
|
agent: 'claude',
|
|
processName: 'claude'
|
|
})
|
|
expect(
|
|
isExpectedAgentProcess(String.raw`C:\Users\dev\AppData\Roaming\npm\claude.exe`, 'claude')
|
|
).toBe(true)
|
|
expect(isExpectedAgentProcess('/usr/local/bin/claude', 'claude')).toBe(true)
|
|
expect(isExpectedAgentProcess('powershell.exe', 'claude')).toBe(false)
|
|
})
|
|
|
|
it('does not recognize Claude print-mode hook subprocesses as interactive agents', () => {
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
'claude --print --model haiku "Analyze this conversation and determine: Does the assistant have more autonomous work to do RIGHT NOW?"'
|
|
)
|
|
).toBeNull()
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`/home/dev/.local/bin/claude -p "Context: This summary will be shown in a list"`
|
|
)
|
|
).toBeNull()
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`C:\Users\dev\AppData\Roaming\npm\claude.exe --output-format=json "hook prompt"`
|
|
)
|
|
).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('claude --resume abc123')).toEqual({
|
|
agent: 'claude',
|
|
processName: 'claude'
|
|
})
|
|
})
|
|
|
|
it('recognizes Command Code without classifying Windows cmd.exe as an agent', () => {
|
|
expect(recognizeAgentProcess('command-code')).toEqual({
|
|
agent: 'command-code',
|
|
processName: 'command-code'
|
|
})
|
|
expect(
|
|
recognizeAgentProcess(String.raw`C:\Users\dev\AppData\Roaming\npm\command-code.cmd`)
|
|
).toEqual({
|
|
agent: 'command-code',
|
|
processName: 'command-code'
|
|
})
|
|
expect(isRecognizedAgentType('command-code')).toBe(true)
|
|
expect(isRecognizedAgentType('cmd.exe')).toBe(false)
|
|
expect(recognizeAgentProcess('cmd.exe')).toBeNull()
|
|
})
|
|
|
|
it('recognizes Ante without classifying ante-prefixed path fragments as the agent', () => {
|
|
expect(recognizeAgentProcess('ante')).toEqual({
|
|
agent: 'ante',
|
|
processName: 'ante'
|
|
})
|
|
expect(recognizeAgentProcess('/Users/dev/.ante/bin/ante')).toEqual({
|
|
agent: 'ante',
|
|
processName: 'ante'
|
|
})
|
|
expect(isExpectedAgentProcess('/Users/dev/.ante/bin/ante', 'ante')).toBe(true)
|
|
expect(isRecognizedAgentType('ante')).toBe(true)
|
|
// Why: 'ante' is a common token in directory and binary names; only the
|
|
// exact normalized basename may classify as the agent.
|
|
expect(recognizeAgentProcess('ante-obsidian')).toBeNull()
|
|
expect(recognizeAgentProcess('antechamber')).toBeNull()
|
|
expect(isExpectedAgentProcess('ante-obsidian', 'ante')).toBe(false)
|
|
})
|
|
|
|
it('does not recognize Ante headless one-shot commands as interactive agents', () => {
|
|
expect(recognizeAgentProcessFromCommandLine('ante -p "summarize this diff"')).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('ante -psummarize')).toBeNull()
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine('ante --prompt "review this for security issues"')
|
|
).toBeNull()
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine('ante --prompt=review --output-format minimal')
|
|
).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('ante --resume ses_123')).toEqual({
|
|
agent: 'ante',
|
|
processName: 'ante'
|
|
})
|
|
})
|
|
|
|
it('does not recognize wrapped Ante headless one-shot commands as interactive agents', () => {
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine('node /Users/dev/.ante/bin/ante --prompt "review"')
|
|
).toBeNull()
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`node C:\Users\dev\.ante\bin\ante.cmd -p review`
|
|
)
|
|
).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('node /Users/dev/.ante/bin/ante')).toEqual({
|
|
agent: 'ante',
|
|
processName: 'ante'
|
|
})
|
|
})
|
|
|
|
it('recognizes Trae by its traecli binary, not the ambiguous trae-cli name', () => {
|
|
expect(recognizeAgentProcess('traecli')).toEqual({
|
|
agent: 'trae',
|
|
processName: 'traecli'
|
|
})
|
|
expect(recognizeAgentProcess('/Users/dev/.local/bin/traecli')).toEqual({
|
|
agent: 'trae',
|
|
processName: 'traecli'
|
|
})
|
|
expect(isExpectedAgentProcess('/Users/dev/.local/bin/traecli', 'traecli')).toBe(true)
|
|
expect(isRecognizedAgentType('traecli')).toBe(true)
|
|
// Why: `trae-cli` and `trae-agent` both name the unrelated open-source bytedance/trae-agent.
|
|
expect(recognizeAgentProcess('trae-cli')).toBeNull()
|
|
expect(recognizeAgentProcess('trae-agent')).toBeNull()
|
|
})
|
|
|
|
it('does not recognize Trae headless one-shot commands as interactive agents', () => {
|
|
expect(recognizeAgentProcessFromCommandLine('traecli -p "summarize this diff"')).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('traecli --print "review this"')).toBeNull()
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine('traecli --output-format json "review this"')
|
|
).toBeNull()
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine('traecli --output-format=stream-json review')
|
|
).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('traecli --resume AUTO')).toEqual({
|
|
agent: 'trae',
|
|
processName: 'traecli'
|
|
})
|
|
// Why: past `--` nothing is a flag, so this is the interactive pane Orca itself launches.
|
|
expect(recognizeAgentProcessFromCommandLine('traecli -- "--print the release notes"')).toEqual({
|
|
agent: 'trae',
|
|
processName: 'traecli'
|
|
})
|
|
})
|
|
|
|
it('recognizes Mistral Vibe by its installed executable and legacy alias', () => {
|
|
expect(recognizeAgentProcess('/home/dev/.local/bin/vibe')).toEqual({
|
|
agent: 'mistral-vibe',
|
|
processName: 'vibe'
|
|
})
|
|
expect(recognizeAgentProcess('mistral-vibe')).toEqual({
|
|
agent: 'mistral-vibe',
|
|
processName: 'mistral-vibe'
|
|
})
|
|
expect(isRecognizedAgentType('vibe')).toBe(true)
|
|
})
|
|
|
|
it('recognizes Qwen Code by its installed qwen executable', () => {
|
|
expect(recognizeAgentProcess('/home/dev/.local/bin/qwen')).toEqual({
|
|
agent: 'qwen-code',
|
|
processName: 'qwen'
|
|
})
|
|
expect(recognizeAgentProcess(String.raw`C:\Users\dev\AppData\Roaming\npm\qwen.cmd`)).toEqual({
|
|
agent: 'qwen-code',
|
|
processName: 'qwen'
|
|
})
|
|
expect(isExpectedAgentProcess('/usr/local/bin/qwen', 'qwen')).toBe(true)
|
|
expect(isRecognizedAgentType('qwen')).toBe(true)
|
|
})
|
|
|
|
it('recognizes agent CLIs launched through interpreter wrappers', () => {
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine('node /Users/dev/.nvm/versions/node/bin/codex')
|
|
).toEqual({ agent: 'codex', processName: 'codex' })
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine('node /Users/dev/.nvm/versions/node/bin/gemini')
|
|
).toEqual({ agent: 'gemini', processName: 'gemini' })
|
|
expect(recognizeAgentProcessFromCommandLine('python3 /opt/homebrew/bin/hermes --tui')).toEqual({
|
|
agent: 'hermes',
|
|
processName: 'hermes'
|
|
})
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine('python3.12 /opt/homebrew/bin/hermes --tui')
|
|
).toEqual({
|
|
agent: 'hermes',
|
|
processName: 'hermes'
|
|
})
|
|
expect(recognizeAgentProcessFromCommandLine('python -m aider')).toEqual({
|
|
agent: 'aider',
|
|
processName: 'aider'
|
|
})
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`python C:\Users\dev\AppData\Roaming\Python\Python312\Scripts\aider.py`
|
|
)
|
|
).toEqual({ agent: 'aider', processName: 'aider' })
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`node C:\Users\dev\AppData\Roaming\npm\codex.cmd`
|
|
)
|
|
).toEqual({ agent: 'codex', processName: 'codex' })
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`node C:\Users\dev\AppData\Roaming\npm\node_modules\@openai\codex\bin\codex.js`
|
|
)
|
|
).toEqual({ agent: 'codex', processName: 'codex' })
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`node C:\Users\dev\AppData\Roaming\npm\node_modules\@google\gemini-cli\bundle\gemini.mjs`
|
|
)
|
|
).toEqual({ agent: 'gemini', processName: 'gemini' })
|
|
})
|
|
|
|
it.each(['earendil-works', 'mariozechner'])('recognizes the @%s Pi npm entrypoint', (scope) => {
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`node.exe C:\Users\dev\AppData\Roaming\npm\node_modules\@${scope}\pi-coding-agent\dist\cli.js`
|
|
)
|
|
).toEqual({ agent: 'pi', processName: 'pi' })
|
|
})
|
|
|
|
it('recognizes Prime Agent by its binary and npm entrypoint', () => {
|
|
expect(recognizeAgentProcess('prime-agent')).toEqual({
|
|
agent: 'prime-agent',
|
|
processName: 'prime-agent'
|
|
})
|
|
expect(recognizeAgentProcess('/opt/homebrew/bin/prime-agent')).toEqual({
|
|
agent: 'prime-agent',
|
|
processName: 'prime-agent'
|
|
})
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
'node /opt/homebrew/lib/node_modules/prime-agent/dist/bundle/cli.js'
|
|
)
|
|
).toEqual({ agent: 'prime-agent', processName: 'prime-agent' })
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`node.exe C:\Users\dev\AppData\Roaming\npm\node_modules\prime-agent\dist\bundle\cli.js`
|
|
)
|
|
).toEqual({ agent: 'prime-agent', processName: 'prime-agent' })
|
|
})
|
|
|
|
it('does not recognize Prime Agent headless one-shot commands as interactive agents', () => {
|
|
expect(recognizeAgentProcessFromCommandLine('prime-agent -p "summarize this diff"')).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('prime-agent --print "review this"')).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('prime-agent --resume abc123')).toEqual({
|
|
agent: 'prime-agent',
|
|
processName: 'prime-agent'
|
|
})
|
|
// Why: past `--` nothing is a flag, so this is the interactive pane Orca itself launches.
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine('prime-agent -- "--print the release notes"')
|
|
).toEqual({ agent: 'prime-agent', processName: 'prime-agent' })
|
|
})
|
|
|
|
it('does not recognize Prime Agent non-interactive --mode runs as interactive agents', () => {
|
|
for (const mode of ['json', 'rpc', 'acp', 'daemon']) {
|
|
expect(recognizeAgentProcessFromCommandLine(`prime-agent --mode ${mode}`)).toBeNull()
|
|
}
|
|
// Why: `text` is the interactive TUI mode Orca hosts.
|
|
expect(recognizeAgentProcessFromCommandLine('prime-agent --mode text')).toEqual({
|
|
agent: 'prime-agent',
|
|
processName: 'prime-agent'
|
|
})
|
|
// Why: the CLI only parses `--mode <value>` as separate tokens, so `--mode=json`
|
|
// is ignored by it and still starts the interactive mode.
|
|
expect(recognizeAgentProcessFromCommandLine('prime-agent --mode=json')).toEqual({
|
|
agent: 'prime-agent',
|
|
processName: 'prime-agent'
|
|
})
|
|
expect(recognizeAgentProcessFromCommandLine('prime-agent -- --mode rpc')).toEqual({
|
|
agent: 'prime-agent',
|
|
processName: 'prime-agent'
|
|
})
|
|
})
|
|
|
|
it('recognizes only the agent subcommand of the generic Orca CLI', () => {
|
|
expect(recognizeAgentProcessFromCommandLine('orca claude-teams')).toEqual({
|
|
agent: 'claude-agent-teams',
|
|
processName: 'orca'
|
|
})
|
|
expect(recognizeAgentProcessFromCommandLine('orca status')).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('orca-dev terminal list')).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('node /usr/local/bin/orca claude-teams')).toEqual({
|
|
agent: 'claude-agent-teams',
|
|
processName: 'orca'
|
|
})
|
|
expect(recognizeAgentProcessFromCommandLine('node /usr/local/bin/orca status')).toBeNull()
|
|
})
|
|
|
|
it('recognizes the versioned Cursor Node wrapper without accepting generic agent processes', () => {
|
|
const cursorEntrypoint = String.raw`C:\Users\dev\AppData\Local\cursor-agent\versions\2026.07.09-a3815c0\index.js`
|
|
|
|
expect(recognizeAgentProcessFromCommandLine(`node.exe ${cursorEntrypoint}`)).toEqual({
|
|
agent: 'cursor',
|
|
processName: 'cursor-agent'
|
|
})
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(`node.exe ${cursorEntrypoint} worker-server`)
|
|
).toEqual({ agent: 'cursor', processName: 'cursor-agent' })
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(String.raw`node.exe C:\repo\cursor-agent\index.js`)
|
|
).toBeNull()
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(String.raw`C:\Users\dev\.grok\bin\agent.exe`)
|
|
).toBeNull()
|
|
})
|
|
|
|
it('does not classify prompt text as a wrapped agent command', () => {
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
'node /tmp/not-an-agent.js "compare opencode vs orca in Gemini CLI"'
|
|
)
|
|
).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine(String.raw`node C:\tmp\not-an-agent.js`)).toBeNull()
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`node C:\repo\server.js --plugin C:\tmp\codex.js`
|
|
)
|
|
).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine(String.raw`node C:\repo\codex.js`)).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine(String.raw`node C:\repo\gemini.mjs`)).toBeNull()
|
|
expect(
|
|
recognizeAgentProcessFromCommandLine(
|
|
String.raw`node C:\repo\node_modules\@example\pi-coding-agent\dist\cli.js`
|
|
)
|
|
).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine(String.raw`python C:\repo\aider.py`)).toBeNull()
|
|
expect(recognizeAgentProcessFromCommandLine('python -m not_aider')).toBeNull()
|
|
})
|
|
|
|
it('identifies only foreground processes that can wrap agent entrypoints', () => {
|
|
expect(isAgentForegroundWrapperProcess('node.exe')).toBe(true)
|
|
expect(isAgentForegroundWrapperProcess('/usr/bin/python3')).toBe(true)
|
|
expect(isAgentForegroundWrapperProcess('python3.12.exe')).toBe(true)
|
|
expect(isAgentForegroundWrapperProcess('bash')).toBe(false)
|
|
expect(isAgentForegroundWrapperProcess('vim.exe')).toBe(false)
|
|
})
|
|
|
|
it('recognizes versioned Grok process names observed from the installed CLI', () => {
|
|
expect(recognizeAgentProcess('grok-0.2.51')).toEqual({
|
|
agent: 'grok',
|
|
processName: 'grok-0.2.51'
|
|
})
|
|
})
|
|
})
|