@phnx-labs/agents-cli 1.20.44 → 1.20.45
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 +9 -1
- package/dist/commands/exec.js +54 -11
- package/dist/commands/secrets.d.ts +18 -0
- package/dist/commands/secrets.js +105 -30
- package/dist/commands/teams.js +61 -3
- package/dist/lib/hosts/dispatch.d.ts +29 -0
- package/dist/lib/hosts/dispatch.js +46 -1
- package/dist/lib/hosts/remote-cmd.d.ts +17 -0
- package/dist/lib/hosts/remote-cmd.js +27 -0
- package/dist/lib/hosts/session-index.d.ts +15 -0
- package/dist/lib/hosts/session-index.js +28 -2
- package/dist/lib/rotate.d.ts +33 -0
- package/dist/lib/rotate.js +37 -0
- package/dist/lib/secrets/remote.d.ts +14 -0
- package/dist/lib/secrets/remote.js +18 -1
- package/dist/lib/session/active.d.ts +4 -0
- package/dist/lib/session/active.js +2 -0
- package/dist/lib/session/db.d.ts +16 -9
- package/dist/lib/session/db.js +66 -44
- package/dist/lib/session/discover.d.ts +4 -0
- package/dist/lib/session/discover.js +84 -13
- package/dist/lib/session/run-names.d.ts +9 -7
- package/dist/lib/session/run-names.js +9 -7
- package/dist/lib/session/state.d.ts +29 -3
- package/dist/lib/session/state.js +84 -5
- package/dist/lib/session/types.d.ts +19 -8
- package/dist/lib/teams/agents.js +9 -0
- package/package.json +1 -1
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* session id of the run it named.
|
|
4
4
|
*
|
|
5
5
|
* `agents run` records `<sessionId>.json` here at launch whenever both a name
|
|
6
|
-
* and a session id are known up front (Claude pre-mints its id — see
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* and a session id are known up front (Claude pre-mints its id — see spawnAgent;
|
|
7
|
+
* a teams teammate's Claude session id is its agent id). The session-discovery
|
|
8
|
+
* pass reads these sidecars and SEEDS the session label by id (via
|
|
9
|
+
* seedLabelsFromNames) — the launch handle becomes the label, refined later by an
|
|
10
|
+
* agent-generated title. Idempotent and re-applied every scan, so seeds survive
|
|
11
|
+
* transcript rescans without being parsed out of the transcript itself.
|
|
11
12
|
*
|
|
12
13
|
* Mirrors the host-task sidecar convention (`~/.agents/.cache/hosts/<id>.json`),
|
|
13
14
|
* one small JSON per run under `~/.agents/.cache/run-names/`.
|
|
@@ -26,7 +27,8 @@ export declare function runNamesDir(): string;
|
|
|
26
27
|
*/
|
|
27
28
|
export declare function recordRunName(rec: Omit<RunNameRecord, 'ts'>): void;
|
|
28
29
|
/**
|
|
29
|
-
* Build the sessionId → name map from every run-name sidecar, for
|
|
30
|
-
* apply onto the index. Returns an empty
|
|
30
|
+
* Build the sessionId → name map from every run-name sidecar, for
|
|
31
|
+
* seedLabelsFromNames to apply onto the index as label seeds. Returns an empty
|
|
32
|
+
* map when the dir doesn't exist yet.
|
|
31
33
|
*/
|
|
32
34
|
export declare function buildRunNameMap(): Map<string, string | null>;
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* session id of the run it named.
|
|
4
4
|
*
|
|
5
5
|
* `agents run` records `<sessionId>.json` here at launch whenever both a name
|
|
6
|
-
* and a session id are known up front (Claude pre-mints its id — see
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* and a session id are known up front (Claude pre-mints its id — see spawnAgent;
|
|
7
|
+
* a teams teammate's Claude session id is its agent id). The session-discovery
|
|
8
|
+
* pass reads these sidecars and SEEDS the session label by id (via
|
|
9
|
+
* seedLabelsFromNames) — the launch handle becomes the label, refined later by an
|
|
10
|
+
* agent-generated title. Idempotent and re-applied every scan, so seeds survive
|
|
11
|
+
* transcript rescans without being parsed out of the transcript itself.
|
|
11
12
|
*
|
|
12
13
|
* Mirrors the host-task sidecar convention (`~/.agents/.cache/hosts/<id>.json`),
|
|
13
14
|
* one small JSON per run under `~/.agents/.cache/run-names/`.
|
|
@@ -37,8 +38,9 @@ export function recordRunName(rec) {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
/**
|
|
40
|
-
* Build the sessionId → name map from every run-name sidecar, for
|
|
41
|
-
* apply onto the index. Returns an empty
|
|
41
|
+
* Build the sessionId → name map from every run-name sidecar, for
|
|
42
|
+
* seedLabelsFromNames to apply onto the index as label seeds. Returns an empty
|
|
43
|
+
* map when the dir doesn't exist yet.
|
|
42
44
|
*/
|
|
43
45
|
export function buildRunNameMap() {
|
|
44
46
|
const map = new Map();
|
|
@@ -44,6 +44,10 @@ export interface SessionState {
|
|
|
44
44
|
pr?: DetectedPr;
|
|
45
45
|
worktree?: DetectedWorktree;
|
|
46
46
|
ticket?: DetectedTicket;
|
|
47
|
+
/** Tracker refs this session CREATED (Linear create_issue / gh issue create). */
|
|
48
|
+
createdTickets?: string[];
|
|
49
|
+
/** Team name this session SPAWNED via `agents teams create/add`. */
|
|
50
|
+
spawnedTeam?: string;
|
|
47
51
|
}
|
|
48
52
|
export interface StateContext {
|
|
49
53
|
/** Session file mtime; drives running-vs-stale. */
|
|
@@ -63,6 +67,24 @@ export declare function detectTicket(text?: string, branch?: string): DetectedTi
|
|
|
63
67
|
export declare function extractPrUrl(output?: string): DetectedPr | undefined;
|
|
64
68
|
/** True when a Bash/exec command string is a `gh pr create`. */
|
|
65
69
|
export declare function isPrCreateCommand(command?: string): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* The team a session SPAWNED, from an `agents teams create/add <name>` command.
|
|
72
|
+
* Returns the team name, or undefined if the command isn't a team spawn. Note this
|
|
73
|
+
* is the opposite of `isTeamOrigin` (which marks sessions spawned BY a team).
|
|
74
|
+
*/
|
|
75
|
+
export declare function detectSpawnedTeam(command?: string): string | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* True when a tool_use call CREATES a tracker ticket — a Linear MCP `create_issue`
|
|
78
|
+
* tool, or a Bash `gh issue create`. The created id is then read from the matching
|
|
79
|
+
* tool_result via {@link extractCreatedTicket}.
|
|
80
|
+
*/
|
|
81
|
+
export declare function isTicketCreateTool(name?: string, command?: string): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Pull a created ticket ref out of a create-issue tool_result. Linear returns a
|
|
84
|
+
* key like `RUSH-1234`; `gh issue create` returns the issue URL, from which we
|
|
85
|
+
* take `#<number>`. Returns undefined when neither shape is present.
|
|
86
|
+
*/
|
|
87
|
+
export declare function extractCreatedTicket(text?: string): string | undefined;
|
|
66
88
|
/**
|
|
67
89
|
* Infer live activity + a preview from a chronological event slice. `pr` /
|
|
68
90
|
* `ticket` / `worktree` are attached by `inferSessionState`; this focuses on the
|
|
@@ -70,13 +92,17 @@ export declare function isPrCreateCommand(command?: string): boolean;
|
|
|
70
92
|
*/
|
|
71
93
|
export declare function inferActivity(events: SessionEvent[], ctx?: StateContext): SessionState;
|
|
72
94
|
/**
|
|
73
|
-
* Scan an event slice for the durable signals
|
|
74
|
-
*
|
|
75
|
-
*
|
|
95
|
+
* Scan an event slice for the durable signals that aren't about the cwd: the PR
|
|
96
|
+
* opened, the injected ticket, plus the artifacts the session PRODUCED — tracker
|
|
97
|
+
* refs it created and any team it spawned. Each `gh pr create` / create-issue tool
|
|
98
|
+
* call is correlated with the nearest following tool_result; the team name comes
|
|
99
|
+
* straight off the `agents teams create/add` command.
|
|
76
100
|
*/
|
|
77
101
|
export declare function detectDurableSignals(events: SessionEvent[]): {
|
|
78
102
|
pr?: DetectedPr;
|
|
79
103
|
ticket?: DetectedTicket;
|
|
104
|
+
createdTickets?: string[];
|
|
105
|
+
spawnedTeam?: string;
|
|
80
106
|
};
|
|
81
107
|
/** Full inference: activity + preview + durable signals + worktree/ticket from ctx. */
|
|
82
108
|
export declare function inferSessionState(events: SessionEvent[], ctx?: StateContext): SessionState;
|
|
@@ -37,6 +37,16 @@ const PR_URL_RE = /https:\/\/github\.com\/[^\s"'()<>]+\/pull\/(\d+)/;
|
|
|
37
37
|
const WORKTREE_RE = /\/\.agents\/worktrees\/([^/]+)/;
|
|
38
38
|
/** gh invocations that create/open a PR. */
|
|
39
39
|
const GH_PR_CREATE_RE = /\bgh\s+pr\s+(?:create|new)\b/;
|
|
40
|
+
/** gh invocation that opens an issue — the created number is read from its result. */
|
|
41
|
+
const GH_ISSUE_CREATE_RE = /\bgh\s+issue\s+create\b/;
|
|
42
|
+
/** A created GitHub issue URL (…/issues/123) in tool-result output. */
|
|
43
|
+
const GH_ISSUE_URL_RE = /https:\/\/github\.com\/[^\s"'()<>]+\/issues\/(\d+)/;
|
|
44
|
+
/**
|
|
45
|
+
* `agents teams create <name>` / `agents teams add <team> …` (also the `ag` alias).
|
|
46
|
+
* The team NAME is the first bareword after the sub-verb, skipping any flags. This
|
|
47
|
+
* is the structural signal that a session SPAWNED a team (vs. was spawned by one).
|
|
48
|
+
*/
|
|
49
|
+
const TEAMS_SPAWN_RE = /\bag(?:ents)?\s+teams?\s+(?:create|add)\s+(?:--?[a-z][\w-]*(?:[= ]\S+)?\s+)*([A-Za-z0-9][\w-]*)/;
|
|
40
50
|
/** Collapse to a single trimmed line for a one-row preview cell. */
|
|
41
51
|
function oneLine(s) {
|
|
42
52
|
return s.replace(/\s+/g, ' ').trim();
|
|
@@ -80,6 +90,46 @@ export function extractPrUrl(output) {
|
|
|
80
90
|
export function isPrCreateCommand(command) {
|
|
81
91
|
return !!command && GH_PR_CREATE_RE.test(command);
|
|
82
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* The team a session SPAWNED, from an `agents teams create/add <name>` command.
|
|
95
|
+
* Returns the team name, or undefined if the command isn't a team spawn. Note this
|
|
96
|
+
* is the opposite of `isTeamOrigin` (which marks sessions spawned BY a team).
|
|
97
|
+
*/
|
|
98
|
+
export function detectSpawnedTeam(command) {
|
|
99
|
+
if (!command)
|
|
100
|
+
return undefined;
|
|
101
|
+
const m = command.match(TEAMS_SPAWN_RE);
|
|
102
|
+
return m ? m[1] : undefined;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* True when a tool_use call CREATES a tracker ticket — a Linear MCP `create_issue`
|
|
106
|
+
* tool, or a Bash `gh issue create`. The created id is then read from the matching
|
|
107
|
+
* tool_result via {@link extractCreatedTicket}.
|
|
108
|
+
*/
|
|
109
|
+
export function isTicketCreateTool(name, command) {
|
|
110
|
+
if (typeof name === 'string' && /linear/i.test(name) && /create[_-]?issue/i.test(name))
|
|
111
|
+
return true;
|
|
112
|
+
// Any shell tool (Bash / shell / local_shell) running `gh issue create`.
|
|
113
|
+
if (!!command && GH_ISSUE_CREATE_RE.test(command))
|
|
114
|
+
return true;
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Pull a created ticket ref out of a create-issue tool_result. Linear returns a
|
|
119
|
+
* key like `RUSH-1234`; `gh issue create` returns the issue URL, from which we
|
|
120
|
+
* take `#<number>`. Returns undefined when neither shape is present.
|
|
121
|
+
*/
|
|
122
|
+
export function extractCreatedTicket(text) {
|
|
123
|
+
if (!text)
|
|
124
|
+
return undefined;
|
|
125
|
+
const lin = text.match(TICKET_RE);
|
|
126
|
+
if (lin && !TICKET_DENYLIST.has(lin[1].split('-')[0]))
|
|
127
|
+
return lin[1];
|
|
128
|
+
const gh = text.match(GH_ISSUE_URL_RE);
|
|
129
|
+
if (gh)
|
|
130
|
+
return `#${gh[1]}`;
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
83
133
|
/** Does an assistant message read as a question directed at the user? */
|
|
84
134
|
function looksLikeQuestion(text) {
|
|
85
135
|
const t = text.trim();
|
|
@@ -181,14 +231,19 @@ export function inferActivity(events, ctx = {}) {
|
|
|
181
231
|
return base;
|
|
182
232
|
}
|
|
183
233
|
/**
|
|
184
|
-
* Scan an event slice for the durable signals
|
|
185
|
-
*
|
|
186
|
-
*
|
|
234
|
+
* Scan an event slice for the durable signals that aren't about the cwd: the PR
|
|
235
|
+
* opened, the injected ticket, plus the artifacts the session PRODUCED — tracker
|
|
236
|
+
* refs it created and any team it spawned. Each `gh pr create` / create-issue tool
|
|
237
|
+
* call is correlated with the nearest following tool_result; the team name comes
|
|
238
|
+
* straight off the `agents teams create/add` command.
|
|
187
239
|
*/
|
|
188
240
|
export function detectDurableSignals(events) {
|
|
189
241
|
let pr;
|
|
190
242
|
let sawPrCreate = false;
|
|
191
243
|
let ticket;
|
|
244
|
+
let sawTicketCreate = false;
|
|
245
|
+
let spawnedTeam;
|
|
246
|
+
const createdTickets = new Set();
|
|
192
247
|
for (const e of events) {
|
|
193
248
|
// Structural PR signal: a real `gh pr create` tool call, then the pull URL
|
|
194
249
|
// from a following tool_result — never a bare URL mentioned in prose.
|
|
@@ -201,21 +256,45 @@ export function detectDurableSignals(events) {
|
|
|
201
256
|
sawPrCreate = false;
|
|
202
257
|
}
|
|
203
258
|
}
|
|
259
|
+
// Produced artifacts: a team spawn is read off the command; a created ticket
|
|
260
|
+
// is a create-issue tool call whose following tool_result carries the new ref.
|
|
261
|
+
if (e.type === 'tool_use') {
|
|
262
|
+
if (!spawnedTeam) {
|
|
263
|
+
const team = detectSpawnedTeam(e.command);
|
|
264
|
+
if (team)
|
|
265
|
+
spawnedTeam = team;
|
|
266
|
+
}
|
|
267
|
+
if (isTicketCreateTool(e.tool, e.command))
|
|
268
|
+
sawTicketCreate = true;
|
|
269
|
+
}
|
|
270
|
+
if (sawTicketCreate && e.type === 'tool_result') {
|
|
271
|
+
const t = extractCreatedTicket(e.output);
|
|
272
|
+
if (t)
|
|
273
|
+
createdTickets.add(t);
|
|
274
|
+
sawTicketCreate = false;
|
|
275
|
+
}
|
|
204
276
|
if (!ticket && e.type === 'message' && e.role === 'user') {
|
|
205
277
|
ticket = detectTicket(e.content);
|
|
206
278
|
}
|
|
207
279
|
}
|
|
208
|
-
return {
|
|
280
|
+
return {
|
|
281
|
+
pr,
|
|
282
|
+
ticket,
|
|
283
|
+
createdTickets: createdTickets.size > 0 ? [...createdTickets] : undefined,
|
|
284
|
+
spawnedTeam,
|
|
285
|
+
};
|
|
209
286
|
}
|
|
210
287
|
/** Full inference: activity + preview + durable signals + worktree/ticket from ctx. */
|
|
211
288
|
export function inferSessionState(events, ctx = {}) {
|
|
212
289
|
const state = inferActivity(events, ctx);
|
|
213
|
-
const { pr, ticket } = detectDurableSignals(events);
|
|
290
|
+
const { pr, ticket, createdTickets, spawnedTeam } = detectDurableSignals(events);
|
|
214
291
|
const worktree = detectWorktree(ctx.cwd, ctx.gitBranch);
|
|
215
292
|
return {
|
|
216
293
|
...state,
|
|
217
294
|
pr: pr ?? state.pr,
|
|
218
295
|
worktree: worktree ?? state.worktree,
|
|
219
296
|
ticket: ticket ?? detectTicket(undefined, ctx.gitBranch) ?? state.ticket,
|
|
297
|
+
createdTickets,
|
|
298
|
+
spawnedTeam,
|
|
220
299
|
};
|
|
221
300
|
}
|
|
@@ -65,16 +65,16 @@ export interface SessionMeta {
|
|
|
65
65
|
version?: string;
|
|
66
66
|
account?: string;
|
|
67
67
|
topic?: string;
|
|
68
|
-
/** Custom name the user gave the session (e.g. Claude Code /rename). */
|
|
69
|
-
label?: string;
|
|
70
68
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* `agents
|
|
74
|
-
*
|
|
75
|
-
*
|
|
69
|
+
* The session's human-readable name — one field, several sources with a plain
|
|
70
|
+
* priority: an agent-generated title / Claude `/rename` wins; else the launch
|
|
71
|
+
* handle seeded from `agents run --name <slug>` (interactive, headless, remote
|
|
72
|
+
* host, or a teams teammate); else it stays unset and the listing falls back to
|
|
73
|
+
* `topic`. Searchable via `agents sessions <label>`. (Before v10 the launch
|
|
74
|
+
* handle lived in a separate immutable `name` column; `--name` now just seeds
|
|
75
|
+
* this label.)
|
|
76
76
|
*/
|
|
77
|
-
|
|
77
|
+
label?: string;
|
|
78
78
|
/** Set when this session was spawned by `agents teams`. */
|
|
79
79
|
teamOrigin?: TeamOrigin;
|
|
80
80
|
/** Durable state signals extracted at scan time by the session-state engine. */
|
|
@@ -86,6 +86,17 @@ export interface SessionMeta {
|
|
|
86
86
|
worktreeSlug?: string;
|
|
87
87
|
/** Tracker ticket ref (e.g. RUSH-1234) from the prompt or branch. */
|
|
88
88
|
ticketId?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Tracker refs the session CREATED during its run — Linear `create_issue` MCP
|
|
91
|
+
* calls or `gh issue create` shell commands — read from the tool result. Distinct
|
|
92
|
+
* from `ticketId` (the injected/worked-on ticket from the prompt or branch).
|
|
93
|
+
*/
|
|
94
|
+
createdTickets?: string[];
|
|
95
|
+
/**
|
|
96
|
+
* Team name this session SPAWNED via `agents teams create/add`. The inverse of
|
|
97
|
+
* `isTeamOrigin` (which marks sessions spawned BY a team).
|
|
98
|
+
*/
|
|
99
|
+
spawnedTeam?: string;
|
|
89
100
|
/**
|
|
90
101
|
* True when the session was spawned programmatically (SDK entrypoint) rather
|
|
91
102
|
* than by a human at the Claude CLI. Captured at scan time from the JSONL
|
package/dist/lib/teams/agents.js
CHANGED
|
@@ -22,6 +22,7 @@ import { getAgentsDir as getSystemAgentsDir, getShimsDir } from '../state.js';
|
|
|
22
22
|
import { AGENTS, getAccountInfo } from '../agents.js';
|
|
23
23
|
import { resolveVersion, isVersionInstalled } from '../versions.js';
|
|
24
24
|
import { sanitizeProcessEnv } from '../secrets/bundles.js';
|
|
25
|
+
import { recordRunName } from '../session/run-names.js';
|
|
25
26
|
let lastMemoryWarnAt = 0;
|
|
26
27
|
// On macOS, os.freemem() returns only the truly-free pool and ignores the
|
|
27
28
|
// large inactive+purgeable cache the kernel will reclaim under pressure, so
|
|
@@ -1067,6 +1068,14 @@ export class AgentManager {
|
|
|
1067
1068
|
throw new Error(`Failed to create agent directory: ${err.message}`);
|
|
1068
1069
|
}
|
|
1069
1070
|
this.agents.set(agentId, agent);
|
|
1071
|
+
// Seed the teammate's session label with its friendly team name, so the run
|
|
1072
|
+
// shows up as `<name>` in `agents sessions` and resolves by it — consistent
|
|
1073
|
+
// with `agents run --name`. For Claude the agent id IS the session id (passed
|
|
1074
|
+
// via --session-id in buildCommand); other agents don't expose a launch-time
|
|
1075
|
+
// id, so they're seeded once discovery captures one. Best-effort.
|
|
1076
|
+
if (agentType === 'claude' && name && !isCloudBacked) {
|
|
1077
|
+
recordRunName({ sessionId: agentId, name, agent: agentType, cwd: resolvedCwd ?? undefined });
|
|
1078
|
+
}
|
|
1070
1079
|
if (isStaged) {
|
|
1071
1080
|
await agent.saveMeta();
|
|
1072
1081
|
debug(`Staged ${agentType} teammate '${name}' in team '${taskName}' (after: ${cleanAfter.join(', ')})`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.45",
|
|
4
4
|
"description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|