@phnx-labs/agents-cli 1.22.91 → 1.22.93
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 +21 -0
- package/README.md +5 -0
- package/dist/commands/exec.js +6 -1
- package/dist/lib/claude-account-token.d.ts +13 -0
- package/dist/lib/claude-account-token.js +29 -0
- package/dist/lib/daemon/attention-notify-service.d.ts +5 -2
- package/dist/lib/daemon/attention-notify-service.js +9 -2
- package/dist/lib/exec-account-home.d.ts +19 -0
- package/dist/lib/exec-account-home.js +35 -1
- package/dist/lib/exec.js +11 -0
- package/dist/lib/feed/answer.js +9 -0
- package/dist/lib/feed/attention.d.ts +18 -2
- package/dist/lib/feed/attention.js +0 -0
- package/dist/lib/feed/feed.d.ts +6 -2
- package/dist/lib/feed/feed.js +10 -4
- package/dist/lib/harness/adapter.d.ts +23 -0
- package/dist/lib/harness/adapter.js +29 -0
- package/dist/lib/harness/adapters/claude.js +5 -3
- package/dist/lib/harness/adapters/copilot.js +4 -2
- package/dist/lib/harness/adapters/grok.js +4 -2
- package/dist/lib/harness/adapters/kimi.js +4 -2
- package/dist/lib/harness/adapters/muse.js +4 -3
- package/dist/lib/harness/adapters/opencode.js +4 -2
- package/dist/lib/helper-download.js +1 -1
- package/dist/lib/helper-versions.js +1 -1
- package/dist/lib/installations/shims.d.ts +2 -2
- package/dist/lib/installations/shims.js +16 -8
- package/dist/lib/menubar/download-menubar.js +4 -1
- package/dist/lib/menubar/install-menubar.d.ts +10 -9
- package/dist/lib/menubar/install-menubar.js +17 -15
- package/dist/lib/secrets-policy.d.ts +5 -2
- package/dist/lib/secrets-policy.js +18 -4
- package/dist/lib/session/active.d.ts +18 -11
- package/dist/lib/session/active.js +52 -44
- package/dist/lib/session/session-cache.d.ts +1 -1
- package/dist/lib/session/session-cache.js +1 -0
- package/dist/lib/session/state.d.ts +26 -0
- package/dist/lib/session/state.js +43 -27
- package/package.json +1 -1
|
@@ -18,6 +18,16 @@ import type { SessionAttachment, SessionEvent, TodoItem, TodoProgress } from './
|
|
|
18
18
|
import { type ProducedArtifact } from './highlights.js';
|
|
19
19
|
export type { TodoItem, TodoProgress };
|
|
20
20
|
export type SessionActivity = 'working' | 'waiting_input' | 'idle';
|
|
21
|
+
/**
|
|
22
|
+
* Why a session is `waiting_input`. The state engine produces `question` and
|
|
23
|
+
* `plan_review` from transcript structure. `permission` is NOT inferred from a
|
|
24
|
+
* transcript any more (PHNX-3999): a tool call with no result looks the same
|
|
25
|
+
* whether the tool is still running or a permission dialog is up, and only the
|
|
26
|
+
* harness's own `permission_prompt` hook event (the feed block) can tell them
|
|
27
|
+
* apart. The value stays in the union because a row from a peer running an older
|
|
28
|
+
* CLI can still carry it; the attention reconciler treats such a row as an
|
|
29
|
+
* unverified claim, never as an approvable request.
|
|
30
|
+
*/
|
|
21
31
|
export type AwaitingReason = 'question' | 'plan_review' | 'permission';
|
|
22
32
|
/** One discrete choice the agent offered the user. */
|
|
23
33
|
export interface QuestionOption {
|
|
@@ -103,6 +113,15 @@ export interface SessionState {
|
|
|
103
113
|
/** Last few assistant turns (most-recent last), one line each — panel context. */
|
|
104
114
|
tail?: string[];
|
|
105
115
|
lastActivityMs?: number;
|
|
116
|
+
/**
|
|
117
|
+
* Timestamp (ms) of the last meaningful transcript event — a message, tool
|
|
118
|
+
* call, tool result, thinking block, or error — when the harness stamped one
|
|
119
|
+
* (PHNX-3999). This is the transcript's own cursor: unlike the file mtime it does
|
|
120
|
+
* not move when the harness appends a hook-firing record or other bookkeeping
|
|
121
|
+
* line, so it is the evidence that the agent did (or did not) work past a given
|
|
122
|
+
* moment — what the attention reconciler compares a hook-raised prompt against.
|
|
123
|
+
*/
|
|
124
|
+
lastEventMs?: number;
|
|
106
125
|
pr?: DetectedPr;
|
|
107
126
|
worktree?: DetectedWorktree;
|
|
108
127
|
ticket?: DetectedTicket;
|
|
@@ -122,6 +141,13 @@ export interface StateContext {
|
|
|
122
141
|
pidAlive?: boolean;
|
|
123
142
|
/** Override the running window (defaults to 2 min, matching active.ts). */
|
|
124
143
|
activeWindowMs?: number;
|
|
144
|
+
/**
|
|
145
|
+
* The clock every elapsed-time signal is measured against (default `Date.now()`).
|
|
146
|
+
* Injected so a cached parse can be re-classified against the CURRENT time — a
|
|
147
|
+
* time-based verdict (the prose-question decay) must expire even when the
|
|
148
|
+
* transcript bytes have not changed (PHNX-3999).
|
|
149
|
+
*/
|
|
150
|
+
nowMs?: number;
|
|
125
151
|
}
|
|
126
152
|
/**
|
|
127
153
|
* Derive live plan progress from a checklist tool call's args. Accepts Claude's
|
|
@@ -399,10 +399,10 @@ export function structuredQuestionFromAsk(args) {
|
|
|
399
399
|
return { text, reason: 'question', options: options.length ? options : undefined };
|
|
400
400
|
}
|
|
401
401
|
/**
|
|
402
|
-
* Canonical approve/
|
|
403
|
-
* agent-supplied option list
|
|
404
|
-
*
|
|
405
|
-
*
|
|
402
|
+
* Canonical approve/send-back choices for Claude's plan-review dialog, which
|
|
403
|
+
* carries no agent-supplied option list. Approve is reliably option 1; keep-planning
|
|
404
|
+
* maps to ESC, which cancels the prompt in every variant (2- or 3-option) — safer
|
|
405
|
+
* than guessing a digit that could differ.
|
|
406
406
|
*/
|
|
407
407
|
function planReviewQuestion() {
|
|
408
408
|
return {
|
|
@@ -414,15 +414,22 @@ function planReviewQuestion() {
|
|
|
414
414
|
],
|
|
415
415
|
};
|
|
416
416
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
417
|
+
/**
|
|
418
|
+
* The harness's own stamp on an event, as ms — or undefined when the event carries
|
|
419
|
+
* none. A stamp LATER than the file's last write cannot have come from the
|
|
420
|
+
* transcript (a parser that finds no stamp fills the field with its own parse
|
|
421
|
+
* time), so it is rejected rather than read as "just happened": the mtime is the
|
|
422
|
+
* physical upper bound on when any transcript line was written.
|
|
423
|
+
*/
|
|
424
|
+
function eventStampMs(e, mtimeMs) {
|
|
425
|
+
if (!e?.timestamp)
|
|
426
|
+
return undefined;
|
|
427
|
+
const ms = Date.parse(e.timestamp);
|
|
428
|
+
if (!Number.isFinite(ms))
|
|
429
|
+
return undefined;
|
|
430
|
+
if (mtimeMs != null && ms > mtimeMs)
|
|
431
|
+
return undefined;
|
|
432
|
+
return ms;
|
|
426
433
|
}
|
|
427
434
|
/** Does an assistant message read as a question directed at the user? */
|
|
428
435
|
function looksLikeQuestion(text) {
|
|
@@ -461,7 +468,8 @@ function lastOf(events, pred) {
|
|
|
461
468
|
*/
|
|
462
469
|
export function inferActivity(events, ctx = {}) {
|
|
463
470
|
const windowMs = ctx.activeWindowMs ?? ACTIVE_WINDOW_MS;
|
|
464
|
-
const
|
|
471
|
+
const nowMs = ctx.nowMs ?? Date.now();
|
|
472
|
+
const fresh = ctx.mtimeMs != null && nowMs - ctx.mtimeMs < windowMs;
|
|
465
473
|
// A non-live process (pidAlive === false) can never be "working"; the strongest
|
|
466
474
|
// it gets is "waiting on you" (a dangling question) or "idle".
|
|
467
475
|
const canWork = ctx.pidAlive !== false && (ctx.pidAlive === true || fresh);
|
|
@@ -494,6 +502,7 @@ export function inferActivity(events, ctx = {}) {
|
|
|
494
502
|
lastRole: lastMsg?.role,
|
|
495
503
|
lastEventKind: last?.type,
|
|
496
504
|
lastActivityMs: ctx.mtimeMs,
|
|
505
|
+
lastEventMs: eventStampMs(last, ctx.mtimeMs),
|
|
497
506
|
preview: previewSource ? describeEvent(previewSource) : undefined,
|
|
498
507
|
todos,
|
|
499
508
|
tail: tail.length ? tail : undefined,
|
|
@@ -514,14 +523,17 @@ export function inferActivity(events, ctx = {}) {
|
|
|
514
523
|
const question = structuredQuestionFromAsk(lastPlanOrAsk.args) ?? { text: 'Asked you a question', reason: 'question' };
|
|
515
524
|
return { ...base, activity: 'waiting_input', awaitingReason: 'question', preview: question.text, question };
|
|
516
525
|
}
|
|
517
|
-
// Pending tool call (tool_use with no following tool_result):
|
|
526
|
+
// Pending tool call (tool_use with no following tool_result): the call is in
|
|
527
|
+
// flight for as long as the process is alive. A command that has run for ten
|
|
528
|
+
// minutes and a permission dialog that has sat for ten minutes leave the SAME
|
|
529
|
+
// transcript — a tool_use with no result and a quiet file — so elapsed time is
|
|
530
|
+
// not evidence of a request, and this engine never labels one `permission`
|
|
531
|
+
// (PHNX-3999: the "is the user idle?" reminder and the two-minute mark were both
|
|
532
|
+
// being rendered with Approve/Deny). The harness's own `permission_prompt` hook
|
|
533
|
+
// event, carried by the feed block, is the only evidence a dialog is up; the
|
|
534
|
+
// attention reconciler confirms it against `lastEventMs`.
|
|
518
535
|
if (last.type === 'tool_use') {
|
|
519
|
-
|
|
520
|
-
return { ...base, activity: 'working' };
|
|
521
|
-
// Alive but the file hasn't moved — likely blocked on a permission prompt.
|
|
522
|
-
if (ctx.pidAlive)
|
|
523
|
-
return { ...base, activity: 'waiting_input', awaitingReason: 'permission', question: permissionQuestion(base.preview) };
|
|
524
|
-
return { ...base, activity: 'idle' };
|
|
536
|
+
return { ...base, activity: canWork ? 'working' : 'idle' };
|
|
525
537
|
}
|
|
526
538
|
// Thinking or a tool result just landed → agent is mid-turn if recently active.
|
|
527
539
|
if (last.type === 'thinking' || last.type === 'tool_result' || last.type === 'error') {
|
|
@@ -537,12 +549,16 @@ export function inferActivity(events, ctx = {}) {
|
|
|
537
549
|
// A prose question takes a free-text reply (no select-list), so no options/keys.
|
|
538
550
|
// Unlike the structural plan/ask signals above, the prose heuristic DECAYS: a
|
|
539
551
|
// question nobody answered within PROSE_QUESTION_FRESH_MS is a session that
|
|
540
|
-
// ended, not one that needs you (RUSH-1522).
|
|
541
|
-
//
|
|
542
|
-
//
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
|
|
552
|
+
// ended, not one that needs you (RUSH-1522). The question's age is the
|
|
553
|
+
// assistant message's own stamp when the harness wrote one — the exact moment
|
|
554
|
+
// it was asked — and the file's last write when it did not; the age is measured
|
|
555
|
+
// against `ctx.nowMs`, never a clock frozen at parse time, so the verdict
|
|
556
|
+
// expires on schedule even while the bytes sit still (PHNX-3999). With no age
|
|
557
|
+
// evidence at all the heuristic must not fire (an unknown-age prose question is
|
|
558
|
+
// a session that ended, closing RUSH-1522's null-mtime hole where a null mtime
|
|
559
|
+
// kept the question forever).
|
|
560
|
+
const askedAtMs = eventStampMs(last, ctx.mtimeMs) ?? ctx.mtimeMs;
|
|
561
|
+
const questionFresh = askedAtMs != null && nowMs - askedAtMs < PROSE_QUESTION_FRESH_MS;
|
|
546
562
|
if (questionFresh && looksLikeQuestion(last.content ?? '')) {
|
|
547
563
|
const text = oneLine(last.content ?? '');
|
|
548
564
|
return { ...base, activity: 'waiting_input', awaitingReason: 'question', question: { text, reason: 'question' } };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.93",
|
|
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",
|