@phnx-labs/agents-cli 1.19.2 → 1.20.0
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/CHANGELOG.md +67 -0
- package/README.md +69 -9
- package/dist/browser.js +0 -0
- package/dist/commands/browser.js +88 -16
- package/dist/commands/cli.d.ts +14 -0
- package/dist/commands/cli.js +244 -0
- package/dist/commands/commands.js +3 -3
- package/dist/commands/computer.js +18 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/exec.js +3 -3
- package/dist/commands/factory.d.ts +3 -14
- package/dist/commands/factory.js +3 -3
- package/dist/commands/hooks.js +3 -3
- package/dist/commands/plugins.js +11 -4
- package/dist/commands/profiles.js +1 -1
- package/dist/commands/prune.js +39 -160
- package/dist/commands/pull.js +56 -3
- package/dist/commands/routines.js +106 -13
- package/dist/commands/secrets.js +5 -7
- package/dist/commands/sessions.d.ts +28 -0
- package/dist/commands/sessions.js +98 -33
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +37 -28
- package/dist/commands/skills.js +3 -3
- package/dist/commands/teams.js +13 -0
- package/dist/commands/versions.d.ts +4 -3
- package/dist/commands/versions.js +131 -127
- package/dist/commands/view.js +12 -12
- package/dist/computer.js +0 -0
- package/dist/index.js +34 -6
- package/dist/lib/acp/harnesses.js +8 -0
- package/dist/lib/agents.js +110 -23
- package/dist/lib/browser/cdp.d.ts +8 -1
- package/dist/lib/browser/cdp.js +40 -3
- package/dist/lib/browser/chrome.d.ts +13 -0
- package/dist/lib/browser/chrome.js +42 -3
- package/dist/lib/browser/domain-skills.d.ts +51 -0
- package/dist/lib/browser/domain-skills.js +157 -0
- package/dist/lib/browser/drivers/local.js +45 -4
- package/dist/lib/browser/drivers/ssh.js +1 -1
- package/dist/lib/browser/ipc.d.ts +8 -1
- package/dist/lib/browser/ipc.js +37 -28
- package/dist/lib/browser/profiles.d.ts +13 -0
- package/dist/lib/browser/profiles.js +41 -1
- package/dist/lib/browser/service.d.ts +3 -0
- package/dist/lib/browser/service.js +21 -5
- package/dist/lib/browser/types.d.ts +7 -0
- package/dist/lib/cli-resources.d.ts +109 -0
- package/dist/lib/cli-resources.js +255 -0
- package/dist/lib/cloud/rush.js +5 -5
- package/dist/lib/command-skills.js +0 -2
- package/dist/lib/computer-rpc.d.ts +3 -0
- package/dist/lib/computer-rpc.js +53 -0
- package/dist/lib/daemon.js +20 -0
- package/dist/lib/exec.d.ts +3 -2
- package/dist/lib/exec.js +44 -9
- package/dist/lib/hooks.js +182 -0
- package/dist/lib/mcp.js +6 -0
- package/dist/lib/migrate.js +1 -1
- package/dist/lib/overdue.d.ts +26 -0
- package/dist/lib/overdue.js +101 -0
- package/dist/lib/permissions.js +5 -1
- package/dist/lib/plugin-marketplace.js +1 -1
- package/dist/lib/profiles-presets.js +37 -0
- package/dist/lib/resources/mcp.js +37 -0
- package/dist/lib/resources.d.ts +1 -1
- package/dist/lib/rotate.js +10 -4
- package/dist/lib/routines-format.d.ts +35 -0
- package/dist/lib/routines-format.js +173 -0
- package/dist/lib/routines.d.ts +7 -1
- package/dist/lib/routines.js +32 -12
- package/dist/lib/runner.js +19 -5
- package/dist/lib/scheduler.js +8 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/bundles.d.ts +22 -1
- package/dist/lib/secrets/bundles.js +234 -36
- package/dist/lib/secrets/index.d.ts +6 -11
- package/dist/lib/secrets/index.js +107 -87
- package/dist/lib/session/active.d.ts +8 -0
- package/dist/lib/session/active.js +3 -2
- package/dist/lib/session/db.d.ts +0 -4
- package/dist/lib/session/db.js +0 -26
- package/dist/lib/session/parse.d.ts +1 -0
- package/dist/lib/session/parse.js +44 -0
- package/dist/lib/session/types.d.ts +1 -1
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/shims.d.ts +1 -1
- package/dist/lib/shims.js +66 -4
- package/dist/lib/state.d.ts +0 -1
- package/dist/lib/state.js +2 -15
- package/dist/lib/teams/agents.js +1 -1
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/parsers.js +153 -3
- package/dist/lib/teams/summarizer.js +18 -2
- package/dist/lib/teams/worktree.js +14 -3
- package/dist/lib/types.d.ts +6 -3
- package/dist/lib/types.js +6 -3
- package/dist/lib/versions.d.ts +10 -2
- package/dist/lib/versions.js +227 -35
- package/package.json +7 -7
- package/npm-shrinkwrap.json +0 -3162
|
@@ -73,11 +73,11 @@ function readLiveTerminals() {
|
|
|
73
73
|
if (!parsed || typeof parsed !== 'object')
|
|
74
74
|
return [];
|
|
75
75
|
const merged = new Map();
|
|
76
|
-
for (const slice of Object.
|
|
76
|
+
for (const [windowId, slice] of Object.entries(parsed)) {
|
|
77
77
|
for (const e of (slice?.entries ?? [])) {
|
|
78
78
|
if (!e?.sessionId || !isPidAlive(e.pid))
|
|
79
79
|
continue;
|
|
80
|
-
merged.set(e.sessionId, e);
|
|
80
|
+
merged.set(e.sessionId, { ...e, windowId });
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
return Array.from(merged.values());
|
|
@@ -259,6 +259,7 @@ export async function listTerminalsActive() {
|
|
|
259
259
|
sessionFile,
|
|
260
260
|
startedAtMs: t.startedAtMs,
|
|
261
261
|
status: classifyActivity(sessionFile),
|
|
262
|
+
windowId: t.windowId,
|
|
262
263
|
};
|
|
263
264
|
});
|
|
264
265
|
}
|
package/dist/lib/session/db.d.ts
CHANGED
|
@@ -155,8 +155,6 @@ export declare function getRowCount(): {
|
|
|
155
155
|
sessions: number;
|
|
156
156
|
textRows: number;
|
|
157
157
|
};
|
|
158
|
-
/** Count sessions older than the given timestamp (for dry-run previews). */
|
|
159
|
-
export declare function countSessionsOlderThan(cutoffMs: number): number;
|
|
160
158
|
/**
|
|
161
159
|
* Rewrite file_path for all sessions whose path starts with oldPrefix, replacing
|
|
162
160
|
* it with newPrefix + the unchanged suffix. Also clears the matching scan_ledger
|
|
@@ -167,5 +165,3 @@ export declare function countSessionsOlderThan(cutoffMs: number): number;
|
|
|
167
165
|
* Returns the number of session rows updated.
|
|
168
166
|
*/
|
|
169
167
|
export declare function updateSessionFilePaths(oldPrefix: string, newPrefix: string): number;
|
|
170
|
-
/** Delete sessions older than the given timestamp. Returns the number of rows deleted. */
|
|
171
|
-
export declare function deleteSessionsOlderThan(cutoffMs: number): number;
|
package/dist/lib/session/db.js
CHANGED
|
@@ -742,13 +742,6 @@ export function getRowCount() {
|
|
|
742
742
|
const textRows = db.prepare(`SELECT COUNT(*) AS c FROM session_text`).get().c;
|
|
743
743
|
return { sessions, textRows };
|
|
744
744
|
}
|
|
745
|
-
/** Count sessions older than the given timestamp (for dry-run previews). */
|
|
746
|
-
export function countSessionsOlderThan(cutoffMs) {
|
|
747
|
-
const db = getDB();
|
|
748
|
-
const cutoffIso = new Date(cutoffMs).toISOString();
|
|
749
|
-
const row = db.prepare(`SELECT COUNT(*) AS n FROM sessions WHERE timestamp < ?`).get(cutoffIso);
|
|
750
|
-
return row.n;
|
|
751
|
-
}
|
|
752
745
|
/**
|
|
753
746
|
* Rewrite file_path for all sessions whose path starts with oldPrefix, replacing
|
|
754
747
|
* it with newPrefix + the unchanged suffix. Also clears the matching scan_ledger
|
|
@@ -775,22 +768,3 @@ export function updateSessionFilePaths(oldPrefix, newPrefix) {
|
|
|
775
768
|
txn();
|
|
776
769
|
return rows.length;
|
|
777
770
|
}
|
|
778
|
-
/** Delete sessions older than the given timestamp. Returns the number of rows deleted. */
|
|
779
|
-
export function deleteSessionsOlderThan(cutoffMs) {
|
|
780
|
-
const db = getDB();
|
|
781
|
-
const cutoffIso = new Date(cutoffMs).toISOString();
|
|
782
|
-
const rows = db.prepare(`SELECT id, file_path FROM sessions WHERE timestamp < ?`).all(cutoffIso);
|
|
783
|
-
if (rows.length === 0)
|
|
784
|
-
return 0;
|
|
785
|
-
const txn = db.transaction(() => {
|
|
786
|
-
for (const { id, file_path } of rows) {
|
|
787
|
-
db.prepare(`DELETE FROM session_text WHERE session_id = ?`).run(id);
|
|
788
|
-
db.prepare(`DELETE FROM sessions WHERE id = ?`).run(id);
|
|
789
|
-
if (file_path) {
|
|
790
|
-
db.prepare(`DELETE FROM scan_ledger WHERE file_path = ?`).run(canonicalLedgerKey(file_path));
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
});
|
|
794
|
-
txn();
|
|
795
|
-
return rows.length;
|
|
796
|
-
}
|
|
@@ -43,6 +43,7 @@ export declare function parseGemini(filePath: string): SessionEvent[];
|
|
|
43
43
|
* Messages have role (user/assistant) and metadata.
|
|
44
44
|
* Parts contain the actual content: text, tool, reasoning, patch, step-start/finish.
|
|
45
45
|
*/
|
|
46
|
+
export declare function parseGrok(filePath: string): SessionEvent[];
|
|
46
47
|
export declare function parseOpenCode(filePath: string): SessionEvent[];
|
|
47
48
|
/** Parse a Rush JSONL session file into normalized events. */
|
|
48
49
|
export declare function parseRush(filePath: string): SessionEvent[];
|
|
@@ -93,6 +93,9 @@ export function parseSession(filePath, agent) {
|
|
|
93
93
|
case 'opencode':
|
|
94
94
|
events = parseOpenCode(filePath);
|
|
95
95
|
break;
|
|
96
|
+
case 'grok':
|
|
97
|
+
events = parseGrok(filePath);
|
|
98
|
+
break;
|
|
96
99
|
case 'rush':
|
|
97
100
|
events = parseRush(filePath);
|
|
98
101
|
break;
|
|
@@ -118,6 +121,8 @@ export function detectAgent(filePath) {
|
|
|
118
121
|
return 'codex';
|
|
119
122
|
if (filePath.includes('/.gemini/') || filePath.includes('\\.gemini\\'))
|
|
120
123
|
return 'gemini';
|
|
124
|
+
if (filePath.includes('/.grok/') || filePath.includes('\\.grok\\'))
|
|
125
|
+
return 'grok';
|
|
121
126
|
if (filePath.includes('/.rush/') || filePath.includes('\\.rush\\'))
|
|
122
127
|
return 'rush';
|
|
123
128
|
if (filePath.includes('/.hermes/') || filePath.includes('\\.hermes\\'))
|
|
@@ -645,6 +650,45 @@ function extractGeminiContent(content) {
|
|
|
645
650
|
* Messages have role (user/assistant) and metadata.
|
|
646
651
|
* Parts contain the actual content: text, tool, reasoning, patch, step-start/finish.
|
|
647
652
|
*/
|
|
653
|
+
export function parseGrok(filePath) {
|
|
654
|
+
// Grok sessions are rich (summary.json + events.jsonl + chat_history.jsonl + updates.jsonl)
|
|
655
|
+
// This is a minimal stub for now so grok appears in `agents sessions`.
|
|
656
|
+
// Full parser (with subagents, tool calls, etc.) can be expanded later.
|
|
657
|
+
try {
|
|
658
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
659
|
+
// If it's a summary.json, create a basic event
|
|
660
|
+
if (filePath.endsWith('summary.json')) {
|
|
661
|
+
const summary = JSON.parse(content);
|
|
662
|
+
return [{
|
|
663
|
+
timestamp: summary.created_at || new Date().toISOString(),
|
|
664
|
+
type: 'session_start',
|
|
665
|
+
content: summary.session_summary || 'Grok session',
|
|
666
|
+
agent: 'grok',
|
|
667
|
+
metadata: { sessionId: summary.id, cwd: summary.cwd },
|
|
668
|
+
}];
|
|
669
|
+
}
|
|
670
|
+
// For JSONL files (events, chat_history, updates), return basic parsed lines
|
|
671
|
+
if (filePath.endsWith('.jsonl')) {
|
|
672
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
673
|
+
return lines.slice(0, 50).map((line, i) => {
|
|
674
|
+
try {
|
|
675
|
+
const obj = JSON.parse(line);
|
|
676
|
+
return {
|
|
677
|
+
timestamp: obj.timestamp || obj.ts || new Date().toISOString(),
|
|
678
|
+
type: obj.type || obj.method || 'grok_event',
|
|
679
|
+
content: typeof obj.content === 'string' ? obj.content : JSON.stringify(obj).slice(0, 200),
|
|
680
|
+
agent: 'grok',
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
catch {
|
|
684
|
+
return { timestamp: new Date().toISOString(), type: 'raw', content: line.slice(0, 200), agent: 'grok' };
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
catch { }
|
|
690
|
+
return [];
|
|
691
|
+
}
|
|
648
692
|
export function parseOpenCode(filePath) {
|
|
649
693
|
const [dbPath, sessionId] = filePath.split('#');
|
|
650
694
|
if (!dbPath || !sessionId)
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* speaks these types.
|
|
8
8
|
*/
|
|
9
9
|
/** Agents that store session data on disk and can be discovered by `agents sessions`. */
|
|
10
|
-
export type SessionAgentId = 'claude' | 'codex' | 'gemini' | 'opencode' | 'openclaw' | 'rush' | 'hermes';
|
|
10
|
+
export type SessionAgentId = 'claude' | 'codex' | 'gemini' | 'opencode' | 'openclaw' | 'rush' | 'hermes' | 'grok';
|
|
11
11
|
/** All agents with session discovery support, in display order. */
|
|
12
12
|
export declare const SESSION_AGENTS: SessionAgentId[];
|
|
13
13
|
/** A single normalized event within a session (message, tool call, thinking, etc.). */
|
|
@@ -7,4 +7,4 @@
|
|
|
7
7
|
* speaks these types.
|
|
8
8
|
*/
|
|
9
9
|
/** All agents with session discovery support, in display order. */
|
|
10
|
-
export const SESSION_AGENTS = ['claude', 'codex', 'gemini', 'opencode', 'openclaw', 'rush', 'hermes'];
|
|
10
|
+
export const SESSION_AGENTS = ['claude', 'codex', 'gemini', 'opencode', 'openclaw', 'rush', 'hermes', 'grok'];
|
package/dist/lib/shims.d.ts
CHANGED
|
@@ -213,7 +213,7 @@ export declare function getPathShadowingExecutable(agent: AgentId): string | nul
|
|
|
213
213
|
* Delete the legacy ~/.agents/shims/<cli> file if it exists, returning whether
|
|
214
214
|
* anything was removed. Pre-split installs put shims under ~/.agents/shims/;
|
|
215
215
|
* the new layout uses ~/.agents-system/shims/. The leftover file causes the
|
|
216
|
-
* repair-prompt loop reported in
|
|
216
|
+
* repair-prompt loop reported in PROJ-789 — `getPathShadowingExecutable` flags
|
|
217
217
|
* it as a shadow but `addShimsToPath` only edits rc files, never the file
|
|
218
218
|
* itself. Removing it ends the loop.
|
|
219
219
|
*/
|
package/dist/lib/shims.js
CHANGED
|
@@ -226,7 +226,22 @@ fi
|
|
|
226
226
|
# written by agents-cli actually take effect.
|
|
227
227
|
export CODEX_HOME="$VERSION_DIR/home/${configDirName}"
|
|
228
228
|
`
|
|
229
|
-
: ''
|
|
229
|
+
: agent === 'copilot'
|
|
230
|
+
? `
|
|
231
|
+
# GitHub Copilot CLI honors COPILOT_HOME to relocate its config and state
|
|
232
|
+
# (settings.json, mcp-config.json, session-state/, logs/, plugins/). Point
|
|
233
|
+
# it at the versioned home so MCP servers, custom agents, and session
|
|
234
|
+
# history are isolated per copilot version.
|
|
235
|
+
export COPILOT_HOME="$VERSION_DIR/home/${configDirName}"
|
|
236
|
+
`
|
|
237
|
+
: agent === 'grok'
|
|
238
|
+
? `
|
|
239
|
+
# Grok Build uses GROK_HOME to isolate its entire configuration tree
|
|
240
|
+
# (skills, hooks, plugins, agents, memory, sessions, config.toml, MCP, etc.).
|
|
241
|
+
# This gives agents-cli full versioned isolation + resource sync for grok.
|
|
242
|
+
export GROK_HOME="$VERSION_DIR/home/.grok"
|
|
243
|
+
`
|
|
244
|
+
: '';
|
|
230
245
|
// Agents that don't natively resolve @-imports in their rules file need
|
|
231
246
|
// agents-cli to recompile when the user edits a rule/preset file. The
|
|
232
247
|
// check is fast (sha256 of ~8 small files) and skips the recompile when
|
|
@@ -376,7 +391,27 @@ if [[ ! "$VERSION" =~ ^(latest|[A-Za-z0-9._+-]{1,64})$ || "$VERSION" == *..* ]];
|
|
|
376
391
|
fi
|
|
377
392
|
|
|
378
393
|
VERSION_DIR="$AGENTS_USER_DIR/.history/versions/$AGENT/$VERSION"
|
|
379
|
-
|
|
394
|
+
|
|
395
|
+
# Grok special case: binary lives in ~/.grok/downloads/, not node_modules.
|
|
396
|
+
# We still use the agents-cli version dir purely for GROK_HOME isolation.
|
|
397
|
+
if [ "$AGENT" = "grok" ]; then
|
|
398
|
+
# Try to find a matching binary for the pinned version in the global grok downloads dir.
|
|
399
|
+
GROK_DOWNLOADS="$HOME/.grok/downloads"
|
|
400
|
+
if [ -d "$GROK_DOWNLOADS" ]; then
|
|
401
|
+
# Prefer a binary whose filename contains the exact version
|
|
402
|
+
BINARY=$(ls "$GROK_DOWNLOADS"/grok-* 2>/dev/null | grep -i "$VERSION" | head -1)
|
|
403
|
+
if [ -z "$BINARY" ]; then
|
|
404
|
+
# Fallback to the "current" grok binary (symlink or latest)
|
|
405
|
+
BINARY=$(ls "$GROK_DOWNLOADS"/grok-* 2>/dev/null | head -1)
|
|
406
|
+
fi
|
|
407
|
+
fi
|
|
408
|
+
if [ -z "$BINARY" ] || [ ! -x "$BINARY" ]; then
|
|
409
|
+
# Last resort: whatever is on PATH (user may have installed grok globally)
|
|
410
|
+
BINARY=$(command -v grok 2>/dev/null || echo "")
|
|
411
|
+
fi
|
|
412
|
+
else
|
|
413
|
+
BINARY="$VERSION_DIR/node_modules/.bin/$CLI_COMMAND"
|
|
414
|
+
fi
|
|
380
415
|
|
|
381
416
|
# Auto-install if not present
|
|
382
417
|
if [ ! -x "$BINARY" ]; then
|
|
@@ -528,7 +563,14 @@ export CLAUDE_CONFIG_DIR="$HOME/.agents/.history/versions/${agent}/${version}/ho
|
|
|
528
563
|
# and rules written by agents-cli actually take effect.
|
|
529
564
|
export CODEX_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
|
|
530
565
|
`
|
|
531
|
-
: ''
|
|
566
|
+
: agent === 'copilot'
|
|
567
|
+
? `
|
|
568
|
+
# Copilot honors COPILOT_HOME to relocate ~/.copilot (settings, mcp-config.json,
|
|
569
|
+
# session-state, logs). Point direct aliases at the versioned home so per-
|
|
570
|
+
# version MCP and session state are isolated.
|
|
571
|
+
export COPILOT_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
|
|
572
|
+
`
|
|
573
|
+
: '';
|
|
532
574
|
const launchArgs = agent === 'codex' ? ' -c check_for_update_on_startup=false' : '';
|
|
533
575
|
return `#!/bin/bash
|
|
534
576
|
# Auto-generated by agents-cli - do not edit
|
|
@@ -719,6 +761,26 @@ export async function switchConfigSymlink(agent, version) {
|
|
|
719
761
|
// Already pointing to correct target, no-op
|
|
720
762
|
return { success: true };
|
|
721
763
|
}
|
|
764
|
+
// openclaw mixes user data (openclaw.json, openclaw.db, per-agent
|
|
765
|
+
// workspaces under ~/.openclaw/{agentId}/, memory/) with the version
|
|
766
|
+
// home — silently swapping the symlink to a fresh version home strips
|
|
767
|
+
// every running agent's config + workspace + memory. Carry the user
|
|
768
|
+
// data forward into the new version home before flipping the symlink
|
|
769
|
+
// (keep-dest preserves anything the new version already shipped).
|
|
770
|
+
// Other agents (Claude, Codex, etc.) keep user data outside the
|
|
771
|
+
// version-home dir, so this is openclaw-only by design.
|
|
772
|
+
if (agent === 'openclaw') {
|
|
773
|
+
try {
|
|
774
|
+
if (fs.existsSync(resolvedCurrent) && fs.statSync(resolvedCurrent).isDirectory()) {
|
|
775
|
+
await copyDirContents(resolvedCurrent, versionConfigPath, 'keep-dest');
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
catch (migrationErr) {
|
|
779
|
+
console.error(`Warning: openclaw data migration from ${resolvedCurrent} -> ${versionConfigPath} ` +
|
|
780
|
+
`failed: ${migrationErr.message}. The previous version's data is intact ` +
|
|
781
|
+
`at the old path; you can copy it manually if needed.`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
722
784
|
// Different target - update it
|
|
723
785
|
fs.unlinkSync(configPath);
|
|
724
786
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
@@ -1182,7 +1244,7 @@ export function getPathShadowingExecutable(agent) {
|
|
|
1182
1244
|
* Delete the legacy ~/.agents/shims/<cli> file if it exists, returning whether
|
|
1183
1245
|
* anything was removed. Pre-split installs put shims under ~/.agents/shims/;
|
|
1184
1246
|
* the new layout uses ~/.agents-system/shims/. The leftover file causes the
|
|
1185
|
-
* repair-prompt loop reported in
|
|
1247
|
+
* repair-prompt loop reported in PROJ-789 — `getPathShadowingExecutable` flags
|
|
1186
1248
|
* it as a shadow but `addShimsToPath` only edits rc files, never the file
|
|
1187
1249
|
* itself. Removing it ends the loop.
|
|
1188
1250
|
*/
|
package/dist/lib/state.d.ts
CHANGED
|
@@ -212,7 +212,6 @@ export declare function recordVersionResources(_agent: AgentId, _version: string
|
|
|
212
212
|
*/
|
|
213
213
|
export declare function ensureVersionResourcePatterns(agent: AgentId, version: string, updates: Partial<Record<Exclude<keyof VersionResources, 'rulesPreset'>, ResourcePattern[]>>): void;
|
|
214
214
|
export declare function getVersionResources(agent: AgentId, version: string): VersionResources | null;
|
|
215
|
-
export declare function clearVersionResources(agent: AgentId, version: string): void;
|
|
216
215
|
/** Active rules preset for an agent@version. Defaults to "default" when unset. */
|
|
217
216
|
export declare function getActiveRulesPreset(agent: AgentId, version: string): string;
|
|
218
217
|
/** Persist the active rules preset for an agent@version. */
|
package/dist/lib/state.js
CHANGED
|
@@ -74,7 +74,7 @@ const DRIVE_DIR = path.join(CACHE_DIR, 'drive');
|
|
|
74
74
|
const TERMINALS_DIR = path.join(CACHE_DIR, 'terminals');
|
|
75
75
|
const LOGS_DIR = path.join(CACHE_DIR, 'logs');
|
|
76
76
|
const RUNTIME_STATE_DIR = path.join(CACHE_DIR, 'state');
|
|
77
|
-
const
|
|
77
|
+
const COMPANION_CACHE_DIR = path.join(CACHE_DIR, 'companion');
|
|
78
78
|
const BROWSER_RUNTIME_DIR = path.join(CACHE_DIR, 'browser');
|
|
79
79
|
const HELPERS_DIR = path.join(CACHE_DIR, 'helpers');
|
|
80
80
|
const DAEMON_DIR = path.join(HELPERS_DIR, 'daemon');
|
|
@@ -294,7 +294,7 @@ export function getLogsDir() { return LOGS_DIR; }
|
|
|
294
294
|
/** Path to per-process runtime state (~/.agents/.cache/state/). */
|
|
295
295
|
export function getRuntimeStateDir() { return RUNTIME_STATE_DIR; }
|
|
296
296
|
/** Path to companion-extension scratch (~/.agents/.cache/companion/). */
|
|
297
|
-
export function getCompanionDir() { return
|
|
297
|
+
export function getCompanionDir() { return COMPANION_CACHE_DIR; }
|
|
298
298
|
/** Path to browser runtime data — chrome-data, pids (~/.agents/.cache/browser/). */
|
|
299
299
|
export function getBrowserRuntimeDir() { return BROWSER_RUNTIME_DIR; }
|
|
300
300
|
/** Path to helper subprocess scratch (~/.agents/.cache/helpers/). */
|
|
@@ -626,19 +626,6 @@ export function getVersionResources(agent, version) {
|
|
|
626
626
|
const meta = readMeta();
|
|
627
627
|
return meta.versions?.[agent]?.[version] || null;
|
|
628
628
|
}
|
|
629
|
-
export function clearVersionResources(agent, version) {
|
|
630
|
-
const meta = readMeta();
|
|
631
|
-
if (meta.versions?.[agent]?.[version]) {
|
|
632
|
-
delete meta.versions[agent][version];
|
|
633
|
-
if (Object.keys(meta.versions[agent]).length === 0) {
|
|
634
|
-
delete meta.versions[agent];
|
|
635
|
-
}
|
|
636
|
-
if (Object.keys(meta.versions).length === 0) {
|
|
637
|
-
delete meta.versions;
|
|
638
|
-
}
|
|
639
|
-
writeMeta(meta);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
629
|
/** Active rules preset for an agent@version. Defaults to "default" when unset. */
|
|
643
630
|
export function getActiveRulesPreset(agent, version) {
|
|
644
631
|
const meta = readMeta();
|
package/dist/lib/teams/agents.js
CHANGED
|
@@ -171,7 +171,7 @@ export function captureProcessStartTime(pid) {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
/** Agent types the team runner supports. */
|
|
174
|
-
const TEAM_AGENT_TYPES = ['codex', 'cursor', 'gemini', 'claude', 'opencode'];
|
|
174
|
+
const TEAM_AGENT_TYPES = ['codex', 'cursor', 'gemini', 'claude', 'opencode', 'grok', 'antigravity'];
|
|
175
175
|
// Suffix appended to all prompts to ensure agents provide a summary
|
|
176
176
|
const PROMPT_SUFFIX = `
|
|
177
177
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Supported agent CLI types for team spawning. */
|
|
2
|
-
export type AgentType = 'codex' | 'gemini' | 'cursor' | 'claude' | 'opencode';
|
|
2
|
+
export type AgentType = 'codex' | 'gemini' | 'cursor' | 'claude' | 'opencode' | 'grok' | 'antigravity';
|
|
3
3
|
/** Normalize a raw JSON event from any agent type into an array of unified event objects. */
|
|
4
4
|
export declare function normalizeEvents(agentType: AgentType, raw: any): any[];
|
|
5
5
|
/** Normalize a raw JSON event, returning only the first unified event (convenience wrapper). */
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Agent event stream parsers.
|
|
3
3
|
*
|
|
4
4
|
* Normalizes the heterogeneous JSON event formats emitted by each agent CLI
|
|
5
|
-
* (Claude, Codex, Gemini, Cursor, OpenCode) into a unified
|
|
6
|
-
* with consistent types: init, message, tool_use, bash,
|
|
7
|
-
* file_create, file_delete, result, error, and others.
|
|
5
|
+
* (Claude, Codex, Gemini, Cursor, OpenCode, Grok, Antigravity) into a unified
|
|
6
|
+
* event schema with consistent types: init, message, tool_use, bash,
|
|
7
|
+
* file_read, file_write, file_create, file_delete, result, error, and others.
|
|
8
8
|
*/
|
|
9
9
|
import { extractFileOpsFromBash } from './file_ops.js';
|
|
10
10
|
const claudeToolUseMap = new Map();
|
|
@@ -25,6 +25,12 @@ export function normalizeEvents(agentType, raw) {
|
|
|
25
25
|
else if (agentType === 'opencode') {
|
|
26
26
|
return normalizeOpencode(raw);
|
|
27
27
|
}
|
|
28
|
+
else if (agentType === 'grok') {
|
|
29
|
+
return normalizeGrok(raw);
|
|
30
|
+
}
|
|
31
|
+
else if (agentType === 'antigravity') {
|
|
32
|
+
return normalizeAntigravity(raw);
|
|
33
|
+
}
|
|
28
34
|
const timestamp = new Date().toISOString();
|
|
29
35
|
return [{
|
|
30
36
|
type: raw.type || 'unknown',
|
|
@@ -824,6 +830,150 @@ function normalizeOpencode(raw) {
|
|
|
824
830
|
timestamp: timestamp,
|
|
825
831
|
}];
|
|
826
832
|
}
|
|
833
|
+
// --- Grok parsing ---
|
|
834
|
+
// Grok's streaming-json mode emits one JSON object per token, with three event
|
|
835
|
+
// types:
|
|
836
|
+
// {"type":"thought","data":"<chunk>"} — reasoning tokens (many, small)
|
|
837
|
+
// {"type":"text","data":"<chunk>"} — visible response tokens (many, small)
|
|
838
|
+
// {"type":"end","stopReason":"EndTurn","sessionId":"<uuid>","requestId":"<uuid>"}
|
|
839
|
+
//
|
|
840
|
+
// Tool calls are NOT exposed as separate events in this format; they appear
|
|
841
|
+
// inside the `thought` text as XML-like markup. Extracting them reliably would
|
|
842
|
+
// require running a streaming XML/markup parser over concatenated thought
|
|
843
|
+
// chunks, which is out of scope for v1. The teams summary will show grok
|
|
844
|
+
// teammates' bash/file ops as empty — known limitation, fixable later by
|
|
845
|
+
// switching to grok's `agent` subcommand (richer event stream) once stable.
|
|
846
|
+
//
|
|
847
|
+
// Tokens are emitted as `message` events with `complete: false` so the
|
|
848
|
+
// summarizer can concatenate them into a final message; `thinking` events are
|
|
849
|
+
// already collapsed by the summarizer's groupAndFlattenEvents pathway.
|
|
850
|
+
function normalizeGrok(raw) {
|
|
851
|
+
if (!raw || typeof raw !== 'object') {
|
|
852
|
+
return [{
|
|
853
|
+
type: 'unknown',
|
|
854
|
+
agent: 'grok',
|
|
855
|
+
raw: raw,
|
|
856
|
+
timestamp: new Date().toISOString(),
|
|
857
|
+
}];
|
|
858
|
+
}
|
|
859
|
+
const eventType = raw.type || 'unknown';
|
|
860
|
+
const timestamp = new Date().toISOString();
|
|
861
|
+
if (eventType === 'thought') {
|
|
862
|
+
const data = typeof raw.data === 'string' ? raw.data : '';
|
|
863
|
+
if (!data)
|
|
864
|
+
return [];
|
|
865
|
+
return [{
|
|
866
|
+
type: 'thinking',
|
|
867
|
+
agent: 'grok',
|
|
868
|
+
content: data,
|
|
869
|
+
timestamp: timestamp,
|
|
870
|
+
}];
|
|
871
|
+
}
|
|
872
|
+
if (eventType === 'text') {
|
|
873
|
+
const data = typeof raw.data === 'string' ? raw.data : '';
|
|
874
|
+
if (!data)
|
|
875
|
+
return [];
|
|
876
|
+
return [{
|
|
877
|
+
type: 'message',
|
|
878
|
+
agent: 'grok',
|
|
879
|
+
content: data,
|
|
880
|
+
complete: false,
|
|
881
|
+
timestamp: timestamp,
|
|
882
|
+
}];
|
|
883
|
+
}
|
|
884
|
+
if (eventType === 'end') {
|
|
885
|
+
const stopReason = typeof raw.stopReason === 'string' ? raw.stopReason : '';
|
|
886
|
+
const status = stopReason === 'EndTurn' || stopReason === 'StopSequence' || stopReason === ''
|
|
887
|
+
? 'success'
|
|
888
|
+
: 'error';
|
|
889
|
+
return [{
|
|
890
|
+
type: 'result',
|
|
891
|
+
agent: 'grok',
|
|
892
|
+
status: status,
|
|
893
|
+
stop_reason: stopReason || null,
|
|
894
|
+
session_id: typeof raw.sessionId === 'string' ? raw.sessionId : null,
|
|
895
|
+
timestamp: timestamp,
|
|
896
|
+
}];
|
|
897
|
+
}
|
|
898
|
+
return [{
|
|
899
|
+
type: eventType,
|
|
900
|
+
agent: 'grok',
|
|
901
|
+
raw: raw,
|
|
902
|
+
timestamp: timestamp,
|
|
903
|
+
}];
|
|
904
|
+
}
|
|
905
|
+
// --- Antigravity parsing ---
|
|
906
|
+
// Intentionally conservative. Antigravity's `agy` binary advertises an
|
|
907
|
+
// `--output-format json` flag in its docs, but the released binary errors with
|
|
908
|
+
// `flags provided but not defined: -output-format` (tracked upstream as
|
|
909
|
+
// google-antigravity/antigravity-cli#7, open as of May 2026). Until JSON
|
|
910
|
+
// streaming stabilizes, this parser treats agy output as a black box:
|
|
911
|
+
// - non-object input (a plain string line, or null/number) becomes a single
|
|
912
|
+
// `message` event with the full content and complete:true so the
|
|
913
|
+
// summarizer captures it without token-level concatenation
|
|
914
|
+
// - objects with a recognizable `type` field (e.g. `init`, `message`,
|
|
915
|
+
// `result`) get a minimal shape-preserving normalization
|
|
916
|
+
// - everything else falls through to the generic unknown-event shape
|
|
917
|
+
// Once agy ships stable streaming JSON, replace this with proper event
|
|
918
|
+
// mapping mirroring normalizeGrok / normalizeClaude.
|
|
919
|
+
function normalizeAntigravity(raw) {
|
|
920
|
+
const timestamp = new Date().toISOString();
|
|
921
|
+
if (typeof raw === 'string') {
|
|
922
|
+
if (!raw)
|
|
923
|
+
return [];
|
|
924
|
+
return [{
|
|
925
|
+
type: 'message',
|
|
926
|
+
agent: 'antigravity',
|
|
927
|
+
content: raw,
|
|
928
|
+
complete: true,
|
|
929
|
+
timestamp: timestamp,
|
|
930
|
+
}];
|
|
931
|
+
}
|
|
932
|
+
if (!raw || typeof raw !== 'object') {
|
|
933
|
+
return [{
|
|
934
|
+
type: 'unknown',
|
|
935
|
+
agent: 'antigravity',
|
|
936
|
+
raw: raw,
|
|
937
|
+
timestamp: timestamp,
|
|
938
|
+
}];
|
|
939
|
+
}
|
|
940
|
+
const eventType = raw.type || 'unknown';
|
|
941
|
+
if (eventType === 'init') {
|
|
942
|
+
return [{
|
|
943
|
+
type: 'init',
|
|
944
|
+
agent: 'antigravity',
|
|
945
|
+
session_id: typeof raw.sessionId === 'string' ? raw.sessionId : null,
|
|
946
|
+
timestamp: timestamp,
|
|
947
|
+
}];
|
|
948
|
+
}
|
|
949
|
+
if (eventType === 'message') {
|
|
950
|
+
const content = typeof raw.content === 'string' ? raw.content : '';
|
|
951
|
+
if (!content)
|
|
952
|
+
return [];
|
|
953
|
+
return [{
|
|
954
|
+
type: 'message',
|
|
955
|
+
agent: 'antigravity',
|
|
956
|
+
content: content,
|
|
957
|
+
complete: raw.complete !== false,
|
|
958
|
+
timestamp: timestamp,
|
|
959
|
+
}];
|
|
960
|
+
}
|
|
961
|
+
if (eventType === 'result') {
|
|
962
|
+
return [{
|
|
963
|
+
type: 'result',
|
|
964
|
+
agent: 'antigravity',
|
|
965
|
+
status: raw.status === 'error' ? 'error' : 'success',
|
|
966
|
+
session_id: typeof raw.sessionId === 'string' ? raw.sessionId : null,
|
|
967
|
+
timestamp: timestamp,
|
|
968
|
+
}];
|
|
969
|
+
}
|
|
970
|
+
return [{
|
|
971
|
+
type: eventType,
|
|
972
|
+
agent: 'antigravity',
|
|
973
|
+
raw: raw,
|
|
974
|
+
timestamp: timestamp,
|
|
975
|
+
}];
|
|
976
|
+
}
|
|
827
977
|
/** Parse a single JSONL line into normalized events. Returns null if the line is not valid JSON. */
|
|
828
978
|
export function parseEvent(agentType, line) {
|
|
829
979
|
try {
|
|
@@ -159,11 +159,18 @@ export function groupAndFlattenEvents(events) {
|
|
|
159
159
|
if (eventType === 'message' || eventType === 'thinking') {
|
|
160
160
|
let count = 1;
|
|
161
161
|
let combinedContent = event.content || '';
|
|
162
|
+
// Streaming token events (complete:false) get concatenated without a
|
|
163
|
+
// separator so tokens reassemble into readable prose. Whole-turn events
|
|
164
|
+
// get joined with newlines so distinct turns/thoughts stay separated.
|
|
165
|
+
const isStreaming = event.complete === false;
|
|
162
166
|
let j = i + 1;
|
|
163
167
|
while (j < events.length && events[j].type === eventType) {
|
|
164
168
|
count++;
|
|
165
169
|
if (events[j].content) {
|
|
166
|
-
|
|
170
|
+
const sep = isStreaming || events[j].complete === false
|
|
171
|
+
? ''
|
|
172
|
+
: (combinedContent ? '\n' : '');
|
|
173
|
+
combinedContent += sep + events[j].content;
|
|
167
174
|
}
|
|
168
175
|
j++;
|
|
169
176
|
}
|
|
@@ -405,7 +412,16 @@ export function summarizeEvents(agentId, agentType, status, events, duration = n
|
|
|
405
412
|
else if (eventType === 'message') {
|
|
406
413
|
const content = event.content || '';
|
|
407
414
|
if (content) {
|
|
408
|
-
|
|
415
|
+
// Streaming token-by-token messages (e.g., grok) arrive as many small
|
|
416
|
+
// `message` events with complete:false; concatenate them so the final
|
|
417
|
+
// turn reads as one message. Whole-turn messages (claude, codex,
|
|
418
|
+
// gemini) keep their complete:true semantics — last one wins.
|
|
419
|
+
if (event.complete === false) {
|
|
420
|
+
summary.finalMessage = (summary.finalMessage || '') + content;
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
summary.finalMessage = content;
|
|
424
|
+
}
|
|
409
425
|
}
|
|
410
426
|
}
|
|
411
427
|
else if (eventType === 'error') {
|
|
@@ -8,7 +8,9 @@ import { execFile } from 'child_process';
|
|
|
8
8
|
import { promisify } from 'util';
|
|
9
9
|
import * as fs from 'fs/promises';
|
|
10
10
|
import * as path from 'path';
|
|
11
|
+
import { safeJoin } from '../paths.js';
|
|
11
12
|
const execFileAsync = promisify(execFile);
|
|
13
|
+
const WORKTREE_NAME_RE = /^[A-Za-z0-9_-]+$/;
|
|
12
14
|
export async function isGitRepo(dir) {
|
|
13
15
|
try {
|
|
14
16
|
await execFileAsync('git', ['rev-parse', '--git-dir'], { cwd: dir });
|
|
@@ -42,8 +44,11 @@ export async function hasUncommittedChanges(worktreePath) {
|
|
|
42
44
|
* @returns The absolute path to the created worktree
|
|
43
45
|
*/
|
|
44
46
|
export async function createWorktree(repoDir, worktreeName) {
|
|
47
|
+
if (!WORKTREE_NAME_RE.test(worktreeName)) {
|
|
48
|
+
throw new Error(`Invalid worktree name: ${worktreeName}`);
|
|
49
|
+
}
|
|
45
50
|
const gitRoot = await getGitRoot(repoDir);
|
|
46
|
-
const worktreePath = path.join(gitRoot, '.agents', 'worktrees', worktreeName);
|
|
51
|
+
const worktreePath = safeJoin(path.join(gitRoot, '.agents', 'worktrees'), worktreeName);
|
|
47
52
|
const branchName = `agents/${worktreeName}`;
|
|
48
53
|
await fs.mkdir(path.dirname(worktreePath), { recursive: true });
|
|
49
54
|
await execFileAsync('git', ['worktree', 'add', '-b', branchName, worktreePath, 'HEAD'], {
|
|
@@ -59,8 +64,11 @@ export async function createWorktree(repoDir, worktreeName) {
|
|
|
59
64
|
* @param deleteBranch - Whether to delete the associated branch
|
|
60
65
|
*/
|
|
61
66
|
export async function removeWorktree(repoDir, worktreeName, deleteBranch = true) {
|
|
67
|
+
if (!WORKTREE_NAME_RE.test(worktreeName)) {
|
|
68
|
+
throw new Error(`Invalid worktree name: ${worktreeName}`);
|
|
69
|
+
}
|
|
62
70
|
const gitRoot = await getGitRoot(repoDir);
|
|
63
|
-
const worktreePath = path.join(gitRoot, '.agents', 'worktrees', worktreeName);
|
|
71
|
+
const worktreePath = safeJoin(path.join(gitRoot, '.agents', 'worktrees'), worktreeName);
|
|
64
72
|
const branchName = `agents/${worktreeName}`;
|
|
65
73
|
try {
|
|
66
74
|
await execFileAsync('git', ['worktree', 'remove', '--force', worktreePath], { cwd: gitRoot });
|
|
@@ -86,7 +94,10 @@ export async function removeWorktree(repoDir, worktreeName, deleteBranch = true)
|
|
|
86
94
|
* Get the worktree path for a given name.
|
|
87
95
|
*/
|
|
88
96
|
export function getWorktreePath(gitRoot, worktreeName) {
|
|
89
|
-
|
|
97
|
+
if (!WORKTREE_NAME_RE.test(worktreeName)) {
|
|
98
|
+
throw new Error(`Invalid worktree name: ${worktreeName}`);
|
|
99
|
+
}
|
|
100
|
+
return safeJoin(path.join(gitRoot, '.agents', 'worktrees'), worktreeName);
|
|
90
101
|
}
|
|
91
102
|
/**
|
|
92
103
|
* Get the branch name for a worktree.
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -203,9 +203,12 @@ export interface RegistryConfig {
|
|
|
203
203
|
/** Built-in registry endpoints shipped with agents-cli. */
|
|
204
204
|
export declare const DEFAULT_REGISTRIES: Record<RegistryType, Record<string, RegistryConfig>>;
|
|
205
205
|
/**
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
206
|
+
* Third-party registries pre-seeded on first install for discoverability.
|
|
207
|
+
*
|
|
208
|
+
* These ship into new users' agents.yaml once, but are not "defaults" — after
|
|
209
|
+
* seeding they behave like any user-added registry (listable, disable-able,
|
|
210
|
+
* removable). Removed users can `agents registry remove <name>` to opt out;
|
|
211
|
+
* once removed they don't come back.
|
|
209
212
|
*/
|
|
210
213
|
export declare const SEEDED_REGISTRIES: Record<RegistryType, Record<string, RegistryConfig>>;
|
|
211
214
|
/** A single installable package within an MCP server entry. */
|
package/dist/lib/types.js
CHANGED
|
@@ -22,9 +22,12 @@ export const DEFAULT_REGISTRIES = {
|
|
|
22
22
|
skill: {},
|
|
23
23
|
};
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* Third-party registries pre-seeded on first install for discoverability.
|
|
26
|
+
*
|
|
27
|
+
* These ship into new users' agents.yaml once, but are not "defaults" — after
|
|
28
|
+
* seeding they behave like any user-added registry (listable, disable-able,
|
|
29
|
+
* removable). Removed users can `agents registry remove <name>` to opt out;
|
|
30
|
+
* once removed they don't come back.
|
|
28
31
|
*/
|
|
29
32
|
export const SEEDED_REGISTRIES = {
|
|
30
33
|
mcp: {},
|