@phnx-labs/agents-cli 1.22.47 → 1.22.49
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 +401 -17
- package/README.md +66 -32
- package/dist/bootstrap.js +4 -2
- package/dist/cli/command-registry.d.ts +1 -0
- package/dist/cli/command-registry.js +2 -0
- package/dist/commands/accounts.d.ts +58 -0
- package/dist/commands/accounts.js +206 -32
- package/dist/commands/artifacts.js +7 -6
- package/dist/commands/attach.js +1 -1
- package/dist/commands/browser.js +501 -103
- package/dist/commands/config.d.ts +0 -1
- package/dist/commands/config.js +0 -20
- package/dist/commands/daemon.d.ts +21 -0
- package/dist/commands/daemon.js +147 -12
- package/dist/commands/exec.js +52 -15
- package/dist/commands/focus.d.ts +14 -0
- package/dist/commands/focus.js +48 -5
- package/dist/commands/go.d.ts +3 -0
- package/dist/commands/go.js +16 -3
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/lease.d.ts +1 -1
- package/dist/commands/lease.js +4 -3
- package/dist/commands/mailboxes.js +4 -3
- package/dist/commands/menubar.js +11 -4
- package/dist/commands/projects.d.ts +16 -1
- package/dist/commands/projects.js +58 -64
- package/dist/commands/resume.js +1 -1
- package/dist/commands/route.js +4 -4
- package/dist/commands/routines.js +3 -2
- package/dist/commands/run-broadcast.d.ts +1 -1
- package/dist/commands/run-broadcast.js +1 -1
- package/dist/commands/sessions-picker.d.ts +22 -0
- package/dist/commands/sessions-picker.js +48 -9
- package/dist/commands/sessions-trace.js +1 -1
- package/dist/commands/sessions.js +1 -0
- package/dist/commands/setup-secrets.js +23 -0
- package/dist/commands/share.d.ts +20 -5
- package/dist/commands/share.js +152 -52
- package/dist/commands/ssh.js +54 -7
- package/dist/commands/traces.d.ts +2 -0
- package/dist/commands/traces.js +184 -0
- package/dist/commands/view.d.ts +1 -1
- package/dist/commands/view.js +17 -34
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/lib/accounting/usage.d.ts +14 -3
- package/dist/lib/accounting/usage.js +64 -18
- package/dist/lib/agent-spec/agents.js +11 -9
- package/dist/lib/browser/drivers/ssh.d.ts +16 -2
- package/dist/lib/browser/drivers/ssh.js +36 -9
- package/dist/lib/browser/ipc.js +8 -3
- package/dist/lib/browser/profiles.d.ts +35 -101
- package/dist/lib/browser/profiles.js +130 -301
- package/dist/lib/browser/registry.d.ts +39 -0
- package/dist/lib/browser/registry.js +149 -0
- package/dist/lib/browser/remote-control.d.ts +8 -6
- package/dist/lib/browser/remote-control.js +8 -6
- package/dist/lib/browser/resolve-target.d.ts +68 -0
- package/dist/lib/browser/resolve-target.js +250 -0
- package/dist/lib/browser/runtime-state.d.ts +40 -16
- package/dist/lib/browser/runtime-state.js +92 -25
- package/dist/lib/browser/service.d.ts +28 -9
- package/dist/lib/browser/service.js +139 -62
- package/dist/lib/browser/task-index.d.ts +70 -0
- package/dist/lib/browser/task-index.js +180 -0
- package/dist/lib/byok-usage.js +2 -2
- package/dist/lib/claude-statusline.d.ts +29 -0
- package/dist/lib/claude-statusline.js +139 -0
- package/dist/lib/cloud/host.d.ts +1 -1
- package/dist/lib/cloud/host.js +1 -1
- package/dist/lib/cloud/rush.js +1 -1
- package/dist/lib/computer/computer-rpc.js +2 -2
- package/dist/lib/computer/download.d.ts +15 -15
- package/dist/lib/computer/download.js +35 -98
- package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
- package/dist/lib/computer/ssh-tunnel.js +12 -22
- package/dist/lib/config-keys.d.ts +2 -8
- package/dist/lib/config-keys.js +2 -13
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +2 -2
- package/dist/lib/crabbox/lease.d.ts +9 -0
- package/dist/lib/crabbox/lease.js +70 -15
- package/dist/lib/crabbox/runtimes.js +1 -1
- package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
- package/dist/lib/daemon/account-state-daemon-service.js +26 -0
- package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
- package/dist/lib/daemon/browser-ipc-service.js +46 -0
- package/dist/lib/daemon/daemon.d.ts +3 -0
- package/dist/lib/daemon/daemon.js +212 -444
- package/dist/lib/daemon/device-probe-service.d.ts +26 -0
- package/dist/lib/daemon/device-probe-service.js +47 -0
- package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
- package/dist/lib/daemon/keychain-reap-service.js +32 -0
- package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
- package/dist/lib/daemon/monitor-engine-service.js +29 -0
- package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
- package/dist/lib/daemon/secrets-broker-service.js +66 -0
- package/dist/lib/daemon/self-heal-service.d.ts +27 -0
- package/dist/lib/daemon/self-heal-service.js +48 -0
- package/dist/lib/daemon/service.d.ts +102 -0
- package/dist/lib/daemon/service.js +98 -0
- package/dist/lib/daemon/session-index-service.d.ts +22 -0
- package/dist/lib/daemon/session-index-service.js +40 -0
- package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
- package/dist/lib/daemon/state-dir-check-service.js +63 -0
- package/dist/lib/daemon/supervisor.d.ts +87 -0
- package/dist/lib/daemon/supervisor.js +298 -0
- package/dist/lib/daemon/watchdog-service.d.ts +19 -0
- package/dist/lib/daemon/watchdog-service.js +40 -0
- package/dist/lib/daemon-health.d.ts +15 -0
- package/dist/lib/daemon-health.js +29 -5
- package/dist/lib/daemon-services.d.ts +8 -1
- package/dist/lib/daemon-services.js +55 -0
- package/dist/lib/daemon-ticks.d.ts +13 -2
- package/dist/lib/daemon-ticks.js +20 -18
- package/dist/lib/device-config.d.ts +0 -2
- package/dist/lib/device-config.js +1 -23
- package/dist/lib/devices/registry.d.ts +1 -1
- package/dist/lib/devices/registry.js +1 -1
- package/dist/lib/devices/stats-cache.d.ts +20 -0
- package/dist/lib/devices/stats-cache.js +49 -5
- package/dist/lib/devices/worker-pick.d.ts +48 -0
- package/dist/lib/devices/worker-pick.js +113 -0
- package/dist/lib/exec.d.ts +67 -13
- package/dist/lib/exec.js +85 -29
- package/dist/lib/harness/adapters/cursor.js +8 -11
- package/dist/lib/helper-download.d.ts +108 -0
- package/dist/lib/helper-download.js +194 -0
- package/dist/lib/helper-versions.d.ts +52 -0
- package/dist/lib/helper-versions.js +58 -0
- package/dist/lib/hooks/cache.js +11 -0
- package/dist/lib/hooks/install.js +2 -2
- package/dist/lib/hosts/credentials.d.ts +1 -1
- package/dist/lib/hosts/credentials.js +1 -1
- package/dist/lib/hosts/dispatch.d.ts +7 -1
- package/dist/lib/hosts/dispatch.js +24 -5
- package/dist/lib/hosts/passthrough.d.ts +8 -15
- package/dist/lib/hosts/passthrough.js +19 -16
- package/dist/lib/hosts/providers/devices.d.ts +1 -1
- package/dist/lib/hosts/providers/devices.js +1 -1
- package/dist/lib/hosts/reconnect.d.ts +179 -21
- package/dist/lib/hosts/reconnect.js +266 -45
- package/dist/lib/installations/migrate.d.ts +4 -5
- package/dist/lib/installations/migrate.js +6 -7
- package/dist/lib/installations/shims.d.ts +1 -1
- package/dist/lib/installations/shims.js +23 -13
- package/dist/lib/installations/versions.js +25 -2
- package/dist/lib/linear-autoclose.d.ts +1 -1
- package/dist/lib/linear-autoclose.js +1 -1
- package/dist/lib/loop.d.ts +2 -2
- package/dist/lib/loop.js +1 -1
- package/dist/lib/menubar/download-menubar.d.ts +47 -0
- package/dist/lib/menubar/download-menubar.js +60 -0
- package/dist/lib/menubar/install-menubar.d.ts +31 -7
- package/dist/lib/menubar/install-menubar.js +79 -21
- package/dist/lib/menubar/notify-desktop.d.ts +3 -3
- package/dist/lib/menubar/notify-desktop.js +6 -6
- package/dist/lib/model-tiers.d.ts +1 -1
- package/dist/lib/project-probe.d.ts +1 -1
- package/dist/lib/project-probe.js +1 -1
- package/dist/lib/project-pull.d.ts +1 -1
- package/dist/lib/project-pull.js +2 -2
- package/dist/lib/projects.d.ts +1 -1
- package/dist/lib/projects.js +1 -1
- package/dist/lib/sandbox.js +6 -7
- package/dist/lib/secrets/download-keychain.d.ts +47 -0
- package/dist/lib/secrets/download-keychain.js +70 -0
- package/dist/lib/secrets/install-helper.d.ts +20 -1
- package/dist/lib/secrets/install-helper.js +44 -3
- package/dist/lib/session/active.js +17 -1
- package/dist/lib/session/db.d.ts +11 -1
- package/dist/lib/session/db.js +138 -6
- package/dist/lib/session/discover.d.ts +21 -1
- package/dist/lib/session/discover.js +33 -5
- package/dist/lib/session/highlights.d.ts +49 -1
- package/dist/lib/session/highlights.js +95 -0
- package/dist/lib/session/host-link.d.ts +16 -2
- package/dist/lib/session/host-link.js +7 -1
- package/dist/lib/session/parse.js +33 -1
- package/dist/lib/session/remote/remote-list.d.ts +9 -0
- package/dist/lib/session/remote/remote-list.js +19 -2
- package/dist/lib/session/remote/watch.js +11 -2
- package/dist/lib/session/session-cache.d.ts +53 -0
- package/dist/lib/session/session-cache.js +88 -0
- package/dist/lib/session/state.js +1 -1
- package/dist/lib/session/tool-calls.d.ts +2 -0
- package/dist/lib/session/tool-calls.js +1 -1
- package/dist/lib/session/trajectory-html.d.ts +5 -1
- package/dist/lib/session/trajectory-html.js +42 -22
- package/dist/lib/session/trajectory-text.js +9 -2
- package/dist/lib/session/trajectory.js +53 -4
- package/dist/lib/session/types.d.ts +15 -0
- package/dist/lib/sha256-asset.d.ts +26 -0
- package/dist/lib/sha256-asset.js +41 -0
- package/dist/lib/share/backend.d.ts +95 -0
- package/dist/lib/share/backend.js +139 -0
- package/dist/lib/share/delete.d.ts +6 -2
- package/dist/lib/share/delete.js +8 -9
- package/dist/lib/share/html.d.ts +22 -0
- package/dist/lib/share/html.js +88 -0
- package/dist/lib/share/provision.d.ts +18 -4
- package/dist/lib/share/provision.js +38 -4
- package/dist/lib/share/publish.d.ts +39 -15
- package/dist/lib/share/publish.js +55 -26
- package/dist/lib/share/worker-template.js +171 -11
- package/dist/lib/sqlite.js +16 -3
- package/dist/lib/ssh-exec.d.ts +65 -0
- package/dist/lib/ssh-exec.js +120 -4
- package/dist/lib/startup/command-registry.js +1 -1
- package/dist/lib/startup/dev-build.js +3 -3
- package/dist/lib/state.d.ts +1 -1
- package/dist/lib/state.js +7 -3
- package/dist/lib/subagents-registry.js +1 -1
- package/dist/lib/terminal/index.d.ts +1 -1
- package/dist/lib/terminal/types.d.ts +1 -1
- package/dist/lib/terminal/types.js +1 -1
- package/dist/lib/tmux/orphan-reap.js +1 -1
- package/dist/lib/tmux/session.js +31 -0
- package/dist/lib/traces/backend.d.ts +29 -0
- package/dist/lib/traces/backend.js +46 -0
- package/dist/lib/traces/classify.d.ts +52 -0
- package/dist/lib/traces/classify.js +72 -0
- package/dist/lib/traces/config.d.ts +10 -0
- package/dist/lib/traces/config.js +2 -0
- package/dist/lib/traces/provision.d.ts +19 -0
- package/dist/lib/traces/provision.js +30 -0
- package/dist/lib/traces/sync.d.ts +150 -0
- package/dist/lib/traces/sync.js +447 -0
- package/dist/lib/traces/worker-template.d.ts +4 -0
- package/dist/lib/traces/worker-template.js +159 -0
- package/dist/lib/types.d.ts +18 -19
- package/dist/lib/types.js +14 -0
- package/dist/lib/usage-refresh.d.ts +1 -1
- package/dist/lib/usage-refresh.js +11 -8
- package/dist/lib/workflows.d.ts +1 -1
- package/package.json +4 -6
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -26
- 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 +0 -135
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
- package/dist/lib/usage-fleet.d.ts +0 -32
- package/dist/lib/usage-fleet.js +0 -125
package/dist/lib/tmux/session.js
CHANGED
|
@@ -34,6 +34,22 @@ export const AGENTS_TMUX_HISTORY_LIMIT = 20_000;
|
|
|
34
34
|
export const AGENTS_TMUX_CONFIG_SCHEMA = 1;
|
|
35
35
|
/** Server-scoped user-option recording which AGENTS_TMUX_CONFIG_SCHEMA is applied. */
|
|
36
36
|
const CONFIG_SCHEMA_OPTION = '@ag_tmux_config_schema';
|
|
37
|
+
/**
|
|
38
|
+
* Session-scoped user options naming WHICH agent session a wrapper session runs,
|
|
39
|
+
* so a status bar (or any tmux format) can show it without shelling out:
|
|
40
|
+
* `#{@ag_session_id}` / `#{@ag_agent}`.
|
|
41
|
+
*
|
|
42
|
+
* The session NAME cannot serve this purpose. runInTmux builds it from
|
|
43
|
+
* `(options.sessionId ?? randomUUID()).slice(0, 8)` (exec.ts), so a run with no
|
|
44
|
+
* pre-assigned id still gets 8 hex characters — indistinguishable from a real
|
|
45
|
+
* short session id but resolving to nothing. Only claude currently takes a
|
|
46
|
+
* forced `--session-id`, so on a box running both, `ag-claude-<real>` and
|
|
47
|
+
* `ag-codex-<fabricated>` look identical. These options are stamped ONLY from
|
|
48
|
+
* `labels.sessionId`, which is set only when the id is genuine — absent rather
|
|
49
|
+
* than wrong, so a consumer can fall back instead of displaying a fake handle.
|
|
50
|
+
*/
|
|
51
|
+
const SESSION_ID_OPTION = '@ag_session_id';
|
|
52
|
+
const AGENT_NAME_OPTION = '@ag_agent';
|
|
37
53
|
let startupConfigSequence = 0;
|
|
38
54
|
function tmuxConfigArgument(value) {
|
|
39
55
|
return `"${value.replace(/([\\"$])/g, '\\$1')}"`;
|
|
@@ -232,6 +248,21 @@ export async function createSession(opts) {
|
|
|
232
248
|
if (pane) {
|
|
233
249
|
await runTmux({ socket, args: ['set-option', '-pt', pane, 'remain-on-exit', 'on', ';', 'set-option', '-g', 'remain-on-exit', 'off'], throwOnError: false }).catch(() => { });
|
|
234
250
|
}
|
|
251
|
+
// Stamp the agent identity onto the session so tmux formats can read it.
|
|
252
|
+
// Best-effort: a failure here must not fail a launch that otherwise worked,
|
|
253
|
+
// and an unset option simply renders empty in a format.
|
|
254
|
+
const identityArgs = [];
|
|
255
|
+
if (opts.labels?.sessionId) {
|
|
256
|
+
identityArgs.push('set-option', '-t', opts.name, SESSION_ID_OPTION, opts.labels.sessionId);
|
|
257
|
+
}
|
|
258
|
+
if (opts.labels?.agent) {
|
|
259
|
+
if (identityArgs.length)
|
|
260
|
+
identityArgs.push(';');
|
|
261
|
+
identityArgs.push('set-option', '-t', opts.name, AGENT_NAME_OPTION, opts.labels.agent);
|
|
262
|
+
}
|
|
263
|
+
if (identityArgs.length) {
|
|
264
|
+
await runTmux({ socket, args: identityArgs, throwOnError: false }).catch(() => { });
|
|
265
|
+
}
|
|
235
266
|
const meta = {
|
|
236
267
|
name: opts.name,
|
|
237
268
|
socket,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TracesBackend — token-source seam for `agents traces sync`.
|
|
3
|
+
*
|
|
4
|
+
* Managed path: signed in to Phoenix (`readSession() != null`), no BYO
|
|
5
|
+
* override. Token is the Phoenix access_token; endpoint is the live
|
|
6
|
+
* `traces.agents-cli.sh` Worker. Zero Cloudflare setup.
|
|
7
|
+
*
|
|
8
|
+
* BYO path (future): TRACES_BASE_URL + TRACES_WRITE_TOKEN env vars. Not
|
|
9
|
+
* exposed in the CLI for M1 — follow-on milestone, same pattern as
|
|
10
|
+
* `agents artifacts setup`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_TRACES_DOMAIN = "traces.agents-cli.sh";
|
|
13
|
+
export interface TracesBackend {
|
|
14
|
+
/** Public base URL of the traces Worker, no trailing slash. */
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
/** Bearer sent as `Authorization`. Phoenix access_token. */
|
|
17
|
+
token: string;
|
|
18
|
+
/** Phoenix userId — the R2 namespace prefix. */
|
|
19
|
+
userId: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the backend for the current machine. Throws when not signed in.
|
|
23
|
+
*
|
|
24
|
+
* BYO override env vars (for testing / self-hosted):
|
|
25
|
+
* AGENTS_TRACES_BASE_URL — Worker URL, no trailing slash
|
|
26
|
+
* AGENTS_TRACES_WRITE_TOKEN — static write token (bypasses Phoenix auth)
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveTracesBackend(): TracesBackend;
|
|
29
|
+
export declare function managedTracesBaseUrl(): string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TracesBackend — token-source seam for `agents traces sync`.
|
|
3
|
+
*
|
|
4
|
+
* Managed path: signed in to Phoenix (`readSession() != null`), no BYO
|
|
5
|
+
* override. Token is the Phoenix access_token; endpoint is the live
|
|
6
|
+
* `traces.agents-cli.sh` Worker. Zero Cloudflare setup.
|
|
7
|
+
*
|
|
8
|
+
* BYO path (future): TRACES_BASE_URL + TRACES_WRITE_TOKEN env vars. Not
|
|
9
|
+
* exposed in the CLI for M1 — follow-on milestone, same pattern as
|
|
10
|
+
* `agents artifacts setup`.
|
|
11
|
+
*/
|
|
12
|
+
import { readSession } from '../identity/client.js';
|
|
13
|
+
export const DEFAULT_TRACES_DOMAIN = 'traces.agents-cli.sh';
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the backend for the current machine. Throws when not signed in.
|
|
16
|
+
*
|
|
17
|
+
* BYO override env vars (for testing / self-hosted):
|
|
18
|
+
* AGENTS_TRACES_BASE_URL — Worker URL, no trailing slash
|
|
19
|
+
* AGENTS_TRACES_WRITE_TOKEN — static write token (bypasses Phoenix auth)
|
|
20
|
+
*/
|
|
21
|
+
export function resolveTracesBackend() {
|
|
22
|
+
const envBase = (process.env['AGENTS_TRACES_BASE_URL'] ?? '').replace(/\/+$/, '').trim();
|
|
23
|
+
const envToken = (process.env['AGENTS_TRACES_WRITE_TOKEN'] ?? '').trim();
|
|
24
|
+
if (envBase && envToken) {
|
|
25
|
+
return { baseUrl: envBase, token: envToken, userId: 'byo' };
|
|
26
|
+
}
|
|
27
|
+
const session = readSession();
|
|
28
|
+
if (!session) {
|
|
29
|
+
throw new Error("Not signed in. Run 'agents auth login' to sync traces to your Phoenix account.");
|
|
30
|
+
}
|
|
31
|
+
if (!session.access_token) {
|
|
32
|
+
throw new Error("Session has no access token. Run 'agents auth login' again.");
|
|
33
|
+
}
|
|
34
|
+
const userId = (session.userId ?? '').trim();
|
|
35
|
+
if (!userId) {
|
|
36
|
+
throw new Error("Signed in but the session has no user id. Run 'agents auth login' again.");
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
baseUrl: managedTracesBaseUrl(),
|
|
40
|
+
token: session.access_token,
|
|
41
|
+
userId,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function managedTracesBaseUrl() {
|
|
45
|
+
return `https://${DEFAULT_TRACES_DOMAIN}`;
|
|
46
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type TraceTopicGroup = 'code' | 'research' | 'review' | 'content' | 'ops';
|
|
2
|
+
export type TraceFailureCause = 'real' | 'guard' | 'hook';
|
|
3
|
+
/** Per-bucket aggregate stats for one day, stored in the rolling bucketHistory. */
|
|
4
|
+
export interface BucketStats {
|
|
5
|
+
key: string;
|
|
6
|
+
date: string;
|
|
7
|
+
count: number;
|
|
8
|
+
/** Tool-error count / total tool calls in this session set (0–1). */
|
|
9
|
+
errorRate: number;
|
|
10
|
+
/** Sessions with ≥1 stall friction signal / total sessions in bucket (0–1). */
|
|
11
|
+
stallRate: number;
|
|
12
|
+
}
|
|
13
|
+
/** Movement signal for one topic bucket compared to its 7-day rolling average. */
|
|
14
|
+
export interface DriftSignal {
|
|
15
|
+
bucket: string;
|
|
16
|
+
/** current errorRate − 7d average (positive = degrading). */
|
|
17
|
+
errorDelta: number;
|
|
18
|
+
/** current stallRate − 7d average (positive = degrading). */
|
|
19
|
+
stallDelta: number;
|
|
20
|
+
severity: 'degrading' | 'stable' | 'improving';
|
|
21
|
+
}
|
|
22
|
+
export interface TopicEvidence {
|
|
23
|
+
cwd?: string | null;
|
|
24
|
+
gitBranch?: string | null;
|
|
25
|
+
topic?: string | null;
|
|
26
|
+
label?: string | null;
|
|
27
|
+
toolMix?: Record<string, number>;
|
|
28
|
+
}
|
|
29
|
+
export interface ClassifiedTopic {
|
|
30
|
+
group: TraceTopicGroup;
|
|
31
|
+
key: string;
|
|
32
|
+
label: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ToolCallFailure {
|
|
35
|
+
tool: string;
|
|
36
|
+
exit_code?: number | null;
|
|
37
|
+
status_code?: number | null;
|
|
38
|
+
error_code?: string | null;
|
|
39
|
+
error?: string | null;
|
|
40
|
+
parse_error?: string | null;
|
|
41
|
+
outcome?: string | null;
|
|
42
|
+
}
|
|
43
|
+
/** Classify a session from metadata and aggregate tool names; never reads transcript text. */
|
|
44
|
+
export declare function classifyTopic(input: TopicEvidence): ClassifiedTopic;
|
|
45
|
+
/** Bucket a failed tool call without inspecting raw tool input or transcript text. */
|
|
46
|
+
export declare function classifyCause(call: ToolCallFailure): TraceFailureCause;
|
|
47
|
+
/**
|
|
48
|
+
* Compare today's per-bucket stats to the last 7 days of history and return
|
|
49
|
+
* movement signals. Buckets with fewer than 3 historical days are skipped —
|
|
50
|
+
* not enough signal to distinguish noise from drift.
|
|
51
|
+
*/
|
|
52
|
+
export declare function computeDriftSignal(history: BucketStats[][], today: BucketStats[]): DriftSignal[];
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
function normalizedEvidence(input) {
|
|
2
|
+
return [input.cwd, input.gitBranch, input.topic, input.label]
|
|
3
|
+
.filter((value) => typeof value === 'string')
|
|
4
|
+
.join(' ')
|
|
5
|
+
.toLowerCase();
|
|
6
|
+
}
|
|
7
|
+
/** Classify a session from metadata and aggregate tool names; never reads transcript text. */
|
|
8
|
+
export function classifyTopic(input) {
|
|
9
|
+
const text = normalizedEvidence(input);
|
|
10
|
+
const tools = Object.keys(input.toolMix ?? {}).map((tool) => tool.toLowerCase());
|
|
11
|
+
const hasTool = (pattern) => tools.some((tool) => pattern.test(tool));
|
|
12
|
+
if (/\b(review|audit|pr[-_/ ]?review|code[-_/ ]?review)\b/.test(text)
|
|
13
|
+
|| hasTool(/review|comment/)) {
|
|
14
|
+
return { group: 'review', key: 'code-review', label: 'Code review' };
|
|
15
|
+
}
|
|
16
|
+
if (/\b(research|investigat|analysis|benchmark|paper|docs?)\b/.test(text)
|
|
17
|
+
|| hasTool(/web_search|search_query|webfetch|browser/)) {
|
|
18
|
+
return { group: 'research', key: 'research', label: 'Research' };
|
|
19
|
+
}
|
|
20
|
+
if (/\b(blog|copy|content|post|article|script|caption)\b/.test(text)) {
|
|
21
|
+
return { group: 'content', key: 'content', label: 'Content' };
|
|
22
|
+
}
|
|
23
|
+
if (/\b(deploy|release|infra|ci|ops|fleet|daemon|provision)\b/.test(text)
|
|
24
|
+
|| hasTool(/ssh|deploy|computer/)) {
|
|
25
|
+
return { group: 'ops', key: 'operations', label: 'Operations' };
|
|
26
|
+
}
|
|
27
|
+
if (/\b(test|fix|bug|feat|refactor|implement|code)\b/.test(text)
|
|
28
|
+
|| hasTool(/edit|write|apply_patch|exec|bash|shell/)) {
|
|
29
|
+
return { group: 'code', key: 'engineering', label: 'Engineering' };
|
|
30
|
+
}
|
|
31
|
+
return { group: 'research', key: 'general', label: 'General' };
|
|
32
|
+
}
|
|
33
|
+
/** Bucket a failed tool call without inspecting raw tool input or transcript text. */
|
|
34
|
+
export function classifyCause(call) {
|
|
35
|
+
const evidence = [call.error_code, call.error, call.parse_error]
|
|
36
|
+
.filter((value) => typeof value === 'string')
|
|
37
|
+
.join(' ')
|
|
38
|
+
.toLowerCase();
|
|
39
|
+
if (/\b(?:git-guard|main-branch-guard)\b/.test(evidence))
|
|
40
|
+
return 'guard';
|
|
41
|
+
if (/permission\b.*\bdenied\b.*\b(?:auto[- ]mode|classifier)\b/.test(evidence)) {
|
|
42
|
+
return 'hook';
|
|
43
|
+
}
|
|
44
|
+
return 'real';
|
|
45
|
+
}
|
|
46
|
+
const DRIFT_THRESHOLD = 0.20;
|
|
47
|
+
/**
|
|
48
|
+
* Compare today's per-bucket stats to the last 7 days of history and return
|
|
49
|
+
* movement signals. Buckets with fewer than 3 historical days are skipped —
|
|
50
|
+
* not enough signal to distinguish noise from drift.
|
|
51
|
+
*/
|
|
52
|
+
export function computeDriftSignal(history, today) {
|
|
53
|
+
const signals = [];
|
|
54
|
+
for (const stat of today) {
|
|
55
|
+
const pastStats = history
|
|
56
|
+
.flatMap((day) => day.filter((b) => b.key === stat.key))
|
|
57
|
+
.slice(-7);
|
|
58
|
+
if (pastStats.length < 3)
|
|
59
|
+
continue;
|
|
60
|
+
const avgError = pastStats.reduce((sum, b) => sum + b.errorRate, 0) / pastStats.length;
|
|
61
|
+
const avgStall = pastStats.reduce((sum, b) => sum + b.stallRate, 0) / pastStats.length;
|
|
62
|
+
const errorDelta = stat.errorRate - avgError;
|
|
63
|
+
const stallDelta = stat.stallRate - avgStall;
|
|
64
|
+
const severity = errorDelta > DRIFT_THRESHOLD || stallDelta > DRIFT_THRESHOLD
|
|
65
|
+
? 'degrading'
|
|
66
|
+
: errorDelta < -DRIFT_THRESHOLD || stallDelta < -DRIFT_THRESHOLD
|
|
67
|
+
? 'improving'
|
|
68
|
+
: 'stable';
|
|
69
|
+
signals.push({ bucket: stat.key, errorDelta, stallDelta, severity });
|
|
70
|
+
}
|
|
71
|
+
return signals.sort((a, b) => b.errorDelta - a.errorDelta);
|
|
72
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Isolated Cloudflare resources for the private Phoenix trace store. */
|
|
2
|
+
export interface TracesConfig {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
accountId: string;
|
|
5
|
+
workerName: string;
|
|
6
|
+
bucketName: string;
|
|
7
|
+
domain?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const DEFAULT_WORKER_NAME = "agents-traces";
|
|
10
|
+
export declare const DEFAULT_BUCKET_NAME = "agents-traces";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type CloudflareRequester } from '../share/provision.js';
|
|
2
|
+
interface ProvisionOptions {
|
|
3
|
+
request?: CloudflareRequester;
|
|
4
|
+
}
|
|
5
|
+
/** Bind the Phoenix identity base URL used to verify every trace read and write. */
|
|
6
|
+
export declare function setPhoenixIdBaseSecret(apiToken: string, accountId: string, workerName: string, phoenixIdBase: string, opts?: ProvisionOptions): Promise<void>;
|
|
7
|
+
export interface ProvisionTracesOptions extends ProvisionOptions {
|
|
8
|
+
apiToken: string;
|
|
9
|
+
accountId: string;
|
|
10
|
+
workerName: string;
|
|
11
|
+
bucketName: string;
|
|
12
|
+
phoenixIdBase: string;
|
|
13
|
+
domain?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Provision the complete isolated traces deployment using the canonical Worker template. */
|
|
16
|
+
export declare function provisionTraces(opts: ProvisionTracesOptions): Promise<{
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
}>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Cloudflare orchestration for the private agents-traces store. The generic
|
|
2
|
+
// request primitives live in share/provision; only the isolated traces resource
|
|
3
|
+
// choices and Worker template belong here.
|
|
4
|
+
import { randomBytes } from 'node:crypto';
|
|
5
|
+
import { addCustomDomain, createBucket, deployWorker, enableWorkersDev, findZoneId, putWorkerSecret, } from '../share/provision.js';
|
|
6
|
+
import { DEFAULT_TRACES_DOMAIN } from './backend.js';
|
|
7
|
+
import { renderTracesWorkerScript } from './worker-template.js';
|
|
8
|
+
/** Bind the Phoenix identity base URL used to verify every trace read and write. */
|
|
9
|
+
export async function setPhoenixIdBaseSecret(apiToken, accountId, workerName, phoenixIdBase, opts = {}) {
|
|
10
|
+
const normalized = phoenixIdBase.replace(/\/+$/, '').trim();
|
|
11
|
+
if (!normalized)
|
|
12
|
+
throw new Error('PHOENIX_ID_BASE is required to verify trace requests.');
|
|
13
|
+
await putWorkerSecret(apiToken, accountId, workerName, 'PHOENIX_ID_BASE', normalized, opts);
|
|
14
|
+
}
|
|
15
|
+
/** Provision the complete isolated traces deployment using the canonical Worker template. */
|
|
16
|
+
export async function provisionTraces(opts) {
|
|
17
|
+
const domain = opts.domain ?? DEFAULT_TRACES_DOMAIN;
|
|
18
|
+
const requestOpts = opts.request ? { request: opts.request } : {};
|
|
19
|
+
await createBucket(opts.apiToken, opts.accountId, opts.bucketName, requestOpts);
|
|
20
|
+
await deployWorker(opts.apiToken, opts.accountId, opts.workerName, renderTracesWorkerScript(), opts.bucketName, requestOpts);
|
|
21
|
+
await putWorkerSecret(opts.apiToken, opts.accountId, opts.workerName, 'WRITE_TOKEN', randomBytes(32).toString('hex'), requestOpts);
|
|
22
|
+
await setPhoenixIdBaseSecret(opts.apiToken, opts.accountId, opts.workerName, opts.phoenixIdBase, requestOpts);
|
|
23
|
+
const workersDevSubdomain = await enableWorkersDev(opts.apiToken, opts.accountId, opts.workerName, requestOpts);
|
|
24
|
+
const zoneId = await findZoneId(opts.apiToken, domain, requestOpts);
|
|
25
|
+
if (!zoneId) {
|
|
26
|
+
return { baseUrl: `https://${opts.workerName}.${workersDevSubdomain}.workers.dev` };
|
|
27
|
+
}
|
|
28
|
+
await addCustomDomain(opts.apiToken, opts.accountId, opts.workerName, zoneId, domain, requestOpts);
|
|
29
|
+
return { baseUrl: `https://${domain}` };
|
|
30
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental sync of derived, redacted SessionTrajectory blobs to the
|
|
3
|
+
* agents-traces R2 store.
|
|
4
|
+
*
|
|
5
|
+
* Security invariant: only the derived signal (steps + gaps + stats +
|
|
6
|
+
* errorCount + programTimeShare) is uploaded — no raw transcript text.
|
|
7
|
+
* redactSecrets() is called inside buildTrajectory() before we PUT.
|
|
8
|
+
*
|
|
9
|
+
* Sync gate: `sessions.db` `file_mtime_ms` is the source of truth. A ledger
|
|
10
|
+
* at `getRuntimeStateDir()/traces-sync.json` records the last sync timestamp
|
|
11
|
+
* per device so re-runs skip unchanged sessions.
|
|
12
|
+
*/
|
|
13
|
+
import { type SessionTrajectory } from '../session/trajectory.js';
|
|
14
|
+
import { type BucketStats, type DriftSignal, type TraceFailureCause, type TraceTopicGroup } from './classify.js';
|
|
15
|
+
export interface SyncOpts {
|
|
16
|
+
/** Limit to N sessions (for testing / --dry-run); no limit when undefined. */
|
|
17
|
+
limit?: number;
|
|
18
|
+
/** When true, skip uploading the per-device index shard. */
|
|
19
|
+
skipIndex?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Compute the derived shards from this device's real sessions.db and WRITE
|
|
22
|
+
* them to `outDir` instead of uploading. No Phoenix auth, no worker, no
|
|
23
|
+
* network — a local export for verifying the real signal. Ignores the
|
|
24
|
+
* incremental watermark so the export reflects every session.
|
|
25
|
+
*/
|
|
26
|
+
dryRun?: boolean;
|
|
27
|
+
/** Directory to write `index.json` + `sessions/<id>.json` when `dryRun` is set. */
|
|
28
|
+
outDir?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface SyncResult {
|
|
31
|
+
uploaded: number;
|
|
32
|
+
skipped: number;
|
|
33
|
+
errors: number;
|
|
34
|
+
}
|
|
35
|
+
/** Push derived, redacted trajectories for this device to the traces store. */
|
|
36
|
+
export declare function syncTraces(opts?: SyncOpts): Promise<SyncResult>;
|
|
37
|
+
export interface TracesIndexShard {
|
|
38
|
+
schema: 1;
|
|
39
|
+
device: string;
|
|
40
|
+
syncedAt: number;
|
|
41
|
+
owner: string;
|
|
42
|
+
stats: {
|
|
43
|
+
sessionsImported: number;
|
|
44
|
+
medianMs: number;
|
|
45
|
+
p90Ms: number;
|
|
46
|
+
needAttention: number;
|
|
47
|
+
toolErrorRate: number;
|
|
48
|
+
};
|
|
49
|
+
needsAttention: IndexedSession[];
|
|
50
|
+
topics: Array<{
|
|
51
|
+
key: string;
|
|
52
|
+
label: string;
|
|
53
|
+
count: number;
|
|
54
|
+
group: TraceTopicGroup;
|
|
55
|
+
}>;
|
|
56
|
+
failures: {
|
|
57
|
+
byToolError: Array<{
|
|
58
|
+
tool: string;
|
|
59
|
+
desc: string;
|
|
60
|
+
cause: TraceFailureCause;
|
|
61
|
+
count: number;
|
|
62
|
+
}>;
|
|
63
|
+
byCause: Record<TraceFailureCause, number>;
|
|
64
|
+
};
|
|
65
|
+
/** Rolling 14-day window of per-bucket daily stats. Each inner array is one day. */
|
|
66
|
+
bucketHistory: BucketStats[][];
|
|
67
|
+
/** Movement signals for buckets with ≥3 days of history. */
|
|
68
|
+
driftSignals: DriftSignal[];
|
|
69
|
+
}
|
|
70
|
+
export interface IndexedSession {
|
|
71
|
+
id: string;
|
|
72
|
+
title: string;
|
|
73
|
+
repo: string;
|
|
74
|
+
device: string;
|
|
75
|
+
agent: string;
|
|
76
|
+
model: string;
|
|
77
|
+
severity: number;
|
|
78
|
+
flags: string[];
|
|
79
|
+
}
|
|
80
|
+
/** Build the redacted rich console shard from indexed metadata and derived caches. */
|
|
81
|
+
export declare function buildIndexShard(rows: SyncRow[], device: string, owner: string, prevShard?: TracesIndexShard | null): TracesIndexShard;
|
|
82
|
+
/** Per-session drill-down detail the console consumes (sessions/<id>.json). */
|
|
83
|
+
export interface SessionDetail {
|
|
84
|
+
schema: 1;
|
|
85
|
+
id: string;
|
|
86
|
+
meta: {
|
|
87
|
+
spanMs: number;
|
|
88
|
+
turns: number;
|
|
89
|
+
tools: number;
|
|
90
|
+
errorCount: number;
|
|
91
|
+
tokens: number;
|
|
92
|
+
costUsd: number;
|
|
93
|
+
outcome: string;
|
|
94
|
+
repo: string;
|
|
95
|
+
agent: string;
|
|
96
|
+
model: string;
|
|
97
|
+
};
|
|
98
|
+
steps: SessionTrajectory['steps'];
|
|
99
|
+
gaps: SessionTrajectory['gaps'];
|
|
100
|
+
/** Steps dropped from `steps` when a run was too long — surfaced so truncation is never silent. */
|
|
101
|
+
truncatedSteps: number;
|
|
102
|
+
whereItWentWrong: string | null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Map the derived trajectory to the console's SessionDetail shape, stripping
|
|
106
|
+
* local-machine PII (full cwd, account) that would expose filesystem paths if
|
|
107
|
+
* written to R2. `repo` is the cwd basename only.
|
|
108
|
+
*/
|
|
109
|
+
export declare function buildSessionDetail(traj: SessionTrajectory): SessionDetail;
|
|
110
|
+
interface SyncLedger {
|
|
111
|
+
lastSyncMtime?: number;
|
|
112
|
+
}
|
|
113
|
+
export declare function readSyncLedger(): SyncLedger;
|
|
114
|
+
export declare function writeSyncLedger(ledger: SyncLedger): void;
|
|
115
|
+
export interface SyncRow {
|
|
116
|
+
id: string;
|
|
117
|
+
short_id: string;
|
|
118
|
+
agent: string;
|
|
119
|
+
origin: string | null;
|
|
120
|
+
routine_name: string | null;
|
|
121
|
+
routine_run_id: string | null;
|
|
122
|
+
version: string | null;
|
|
123
|
+
account: string | null;
|
|
124
|
+
account_key: string | null;
|
|
125
|
+
account_org: string | null;
|
|
126
|
+
mode: string | null;
|
|
127
|
+
timestamp: string;
|
|
128
|
+
last_activity: string | null;
|
|
129
|
+
project: string | null;
|
|
130
|
+
cwd: string | null;
|
|
131
|
+
git_branch: string | null;
|
|
132
|
+
topic: string | null;
|
|
133
|
+
label: string | null;
|
|
134
|
+
message_count: number | null;
|
|
135
|
+
token_count: number | null;
|
|
136
|
+
output_tokens: number | null;
|
|
137
|
+
input_tokens: number | null;
|
|
138
|
+
cache_read_tokens: number | null;
|
|
139
|
+
cache_write_tokens: number | null;
|
|
140
|
+
cost_usd: number | null;
|
|
141
|
+
cost_usd_nocache: number | null;
|
|
142
|
+
duration_ms: number | null;
|
|
143
|
+
model: string | null;
|
|
144
|
+
tool_call_count: number | null;
|
|
145
|
+
file_path: string;
|
|
146
|
+
file_mtime_ms: number | null;
|
|
147
|
+
file_size: number | null;
|
|
148
|
+
machine: string | null;
|
|
149
|
+
}
|
|
150
|
+
export {};
|