@agentuity/cli 1.0.1 → 1.0.2
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/AGENTS.md +40 -24
- package/bin/cli.ts +47 -22
- package/dist/agent-detection.d.ts +23 -38
- package/dist/agent-detection.d.ts.map +1 -1
- package/dist/agent-detection.js +412 -153
- package/dist/agent-detection.js.map +1 -1
- package/dist/ai-help.d.ts +23 -0
- package/dist/ai-help.d.ts.map +1 -0
- package/dist/ai-help.js +328 -0
- package/dist/ai-help.js.map +1 -0
- package/dist/api.js +1 -1
- package/dist/api.js.map +1 -1
- package/dist/auth.d.ts +10 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +176 -16
- package/dist/auth.js.map +1 -1
- package/dist/banner.d.ts.map +1 -1
- package/dist/banner.js +5 -0
- package/dist/banner.js.map +1 -1
- package/dist/cache/agent-intro.d.ts +13 -0
- package/dist/cache/agent-intro.d.ts.map +1 -0
- package/dist/cache/agent-intro.js +54 -0
- package/dist/cache/agent-intro.js.map +1 -0
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +1 -0
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/resource-region.d.ts +3 -2
- package/dist/cache/resource-region.d.ts.map +1 -1
- package/dist/cache/resource-region.js +13 -4
- package/dist/cache/resource-region.js.map +1 -1
- package/dist/catalyst.d.ts +7 -0
- package/dist/catalyst.d.ts.map +1 -0
- package/dist/catalyst.js +15 -0
- package/dist/catalyst.js.map +1 -0
- package/dist/cli.d.ts +12 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +290 -67
- package/dist/cli.js.map +1 -1
- package/dist/cmd/ai/detect.d.ts +3 -0
- package/dist/cmd/ai/detect.d.ts.map +1 -0
- package/dist/cmd/ai/detect.js +49 -0
- package/dist/cmd/ai/detect.js.map +1 -0
- package/dist/cmd/ai/index.d.ts.map +1 -1
- package/dist/cmd/ai/index.js +11 -1
- package/dist/cmd/ai/index.js.map +1 -1
- package/dist/cmd/ai/intro.d.ts +7 -0
- package/dist/cmd/ai/intro.d.ts.map +1 -0
- package/dist/cmd/ai/intro.js +141 -0
- package/dist/cmd/ai/intro.js.map +1 -0
- package/dist/cmd/ai/opencode/run.d.ts.map +1 -1
- package/dist/cmd/ai/opencode/run.js +5 -0
- package/dist/cmd/ai/opencode/run.js.map +1 -1
- package/dist/cmd/build/ast.d.ts.map +1 -1
- package/dist/cmd/build/ast.js +79 -0
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.js +2 -0
- package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
- package/dist/cmd/build/vite/docs-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/docs-generator.js +15 -1
- package/dist/cmd/build/vite/docs-generator.js.map +1 -1
- package/dist/cmd/build/vite/env-types-generator.d.ts +26 -0
- package/dist/cmd/build/vite/env-types-generator.d.ts.map +1 -0
- package/dist/cmd/build/vite/env-types-generator.js +110 -0
- package/dist/cmd/build/vite/env-types-generator.js.map +1 -0
- package/dist/cmd/build/vite/index.d.ts +2 -0
- package/dist/cmd/build/vite/index.d.ts.map +1 -1
- package/dist/cmd/build/vite/index.js +12 -1
- package/dist/cmd/build/vite/index.js.map +1 -1
- package/dist/cmd/build/vite/public-asset-path-plugin.d.ts.map +1 -1
- package/dist/cmd/build/vite/public-asset-path-plugin.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts +2 -0
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +10 -1
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/cloud/db/create.js.map +1 -1
- package/dist/cmd/cloud/db/delete.js.map +1 -1
- package/dist/cmd/cloud/db/get.d.ts.map +1 -1
- package/dist/cmd/cloud/db/get.js +27 -12
- package/dist/cmd/cloud/db/get.js.map +1 -1
- package/dist/cmd/cloud/deploy-fork.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy-fork.js +2 -0
- package/dist/cmd/cloud/deploy-fork.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +17 -0
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/env/import.js.map +1 -1
- package/dist/cmd/cloud/env/list.js.map +1 -1
- package/dist/cmd/cloud/env/push.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/util.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/util.js +3 -3
- package/dist/cmd/cloud/keyvalue/util.js.map +1 -1
- package/dist/cmd/cloud/machine/list.js +3 -3
- package/dist/cmd/cloud/machine/list.js.map +1 -1
- package/dist/cmd/cloud/region/index.js.map +1 -1
- package/dist/cmd/cloud/region-lookup.d.ts +7 -4
- package/dist/cmd/cloud/region-lookup.d.ts.map +1 -1
- package/dist/cmd/cloud/region-lookup.js +59 -14
- package/dist/cmd/cloud/region-lookup.js.map +1 -1
- package/dist/cmd/cloud/sandbox/cp.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/cp.js +7 -5
- package/dist/cmd/cloud/sandbox/cp.js.map +1 -1
- package/dist/cmd/cloud/sandbox/create.js +1 -1
- package/dist/cmd/cloud/sandbox/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/delete.js +8 -7
- package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
- package/dist/cmd/cloud/sandbox/download.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/download.js +7 -5
- package/dist/cmd/cloud/sandbox/download.js.map +1 -1
- package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/env.js +7 -5
- package/dist/cmd/cloud/sandbox/env.js.map +1 -1
- package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/exec.js +7 -5
- package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
- package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/get.js +12 -7
- package/dist/cmd/cloud/sandbox/get.js.map +1 -1
- package/dist/cmd/cloud/sandbox/list.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/list.js +40 -63
- package/dist/cmd/cloud/sandbox/list.js.map +1 -1
- package/dist/cmd/cloud/sandbox/ls.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/ls.js +7 -5
- package/dist/cmd/cloud/sandbox/ls.js.map +1 -1
- package/dist/cmd/cloud/sandbox/mkdir.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/mkdir.js +7 -5
- package/dist/cmd/cloud/sandbox/mkdir.js.map +1 -1
- package/dist/cmd/cloud/sandbox/rm.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/rm.js +7 -5
- package/dist/cmd/cloud/sandbox/rm.js.map +1 -1
- package/dist/cmd/cloud/sandbox/rmdir.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/rmdir.js +7 -5
- package/dist/cmd/cloud/sandbox/rmdir.js.map +1 -1
- package/dist/cmd/cloud/sandbox/run.js +1 -1
- package/dist/cmd/cloud/sandbox/run.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
- package/dist/cmd/cloud/sandbox/upload.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/upload.js +7 -5
- package/dist/cmd/cloud/sandbox/upload.js.map +1 -1
- package/dist/cmd/cloud/sandbox/util.d.ts +2 -2
- package/dist/cmd/cloud/sandbox/util.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/util.js +14 -13
- package/dist/cmd/cloud/sandbox/util.js.map +1 -1
- package/dist/cmd/cloud/ssh.d.ts.map +1 -1
- package/dist/cmd/cloud/ssh.js +3 -3
- package/dist/cmd/cloud/ssh.js.map +1 -1
- package/dist/cmd/cloud/storage/create.js.map +1 -1
- package/dist/cmd/cloud/storage/delete.js.map +1 -1
- package/dist/cmd/cloud/storage/get.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/get.js +5 -11
- package/dist/cmd/cloud/storage/get.js.map +1 -1
- package/dist/cmd/cloud/storage/list.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/list.js +6 -6
- package/dist/cmd/cloud/storage/list.js.map +1 -1
- package/dist/cmd/cloud/stream/create.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/create.js +7 -4
- package/dist/cmd/cloud/stream/create.js.map +1 -1
- package/dist/cmd/cloud/stream/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/delete.js +25 -4
- package/dist/cmd/cloud/stream/delete.js.map +1 -1
- package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/get.js +91 -62
- package/dist/cmd/cloud/stream/get.js.map +1 -1
- package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/list.js +66 -38
- package/dist/cmd/cloud/stream/list.js.map +1 -1
- package/dist/cmd/cloud/stream/util.d.ts +20 -0
- package/dist/cmd/cloud/stream/util.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/util.js +27 -3
- package/dist/cmd/cloud/stream/util.js.map +1 -1
- package/dist/cmd/cloud/vector/util.d.ts.map +1 -1
- package/dist/cmd/cloud/vector/util.js +3 -3
- package/dist/cmd/cloud/vector/util.js.map +1 -1
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/git/account/add.js.map +1 -1
- package/dist/cmd/git/list.js.map +1 -1
- package/dist/cmd/project/add/database.d.ts +2 -0
- package/dist/cmd/project/add/database.d.ts.map +1 -0
- package/dist/cmd/project/add/database.js +123 -0
- package/dist/cmd/project/add/database.js.map +1 -0
- package/dist/cmd/project/add/domain.d.ts +2 -0
- package/dist/cmd/project/add/domain.d.ts.map +1 -0
- package/dist/cmd/project/add/domain.js +152 -0
- package/dist/cmd/project/add/domain.js.map +1 -0
- package/dist/cmd/project/add/index.d.ts +2 -0
- package/dist/cmd/project/add/index.d.ts.map +1 -0
- package/dist/cmd/project/add/index.js +35 -0
- package/dist/cmd/project/add/index.js.map +1 -0
- package/dist/cmd/project/add/storage.d.ts +2 -0
- package/dist/cmd/project/add/storage.d.ts.map +1 -0
- package/dist/cmd/project/add/storage.js +123 -0
- package/dist/cmd/project/add/storage.js.map +1 -0
- package/dist/cmd/project/auth/init.js.map +1 -1
- package/dist/cmd/project/index.d.ts.map +1 -1
- package/dist/cmd/project/index.js +7 -0
- package/dist/cmd/project/index.js.map +1 -1
- package/dist/cmd/project/reconcile.d.ts.map +1 -1
- package/dist/cmd/project/reconcile.js +32 -0
- package/dist/cmd/project/reconcile.js.map +1 -1
- package/dist/cmd/support/report.js.map +1 -1
- package/dist/cmd/support/system.js +2 -2
- package/dist/cmd/support/system.js.map +1 -1
- package/dist/config.d.ts +6 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +31 -7
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +5 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/repl.js +2 -1
- package/dist/repl.js.map +1 -1
- package/dist/tui/box.d.ts +3 -1
- package/dist/tui/box.d.ts.map +1 -1
- package/dist/tui/box.js +22 -7
- package/dist/tui/box.js.map +1 -1
- package/dist/tui/colors.d.ts +0 -3
- package/dist/tui/colors.d.ts.map +1 -1
- package/dist/tui/colors.js +76 -23
- package/dist/tui/colors.js.map +1 -1
- package/dist/tui/prompt.d.ts +2 -0
- package/dist/tui/prompt.d.ts.map +1 -1
- package/dist/tui/prompt.js +44 -3
- package/dist/tui/prompt.js.map +1 -1
- package/dist/tui/symbols.d.ts +0 -4
- package/dist/tui/symbols.d.ts.map +1 -1
- package/dist/tui/symbols.js +5 -0
- package/dist/tui/symbols.js.map +1 -1
- package/dist/tui.d.ts +8 -0
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +54 -9
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +37 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +5 -0
- package/dist/version-check.js.map +1 -1
- package/package.json +6 -6
- package/src/agent-detection.ts +457 -160
- package/src/ai-help.ts +391 -0
- package/src/api.ts +1 -1
- package/src/auth.ts +226 -17
- package/src/banner.ts +5 -0
- package/src/cache/agent-intro.ts +61 -0
- package/src/cache/index.ts +2 -0
- package/src/cache/resource-region.ts +28 -7
- package/src/catalyst.ts +16 -0
- package/src/cli.ts +375 -93
- package/src/cmd/ai/detect.ts +52 -0
- package/src/cmd/ai/index.ts +11 -1
- package/src/cmd/ai/intro.ts +154 -0
- package/src/cmd/ai/opencode/run.ts +5 -0
- package/src/cmd/build/ast.ts +97 -0
- package/src/cmd/build/vite/bun-dev-server.ts +2 -0
- package/src/cmd/build/vite/docs-generator.ts +15 -1
- package/src/cmd/build/vite/env-types-generator.ts +145 -0
- package/src/cmd/build/vite/index.ts +15 -0
- package/src/cmd/build/vite/public-asset-path-plugin.ts +8 -2
- package/src/cmd/build/vite/vite-builder.ts +31 -11
- package/src/cmd/cloud/db/create.ts +16 -16
- package/src/cmd/cloud/db/delete.ts +19 -19
- package/src/cmd/cloud/db/get.ts +32 -17
- package/src/cmd/cloud/deploy-fork.ts +2 -0
- package/src/cmd/cloud/deploy.ts +17 -0
- package/src/cmd/cloud/env/import.ts +6 -6
- package/src/cmd/cloud/env/list.ts +11 -11
- package/src/cmd/cloud/env/push.ts +6 -6
- package/src/cmd/cloud/keyvalue/util.ts +3 -3
- package/src/cmd/cloud/machine/list.ts +3 -3
- package/src/cmd/cloud/region/index.ts +3 -3
- package/src/cmd/cloud/region-lookup.ts +82 -22
- package/src/cmd/cloud/sandbox/cp.ts +9 -4
- package/src/cmd/cloud/sandbox/create.ts +1 -1
- package/src/cmd/cloud/sandbox/delete.ts +10 -7
- package/src/cmd/cloud/sandbox/download.ts +8 -5
- package/src/cmd/cloud/sandbox/env.ts +8 -5
- package/src/cmd/cloud/sandbox/exec.ts +10 -5
- package/src/cmd/cloud/sandbox/get.ts +13 -7
- package/src/cmd/cloud/sandbox/list.ts +47 -73
- package/src/cmd/cloud/sandbox/ls.ts +9 -5
- package/src/cmd/cloud/sandbox/mkdir.ts +9 -5
- package/src/cmd/cloud/sandbox/rm.ts +9 -5
- package/src/cmd/cloud/sandbox/rmdir.ts +9 -5
- package/src/cmd/cloud/sandbox/run.ts +1 -1
- package/src/cmd/cloud/sandbox/snapshot/build.ts +31 -31
- package/src/cmd/cloud/sandbox/snapshot/get.ts +17 -17
- package/src/cmd/cloud/sandbox/upload.ts +8 -5
- package/src/cmd/cloud/sandbox/util.ts +15 -14
- package/src/cmd/cloud/ssh.ts +2 -4
- package/src/cmd/cloud/storage/create.ts +16 -16
- package/src/cmd/cloud/storage/delete.ts +19 -19
- package/src/cmd/cloud/storage/get.ts +5 -16
- package/src/cmd/cloud/storage/list.ts +12 -6
- package/src/cmd/cloud/stream/create.ts +8 -4
- package/src/cmd/cloud/stream/delete.ts +28 -4
- package/src/cmd/cloud/stream/get.ts +102 -64
- package/src/cmd/cloud/stream/list.ts +76 -44
- package/src/cmd/cloud/stream/util.ts +39 -3
- package/src/cmd/cloud/vector/util.ts +3 -3
- package/src/cmd/dev/index.ts +4 -4
- package/src/cmd/git/account/add.ts +5 -5
- package/src/cmd/git/list.ts +7 -7
- package/src/cmd/project/add/database.ts +145 -0
- package/src/cmd/project/add/domain.ts +181 -0
- package/src/cmd/project/add/index.ts +35 -0
- package/src/cmd/project/add/storage.ts +147 -0
- package/src/cmd/project/auth/init.ts +6 -6
- package/src/cmd/project/index.ts +7 -0
- package/src/cmd/project/reconcile.ts +40 -0
- package/src/cmd/support/report.ts +5 -5
- package/src/cmd/support/system.ts +2 -2
- package/src/config.ts +40 -12
- package/src/errors.ts +7 -0
- package/src/index.ts +11 -0
- package/src/repl.ts +4 -1
- package/src/tui/box.ts +24 -9
- package/src/tui/colors.ts +83 -26
- package/src/tui/prompt.ts +55 -3
- package/src/tui/symbols.ts +6 -0
- package/src/tui.ts +55 -9
- package/src/types.ts +46 -2
- package/src/version-check.ts +6 -0
package/src/agent-detection.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dlopen, FFIType, ptr } from 'bun:ffi';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Map of process names to internal agent short names.
|
|
5
|
-
* The key is the process name (or substring) that appears in the parent process command line.
|
|
6
|
+
* The key is the process name (or substring) that appears in the parent process path or command line.
|
|
6
7
|
* The value is the internal short name used to identify the agent.
|
|
7
8
|
*
|
|
8
9
|
* Process names verified via `agentuity cloud sandbox run --runtime <agent>:latest`:
|
|
@@ -30,6 +31,7 @@ export const KNOWN_AGENTS: [string, string][] = [
|
|
|
30
31
|
['windsurf', 'windsurf'],
|
|
31
32
|
['zed', 'zed'],
|
|
32
33
|
['amp', 'amp'],
|
|
34
|
+
['warp', 'warp'],
|
|
33
35
|
// TODO: VSCode Agent Mode detection - need to find a reliable way to detect
|
|
34
36
|
// when VSCode's built-in agent (Copilot Chat) is running commands vs just
|
|
35
37
|
// running in VSCode's integrated terminal. May need env var detection.
|
|
@@ -38,234 +40,529 @@ export const KNOWN_AGENTS: [string, string][] = [
|
|
|
38
40
|
export type KnownAgent = (typeof KNOWN_AGENTS)[number][1];
|
|
39
41
|
|
|
40
42
|
/**
|
|
41
|
-
*
|
|
43
|
+
* Display names for known agents (human-friendly names)
|
|
42
44
|
*/
|
|
43
|
-
|
|
45
|
+
export const AGENT_DISPLAY_NAMES: Record<string, string> = {
|
|
46
|
+
opencode: 'Open Code',
|
|
47
|
+
codex: 'OpenAI Codex',
|
|
48
|
+
cursor: 'Cursor',
|
|
49
|
+
'claude-code': 'Claude Code',
|
|
50
|
+
copilot: 'GitHub Copilot',
|
|
51
|
+
gemini: 'Gemini',
|
|
52
|
+
cline: 'Cline',
|
|
53
|
+
roo: 'Roo Code',
|
|
54
|
+
windsurf: 'Windsurf',
|
|
55
|
+
zed: 'Zed',
|
|
56
|
+
amp: 'Amp',
|
|
57
|
+
warp: 'Warp',
|
|
58
|
+
};
|
|
44
59
|
|
|
45
60
|
/**
|
|
46
|
-
*
|
|
61
|
+
* Get the display name for an agent ID
|
|
47
62
|
*/
|
|
48
|
-
|
|
63
|
+
export function getAgentDisplayName(agentId: string): string {
|
|
64
|
+
return AGENT_DISPLAY_NAMES[agentId] || agentId;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// FFI-based Fast Process Path Resolution
|
|
69
|
+
// ============================================================================
|
|
70
|
+
|
|
71
|
+
const PATH_MAX = 4096; // Linux PATH_MAX, also sufficient for macOS
|
|
72
|
+
const ARG_MAX = 65536; // Maximum command line length
|
|
49
73
|
|
|
50
74
|
/**
|
|
51
|
-
*
|
|
75
|
+
* Type for the FFI function that gets a process path
|
|
52
76
|
*/
|
|
53
|
-
|
|
77
|
+
type GetProcessPath = (pid: number) => string | null;
|
|
54
78
|
|
|
55
79
|
/**
|
|
56
|
-
*
|
|
80
|
+
* Type for the FFI function that gets a parent PID
|
|
57
81
|
*/
|
|
58
|
-
|
|
59
|
-
return new Promise((resolve) => {
|
|
60
|
-
const ps = spawn('ps', ['-p', String(pid), '-o', 'ppid=,command='], {
|
|
61
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
let output = '';
|
|
65
|
-
|
|
66
|
-
ps.stdout.on('data', (data: Buffer) => {
|
|
67
|
-
output += data.toString();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
ps.on('close', () => {
|
|
71
|
-
const trimmed = output.trim();
|
|
72
|
-
if (!trimmed) {
|
|
73
|
-
resolve(undefined);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Output format: " PPID COMMAND" (ppid is right-aligned, then space, then command)
|
|
78
|
-
const match = trimmed.match(/^\s*(\d+)\s+(.+)$/);
|
|
79
|
-
if (!match) {
|
|
80
|
-
resolve(undefined);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const ppidStr = match[1];
|
|
85
|
-
const commandStr = match[2];
|
|
86
|
-
if (!ppidStr || !commandStr) {
|
|
87
|
-
resolve(undefined);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const ppid = parseInt(ppidStr, 10);
|
|
92
|
-
const command = commandStr.toLowerCase();
|
|
93
|
-
|
|
94
|
-
if (isNaN(ppid) || ppid <= 1 || !command) {
|
|
95
|
-
resolve(undefined);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
82
|
+
type GetParentPid = (pid: number) => number | null;
|
|
98
83
|
|
|
99
|
-
|
|
100
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Type for the FFI function that gets command line arguments
|
|
86
|
+
*/
|
|
87
|
+
type GetProcessCmdline = (pid: number) => string | null;
|
|
101
88
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
89
|
+
interface FFIFunctions {
|
|
90
|
+
getProcessPath: GetProcessPath;
|
|
91
|
+
getParentPid: GetParentPid;
|
|
92
|
+
getProcessCmdline: GetProcessCmdline;
|
|
106
93
|
}
|
|
107
94
|
|
|
108
95
|
/**
|
|
109
|
-
*
|
|
96
|
+
* Stub FFI functions for unsupported platforms or when FFI fails to load.
|
|
110
97
|
*/
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
98
|
+
const unsupportedFFI: FFIFunctions = {
|
|
99
|
+
getProcessPath: () => null,
|
|
100
|
+
getParentPid: () => null,
|
|
101
|
+
getProcessCmdline: () => null,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Initialize FFI functions for the current platform.
|
|
106
|
+
* Returns null functions for unsupported platforms or if FFI initialization fails.
|
|
107
|
+
*/
|
|
108
|
+
function initFFI(): FFIFunctions {
|
|
109
|
+
try {
|
|
110
|
+
if (process.platform === 'darwin') {
|
|
111
|
+
return initDarwinFFI();
|
|
112
|
+
} else if (process.platform === 'linux') {
|
|
113
|
+
return initLinuxFFI();
|
|
115
114
|
}
|
|
115
|
+
} catch (err) {
|
|
116
|
+
// FFI initialization failed (e.g., missing libc on musl/Alpine, dlopen error)
|
|
117
|
+
// Fall back to unsupported stub - agent detection will be skipped
|
|
118
|
+
if (process.env.AGENTUITY_DEBUG_AGENT_DETECTION === '1') {
|
|
119
|
+
console.error('[agent-detection] FFI initialization failed:', err instanceof Error ? err.message : err);
|
|
120
|
+
}
|
|
121
|
+
return unsupportedFFI;
|
|
116
122
|
}
|
|
117
|
-
|
|
123
|
+
// Unsupported platform (Windows, etc.)
|
|
124
|
+
return unsupportedFFI;
|
|
118
125
|
}
|
|
119
126
|
|
|
120
127
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
128
|
+
* Initialize macOS FFI functions using libSystem.dylib
|
|
129
|
+
*
|
|
130
|
+
* NOTE: Shared mutable buffers (pathBuf, argBuf, kinfoBuffer) are safe only in
|
|
131
|
+
* single-threaded use. Detection is synchronous and single-threaded, but any
|
|
132
|
+
* future concurrent usage would corrupt these buffers.
|
|
123
133
|
*/
|
|
124
|
-
function
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
134
|
+
function initDarwinFFI(): FFIFunctions {
|
|
135
|
+
// Shared buffers - reused across calls (single-threaded assumption)
|
|
136
|
+
const pathBuf = new Uint8Array(PATH_MAX);
|
|
137
|
+
const argBuf = new Uint8Array(ARG_MAX);
|
|
138
|
+
|
|
139
|
+
// Size of kinfo_proc struct on macOS (arm64 and x86_64)
|
|
140
|
+
const KINFO_PROC_SIZE = 648;
|
|
141
|
+
const kinfoBuffer = new Uint8Array(KINFO_PROC_SIZE);
|
|
142
|
+
|
|
143
|
+
// Load libSystem for proc_pidpath and sysctl
|
|
144
|
+
const lib = dlopen('libSystem.dylib', {
|
|
145
|
+
proc_pidpath: {
|
|
146
|
+
args: [FFIType.i32, FFIType.ptr, FFIType.u32],
|
|
147
|
+
returns: FFIType.i32,
|
|
148
|
+
},
|
|
149
|
+
sysctl: {
|
|
150
|
+
args: [FFIType.ptr, FFIType.u32, FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.u64],
|
|
151
|
+
returns: FFIType.i32,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
131
154
|
|
|
132
|
-
|
|
155
|
+
const getProcessPath: GetProcessPath = (pid: number) => {
|
|
156
|
+
if (pid <= 0) return null;
|
|
157
|
+
try {
|
|
158
|
+
const len = lib.symbols.proc_pidpath(pid, ptr(pathBuf), PATH_MAX);
|
|
159
|
+
if (len > 0) {
|
|
160
|
+
return new TextDecoder().decode(pathBuf.subarray(0, len)).replace(/\0.*/, '');
|
|
161
|
+
}
|
|
162
|
+
} catch {
|
|
163
|
+
// Ignore errors (process may have died, permission denied, etc.)
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
};
|
|
133
167
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
168
|
+
// Get parent PID using sysctl KERN_PROC_PID
|
|
169
|
+
// CTL_KERN = 1, KERN_PROC = 14, KERN_PROC_PID = 1
|
|
170
|
+
const getParentPid: GetParentPid = (pid: number) => {
|
|
171
|
+
if (pid <= 1) return null;
|
|
172
|
+
try {
|
|
173
|
+
const mib = new Int32Array([1, 14, 1, pid]);
|
|
174
|
+
const sizePtr = new BigUint64Array([BigInt(KINFO_PROC_SIZE)]);
|
|
175
|
+
|
|
176
|
+
const result = lib.symbols.sysctl(ptr(mib), 4, ptr(kinfoBuffer), ptr(sizePtr), null, 0);
|
|
177
|
+
if (result === 0) {
|
|
178
|
+
// e_ppid offset in kinfo_proc on macOS arm64/x86_64:
|
|
179
|
+
// kp_eproc starts at 296, e_ppid is at offset 264 within kp_eproc
|
|
180
|
+
// Total offset: 560
|
|
181
|
+
const view = new DataView(kinfoBuffer.buffer);
|
|
182
|
+
const ppid = view.getInt32(560, true); // little-endian
|
|
183
|
+
return ppid > 1 ? ppid : null;
|
|
137
184
|
}
|
|
185
|
+
} catch {
|
|
186
|
+
// Ignore errors
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
};
|
|
138
190
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
191
|
+
// Get command line using sysctl KERN_PROCARGS2
|
|
192
|
+
// CTL_KERN = 1, KERN_PROCARGS2 = 49
|
|
193
|
+
const getProcessCmdline: GetProcessCmdline = (pid: number) => {
|
|
194
|
+
if (pid <= 0) return null;
|
|
195
|
+
try {
|
|
196
|
+
// MIB for KERN_PROCARGS2: [CTL_KERN, KERN_PROCARGS2, pid]
|
|
197
|
+
const mib = new Int32Array([1, 49, pid]);
|
|
198
|
+
const sizePtr = new BigUint64Array([BigInt(ARG_MAX)]);
|
|
199
|
+
|
|
200
|
+
const result = lib.symbols.sysctl(
|
|
201
|
+
ptr(mib),
|
|
202
|
+
3,
|
|
203
|
+
ptr(argBuf),
|
|
204
|
+
ptr(sizePtr),
|
|
205
|
+
null,
|
|
206
|
+
0
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
if (result === 0) {
|
|
210
|
+
const size = Number(sizePtr[0]);
|
|
211
|
+
if (size > 0) {
|
|
212
|
+
// KERN_PROCARGS2 format:
|
|
213
|
+
// - 4 bytes: argc (number of arguments)
|
|
214
|
+
// - exec_path\0
|
|
215
|
+
// - padding (zeros until aligned)
|
|
216
|
+
// - arg0\0arg1\0arg2\0...
|
|
217
|
+
// We want to extract exactly argc args (not env vars that follow)
|
|
218
|
+
const data = argBuf.subarray(0, size);
|
|
219
|
+
|
|
220
|
+
// Read argc (first 4 bytes, little-endian)
|
|
221
|
+
const argc = new DataView(data.buffer, data.byteOffset).getInt32(0, true);
|
|
222
|
+
let offset = 4;
|
|
223
|
+
|
|
224
|
+
// Skip exec_path (find first null)
|
|
225
|
+
while (offset < size && data[offset] !== 0) offset++;
|
|
226
|
+
offset++; // Skip the null
|
|
227
|
+
|
|
228
|
+
// Skip padding (multiple nulls)
|
|
229
|
+
while (offset < size && data[offset] === 0) offset++;
|
|
230
|
+
|
|
231
|
+
// Now we're at the arguments - collect exactly argc args
|
|
232
|
+
const args: string[] = [];
|
|
233
|
+
let start = offset;
|
|
234
|
+
for (let i = offset; i < size; i++) {
|
|
235
|
+
if (data[i] === 0) {
|
|
236
|
+
if (i > start) {
|
|
237
|
+
args.push(new TextDecoder().decode(data.subarray(start, i)));
|
|
238
|
+
}
|
|
239
|
+
start = i + 1;
|
|
240
|
+
// Stop after collecting argc args
|
|
241
|
+
if (args.length >= argc) break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return args.join(' ');
|
|
246
|
+
}
|
|
143
247
|
}
|
|
248
|
+
} catch {
|
|
249
|
+
// Ignore errors
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
};
|
|
144
253
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
254
|
+
return { getProcessPath, getParentPid, getProcessCmdline };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Initialize Linux FFI functions using libc.so.6
|
|
259
|
+
*/
|
|
260
|
+
function initLinuxFFI(): FFIFunctions {
|
|
261
|
+
const pathBuf = new Uint8Array(PATH_MAX);
|
|
262
|
+
|
|
263
|
+
const lib = dlopen('libc.so.6', {
|
|
264
|
+
readlink: {
|
|
265
|
+
args: [FFIType.cstring, FFIType.ptr, FFIType.u64],
|
|
266
|
+
returns: FFIType.i64,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const getProcessPath: GetProcessPath = (pid: number) => {
|
|
271
|
+
if (pid <= 0) return null;
|
|
272
|
+
try {
|
|
273
|
+
const procPath = Buffer.from(`/proc/${pid}/exe\0`);
|
|
274
|
+
const len = Number(lib.symbols.readlink(ptr(procPath), ptr(pathBuf), PATH_MAX));
|
|
275
|
+
if (len > 0) {
|
|
276
|
+
return new TextDecoder().decode(pathBuf.subarray(0, len));
|
|
149
277
|
}
|
|
278
|
+
} catch {
|
|
279
|
+
// Ignore errors (process may have died, permission denied, etc.)
|
|
280
|
+
}
|
|
281
|
+
return null;
|
|
282
|
+
};
|
|
150
283
|
|
|
151
|
-
|
|
152
|
-
|
|
284
|
+
const getParentPid: GetParentPid = (pid: number) => {
|
|
285
|
+
if (pid <= 1) return null;
|
|
286
|
+
try {
|
|
287
|
+
// Read /proc/{pid}/stat to get parent PID (4th field)
|
|
288
|
+
const statPath = `/proc/${pid}/stat`;
|
|
289
|
+
const content = readFileSync(statPath, 'utf-8');
|
|
290
|
+
// Format: pid (comm) state ppid ...
|
|
291
|
+
// Need to handle comm with spaces/parens: find last ')' then parse
|
|
292
|
+
const lastParen = content.lastIndexOf(')');
|
|
293
|
+
if (lastParen === -1) return null;
|
|
294
|
+
const rest = content.slice(lastParen + 2); // Skip ') '
|
|
295
|
+
const fields = rest.split(' ');
|
|
296
|
+
const ppidField = fields[1]; // ppid is 2nd field after state
|
|
297
|
+
if (!ppidField) return null;
|
|
298
|
+
const ppid = parseInt(ppidField, 10);
|
|
299
|
+
return isNaN(ppid) || ppid <= 1 ? null : ppid;
|
|
300
|
+
} catch {
|
|
301
|
+
// Ignore errors
|
|
153
302
|
}
|
|
303
|
+
return null;
|
|
304
|
+
};
|
|
154
305
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
306
|
+
// Read command line from /proc/{pid}/cmdline (null-separated args)
|
|
307
|
+
const getProcessCmdline: GetProcessCmdline = (pid: number) => {
|
|
308
|
+
if (pid <= 0) return null;
|
|
309
|
+
try {
|
|
310
|
+
const cmdlinePath = `/proc/${pid}/cmdline`;
|
|
311
|
+
const content = readFileSync(cmdlinePath);
|
|
312
|
+
if (content.length > 0) {
|
|
313
|
+
// Replace null bytes with spaces to get full command line
|
|
314
|
+
return new TextDecoder().decode(content).replace(/\0/g, ' ').trim();
|
|
315
|
+
}
|
|
316
|
+
} catch {
|
|
317
|
+
// Ignore errors
|
|
159
318
|
}
|
|
319
|
+
return null;
|
|
320
|
+
};
|
|
160
321
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
322
|
+
return { getProcessPath, getParentPid, getProcessCmdline };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Initialize FFI functions lazily
|
|
326
|
+
let ffi: FFIFunctions | null = null;
|
|
327
|
+
|
|
328
|
+
function getFFI(): FFIFunctions {
|
|
329
|
+
if (!ffi) {
|
|
330
|
+
ffi = initFFI();
|
|
331
|
+
}
|
|
332
|
+
return ffi;
|
|
165
333
|
}
|
|
166
334
|
|
|
335
|
+
// ============================================================================
|
|
336
|
+
// Agent Detection Logic
|
|
337
|
+
// ============================================================================
|
|
338
|
+
|
|
167
339
|
/**
|
|
168
|
-
*
|
|
169
|
-
* Call this early in CLI startup to begin detection in the background.
|
|
340
|
+
* Cached detection result (null = not yet run, undefined = no agent detected)
|
|
170
341
|
*/
|
|
171
|
-
|
|
172
|
-
if (detectionPromise !== null) {
|
|
173
|
-
// Already started
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
342
|
+
let cachedResult: string | undefined | null = null;
|
|
176
343
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
344
|
+
/**
|
|
345
|
+
* Check if a path's basename matches any known agent
|
|
346
|
+
*/
|
|
347
|
+
function matchAgentPath(path: string): string | undefined {
|
|
348
|
+
// Extract basename from path
|
|
349
|
+
const basename = path.split('/').pop()?.toLowerCase() ?? '';
|
|
350
|
+
for (const [processName, agentName] of KNOWN_AGENTS) {
|
|
351
|
+
if (basename.includes(processName)) {
|
|
352
|
+
return agentName;
|
|
186
353
|
}
|
|
187
|
-
|
|
188
|
-
|
|
354
|
+
}
|
|
355
|
+
return undefined;
|
|
189
356
|
}
|
|
190
357
|
|
|
191
358
|
/**
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* @example
|
|
197
|
-
* ```typescript
|
|
198
|
-
* onAgentDetected((agent) => {
|
|
199
|
-
* if (agent) {
|
|
200
|
-
* console.log(`Detected agent: ${agent}`);
|
|
201
|
-
* }
|
|
202
|
-
* });
|
|
203
|
-
* ```
|
|
359
|
+
* Check if a cmdline matches any known agent.
|
|
360
|
+
* Only checks argv[0] and arguments that look like executable paths,
|
|
361
|
+
* NOT environment variables or arbitrary path strings.
|
|
204
362
|
*/
|
|
205
|
-
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
363
|
+
function matchAgentCmdline(cmdline: string): string | undefined {
|
|
364
|
+
// Split cmdline into arguments (null-separated on macOS/Linux, but we get space-separated here)
|
|
365
|
+
// The cmdline format from our FFI is: "cmd arg1 arg2 ENV1=val1 ENV2=val2..."
|
|
366
|
+
// We only want to check the command and args that look like executables
|
|
367
|
+
const parts = cmdline.split(/\s+/);
|
|
368
|
+
|
|
369
|
+
for (const part of parts) {
|
|
370
|
+
// Skip environment variables (contain =)
|
|
371
|
+
if (part.includes('=')) {
|
|
372
|
+
continue;
|
|
212
373
|
}
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
374
|
|
|
216
|
-
|
|
217
|
-
|
|
375
|
+
// Check if this looks like an executable path or command
|
|
376
|
+
// - Starts with / (absolute path)
|
|
377
|
+
// - Is a simple command name (no path separators, no special chars)
|
|
378
|
+
const isPath = part.startsWith('/');
|
|
379
|
+
const isSimpleCommand = !part.includes('/') && !part.includes('=') && part.length < 50;
|
|
380
|
+
|
|
381
|
+
if (isPath || isSimpleCommand) {
|
|
382
|
+
const basename = part.split('/').pop()?.toLowerCase() ?? '';
|
|
383
|
+
for (const [processName, agentName] of KNOWN_AGENTS) {
|
|
384
|
+
if (basename.includes(processName)) {
|
|
385
|
+
return agentName;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return undefined;
|
|
218
391
|
}
|
|
219
392
|
|
|
220
393
|
/**
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
* Use this for synchronous access when you don't want to wait for detection.
|
|
394
|
+
* Check if stdin is connected to a TTY (interactive terminal).
|
|
395
|
+
* When humans type commands, stdin is usually a TTY.
|
|
396
|
+
* When agents run commands programmatically, stdin is usually piped/closed.
|
|
226
397
|
*/
|
|
227
|
-
|
|
228
|
-
return
|
|
398
|
+
function isInteractiveSession(): boolean {
|
|
399
|
+
return process.stdin.isTTY === true;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Enable debug output with AGENTUITY_DEBUG_AGENT_DETECTION=1
|
|
403
|
+
const DEBUG = process.env.AGENTUITY_DEBUG_AGENT_DETECTION === '1';
|
|
404
|
+
|
|
405
|
+
function debugLog(...args: unknown[]): void {
|
|
406
|
+
if (DEBUG) {
|
|
407
|
+
console.error('[agent-detection]', ...args);
|
|
408
|
+
}
|
|
229
409
|
}
|
|
230
410
|
|
|
231
411
|
/**
|
|
232
|
-
*
|
|
233
|
-
*
|
|
412
|
+
* Synchronously detect the parent agent by walking up the process tree.
|
|
413
|
+
* Uses FFI for fast process path and command line resolution.
|
|
234
414
|
*
|
|
235
|
-
*
|
|
415
|
+
* Detection strategy:
|
|
416
|
+
* - If stdin is a TTY (interactive session), don't report as agent
|
|
417
|
+
* (human is typing commands, even if inside an agent's terminal)
|
|
418
|
+
* - If stdin is NOT a TTY, check the process tree for known agents
|
|
236
419
|
*/
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
420
|
+
function detectParentAgent(): string | undefined {
|
|
421
|
+
debugLog('Starting detection, PID:', process.pid, 'PPID:', process.ppid);
|
|
422
|
+
debugLog('stdin.isTTY:', process.stdin.isTTY);
|
|
423
|
+
|
|
424
|
+
// Dump relevant env vars for debugging agent detection
|
|
425
|
+
if (DEBUG) {
|
|
426
|
+
const relevantEnvVars = Object.entries(process.env)
|
|
427
|
+
.filter(([key]) =>
|
|
428
|
+
key.startsWith('WARP') ||
|
|
429
|
+
key.startsWith('TERM') ||
|
|
430
|
+
key.startsWith('AGENTUITY') ||
|
|
431
|
+
key === 'SHELL' ||
|
|
432
|
+
key === 'LC_TERMINAL' ||
|
|
433
|
+
key === 'ITERM_SESSION_ID' ||
|
|
434
|
+
key === 'VSCODE_INJECTION' ||
|
|
435
|
+
key === 'CURSOR_TRACE_ID'
|
|
436
|
+
)
|
|
437
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
438
|
+
.join(', ');
|
|
439
|
+
debugLog('Relevant env vars:', relevantEnvVars || '(none)');
|
|
440
|
+
debugLog('WARP_AGENT_MODE:', process.env.WARP_AGENT_MODE ?? '(not set)');
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Short-circuit: if AGENTUITY_AGENT_MODE is set, use it directly
|
|
444
|
+
// Use "false", "0", or "none" to explicitly disable detection
|
|
445
|
+
const agentMode = process.env.AGENTUITY_AGENT_MODE;
|
|
446
|
+
if (agentMode) {
|
|
447
|
+
if (agentMode === 'false' || agentMode === '0' || agentMode === 'none') {
|
|
448
|
+
debugLog('AGENTUITY_AGENT_MODE explicitly disabled:', agentMode);
|
|
449
|
+
return undefined;
|
|
450
|
+
}
|
|
451
|
+
debugLog('Using AGENTUITY_AGENT_MODE:', agentMode);
|
|
452
|
+
return agentMode;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Warp terminal: detect via WARP_AGENT_MODE env var (set by Warp AI)
|
|
456
|
+
// Note: Warp doesn't currently set this, but will in the future
|
|
457
|
+
if (process.env.WARP_AGENT_MODE === 'true') {
|
|
458
|
+
debugLog('Detected Warp AI via WARP_AGENT_MODE=true');
|
|
459
|
+
return 'warp';
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// If this is an interactive session (TTY), assume human is running the command
|
|
463
|
+
// Note: Warp AI also runs with TTY=true, so Warp users should set
|
|
464
|
+
// AGENTUITY_AGENT_MODE=warp until Warp sets WARP_AGENT_MODE=true
|
|
465
|
+
if (isInteractiveSession()) {
|
|
466
|
+
debugLog('Interactive session (TTY), skipping detection');
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Unsupported on Windows (for now)
|
|
471
|
+
if (process.platform === 'win32') {
|
|
472
|
+
debugLog('Windows not supported');
|
|
473
|
+
return undefined;
|
|
240
474
|
}
|
|
475
|
+
|
|
476
|
+
const { getProcessPath, getParentPid, getProcessCmdline } = getFFI();
|
|
477
|
+
|
|
478
|
+
// Guard for no parent process (e.g., PID 1 in containers)
|
|
479
|
+
const ppid = process.ppid;
|
|
480
|
+
if (!ppid || ppid <= 1) {
|
|
481
|
+
debugLog('No parent process (ppid:', ppid, ')');
|
|
482
|
+
return undefined;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Walk up the process tree looking for known agents
|
|
486
|
+
const maxDepth = 10;
|
|
487
|
+
let currentPid = ppid;
|
|
488
|
+
|
|
489
|
+
for (let depth = 0; depth < maxDepth && currentPid > 1; depth++) {
|
|
490
|
+
// Check the executable path for agent match
|
|
491
|
+
const path = getProcessPath(currentPid);
|
|
492
|
+
debugLog(`[${depth}] PID ${currentPid} path:`, path);
|
|
493
|
+
|
|
494
|
+
if (path) {
|
|
495
|
+
const agent = matchAgentPath(path);
|
|
496
|
+
if (agent) {
|
|
497
|
+
debugLog(`[${depth}] Matched agent from path:`, agent);
|
|
498
|
+
return agent;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Check the command line (for agents running as node/bun scripts)
|
|
503
|
+
const cmdline = getProcessCmdline(currentPid);
|
|
504
|
+
debugLog(`[${depth}] PID ${currentPid} cmdline:`, cmdline?.substring(0, 200) + (cmdline && cmdline.length > 200 ? '...' : ''));
|
|
505
|
+
|
|
506
|
+
if (cmdline) {
|
|
507
|
+
const agent = matchAgentCmdline(cmdline);
|
|
508
|
+
if (agent) {
|
|
509
|
+
debugLog(`[${depth}] Matched agent from cmdline:`, agent);
|
|
510
|
+
return agent;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Move up
|
|
515
|
+
const parentPid = getParentPid(currentPid);
|
|
516
|
+
debugLog(`[${depth}] Parent of ${currentPid}:`, parentPid);
|
|
517
|
+
if (!parentPid) break;
|
|
518
|
+
currentPid = parentPid;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
debugLog('No agent found');
|
|
522
|
+
return undefined;
|
|
241
523
|
}
|
|
242
524
|
|
|
243
525
|
/**
|
|
244
|
-
*
|
|
245
|
-
* Returns the agent
|
|
526
|
+
* Get the executing agent if the CLI is being run from a known coding agent.
|
|
527
|
+
* Returns the agent ID if detected, undefined otherwise.
|
|
246
528
|
*
|
|
247
|
-
* This function
|
|
248
|
-
*
|
|
529
|
+
* This function runs synchronously using FFI for fast process path resolution.
|
|
530
|
+
* Results are cached after the first call.
|
|
249
531
|
*
|
|
250
532
|
* @example
|
|
251
533
|
* ```typescript
|
|
252
|
-
* const agent =
|
|
534
|
+
* const agent = getExecutingAgent();
|
|
253
535
|
* if (agent) {
|
|
254
536
|
* logger.debug(`Running from agent: ${agent}`);
|
|
255
537
|
* }
|
|
256
538
|
* ```
|
|
257
539
|
*/
|
|
258
|
-
export
|
|
259
|
-
// Return cached result if
|
|
540
|
+
export function getExecutingAgent(): string | undefined {
|
|
541
|
+
// Return cached result if already detected
|
|
260
542
|
if (cachedResult !== null) {
|
|
261
|
-
return cachedResult;
|
|
543
|
+
return cachedResult ?? undefined;
|
|
262
544
|
}
|
|
263
545
|
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
546
|
+
// Run detection and cache
|
|
547
|
+
cachedResult = detectParentAgent();
|
|
548
|
+
return cachedResult;
|
|
549
|
+
}
|
|
268
550
|
|
|
269
|
-
|
|
270
|
-
|
|
551
|
+
/**
|
|
552
|
+
* Get environment variables to pass to subprocesses for agent detection.
|
|
553
|
+
* This allows child processes to skip re-detection by using the cached result.
|
|
554
|
+
*
|
|
555
|
+
* @example
|
|
556
|
+
* ```typescript
|
|
557
|
+
* const proc = Bun.spawn(['bun', 'run', 'dev'], {
|
|
558
|
+
* env: { ...process.env, ...getAgentEnv() },
|
|
559
|
+
* });
|
|
560
|
+
* ```
|
|
561
|
+
*/
|
|
562
|
+
export function getAgentEnv(): Record<string, string> {
|
|
563
|
+
const agent = getExecutingAgent();
|
|
564
|
+
if (agent) {
|
|
565
|
+
return { AGENTUITY_AGENT_MODE: agent };
|
|
566
|
+
}
|
|
567
|
+
return {};
|
|
271
568
|
}
|