mirror of
https://github.com/stablyai/orca.git
synced 2026-10-25 00:03:59 +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>
153 lines
5.4 KiB
TypeScript
153 lines
5.4 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import { buildAiVaultResumeCommand } from './ai-vault-resume-command'
|
|
|
|
describe('buildAiVaultResumeCommand', () => {
|
|
it('uses Antigravity conversation ids instead of Gemini resume flags', () => {
|
|
expect(
|
|
buildAiVaultResumeCommand({
|
|
agent: 'antigravity',
|
|
sessionId: 'aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee',
|
|
cwd: '/repo/app',
|
|
platform: 'darwin'
|
|
})
|
|
).toBe("cd '/repo/app' && agy --conversation 'aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee'")
|
|
})
|
|
|
|
it('builds a self-contained cmd wrapper when no live shell is known', () => {
|
|
expect(
|
|
buildAiVaultResumeCommand({
|
|
agent: 'codex',
|
|
sessionId: 'session-1',
|
|
cwd: 'C:\\Users\\Ada Lovelace\\repo',
|
|
platform: 'win32'
|
|
})
|
|
).toBe('cmd /d /s /c "cd /d ""C:\\Users\\Ada Lovelace\\repo"" && codex resume ""session-1"""')
|
|
})
|
|
|
|
it('builds a direct queued command for a live cmd shell', () => {
|
|
expect(
|
|
buildAiVaultResumeCommand({
|
|
agent: 'omp',
|
|
sessionId: 'session-one',
|
|
resumeFilePath: 'C:\\Users\\Ada Lovelace\\.omp\\sessions\\A&B session one.jsonl',
|
|
cwd: 'C:\\Users\\Ada Lovelace\\A&B repo',
|
|
platform: 'win32',
|
|
shell: 'cmd'
|
|
})
|
|
).toBe(
|
|
'cd /d "C:\\Users\\Ada Lovelace\\A&B repo" && omp --resume "C:\\Users\\Ada Lovelace\\.omp\\sessions\\A&B session one.jsonl"'
|
|
)
|
|
})
|
|
|
|
it('emits no CODEX_HOME stamp for real-home canonical sessions', () => {
|
|
// Backfilled sessions dedupe to the real-home row (codexHome null); their
|
|
// resume must run against the user's own ~/.codex, never the frozen
|
|
// managed home whose auth.json stops refreshing after the flip.
|
|
const command = buildAiVaultResumeCommand({
|
|
agent: 'codex',
|
|
sessionId: 'session-1',
|
|
cwd: '/repo/app',
|
|
platform: 'darwin',
|
|
codexHome: null
|
|
})
|
|
expect(command).toBe("cd '/repo/app' && codex resume 'session-1'")
|
|
expect(command).not.toContain('CODEX_HOME')
|
|
})
|
|
|
|
it('carries non-default Codex homes in copied resume commands', () => {
|
|
expect(
|
|
buildAiVaultResumeCommand({
|
|
agent: 'codex',
|
|
sessionId: 'session-1',
|
|
cwd: '/repo/app',
|
|
platform: 'darwin',
|
|
codexHome: '/Users/ada/Library/Application Support/Orca/codex-runtime-home/home'
|
|
})
|
|
).toBe(
|
|
"cd '/repo/app' && CODEX_HOME='/Users/ada/Library/Application Support/Orca/codex-runtime-home/home' codex resume 'session-1'"
|
|
)
|
|
|
|
expect(
|
|
buildAiVaultResumeCommand({
|
|
agent: 'codex',
|
|
sessionId: 'session-1',
|
|
cwd: 'C:\\Users\\Ada Lovelace\\repo',
|
|
platform: 'win32',
|
|
codexHome: 'C:\\Users\\Ada\\AppData\\Roaming\\Orca\\codex-runtime-home\\home'
|
|
})
|
|
).toBe(
|
|
'cmd /d /s /c "cd /d ""C:\\Users\\Ada Lovelace\\repo"" && set ""CODEX_HOME=C:\\Users\\Ada\\AppData\\Roaming\\Orca\\codex-runtime-home\\home"" && codex resume ""session-1"""'
|
|
)
|
|
})
|
|
|
|
it('resumes OMP by absolute transcript path so it resolves across session-dir roots', () => {
|
|
expect(
|
|
buildAiVaultResumeCommand({
|
|
agent: 'omp',
|
|
sessionId: '019f27cd-4268-7000-96e7-62f42a55c144',
|
|
resumeFilePath:
|
|
'/Users/ada/.omp/agent/sessions/repo/2026-07-03T11-30-29-357Z_019f27be/OmpScannerTests.jsonl',
|
|
cwd: '/Users/ada/repo',
|
|
platform: 'darwin'
|
|
})
|
|
).toBe(
|
|
"cd '/Users/ada/repo' && omp --resume '/Users/ada/.omp/agent/sessions/repo/2026-07-03T11-30-29-357Z_019f27be/OmpScannerTests.jsonl'"
|
|
)
|
|
})
|
|
|
|
it('quotes queued OMP resume paths for the provided Windows shell', () => {
|
|
expect(
|
|
buildAiVaultResumeCommand({
|
|
agent: 'omp',
|
|
sessionId: '019f27cd-4268-7000-96e7-62f42a55c144',
|
|
resumeFilePath: 'C:\\Users\\Ada Lovelace\\.omp\\agent\\sessions\\repo\\sess.jsonl',
|
|
cwd: 'C:\\Users\\Ada Lovelace\\repo',
|
|
platform: 'win32',
|
|
shell: 'powershell'
|
|
})
|
|
).toBe(
|
|
"Set-Location -LiteralPath 'C:\\Users\\Ada Lovelace\\repo'; omp --resume 'C:\\Users\\Ada Lovelace\\.omp\\agent\\sessions\\repo\\sess.jsonl'"
|
|
)
|
|
})
|
|
|
|
it('falls back to the session id when no OMP transcript path is known', () => {
|
|
expect(
|
|
buildAiVaultResumeCommand({
|
|
agent: 'omp',
|
|
sessionId: '019f27cd-4268-7000-96e7-62f42a55c144',
|
|
resumeFilePath: null,
|
|
cwd: '/Users/ada/repo',
|
|
platform: 'darwin'
|
|
})
|
|
).toBe("cd '/Users/ada/repo' && omp --resume '019f27cd-4268-7000-96e7-62f42a55c144'")
|
|
})
|
|
|
|
it('resumes Prime Agent by absolute transcript path like OMP', () => {
|
|
expect(
|
|
buildAiVaultResumeCommand({
|
|
agent: 'prime-agent',
|
|
sessionId: 'dddddddd-eeee-4fff-8aaa-111111111111',
|
|
resumeFilePath:
|
|
'/Users/ada/.prime/agent/sessions/dddddddd-eeee-4fff-8aaa-111111111111.jsonl',
|
|
cwd: '/Users/ada/repo',
|
|
platform: 'darwin'
|
|
})
|
|
).toBe(
|
|
"cd '/Users/ada/repo' && prime-agent --resume '/Users/ada/.prime/agent/sessions/dddddddd-eeee-4fff-8aaa-111111111111.jsonl'"
|
|
)
|
|
})
|
|
|
|
it('falls back to the session id when no Prime Agent transcript path is known', () => {
|
|
expect(
|
|
buildAiVaultResumeCommand({
|
|
agent: 'prime-agent',
|
|
sessionId: 'dddddddd-eeee-4fff-8aaa-111111111111',
|
|
resumeFilePath: null,
|
|
cwd: '/Users/ada/repo',
|
|
platform: 'darwin'
|
|
})
|
|
).toBe("cd '/Users/ada/repo' && prime-agent --resume 'dddddddd-eeee-4fff-8aaa-111111111111'")
|
|
})
|
|
})
|