@phnx-labs/agents-cli 1.22.45 → 1.22.47
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 +303 -0
- package/LICENSE +102 -182
- package/README.md +54 -17
- package/dist/cli/command-registry.d.ts +2 -1
- package/dist/cli/command-registry.js +6 -2
- package/dist/commands/accounts.js +56 -12
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +229 -0
- package/dist/commands/browser-sessions-picker.js +9 -7
- package/dist/commands/browser.js +266 -15
- package/dist/commands/config.js +26 -4
- package/dist/commands/cost.d.ts +1 -1
- package/dist/commands/detach.d.ts +13 -0
- package/dist/commands/detach.js +11 -8
- package/dist/commands/exec.js +55 -38
- package/dist/commands/feed-watch.d.ts +2 -0
- package/dist/commands/feed-watch.js +28 -0
- package/dist/commands/feed.js +27 -0
- package/dist/commands/feedback.js +3 -19
- package/dist/commands/insights.d.ts +1 -1
- package/dist/commands/insights.js +2 -2
- package/dist/commands/lease.js +3 -3
- package/dist/commands/routines.js +1 -1
- package/dist/commands/sessions-picker-factory.d.ts +2 -1
- package/dist/commands/sessions-picker-factory.js +1 -1
- package/dist/commands/sessions-stop.d.ts +24 -0
- package/dist/commands/sessions-stop.js +66 -0
- package/dist/commands/sessions-trace.d.ts +82 -0
- package/dist/commands/sessions-trace.js +387 -0
- package/dist/commands/sessions.js +4 -0
- package/dist/commands/ssh.d.ts +15 -0
- package/dist/commands/ssh.js +194 -16
- package/dist/commands/teams.js +34 -2
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +19 -32
- package/dist/commands/watchdog.js +9 -14
- package/dist/lib/account-capabilities.d.ts +4 -0
- package/dist/lib/account-capabilities.js +13 -9
- package/dist/lib/account-registry.d.ts +2 -0
- package/dist/lib/account-registry.js +30 -2
- package/dist/lib/accounting/rotate.d.ts +9 -0
- package/dist/lib/accounting/rotate.js +12 -2
- package/dist/lib/accounting/usage.d.ts +92 -6
- package/dist/lib/accounting/usage.js +329 -75
- package/dist/lib/agent-modes.js +29 -1
- package/dist/lib/agent-spec/agents.d.ts +4 -3
- package/dist/lib/agent-spec/agents.js +20 -7
- package/dist/lib/analytics/mix-commands.d.ts +1 -1
- package/dist/lib/analytics/mix-commands.js +2 -2
- package/dist/lib/auth-health.d.ts +13 -3
- package/dist/lib/auth-health.js +50 -8
- package/dist/lib/browser/devices.d.ts +19 -0
- package/dist/lib/browser/devices.js +23 -0
- package/dist/lib/browser/ipc.d.ts +1 -1
- package/dist/lib/browser/ipc.js +33 -7
- package/dist/lib/browser/profiles.d.ts +106 -1
- package/dist/lib/browser/profiles.js +300 -17
- package/dist/lib/browser/remote-control.d.ts +26 -2
- package/dist/lib/browser/remote-control.js +33 -3
- package/dist/lib/browser/runtime-state.d.ts +9 -0
- package/dist/lib/browser/runtime-state.js +24 -8
- package/dist/lib/browser/service.d.ts +37 -0
- package/dist/lib/browser/service.js +78 -20
- package/dist/lib/browser/sessions-list.d.ts +1 -3
- package/dist/lib/browser/sessions-list.js +3 -16
- package/dist/lib/browser/types.d.ts +12 -1
- package/dist/lib/cli-resources.js +17 -15
- package/dist/lib/codex-policy.d.ts +11 -1
- package/dist/lib/codex-policy.js +20 -5
- package/dist/lib/config-keys.d.ts +4 -3
- package/dist/lib/config-keys.js +17 -7
- package/dist/lib/daemon/runner.js +15 -2
- package/dist/lib/daemon-ticks.js +2 -2
- package/dist/lib/device-config.d.ts +25 -8
- package/dist/lib/device-config.js +90 -17
- package/dist/lib/devices/config-migration.d.ts +7 -0
- package/dist/lib/devices/config-migration.js +67 -3
- package/dist/lib/devices/harness-inventory.js +20 -3
- package/dist/lib/devices/health.d.ts +27 -2
- package/dist/lib/devices/health.js +39 -9
- package/dist/lib/devices/interactive-host.d.ts +15 -0
- package/dist/lib/devices/interactive-host.js +53 -0
- package/dist/lib/devices/registry.d.ts +61 -4
- package/dist/lib/devices/registry.js +94 -38
- package/dist/lib/devices/stats-cache.d.ts +6 -0
- package/dist/lib/devices/stats-cache.js +10 -2
- package/dist/lib/exec.d.ts +25 -3
- package/dist/lib/exec.js +75 -22
- package/dist/lib/feed/answer.d.ts +29 -0
- package/dist/lib/feed/answer.js +145 -0
- package/dist/lib/feed/attention.d.ts +83 -0
- package/dist/lib/feed/attention.js +0 -0
- package/dist/lib/feed/feed.d.ts +109 -0
- package/dist/lib/feed/feed.js +154 -3
- package/dist/lib/feed/pr-status.d.ts +14 -0
- package/dist/lib/feed/pr-status.js +47 -0
- package/dist/lib/feed/watch.d.ts +59 -0
- package/dist/lib/feed/watch.js +180 -0
- package/dist/lib/fleet/capture.js +6 -0
- package/dist/lib/fleet/remote-login.js +8 -3
- package/dist/lib/fleet/types.d.ts +17 -0
- package/dist/lib/harness/adapter.d.ts +11 -0
- package/dist/lib/harness/adapters/claude.js +38 -24
- package/dist/lib/harness/adapters/codex.js +2 -4
- package/dist/lib/hooks/cache.js +8 -0
- package/dist/lib/hooks/match.d.ts +3 -0
- package/dist/lib/hooks/match.js +25 -0
- package/dist/lib/hosts/passthrough.js +16 -0
- package/dist/lib/hosts/registry.js +7 -0
- package/dist/lib/identity/client.d.ts +53 -0
- package/dist/lib/identity/client.js +106 -0
- package/dist/lib/identity/index.d.ts +115 -0
- package/dist/lib/identity/index.js +82 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +13 -0
- package/dist/lib/menubar/install-menubar.js +26 -2
- package/dist/lib/open-url.d.ts +60 -2
- package/dist/lib/open-url.js +167 -12
- package/dist/lib/probe.d.ts +8 -0
- package/dist/lib/probe.js +105 -0
- package/dist/lib/refresh-coordinator.d.ts +1 -1
- package/dist/lib/refresh-coordinator.js +1 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +2 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +13 -1
- package/dist/lib/session/active.d.ts +67 -0
- package/dist/lib/session/active.js +69 -1
- package/dist/lib/session/db.js +4 -2
- package/dist/lib/session/discover.d.ts +2 -0
- package/dist/lib/session/discover.js +12 -6
- package/dist/lib/session/prompt.d.ts +35 -0
- package/dist/lib/session/prompt.js +56 -0
- package/dist/lib/session/remote/watch.d.ts +1 -1
- package/dist/lib/session/remote/watch.js +6 -1
- package/dist/lib/session/render.js +7 -3
- package/dist/lib/session/session-cache.js +5 -1
- package/dist/lib/session/trajectory-compare.d.ts +47 -0
- package/dist/lib/session/trajectory-compare.js +116 -0
- package/dist/lib/session/trajectory-html.d.ts +15 -0
- package/dist/lib/session/trajectory-html.js +750 -0
- package/dist/lib/session/trajectory-lineage.d.ts +92 -0
- package/dist/lib/session/trajectory-lineage.js +195 -0
- package/dist/lib/session/trajectory-text.d.ts +37 -0
- package/dist/lib/session/trajectory-text.js +277 -0
- package/dist/lib/session/trajectory.d.ts +102 -0
- package/dist/lib/session/trajectory.js +335 -0
- package/dist/lib/startup/command-registry.d.ts +5 -2
- package/dist/lib/startup/command-registry.js +8 -5
- package/dist/lib/state.d.ts +1 -1
- package/dist/lib/state.js +4 -2
- package/dist/lib/teams/agents.js +9 -0
- package/dist/lib/terminal/inject.d.ts +19 -0
- package/dist/lib/terminal/inject.js +19 -9
- package/dist/lib/tmux/session.d.ts +46 -13
- package/dist/lib/tmux/session.js +173 -27
- package/dist/lib/types.d.ts +16 -0
- package/dist/lib/usage-backoff.d.ts +2 -1
- package/dist/lib/usage-backoff.js +60 -19
- package/dist/lib/usage-refresh.d.ts +17 -3
- package/dist/lib/usage-refresh.js +57 -11
- package/dist/lib/view-types.d.ts +2 -2
- package/dist/lib/watchdog/log.d.ts +1 -1
- package/dist/lib/watchdog/log.js +1 -1
- package/dist/lib/watchdog/runner.d.ts +36 -45
- package/dist/lib/watchdog/runner.js +150 -119
- package/dist/lib/watchdog/service.d.ts +0 -1
- package/dist/lib/watchdog/service.js +0 -1
- package/dist/lib/watchdog/watchdog-agent.d.ts +38 -0
- package/dist/lib/watchdog/watchdog-agent.js +59 -0
- package/dist/lib/watchdog/watchdog.d.ts +11 -2
- package/dist/lib/watchdog/watchdog.js +37 -104
- package/package.json +2 -2
- package/dist/commands/usage.d.ts +0 -27
- package/dist/commands/usage.js +0 -96
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* floor and ceiling are both 5 minutes.
|
|
20
20
|
* - **Hard hourly cap** (`HOURLY_CALL_CAP`) so a stuck "due" loop cannot
|
|
21
21
|
* hammer an endpoint past ~12 calls/account/hour.
|
|
22
|
-
* - **429 backoff.**
|
|
23
|
-
*
|
|
22
|
+
* - **429 backoff.** An account (or provider-wide scope) under
|
|
23
|
+
* `usageRateLimitedUntil` is skipped — no live fetch or re-armed penalty.
|
|
24
24
|
* - **File-only credentials on the daemon path.** Refresh never opens the
|
|
25
25
|
* ACL-bound macOS keychain item (Touch ID storm). It uses the no-ACL
|
|
26
26
|
* access-token cache / setup-token / `.credentials.json` only
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
* serializes read-modify-write; no lost updates.
|
|
38
38
|
* 3. **macOS keychain ACL / Touch ID** — fileOnly refresh never calls
|
|
39
39
|
* `security find-generic-password` on Claude's ACL item.
|
|
40
|
-
* 4. **
|
|
41
|
-
*
|
|
40
|
+
* 4. **Account 429** — that account is skipped until Retry-After while its
|
|
41
|
+
* siblings continue; a provider-wide penalty still skips all accounts.
|
|
42
42
|
* 5. **Expired access token (no refresh)** — usage path never rotates
|
|
43
43
|
* single-use refresh tokens; counts as `failed`, reschedules 5m later.
|
|
44
44
|
* 6. **No file credential on this host** — account skipped / failed; no
|
|
@@ -71,6 +71,12 @@ export const REFRESH_BURN_DIVISOR = 4;
|
|
|
71
71
|
export const HOURLY_CALL_CAP = 12;
|
|
72
72
|
/** How often the daemon wakes to *consider* a refresh pass (due accounts only). */
|
|
73
73
|
export const USAGE_REFRESH_TICK_MS = 60 * 1000;
|
|
74
|
+
/** Consecutive failed live reads before one broken account is quarantined. */
|
|
75
|
+
export const FAILURE_QUARANTINE_THRESHOLD = 3;
|
|
76
|
+
/** A chronic offender waits this long while healthy siblings keep their cadence. */
|
|
77
|
+
export const FAILURE_QUARANTINE_MS = 30 * 60 * 1000;
|
|
78
|
+
const SKIP_JITTER_MIN_MS = 2_000;
|
|
79
|
+
const SKIP_JITTER_RANGE_MS = 3_001;
|
|
74
80
|
const HOUR_MS = 60 * 60 * 1000;
|
|
75
81
|
/** Test seam for the headroom cache path (see usage.ts `setClaudeUsageCachePathForTest`). */
|
|
76
82
|
let headroomCachePathOverride = null;
|
|
@@ -169,8 +175,47 @@ export function nextHeadroomEntry(prev, snapshot, now) {
|
|
|
169
175
|
nextRefreshAt: now + computeNextRefreshDelayMs(headroom.minutesToLimit),
|
|
170
176
|
callTimestamps,
|
|
171
177
|
computedAt: now,
|
|
178
|
+
consecutiveFailures: snapshot ? 0 : (prev?.consecutiveFailures ?? 0) + 1,
|
|
172
179
|
};
|
|
173
180
|
}
|
|
181
|
+
/** Stable per-account delay in [2s, 5s], used to spread skipped accounts. */
|
|
182
|
+
function skipJitterMs(usageKey) {
|
|
183
|
+
let hash = 0;
|
|
184
|
+
for (const char of usageKey)
|
|
185
|
+
hash = (hash * 31 + char.charCodeAt(0)) >>> 0;
|
|
186
|
+
return SKIP_JITTER_MIN_MS + (hash % SKIP_JITTER_RANGE_MS);
|
|
187
|
+
}
|
|
188
|
+
function skippedHeadroomEntry(prev, usageKey, now, index) {
|
|
189
|
+
return {
|
|
190
|
+
status: prev?.status ?? null,
|
|
191
|
+
minutesToLimit: prev?.minutesToLimit ?? null,
|
|
192
|
+
sessionUsedPercent: prev?.sessionUsedPercent ?? null,
|
|
193
|
+
capturedAt: prev?.capturedAt ?? null,
|
|
194
|
+
nextRefreshAt: now + (index + 1) * skipJitterMs(usageKey),
|
|
195
|
+
callTimestamps: pruneCallTimestamps(prev?.callTimestamps ?? [], now),
|
|
196
|
+
computedAt: now,
|
|
197
|
+
consecutiveFailures: prev?.consecutiveFailures ?? 0,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function failedHeadroomEntry(prev, now) {
|
|
201
|
+
const next = nextHeadroomEntry(prev, null, now);
|
|
202
|
+
if ((next.consecutiveFailures ?? 0) >= FAILURE_QUARANTINE_THRESHOLD) {
|
|
203
|
+
next.nextRefreshAt = now + FAILURE_QUARANTINE_MS;
|
|
204
|
+
}
|
|
205
|
+
return next;
|
|
206
|
+
}
|
|
207
|
+
/** Cold accounts lead each pass; both cold and cached groups rotate every tick. */
|
|
208
|
+
export function orderUsageAccounts(accounts, cache, tick) {
|
|
209
|
+
const rotate = (group) => {
|
|
210
|
+
if (group.length < 2)
|
|
211
|
+
return group;
|
|
212
|
+
const start = tick % group.length;
|
|
213
|
+
return [...group.slice(start), ...group.slice(0, start)];
|
|
214
|
+
};
|
|
215
|
+
const cold = accounts.filter((account) => cache[account.usageKey] == null);
|
|
216
|
+
const cached = accounts.filter((account) => cache[account.usageKey] != null);
|
|
217
|
+
return [...rotate(cold), ...rotate(cached)];
|
|
218
|
+
}
|
|
174
219
|
/**
|
|
175
220
|
* Enumerate the usage accounts whose credentials live on THIS host — one
|
|
176
221
|
* per unique usage key, deduped to the most-recently-active version (the same
|
|
@@ -217,7 +262,7 @@ export async function buildLocalUsageAccounts() {
|
|
|
217
262
|
}
|
|
218
263
|
/**
|
|
219
264
|
* One refresher tick: for each local account that is due, under its hourly cap,
|
|
220
|
-
* and not
|
|
265
|
+
* and not backed off, live-fetch its usage, update the cache, and
|
|
221
266
|
* reschedule from the new burn projection. Never throws — a single account's
|
|
222
267
|
* failed fetch leaves its cache untouched and counts as `failed`.
|
|
223
268
|
*/
|
|
@@ -230,14 +275,15 @@ export async function runUsageRefresh(deps) {
|
|
|
230
275
|
skippedCap: 0,
|
|
231
276
|
failed: 0,
|
|
232
277
|
};
|
|
233
|
-
const accounts = await deps.listAccounts();
|
|
234
278
|
const cache = readHeadroomCache();
|
|
279
|
+
const accounts = orderUsageAccounts(await deps.listAccounts(), cache, Math.floor(now / USAGE_REFRESH_TICK_MS));
|
|
235
280
|
const updates = {};
|
|
236
|
-
for (const account of accounts) {
|
|
281
|
+
for (const [index, account] of accounts.entries()) {
|
|
237
282
|
const entry = cache[account.usageKey] ?? null;
|
|
238
|
-
// A provider
|
|
283
|
+
// A penalized account/provider is off-limits — poking it re-arms the
|
|
239
284
|
// penalty (the whole reason usage-backoff exists).
|
|
240
|
-
if ((deps.backoffUntil(account.agentId) ?? 0) > now) {
|
|
285
|
+
if ((deps.backoffUntil(account.agentId, account.usageKey) ?? 0) > now) {
|
|
286
|
+
updates[account.usageKey] = skippedHeadroomEntry(entry, account.usageKey, now, index);
|
|
241
287
|
result.skippedBackoff += 1;
|
|
242
288
|
continue;
|
|
243
289
|
}
|
|
@@ -259,12 +305,12 @@ export async function runUsageRefresh(deps) {
|
|
|
259
305
|
// No live snapshot (expired token / fetch miss): don't rewrite the usage
|
|
260
306
|
// cache, but still record the call + reschedule so a broken account
|
|
261
307
|
// isn't retried every tick.
|
|
262
|
-
updates[account.usageKey] =
|
|
308
|
+
updates[account.usageKey] = failedHeadroomEntry(entry, now);
|
|
263
309
|
result.failed += 1;
|
|
264
310
|
}
|
|
265
311
|
}
|
|
266
312
|
catch {
|
|
267
|
-
updates[account.usageKey] =
|
|
313
|
+
updates[account.usageKey] = failedHeadroomEntry(entry, now);
|
|
268
314
|
result.failed += 1;
|
|
269
315
|
}
|
|
270
316
|
}
|
package/dist/lib/view-types.d.ts
CHANGED
|
@@ -28,8 +28,8 @@ export interface ViewJsonVersion {
|
|
|
28
28
|
resetsAt: string | null;
|
|
29
29
|
}>;
|
|
30
30
|
unavailable?: {
|
|
31
|
-
reason: 'session_limit';
|
|
32
|
-
resetsAt
|
|
31
|
+
reason: 'session_limit' | 'out_of_credits';
|
|
32
|
+
resetsAt?: string;
|
|
33
33
|
};
|
|
34
34
|
lastActive: string | null;
|
|
35
35
|
path: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Same path the Fleet reader pulls (apps/ext/src/core/watchdogLog.ts). */
|
|
2
2
|
export declare const WATCHDOG_LOG_PATH: string;
|
|
3
3
|
/** Kinds the Factory reader accepts. Keep in lockstep with watchdogLog.ts. */
|
|
4
|
-
export type WatchdogEventKind = 'tick' | 'decision' | 'nudge' | 'rotate' | 'error';
|
|
4
|
+
export type WatchdogEventKind = 'tick' | 'decision' | 'nudge' | 'undelivered' | 'rotate' | 'error';
|
|
5
5
|
/** JSONL row shape — a verbatim replica of the Factory reader's WatchdogEvent. */
|
|
6
6
|
export interface WatchdogEvent {
|
|
7
7
|
ts: number;
|
package/dist/lib/watchdog/log.js
CHANGED
|
@@ -18,7 +18,7 @@ import * as path from 'path';
|
|
|
18
18
|
import { withFileLock, atomicWriteFileSync, ensureLockTarget } from '../fs-atomic.js';
|
|
19
19
|
/** Same path the Fleet reader pulls (apps/ext/src/core/watchdogLog.ts). */
|
|
20
20
|
export const WATCHDOG_LOG_PATH = path.join(os.homedir(), '.agents', '.cache', 'logs', 'watchdog.log');
|
|
21
|
-
const WATCHDOG_EVENT_KINDS = new Set(['tick', 'decision', 'nudge', 'rotate', 'error']);
|
|
21
|
+
const WATCHDOG_EVENT_KINDS = new Set(['tick', 'decision', 'nudge', 'undelivered', 'rotate', 'error']);
|
|
22
22
|
/** Parse the audit log without letting a partial final append hide valid rows. */
|
|
23
23
|
export function parseWatchdogEvents(text) {
|
|
24
24
|
const events = [];
|
|
@@ -3,19 +3,23 @@
|
|
|
3
3
|
* working auto-nudge (RUSH-1415). The `agents watchdog` command drives it, so
|
|
4
4
|
* the whole loop runs WITHOUT the Swift menu-bar.
|
|
5
5
|
*
|
|
6
|
-
* One tick
|
|
6
|
+
* One tick:
|
|
7
7
|
*
|
|
8
8
|
* getActiveSessions() (session/active.ts)
|
|
9
|
-
* -> classifyTerminal(...)
|
|
10
|
-
* -> readWatchdogTail(...)
|
|
11
|
-
* ->
|
|
9
|
+
* -> classifyTerminal(...) per session (watchdog/watchdog.ts) — which are idle?
|
|
10
|
+
* -> readWatchdogTail(...) for the idle ones (watchdog/read.ts) — task + transcript tail
|
|
11
|
+
* -> the watchdog AGENT judges them ALL at once (watchdog/watchdog-agent.ts)
|
|
12
|
+
* — idle-but-unfinished -> nudge; idle-and-done / needs-human -> skip
|
|
12
13
|
* -> resolveInjectTargetForSession(...) (terminal/resolve.ts) — THE safety gate: addressable or an honest refusal
|
|
13
|
-
* -> injectIntoTerminal(target,text) (terminal/inject.ts) — deliver
|
|
14
|
+
* -> injectIntoTerminal(target,text) (terminal/inject.ts) — deliver into the EXACT split, then CONFIRM
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* There is no heuristic decider — no regex over the tail guessing done-vs-stuck.
|
|
17
|
+
* The agent is the whole decider, given every idle session's task + tail in ONE
|
|
18
|
+
* `agents run --mode plan` call per tick. The safety gate is absolute: a nudge is
|
|
19
|
+
* delivered ONLY when the resolver returns `addressable: true`, and it is booked in
|
|
20
|
+
* the cooldown ledger ONLY when delivery is CONFIRMED. On `addressable: false` the
|
|
21
|
+
* reason is recorded to a state file the menu-bar can surface and the session is
|
|
22
|
+
* SKIPPED — never a guessed / frontmost target.
|
|
19
23
|
*
|
|
20
24
|
* Persistence (all under ~/.agents/.cache/state/watchdog/, tray-readable):
|
|
21
25
|
* - nudges.json — { [sessionId]: lastNudgeMs } — enforces the cooldown.
|
|
@@ -25,15 +29,16 @@
|
|
|
25
29
|
* - rotate/<sessionId>.json — the in-place rotate state machine (rotate.ts).
|
|
26
30
|
* - rotate-skips.json — zero-healthy skip suppression: { [sessionId]: suppressUntilMs }.
|
|
27
31
|
*
|
|
28
|
-
* The pure logic (classifyTerminal
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
+
* The pure logic (classifyTerminal) is imported and never re-implemented; the
|
|
33
|
+
* runner only supplies its I/O (sessions, tails, clock, policy, the agent decider,
|
|
34
|
+
* injection) — each an injectable seam so runner.test.ts drives real synthetic
|
|
35
|
+
* sessions without a live terminal or a real `agents run`.
|
|
32
36
|
*/
|
|
33
37
|
import type { ActiveSession } from '../session/active.js';
|
|
34
38
|
import { type PresenceTransition } from '../session/presence.js';
|
|
35
39
|
import { type InjectRail, type InjectResult, type InjectTarget } from '../terminal/index.js';
|
|
36
40
|
import { type StallStatus, type WatchdogCandidate } from './watchdog.js';
|
|
41
|
+
import { type WatchdogAgentDecider } from './watchdog-agent.js';
|
|
37
42
|
import { type OpenBlock } from '../feed/feed.js';
|
|
38
43
|
import { type RotateGateResult, type RotatePhase, type RotateState } from './rotate.js';
|
|
39
44
|
/** Per-session policy sentinel. `keep` is the default (watchdog may nudge). */
|
|
@@ -52,16 +57,7 @@ export interface WatchdogTickOptions {
|
|
|
52
57
|
nudge?: boolean;
|
|
53
58
|
/** Nudge text delivered into the terminal. Default "Continue." */
|
|
54
59
|
nudgeText?: string;
|
|
55
|
-
/**
|
|
56
|
-
* Force the LLM decider (`agents run`) on EVERY stalled candidate instead of the
|
|
57
|
-
* hybrid path. Default false. Even when false the tick still ESCALATES the
|
|
58
|
-
* judgment-heavy cases (parked-on-question, ambiguous stalls) to the smart brain
|
|
59
|
-
* — `smart: true` additionally routes the obvious promise-without-toolcall
|
|
60
|
-
* nudges through it. Non-reproducible; the deterministic pre-filter is the
|
|
61
|
-
* reproducible default.
|
|
62
|
-
*/
|
|
63
|
-
smart?: boolean;
|
|
64
|
-
/** Agent the smart decider runs as (and the built-in fallback prompt). Default 'claude'. */
|
|
60
|
+
/** Agent the watchdog decider runs as (and the built-in prompt). Default 'claude'. */
|
|
65
61
|
smartAgent?: string;
|
|
66
62
|
/** Threshold overrides. Missing fields fall back to DEFAULT_THRESHOLDS. */
|
|
67
63
|
thresholds?: Partial<WatchdogThresholds>;
|
|
@@ -82,13 +78,20 @@ export interface WatchdogTickOptions {
|
|
|
82
78
|
/** Per-session policy. Default = the on-disk sentinel. */
|
|
83
79
|
policyFor?: (s: ActiveSession) => WatchdogPolicy;
|
|
84
80
|
/**
|
|
85
|
-
* The
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
81
|
+
* The decider seam: given an idle candidate, decide nudge vs skip and craft the
|
|
82
|
+
* message. Production leaves this unset — the tick judges every idle session in
|
|
83
|
+
* ONE batched `agents run … --mode plan` call (watchdog-agent.ts). Tests inject a
|
|
84
|
+
* synthetic per-candidate decider so the decision path is exercised without
|
|
85
|
+
* shelling out.
|
|
90
86
|
*/
|
|
91
87
|
smartDecider?: SmartDecider;
|
|
88
|
+
/**
|
|
89
|
+
* The batched agent decider used in production (all idle candidates → one call).
|
|
90
|
+
* Default `makeWatchdogAgentDecider`. Tests inject one (e.g. returning an empty
|
|
91
|
+
* map) to exercise the batched path and the no-verdict fallback without shelling
|
|
92
|
+
* out. Ignored when `smartDecider` (the per-candidate test seam) is set.
|
|
93
|
+
*/
|
|
94
|
+
agentDecider?: WatchdogAgentDecider;
|
|
92
95
|
/** Open feed block for a session (parked-on-question detection). Default reads the feed. */
|
|
93
96
|
openBlockFor?: (s: ActiveSession) => OpenBlock | null;
|
|
94
97
|
/** Inject primitive. Default injectIntoTerminal — tests capture the resolved target. */
|
|
@@ -206,31 +209,19 @@ export interface WatchdogTickResult {
|
|
|
206
209
|
export declare function readPolicySentinel(dir: string, sessionId: string): WatchdogPolicy;
|
|
207
210
|
/** Write a per-session policy sentinel (used by the CLI `agents watchdog policy`). */
|
|
208
211
|
export declare function writePolicySentinel(dir: string, sessionId: string, policy: WatchdogPolicy): void;
|
|
209
|
-
/** A
|
|
212
|
+
/** A decision for one idle session. `text` overrides the default nudge text. */
|
|
210
213
|
export interface NudgeDecision {
|
|
211
214
|
nudge: boolean;
|
|
212
215
|
reason: string;
|
|
213
216
|
text?: string;
|
|
214
217
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
* "session is done" skip does NOT trigger a reminder.
|
|
218
|
+
* On a skip, `true` = the agent judged the session genuinely needs the human
|
|
219
|
+
* (surface it — self-file reminder / owner page); `false` / absent = idle-and-
|
|
220
|
+
* done, leave it alone. Gates the self-file reminder so a finished session is
|
|
221
|
+
* never poked.
|
|
220
222
|
*/
|
|
221
223
|
needsHuman?: boolean;
|
|
222
224
|
}
|
|
223
|
-
/**
|
|
224
|
-
* The smart brain. Resolves a `watchdog` workflow for the session's cwd
|
|
225
|
-
* (project > user > system precedence, via resolveWorkflowRef) so a repo/user
|
|
226
|
-
* override AND `model:` frontmatter come for free; when a workflow resolves it
|
|
227
|
-
* runs `agents run watchdog --mode plan <prompt>`, else it falls back to the
|
|
228
|
-
* improved built-in prompt via `agents run <agent> --mode plan <prompt>`. Plan
|
|
229
|
-
* mode keeps the decider read-only. Best-effort and NON-deterministic: any
|
|
230
|
-
* failure (decider unavailable, no verdict) returns a SAFE skip — a parked
|
|
231
|
-
* question we cannot judge is left for the human, never blindly nudged.
|
|
232
|
-
*/
|
|
233
|
-
export declare function makeDefaultSmartDecider(agent: string): SmartDecider;
|
|
234
225
|
/**
|
|
235
226
|
* Run ONE watchdog pass. Returns a structured outcome per live session; injects
|
|
236
227
|
* only when `opts.nudge` is set AND the safety gate says addressable AND policy
|