@phnx-labs/agents-cli 1.20.74 → 1.20.76
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 +893 -0
- package/README.md +36 -10
- package/dist/bin/agents +0 -0
- package/dist/commands/apply.js +21 -3
- package/dist/commands/attach.d.ts +10 -0
- package/dist/commands/attach.js +41 -0
- package/dist/commands/computer.js +3 -3
- package/dist/commands/detach-core.d.ts +46 -0
- package/dist/commands/detach-core.js +61 -0
- package/dist/commands/detach.d.ts +5 -0
- package/dist/commands/detach.js +171 -0
- package/dist/commands/doctor.js +18 -0
- package/dist/commands/exec.js +31 -3
- package/dist/commands/feed.d.ts +7 -1
- package/dist/commands/feed.js +47 -4
- package/dist/commands/go.d.ts +9 -2
- package/dist/commands/go.js +16 -6
- package/dist/commands/harness.d.ts +14 -0
- package/dist/commands/harness.js +145 -0
- package/dist/commands/import.js +38 -8
- package/dist/commands/inspect.js +5 -2
- package/dist/commands/profiles.d.ts +20 -0
- package/dist/commands/profiles.js +68 -21
- package/dist/commands/repo.js +14 -0
- package/dist/commands/routines.js +15 -2
- package/dist/commands/secrets.js +44 -21
- package/dist/commands/sessions-browser.js +1 -1
- package/dist/commands/sessions-export.js +19 -5
- package/dist/commands/sessions-migrate.d.ts +29 -0
- package/dist/commands/sessions-migrate.js +596 -0
- package/dist/commands/sessions-picker.js +16 -1
- package/dist/commands/sessions.d.ts +36 -13
- package/dist/commands/sessions.js +210 -40
- package/dist/commands/setup-computer.js +2 -2
- package/dist/commands/ssh.js +71 -35
- package/dist/commands/status.js +10 -2
- package/dist/commands/versions.js +15 -6
- package/dist/commands/view.d.ts +5 -0
- package/dist/commands/view.js +24 -4
- package/dist/commands/watchdog.d.ts +7 -2
- package/dist/commands/watchdog.js +73 -57
- package/dist/index.js +58 -7
- package/dist/lib/activity.d.ts +14 -4
- package/dist/lib/activity.js +17 -5
- package/dist/lib/actor.d.ts +50 -0
- package/dist/lib/actor.js +166 -0
- package/dist/lib/agent-spec/provider.js +2 -1
- package/dist/lib/agent-spec/resolve.js +19 -5
- package/dist/lib/agent-spec/types.d.ts +9 -1
- package/dist/lib/agents.d.ts +21 -0
- package/dist/lib/agents.js +70 -6
- package/dist/lib/cloud/codex.d.ts +2 -0
- package/dist/lib/cloud/codex.js +14 -3
- package/dist/lib/cloud/session-index.d.ts +32 -0
- package/dist/lib/cloud/session-index.js +58 -0
- package/dist/lib/cloud/store.d.ts +7 -0
- package/dist/lib/cloud/store.js +25 -0
- package/dist/lib/config-transfer.js +4 -0
- package/dist/lib/daemon.js +18 -2
- package/dist/lib/devices/connect.d.ts +15 -1
- package/dist/lib/devices/connect.js +15 -1
- package/dist/lib/devices/fleet.d.ts +21 -0
- package/dist/lib/devices/fleet.js +26 -0
- package/dist/lib/devices/health-report.d.ts +38 -0
- package/dist/lib/devices/health-report.js +214 -0
- package/dist/lib/devices/health.js +4 -1
- package/dist/lib/devices/reachability.d.ts +31 -0
- package/dist/lib/devices/reachability.js +40 -0
- package/dist/lib/devices/registry.d.ts +33 -0
- package/dist/lib/devices/registry.js +37 -0
- package/dist/lib/devices/resolve-target.d.ts +15 -27
- package/dist/lib/devices/resolve-target.js +63 -102
- package/dist/lib/devices/sync.d.ts +18 -0
- package/dist/lib/devices/sync.js +23 -1
- package/dist/lib/devices/tailscale.d.ts +3 -0
- package/dist/lib/devices/tailscale.js +1 -0
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/exec.d.ts +66 -0
- package/dist/lib/exec.js +195 -6
- package/dist/lib/feed-post.d.ts +63 -0
- package/dist/lib/feed-post.js +204 -0
- package/dist/lib/fleet/apply.d.ts +32 -2
- package/dist/lib/fleet/apply.js +97 -10
- package/dist/lib/fleet/types.d.ts +11 -0
- package/dist/lib/fs-walk.d.ts +13 -0
- package/dist/lib/fs-walk.js +16 -7
- package/dist/lib/hooks.d.ts +1 -0
- package/dist/lib/hooks.js +156 -0
- package/dist/lib/hosts/dispatch.d.ts +7 -0
- package/dist/lib/hosts/dispatch.js +2 -0
- package/dist/lib/hosts/passthrough.js +2 -0
- package/dist/lib/hosts/reconnect.d.ts +78 -0
- package/dist/lib/hosts/reconnect.js +127 -0
- package/dist/lib/hosts/registry.d.ts +75 -14
- package/dist/lib/hosts/registry.js +205 -30
- package/dist/lib/hosts/remote-cmd.js +1 -0
- package/dist/lib/hosts/run-target.d.ts +13 -5
- package/dist/lib/hosts/run-target.js +26 -10
- package/dist/lib/hosts/session-index.d.ts +18 -1
- package/dist/lib/hosts/session-index.js +37 -2
- package/dist/lib/hosts/session-marker.d.ts +33 -0
- package/dist/lib/hosts/session-marker.js +51 -0
- package/dist/lib/hosts/tasks.d.ts +8 -4
- package/dist/lib/import.d.ts +2 -0
- package/dist/lib/import.js +35 -2
- package/dist/lib/menubar/install-menubar.d.ts +20 -0
- package/dist/lib/menubar/install-menubar.js +45 -1
- package/dist/lib/migrate.d.ts +16 -0
- package/dist/lib/migrate.js +36 -0
- package/dist/lib/models.d.ts +27 -0
- package/dist/lib/models.js +54 -1
- package/dist/lib/overdue.d.ts +7 -2
- package/dist/lib/overdue.js +18 -11
- package/dist/lib/picker.js +4 -1
- package/dist/lib/platform/process.d.ts +17 -0
- package/dist/lib/platform/process.js +70 -0
- package/dist/lib/profiles-presets.js +9 -7
- package/dist/lib/profiles.d.ts +31 -0
- package/dist/lib/profiles.js +70 -0
- package/dist/lib/pty-server.d.ts +2 -10
- package/dist/lib/pty-server.js +4 -38
- package/dist/lib/registry.d.ts +1 -1
- package/dist/lib/registry.js +48 -8
- package/dist/lib/rotate.d.ts +18 -0
- package/dist/lib/rotate.js +28 -0
- package/dist/lib/routines.d.ts +26 -0
- package/dist/lib/routines.js +19 -2
- package/dist/lib/runner.js +104 -7
- package/dist/lib/secrets/agent.d.ts +66 -1
- package/dist/lib/secrets/agent.js +154 -90
- package/dist/lib/secrets/bundles.d.ts +34 -0
- package/dist/lib/secrets/bundles.js +179 -19
- package/dist/lib/secrets/index.d.ts +15 -1
- package/dist/lib/secrets/index.js +21 -3
- package/dist/lib/secrets/rc-hygiene.d.ts +55 -0
- package/dist/lib/secrets/rc-hygiene.js +154 -0
- package/dist/lib/secrets/remote.d.ts +7 -4
- package/dist/lib/secrets/remote.js +7 -4
- package/dist/lib/secrets/sync-commands.d.ts +21 -0
- package/dist/lib/secrets/sync-commands.js +21 -0
- package/dist/lib/self-update.d.ts +23 -2
- package/dist/lib/self-update.js +86 -5
- package/dist/lib/session/active.d.ts +114 -7
- package/dist/lib/session/active.js +323 -52
- package/dist/lib/session/cloud.js +2 -1
- package/dist/lib/session/db.d.ts +48 -0
- package/dist/lib/session/db.js +238 -14
- package/dist/lib/session/detached.d.ts +30 -0
- package/dist/lib/session/detached.js +92 -0
- package/dist/lib/session/discover.d.ts +465 -2
- package/dist/lib/session/discover.js +1648 -468
- package/dist/lib/session/fork.js +2 -1
- package/dist/lib/session/hook-sessions.d.ts +43 -0
- package/dist/lib/session/hook-sessions.js +135 -0
- package/dist/lib/session/linear.d.ts +6 -0
- package/dist/lib/session/linear.js +55 -0
- package/dist/lib/session/migrate-targets.d.ts +65 -0
- package/dist/lib/session/migrate-targets.js +94 -0
- package/dist/lib/session/migrations.d.ts +37 -0
- package/dist/lib/session/migrations.js +60 -0
- package/dist/lib/session/parse.d.ts +18 -0
- package/dist/lib/session/parse.js +130 -30
- package/dist/lib/session/pid-registry.d.ts +34 -2
- package/dist/lib/session/pid-registry.js +49 -2
- package/dist/lib/session/render.d.ts +7 -2
- package/dist/lib/session/render.js +22 -10
- package/dist/lib/session/short-id.d.ts +17 -0
- package/dist/lib/session/short-id.js +20 -0
- package/dist/lib/session/state.js +6 -2
- package/dist/lib/shims.d.ts +1 -1
- package/dist/lib/shims.js +20 -6
- package/dist/lib/sqlite.d.ts +3 -2
- package/dist/lib/sqlite.js +27 -4
- package/dist/lib/staleness/detectors/commands.js +1 -1
- package/dist/lib/staleness/detectors/workflows.js +13 -2
- package/dist/lib/staleness/writers/commands.js +7 -7
- package/dist/lib/staleness/writers/workflows.d.ts +4 -2
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +3 -0
- package/dist/lib/state.d.ts +1 -1
- package/dist/lib/state.js +1 -32
- package/dist/lib/teams/agents.d.ts +19 -10
- package/dist/lib/teams/agents.js +40 -39
- package/dist/lib/types.d.ts +37 -2
- package/dist/lib/usage.d.ts +33 -1
- package/dist/lib/usage.js +172 -12
- package/dist/lib/versions.d.ts +9 -2
- package/dist/lib/versions.js +37 -8
- package/dist/lib/watchdog/log.d.ts +43 -0
- package/dist/lib/watchdog/log.js +69 -0
- package/dist/lib/watchdog/routine.d.ts +44 -0
- package/dist/lib/watchdog/routine.js +69 -0
- package/dist/lib/watchdog/runner.d.ts +51 -7
- package/dist/lib/watchdog/runner.js +239 -64
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdog.js +31 -16
- package/dist/lib/workflows.d.ts +16 -0
- package/dist/lib/workflows.js +110 -1
- package/package.json +1 -1
|
@@ -1014,44 +1014,144 @@ export function parseAntigravity(dbPath) {
|
|
|
1014
1014
|
* Messages have role (user/assistant) and metadata.
|
|
1015
1015
|
* Parts contain the actual content: text, tool, reasoning, patch, step-start/finish.
|
|
1016
1016
|
*/
|
|
1017
|
+
/**
|
|
1018
|
+
* Parse a Grok (xAI CLI) session into normalized events.
|
|
1019
|
+
*
|
|
1020
|
+
* A Grok session dir holds several files; the conversation transcript is
|
|
1021
|
+
* `chat_history.jsonl`, one JSON object per line with a `type`:
|
|
1022
|
+
* - `system` — the system prompt (skipped; not conversational)
|
|
1023
|
+
* - `user` — `content` is an array of `{ type: 'text', text }` blocks
|
|
1024
|
+
* - `assistant` — `content` is a string, plus a `tool_calls[]` array of
|
|
1025
|
+
* `{ id, name, arguments }` (arguments is a JSON string)
|
|
1026
|
+
* - `reasoning` — chain-of-thought; text (when present) lives in `summary`
|
|
1027
|
+
* - `tool_result` — `{ tool_call_id, content }`, correlated back to the call
|
|
1028
|
+
*
|
|
1029
|
+
* The scanner records `summary.json` as the session's filePath (see
|
|
1030
|
+
* `readGrokMeta` in discover.ts), so resolve `chat_history.jsonl` from the same
|
|
1031
|
+
* dir; also accept being handed the transcript file directly. Per-line
|
|
1032
|
+
* timestamps aren't stored, so every event carries the session's `created_at`
|
|
1033
|
+
* (from summary.json), falling back to the transcript's mtime.
|
|
1034
|
+
*/
|
|
1017
1035
|
export function parseGrok(filePath) {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1036
|
+
const sessionDir = path.dirname(filePath);
|
|
1037
|
+
const historyPath = filePath.endsWith('chat_history.jsonl')
|
|
1038
|
+
? filePath
|
|
1039
|
+
: path.join(sessionDir, 'chat_history.jsonl');
|
|
1040
|
+
if (!fs.existsSync(historyPath))
|
|
1041
|
+
return [];
|
|
1042
|
+
let timestamp;
|
|
1021
1043
|
try {
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
content: summary.session_summary || 'Grok session',
|
|
1030
|
-
agent: 'grok',
|
|
1031
|
-
metadata: { sessionId: summary.id, cwd: summary.cwd },
|
|
1032
|
-
}];
|
|
1044
|
+
const summaryPath = filePath.endsWith('summary.json')
|
|
1045
|
+
? filePath
|
|
1046
|
+
: path.join(sessionDir, 'summary.json');
|
|
1047
|
+
if (fs.existsSync(summaryPath)) {
|
|
1048
|
+
const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf-8'));
|
|
1049
|
+
if (typeof summary?.created_at === 'string')
|
|
1050
|
+
timestamp = summary.created_at;
|
|
1033
1051
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1052
|
+
}
|
|
1053
|
+
catch { /* fall back to mtime below */ }
|
|
1054
|
+
if (!timestamp) {
|
|
1055
|
+
let mtime = null;
|
|
1056
|
+
try {
|
|
1057
|
+
mtime = fs.statSync(historyPath).mtime;
|
|
1058
|
+
}
|
|
1059
|
+
catch {
|
|
1060
|
+
mtime = null;
|
|
1061
|
+
}
|
|
1062
|
+
timestamp = (mtime ?? new Date()).toISOString();
|
|
1063
|
+
}
|
|
1064
|
+
const content = safeReadSessionFile(historyPath);
|
|
1065
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
1066
|
+
const events = [];
|
|
1067
|
+
const toolCallMap = new Map();
|
|
1068
|
+
// Grok text is either a plain string (assistant) or an array of typed blocks
|
|
1069
|
+
// (user: `{ type: 'text', text }`; reasoning summary: `{ text | summary_text }`).
|
|
1070
|
+
const extractText = (raw) => {
|
|
1071
|
+
if (typeof raw === 'string')
|
|
1072
|
+
return raw.trim();
|
|
1073
|
+
if (Array.isArray(raw)) {
|
|
1074
|
+
return raw
|
|
1075
|
+
.map((part) => typeof part?.text === 'string'
|
|
1076
|
+
? part.text
|
|
1077
|
+
: typeof part?.summary_text === 'string'
|
|
1078
|
+
? part.summary_text
|
|
1079
|
+
: '')
|
|
1080
|
+
.join('')
|
|
1081
|
+
.trim();
|
|
1082
|
+
}
|
|
1083
|
+
return '';
|
|
1084
|
+
};
|
|
1085
|
+
for (const line of lines) {
|
|
1086
|
+
let msg;
|
|
1087
|
+
try {
|
|
1088
|
+
msg = JSON.parse(line);
|
|
1089
|
+
}
|
|
1090
|
+
catch {
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
const type = msg?.type;
|
|
1094
|
+
if (type === 'user') {
|
|
1095
|
+
const text = extractText(msg.content);
|
|
1096
|
+
if (text)
|
|
1097
|
+
events.push({ type: 'message', agent: 'grok', timestamp, role: 'user', content: text });
|
|
1098
|
+
}
|
|
1099
|
+
else if (type === 'assistant') {
|
|
1100
|
+
const text = extractText(msg.content);
|
|
1101
|
+
if (text)
|
|
1102
|
+
events.push({ type: 'message', agent: 'grok', timestamp, role: 'assistant', content: text });
|
|
1103
|
+
const calls = Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
|
|
1104
|
+
for (const call of calls) {
|
|
1105
|
+
const toolName = typeof call?.name === 'string' ? call.name : 'unknown';
|
|
1106
|
+
let args = {};
|
|
1107
|
+
if (call?.arguments && typeof call.arguments === 'object') {
|
|
1108
|
+
args = call.arguments;
|
|
1046
1109
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1110
|
+
else if (typeof call?.arguments === 'string') {
|
|
1111
|
+
try {
|
|
1112
|
+
args = JSON.parse(call.arguments);
|
|
1113
|
+
}
|
|
1114
|
+
catch {
|
|
1115
|
+
args = { _raw: call.arguments };
|
|
1116
|
+
}
|
|
1049
1117
|
}
|
|
1118
|
+
if (typeof call?.id === 'string')
|
|
1119
|
+
toolCallMap.set(call.id, toolName);
|
|
1120
|
+
events.push({
|
|
1121
|
+
type: 'tool_use',
|
|
1122
|
+
agent: 'grok',
|
|
1123
|
+
timestamp,
|
|
1124
|
+
tool: toolName,
|
|
1125
|
+
args,
|
|
1126
|
+
path: args.path || args.file_path || undefined,
|
|
1127
|
+
command: typeof args.command === 'string' ? args.command : undefined,
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
else if (type === 'reasoning') {
|
|
1132
|
+
const text = extractText(msg.summary);
|
|
1133
|
+
if (text)
|
|
1134
|
+
events.push({ type: 'thinking', agent: 'grok', timestamp, content: text });
|
|
1135
|
+
}
|
|
1136
|
+
else if (type === 'tool_result') {
|
|
1137
|
+
const callId = typeof msg.tool_call_id === 'string' ? msg.tool_call_id : undefined;
|
|
1138
|
+
const toolName = (callId && toolCallMap.get(callId)) || 'unknown';
|
|
1139
|
+
const output = typeof msg.content === 'string' ? msg.content : extractText(msg.content);
|
|
1140
|
+
const isError = typeof output === 'string' && output.startsWith('Error:');
|
|
1141
|
+
events.push({
|
|
1142
|
+
type: isError ? 'error' : 'tool_result',
|
|
1143
|
+
agent: 'grok',
|
|
1144
|
+
timestamp,
|
|
1145
|
+
tool: toolName,
|
|
1146
|
+
success: !isError,
|
|
1147
|
+
output: output.length > 500 ? output.slice(0, 497) + '...' : output,
|
|
1050
1148
|
});
|
|
1149
|
+
if (callId)
|
|
1150
|
+
toolCallMap.delete(callId);
|
|
1051
1151
|
}
|
|
1152
|
+
// `system` lines are the system prompt — intentionally skipped.
|
|
1052
1153
|
}
|
|
1053
|
-
|
|
1054
|
-
return [];
|
|
1154
|
+
return events;
|
|
1055
1155
|
}
|
|
1056
1156
|
export function parseOpenCode(filePath) {
|
|
1057
1157
|
const [dbPath, sessionId] = filePath.split('#');
|
|
@@ -4,6 +4,23 @@ export interface PidSessionEntry {
|
|
|
4
4
|
/** The launch session id. Present for agents launched with a known id (Claude). */
|
|
5
5
|
sessionId?: string;
|
|
6
6
|
cwd?: string;
|
|
7
|
+
/**
|
|
8
|
+
* The launch id minted by `ag run` and exported to the child as
|
|
9
|
+
* `AGENT_LAUNCH_ID`. The agent's SessionStart hook records the SAME id in its
|
|
10
|
+
* own state file (`terminals/sessions/<pid>.json`, `launch_id`), so the two
|
|
11
|
+
* records reconcile by this key even when the hook runs under a DIFFERENT pid
|
|
12
|
+
* (tmux pane leaf, cmd.exe wrapper) — see resolveHookSessionId in
|
|
13
|
+
* session/hook-sessions.ts. This
|
|
14
|
+
* is how a non-Claude launch (whose id we don't know at spawn) gets an exact
|
|
15
|
+
* session id at listing time instead of the newest-jsonl heuristic.
|
|
16
|
+
*/
|
|
17
|
+
launchId?: string;
|
|
18
|
+
/**
|
|
19
|
+
* `AGENT_TERMINAL_ID` when the launch inherited one (a Factory VS Code tab).
|
|
20
|
+
* A secondary join key to the hook's `terminal_id`, for agents the extension
|
|
21
|
+
* spawned. Best-effort — undefined outside the extension.
|
|
22
|
+
*/
|
|
23
|
+
terminalId?: string;
|
|
7
24
|
/**
|
|
8
25
|
* `$TMUX_PANE` at launch — stored for diagnostics and possible future
|
|
9
26
|
* disambiguation. NOT currently consulted on read: the listing path keys
|
|
@@ -18,5 +35,20 @@ export declare function extractSessionIdArg(args: string[]): string | undefined;
|
|
|
18
35
|
export declare function writePidSessionEntry(entry: PidSessionEntry): void;
|
|
19
36
|
/** Look up a live pid's recorded session. Returns undefined if absent/corrupt. */
|
|
20
37
|
export declare function readPidSessionEntry(pid: number): PidSessionEntry | undefined;
|
|
21
|
-
/**
|
|
22
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Every recorded launch, one entry per live-or-dead pid. Used to index launches
|
|
40
|
+
* by their `tmuxPane` so the authoritative tmux source can attribute a pane it did
|
|
41
|
+
* NOT wrap (an agent bare-spawned into an existing pane) to its exact launch —
|
|
42
|
+
* the caller filters to live pids. Best-effort: unreadable/corrupt files are
|
|
43
|
+
* skipped, a missing dir yields `[]`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function listPidSessionEntries(): PidSessionEntry[];
|
|
46
|
+
/**
|
|
47
|
+
* Remove entries whose pid is no longer alive. Best-effort housekeeping.
|
|
48
|
+
*
|
|
49
|
+
* `isAlive` receives the entry's recorded `startedAtMs` so it can reject a pid
|
|
50
|
+
* that a newer process recycled (a "zombie" registry entry), not just one that
|
|
51
|
+
* no longer exists. An unreadable/corrupt entry falls back to the pid-only
|
|
52
|
+
* check.
|
|
53
|
+
*/
|
|
54
|
+
export declare function prunePidSessionRegistry(isAlive: (pid: number, startedAtMs?: number) => boolean): void;
|
|
@@ -81,7 +81,43 @@ export function readPidSessionEntry(pid) {
|
|
|
81
81
|
}
|
|
82
82
|
return undefined;
|
|
83
83
|
}
|
|
84
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Every recorded launch, one entry per live-or-dead pid. Used to index launches
|
|
86
|
+
* by their `tmuxPane` so the authoritative tmux source can attribute a pane it did
|
|
87
|
+
* NOT wrap (an agent bare-spawned into an existing pane) to its exact launch —
|
|
88
|
+
* the caller filters to live pids. Best-effort: unreadable/corrupt files are
|
|
89
|
+
* skipped, a missing dir yields `[]`.
|
|
90
|
+
*/
|
|
91
|
+
export function listPidSessionEntries() {
|
|
92
|
+
let files;
|
|
93
|
+
try {
|
|
94
|
+
files = fs.readdirSync(pidRegistryDir()).filter(f => f.endsWith('.json'));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
const out = [];
|
|
100
|
+
for (const f of files) {
|
|
101
|
+
try {
|
|
102
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(pidRegistryDir(), f), 'utf8'));
|
|
103
|
+
if (parsed && typeof parsed === 'object' && typeof parsed.pid === 'number') {
|
|
104
|
+
out.push(parsed);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
/* raced with a writer/pruner, or corrupt — skip */
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Remove entries whose pid is no longer alive. Best-effort housekeeping.
|
|
115
|
+
*
|
|
116
|
+
* `isAlive` receives the entry's recorded `startedAtMs` so it can reject a pid
|
|
117
|
+
* that a newer process recycled (a "zombie" registry entry), not just one that
|
|
118
|
+
* no longer exists. An unreadable/corrupt entry falls back to the pid-only
|
|
119
|
+
* check.
|
|
120
|
+
*/
|
|
85
121
|
export function prunePidSessionRegistry(isAlive) {
|
|
86
122
|
let files;
|
|
87
123
|
try {
|
|
@@ -92,7 +128,18 @@ export function prunePidSessionRegistry(isAlive) {
|
|
|
92
128
|
}
|
|
93
129
|
for (const f of files) {
|
|
94
130
|
const pid = Number(f.slice(0, -'.json'.length));
|
|
95
|
-
if (!Number.isInteger(pid)
|
|
131
|
+
if (!Number.isInteger(pid))
|
|
132
|
+
continue;
|
|
133
|
+
let startedAtMs;
|
|
134
|
+
try {
|
|
135
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(pidRegistryDir(), f), 'utf8'));
|
|
136
|
+
if (typeof parsed?.startedAtMs === 'number')
|
|
137
|
+
startedAtMs = parsed.startedAtMs;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
/* unreadable/corrupt — fall back to the pid-only liveness check */
|
|
141
|
+
}
|
|
142
|
+
if (isAlive(pid, startedAtMs))
|
|
96
143
|
continue;
|
|
97
144
|
try {
|
|
98
145
|
fs.unlinkSync(path.join(pidRegistryDir(), f));
|
|
@@ -12,10 +12,15 @@ import type { SessionEvent, SessionMeta } from './types.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function relativeToCwd(absPath: string, cwd?: string): string;
|
|
14
14
|
/**
|
|
15
|
-
* Wrap label in an OSC 8 hyperlink when the terminal
|
|
16
|
-
* Degrades to plain label otherwise.
|
|
15
|
+
* Wrap a filesystem path label in an OSC 8 `file://` hyperlink when the terminal
|
|
16
|
+
* supports it. Degrades to the plain label otherwise.
|
|
17
17
|
*/
|
|
18
18
|
export declare function linkPath(absPath: string, label: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Wrap a label in an OSC 8 hyperlink to an arbitrary URL (a PR on GitHub, a Linear
|
|
21
|
+
* issue, …) when the terminal supports it. Degrades to the plain label otherwise.
|
|
22
|
+
*/
|
|
23
|
+
export declare function linkUrl(url: string, label: string): string;
|
|
19
24
|
/**
|
|
20
25
|
* Unwrap wrapper prefixes to find the actual executable.
|
|
21
26
|
*/
|
|
@@ -28,19 +28,31 @@ export function relativeToCwd(absPath, cwd) {
|
|
|
28
28
|
}
|
|
29
29
|
return absPath;
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*/
|
|
35
|
-
export function linkPath(absPath, label) {
|
|
36
|
-
if (process.stdout.isTTY &&
|
|
31
|
+
/** Best-effort feature-detect for OSC 8 hyperlink support in the current TTY. */
|
|
32
|
+
function supportsHyperlinks() {
|
|
33
|
+
return Boolean(process.stdout.isTTY &&
|
|
37
34
|
(process.env.TERM_PROGRAM ||
|
|
38
35
|
process.env.WT_SESSION ||
|
|
39
36
|
process.env.KITTY_WINDOW_ID ||
|
|
40
|
-
process.env.WEZTERM_PANE))
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
process.env.WEZTERM_PANE));
|
|
38
|
+
}
|
|
39
|
+
/** Wrap `label` in an OSC 8 hyperlink to `target`. Callers gate on {@link supportsHyperlinks}. */
|
|
40
|
+
function osc8(target, label) {
|
|
41
|
+
return `\x1b]8;;${target}\x1b\\${label}\x1b]8;;\x1b\\`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Wrap a filesystem path label in an OSC 8 `file://` hyperlink when the terminal
|
|
45
|
+
* supports it. Degrades to the plain label otherwise.
|
|
46
|
+
*/
|
|
47
|
+
export function linkPath(absPath, label) {
|
|
48
|
+
return supportsHyperlinks() ? osc8(`file://${absPath}`, label) : label;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Wrap a label in an OSC 8 hyperlink to an arbitrary URL (a PR on GitHub, a Linear
|
|
52
|
+
* issue, …) when the terminal supports it. Degrades to the plain label otherwise.
|
|
53
|
+
*/
|
|
54
|
+
export function linkUrl(url, label) {
|
|
55
|
+
return supportsHyperlinks() ? osc8(url, label) : label;
|
|
44
56
|
}
|
|
45
57
|
// ── Command grouping ──────────────────────────────────────────────────────────
|
|
46
58
|
/**
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical derivation of a session's 8-char `shortId`.
|
|
3
|
+
*
|
|
4
|
+
* Every session parser used to inline `id.slice(0, 8)` — some with a
|
|
5
|
+
* `.replace(prefix, '')` strip first (`session_`, `api-`, `ses_`). That strip is
|
|
6
|
+
* the bug: an id that is *only* its prefix (a bare `session_` directory, an id of
|
|
7
|
+
* exactly `api-`) strips to `''`, and `''.slice(0, 8)` is still `''`. An empty
|
|
8
|
+
* shortId then passes the `short_id TEXT NOT NULL` constraint (empty string is not
|
|
9
|
+
* NULL) yet matches nothing in the `short_id LIKE ?` picker lookups — a silently
|
|
10
|
+
* corrupt index row.
|
|
11
|
+
*
|
|
12
|
+
* This helper is the single source of truth: it strips the optional prefix, takes
|
|
13
|
+
* the first 8 chars, and — the whole point — guarantees a NON-EMPTY result by
|
|
14
|
+
* falling back to the unstripped id when the strip empties it, so a row always
|
|
15
|
+
* stays addressable by its shortId.
|
|
16
|
+
*/
|
|
17
|
+
export declare function deriveShortId(id: string, stripPrefix?: RegExp): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical derivation of a session's 8-char `shortId`.
|
|
3
|
+
*
|
|
4
|
+
* Every session parser used to inline `id.slice(0, 8)` — some with a
|
|
5
|
+
* `.replace(prefix, '')` strip first (`session_`, `api-`, `ses_`). That strip is
|
|
6
|
+
* the bug: an id that is *only* its prefix (a bare `session_` directory, an id of
|
|
7
|
+
* exactly `api-`) strips to `''`, and `''.slice(0, 8)` is still `''`. An empty
|
|
8
|
+
* shortId then passes the `short_id TEXT NOT NULL` constraint (empty string is not
|
|
9
|
+
* NULL) yet matches nothing in the `short_id LIKE ?` picker lookups — a silently
|
|
10
|
+
* corrupt index row.
|
|
11
|
+
*
|
|
12
|
+
* This helper is the single source of truth: it strips the optional prefix, takes
|
|
13
|
+
* the first 8 chars, and — the whole point — guarantees a NON-EMPTY result by
|
|
14
|
+
* falling back to the unstripped id when the strip empties it, so a row always
|
|
15
|
+
* stays addressable by its shortId.
|
|
16
|
+
*/
|
|
17
|
+
export function deriveShortId(id, stripPrefix) {
|
|
18
|
+
const stripped = stripPrefix ? id.replace(stripPrefix, '') : id;
|
|
19
|
+
return stripped.slice(0, 8) || id.slice(0, 8) || id;
|
|
20
|
+
}
|
|
@@ -378,8 +378,12 @@ export function inferActivity(events, ctx = {}) {
|
|
|
378
378
|
// A prose question takes a free-text reply (no select-list), so no options/keys.
|
|
379
379
|
// Unlike the structural plan/ask signals above, the prose heuristic DECAYS: a
|
|
380
380
|
// question nobody answered within PROSE_QUESTION_FRESH_MS is a session that
|
|
381
|
-
// ended, not one that needs you (RUSH-1522).
|
|
382
|
-
|
|
381
|
+
// ended, not one that needs you (RUSH-1522). A prose "?" is only "waiting on
|
|
382
|
+
// you" while the file is DEMONSTRABLY fresh — with no mtime signal at all we
|
|
383
|
+
// cannot assert freshness, so the heuristic must not fire (an unknown-age
|
|
384
|
+
// prose question is a session that ended, closing RUSH-1522's null-mtime hole
|
|
385
|
+
// where a null mtime kept the question forever).
|
|
386
|
+
const questionFresh = ctx.mtimeMs != null && Date.now() - ctx.mtimeMs < PROSE_QUESTION_FRESH_MS;
|
|
383
387
|
if (questionFresh && looksLikeQuestion(last.content ?? '')) {
|
|
384
388
|
const text = oneLine(last.content ?? '');
|
|
385
389
|
return { ...base, activity: 'waiting_input', awaitingReason: 'question', question: { text, reason: 'question' } };
|
package/dist/lib/shims.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ export interface ConflictInfo {
|
|
|
77
77
|
* top-level entry add/remove — deep edits to plugin contents won't
|
|
78
78
|
* trigger auto-resync, run `agents sync` for that.
|
|
79
79
|
*/
|
|
80
|
-
export declare const SHIM_SCHEMA_VERSION =
|
|
80
|
+
export declare const SHIM_SCHEMA_VERSION = 27;
|
|
81
81
|
/**
|
|
82
82
|
* Generate the full bash shim script for the given agent. The returned string
|
|
83
83
|
* is written to ~/.agents/shims/{cliCommand} and made executable.
|
package/dist/lib/shims.js
CHANGED
|
@@ -222,7 +222,7 @@ async function promptConflictStrategy(conflictInfos) {
|
|
|
222
222
|
// The old dispatcher checked only the global dir, so a pinned grok that
|
|
223
223
|
// installed into the versioned home fell through to the "not installed"
|
|
224
224
|
// error.
|
|
225
|
-
export const SHIM_SCHEMA_VERSION =
|
|
225
|
+
export const SHIM_SCHEMA_VERSION = 27;
|
|
226
226
|
/** Internal marker string used to embed the schema version in shim scripts. */
|
|
227
227
|
const SHIM_VERSION_MARKER = 'agents-shim-version:';
|
|
228
228
|
function shellQuote(value) {
|
|
@@ -278,13 +278,20 @@ export COPILOT_HOME="$VERSION_DIR/home/${configDirName}"
|
|
|
278
278
|
# This gives agents-cli full versioned isolation + resource sync for grok.
|
|
279
279
|
export GROK_HOME="$VERSION_DIR/home/.grok"
|
|
280
280
|
`
|
|
281
|
-
: agent === '
|
|
281
|
+
: agent === 'opencode'
|
|
282
282
|
? `
|
|
283
|
+
# OpenCode reads plugins, agents, commands, and other config-directory
|
|
284
|
+
# resources from OPENCODE_CONFIG_DIR. Point it at the versioned global config
|
|
285
|
+
# tree where agents-cli syncs OpenCode resources.
|
|
286
|
+
export OPENCODE_CONFIG_DIR="$VERSION_DIR/home/.config/opencode"
|
|
287
|
+
`
|
|
288
|
+
: agent === 'kimi'
|
|
289
|
+
? `
|
|
283
290
|
# Kimi Code CLI honors KIMI_CODE_HOME to relocate ~/.kimi-code (config.toml,
|
|
284
291
|
# mcp.json, sessions, skills, hooks). Point it at the versioned home.
|
|
285
292
|
export KIMI_CODE_HOME="$VERSION_DIR/home/${configDirName}"
|
|
286
293
|
`
|
|
287
|
-
|
|
294
|
+
: '';
|
|
288
295
|
const launchArgs = agent === 'codex' ? ' -c check_for_update_on_startup=false' : '';
|
|
289
296
|
return `#!/bin/bash
|
|
290
297
|
# Auto-generated by agents-cli - do not edit
|
|
@@ -867,7 +874,7 @@ function assertSafeVersion(version) {
|
|
|
867
874
|
* KEEP IN SYNC with the `managedEnv` switch in `generateVersionedAliasScript`.
|
|
868
875
|
* The colocated test `shims.isolation-capability.test.ts` enforces this.
|
|
869
876
|
*/
|
|
870
|
-
export const CONFIG_ENV_ISOLATED_AGENTS = ['claude', 'codex', 'copilot', 'grok', 'kimi'];
|
|
877
|
+
export const CONFIG_ENV_ISOLATED_AGENTS = ['claude', 'codex', 'copilot', 'grok', 'kimi', 'opencode'];
|
|
871
878
|
/**
|
|
872
879
|
* Whether an agent supports a clean `--isolated` install — i.e. its config
|
|
873
880
|
* location can be redirected by an env var so the isolated copy stays fully
|
|
@@ -912,13 +919,20 @@ export COPILOT_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${
|
|
|
912
919
|
# aliases at the versioned home for isolation parity with the main shim.
|
|
913
920
|
export GROK_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
|
|
914
921
|
`
|
|
915
|
-
: agent === '
|
|
922
|
+
: agent === 'opencode'
|
|
916
923
|
? `
|
|
924
|
+
# OpenCode reads plugins, agents, commands, and other config-directory
|
|
925
|
+
# resources from OPENCODE_CONFIG_DIR. Point direct aliases at the versioned
|
|
926
|
+
# global config tree where agents-cli syncs OpenCode resources.
|
|
927
|
+
export OPENCODE_CONFIG_DIR="$HOME/.agents/.history/versions/${agent}/${version}/home/.config/opencode"
|
|
928
|
+
`
|
|
929
|
+
: agent === 'kimi'
|
|
930
|
+
? `
|
|
917
931
|
# Kimi Code CLI honors KIMI_CODE_HOME to relocate ~/.kimi-code (config.toml,
|
|
918
932
|
# mcp.json, sessions, skills, hooks). Point direct aliases at the versioned home.
|
|
919
933
|
export KIMI_CODE_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
|
|
920
934
|
`
|
|
921
|
-
|
|
935
|
+
: '';
|
|
922
936
|
const launchArgs = agent === 'codex' ? ' -c check_for_update_on_startup=false' : '';
|
|
923
937
|
// Resolve the binary the same way the main shim does (see generateShimScript).
|
|
924
938
|
// Grok and Droid do NOT ship into node_modules/.bin — Grok downloads a native
|
package/dist/lib/sqlite.d.ts
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Picks `bun:sqlite` under Bun and `node:sqlite` under Node (>=22.5). Avoids
|
|
5
5
|
* the native `better-sqlite3` addon entirely so there is no prebuild compile
|
|
6
|
-
* and no Node/Bun ABI mismatch
|
|
7
|
-
*
|
|
6
|
+
* and no Node/Bun ABI mismatch. Both runtimes are production: `dist/index.js`
|
|
7
|
+
* runs under Node, while the signed standalone `dist/bin/agents` (what the
|
|
8
|
+
* shims exec) embeds Bun.
|
|
8
9
|
*
|
|
9
10
|
* Exposes the small better-sqlite3-shaped surface area the rest of the
|
|
10
11
|
* codebase already uses: `prepare/exec/pragma/transaction/close` on the DB,
|
package/dist/lib/sqlite.js
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Picks `bun:sqlite` under Bun and `node:sqlite` under Node (>=22.5). Avoids
|
|
5
5
|
* the native `better-sqlite3` addon entirely so there is no prebuild compile
|
|
6
|
-
* and no Node/Bun ABI mismatch
|
|
7
|
-
*
|
|
6
|
+
* and no Node/Bun ABI mismatch. Both runtimes are production: `dist/index.js`
|
|
7
|
+
* runs under Node, while the signed standalone `dist/bin/agents` (what the
|
|
8
|
+
* shims exec) embeds Bun.
|
|
8
9
|
*
|
|
9
10
|
* Exposes the small better-sqlite3-shaped surface area the rest of the
|
|
10
11
|
* codebase already uses: `prepare/exec/pragma/transaction/close` on the DB,
|
|
@@ -21,9 +22,31 @@ const sqliteMod = isBun
|
|
|
21
22
|
// bun:sqlite exports `Database`; node:sqlite exports `DatabaseSync`.
|
|
22
23
|
const NativeDatabase = sqliteMod.Database
|
|
23
24
|
?? sqliteMod.DatabaseSync;
|
|
25
|
+
/**
|
|
26
|
+
* bun:sqlite binds a named-parameter object ONLY when its keys carry the SQL
|
|
27
|
+
* sigil (`{ '@id': … }` for `VALUES (@id)`); bare keys (`{ id: … }`) match
|
|
28
|
+
* nothing and every parameter stays NULL, so the first NOT NULL column raises a
|
|
29
|
+
* constraint error and the write is lost. node:sqlite accepts the bare keys.
|
|
30
|
+
* `strict: true` makes bun accept them too, so the bare-key call shape this
|
|
31
|
+
* codebase uses works on both runtimes. node:sqlite has no such option and
|
|
32
|
+
* rejects a second argument that isn't an object, so the argument list is built
|
|
33
|
+
* per runtime.
|
|
34
|
+
*
|
|
35
|
+
* The two runtimes are still not interchangeable at the edges, and only bun's
|
|
36
|
+
* half is exercised by the shipped binary rather than by vitest — so keep binds
|
|
37
|
+
* inside the intersection:
|
|
38
|
+
* - omit a named key: bun throws `Missing parameter "x"`, node binds NULL.
|
|
39
|
+
* - pass an extra named key: bun accepts it, node throws `Unknown named
|
|
40
|
+
* parameter`.
|
|
41
|
+
* - use sigil keys (`{'@id': …}`): node accepts them, strict bun rejects them.
|
|
42
|
+
* - a single non-plain object positional arg (e.g. `run(new Date())`) reaches
|
|
43
|
+
* the named path via bindArgs below and throws under strict bun.
|
|
44
|
+
*/
|
|
45
|
+
const NATIVE_ARGS = isBun ? [{ strict: true }] : [];
|
|
24
46
|
function bindArgs(params) {
|
|
25
47
|
// Both bindings accept positional `(a, b, c)` and named `({ a, b, c })`
|
|
26
|
-
// forms
|
|
48
|
+
// forms — bun only under `strict: true` (see NATIVE_ARGS). Pass an object
|
|
49
|
+
// through unchanged so callers using named binds work.
|
|
27
50
|
if (params.length === 1 &&
|
|
28
51
|
params[0] !== null &&
|
|
29
52
|
typeof params[0] === 'object' &&
|
|
@@ -51,7 +74,7 @@ class StatementImpl {
|
|
|
51
74
|
class Database {
|
|
52
75
|
inner;
|
|
53
76
|
constructor(filename) {
|
|
54
|
-
this.inner = new NativeDatabase(filename);
|
|
77
|
+
this.inner = new NativeDatabase(filename, ...NATIVE_ARGS);
|
|
55
78
|
}
|
|
56
79
|
prepare(sql) {
|
|
57
80
|
return new StatementImpl(this.inner.prepare(sql));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Commands detector — mirrors versions.ts:343-357. Inspects the version home,
|
|
3
3
|
* returns command names. Honors the commands-as-skills marker for skills-only
|
|
4
|
-
* agents (
|
|
4
|
+
* agents (kimi, Codex >= 0.117.0, …); falls back to scanning
|
|
5
5
|
* `{agentDir}/<commandsSubdir>/` for the native path.
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from 'fs';
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Workflows detector — scans
|
|
3
|
-
*
|
|
2
|
+
* Workflows detector — scans the agent-specific on-disk layout for synced
|
|
3
|
+
* workflow names (Claude WORKFLOW.md trees, Kimi flow skills, Antigravity
|
|
4
|
+
* global_workflows, Goose recipes, OpenClaw Lobster, Grok Rhai).
|
|
4
5
|
*/
|
|
5
6
|
import * as fs from 'fs';
|
|
6
7
|
import * as os from 'os';
|
|
7
8
|
import * as path from 'path';
|
|
8
9
|
import * as yaml from 'yaml';
|
|
9
10
|
import { capableAgents } from '../../capabilities.js';
|
|
11
|
+
import { grokWorkflowMarker } from '../../workflows.js';
|
|
10
12
|
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
11
13
|
function buildWorkflowsDetector(agent) {
|
|
12
14
|
return {
|
|
@@ -93,6 +95,15 @@ function buildWorkflowsDetector(agent) {
|
|
|
93
95
|
})
|
|
94
96
|
.map(d => d.name.slice(0, -'.lobster'.length));
|
|
95
97
|
}
|
|
98
|
+
if (agent === 'grok') {
|
|
99
|
+
const workflowsDir = path.join(versionHome, '.grok', 'workflows');
|
|
100
|
+
if (!fs.existsSync(workflowsDir))
|
|
101
|
+
return [];
|
|
102
|
+
return fs.readdirSync(workflowsDir, { withFileTypes: true })
|
|
103
|
+
.filter(d => d.isFile() && d.name.endsWith('.rhai') && !d.name.startsWith('.'))
|
|
104
|
+
.filter(d => grokWorkflowMarker(path.join(workflowsDir, d.name)) === d.name.slice(0, -'.rhai'.length))
|
|
105
|
+
.map(d => d.name.slice(0, -'.rhai'.length));
|
|
106
|
+
}
|
|
96
107
|
const workflowsDir = path.join(versionHome, 'workflows');
|
|
97
108
|
if (!fs.existsSync(workflowsDir))
|
|
98
109
|
return [];
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Two physical formats, picked per-(agent, version) at write time:
|
|
5
5
|
*
|
|
6
6
|
* - command-as-skill — fires when `shouldInstallCommandAsSkill(agent, version)`
|
|
7
|
-
* is true. Used for
|
|
8
|
-
*
|
|
9
|
-
* Writes `{agentDir}/skills/<name>/SKILL.md` with the
|
|
10
|
-
* marker; the agent picks it up as a slash-command equivalent.
|
|
7
|
+
* is true. Used for Codex >= 0.117.0 (commands capability ends, skills
|
|
8
|
+
* capability remains) and agents with skills but no native command-file dir
|
|
9
|
+
* such as kimi. Writes `{agentDir}/skills/<name>/SKILL.md` with the
|
|
10
|
+
* `agents_command` marker; the agent picks it up as a slash-command equivalent.
|
|
11
11
|
*
|
|
12
12
|
* - native command file — `{agentDir}/<commandsSubdir>/<name>.md` (or .toml
|
|
13
13
|
* when the agent's format is toml). Standard path for Claude, Codex
|
|
@@ -83,8 +83,8 @@ function buildCommandsWriter(agent) {
|
|
|
83
83
|
// Built lazily on first access — see lazy-map.ts for the cycle rationale.
|
|
84
84
|
//
|
|
85
85
|
// Registration covers two cases:
|
|
86
|
-
// - native commands (claude, codex < 0.117.0, gemini, etc.) — `commands` cap
|
|
87
|
-
// - commands-as-skills (
|
|
86
|
+
// - native commands (claude, codex < 0.117.0, gemini, grok, etc.) — `commands` cap
|
|
87
|
+
// - commands-as-skills (kimi, codex >= 0.117.0)
|
|
88
88
|
//
|
|
89
89
|
// Agents that have skills but use a NATIVE non-file slash-command system
|
|
90
90
|
// (openclaw → Gateway-based commands) are NOT registered. They declare
|
|
@@ -97,7 +97,7 @@ export const commandsWriters = lazyAgentMap(() => {
|
|
|
97
97
|
if (cfg.capabilities.commands === false && (!cfg.skillsDir || cfg.skillsDir === ''))
|
|
98
98
|
continue;
|
|
99
99
|
// Skills-capable agent with no native command-file dir: convert commands to
|
|
100
|
-
// skills by default (
|
|
100
|
+
// skills by default (kimi, …). Opt out only agents with their own
|
|
101
101
|
// slash-command runtime (openclaw).
|
|
102
102
|
if (cfg.capabilities.commands === false && (!cfg.commandsSubdir || cfg.commandsSubdir === '')) {
|
|
103
103
|
if (cfg.nativeCommandRuntime)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Workflows writer — copies each workflow
|
|
3
|
-
*
|
|
2
|
+
* Workflows writer — copies each selected workflow into the agent-specific
|
|
3
|
+
* on-disk layout via `syncWorkflowToVersion` (Claude: WORKFLOW.md tree under
|
|
4
|
+
* `{versionHome}/workflows/`; Grok: native `.rhai` under
|
|
5
|
+
* `{versionHome}/.grok/workflows/`).
|
|
4
6
|
*/
|
|
5
7
|
import type { AgentId } from '../../types.js';
|
|
6
8
|
import type { ResourceWriter } from './types.js';
|
|
@@ -57,6 +57,7 @@ export declare const loadApply: ModuleLoader;
|
|
|
57
57
|
export declare const loadCheck: ModuleLoader;
|
|
58
58
|
export declare const loadStatus: ModuleLoader;
|
|
59
59
|
export declare const loadProfiles: ModuleLoader;
|
|
60
|
+
export declare const loadHarness: ModuleLoader;
|
|
60
61
|
export declare const loadSecrets: ModuleLoader;
|
|
61
62
|
export declare const loadLogin: ModuleLoader;
|
|
62
63
|
export declare const loadWallet: ModuleLoader;
|