triagent 0.1.0-alpha8 → 0.1.0-beta2

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.
Files changed (47) hide show
  1. package/README.md +101 -1
  2. package/package.json +9 -3
  3. package/src/cli/config.ts +118 -2
  4. package/src/config.ts +23 -3
  5. package/src/index.ts +262 -6
  6. package/src/integrations/elasticsearch/client.ts +210 -0
  7. package/src/integrations/grafana/client.ts +186 -0
  8. package/src/integrations/kubernetes/multi-cluster.ts +199 -0
  9. package/src/integrations/kubernetes/types.ts +24 -0
  10. package/src/integrations/loki/client.ts +219 -0
  11. package/src/integrations/prometheus/client.ts +163 -0
  12. package/src/integrations/slack/client.ts +265 -0
  13. package/src/integrations/teams/client.ts +199 -0
  14. package/src/mastra/agents/debugger.ts +164 -109
  15. package/src/mastra/index.ts +2 -2
  16. package/src/mastra/tools/approval-store.ts +180 -0
  17. package/src/mastra/tools/cli.ts +94 -2
  18. package/src/mastra/tools/cost.ts +389 -0
  19. package/src/mastra/tools/logs.ts +210 -0
  20. package/src/mastra/tools/network.ts +253 -0
  21. package/src/mastra/tools/prometheus.ts +221 -0
  22. package/src/mastra/tools/remediation.ts +365 -0
  23. package/src/mastra/tools/runbook.ts +186 -0
  24. package/src/sandbox/bashlet.ts +76 -10
  25. package/src/server/routes/history.ts +207 -0
  26. package/src/server/routes/notifications.ts +236 -0
  27. package/src/server/webhook.ts +36 -2
  28. package/src/storage/index.ts +3 -0
  29. package/src/storage/investigation-history.ts +277 -0
  30. package/src/storage/runbook-index.ts +330 -0
  31. package/src/storage/types.ts +72 -0
  32. package/src/tui/app.tsx +278 -198
  33. package/src/tui/components/approval-dialog.tsx +147 -0
  34. package/src/tui/components/approval-modal.tsx +278 -0
  35. package/src/tui/components/centered-layout.tsx +33 -0
  36. package/src/tui/components/editor.tsx +87 -0
  37. package/src/tui/components/header.tsx +53 -0
  38. package/src/tui/components/index.ts +55 -0
  39. package/src/tui/components/message-item.tsx +131 -0
  40. package/src/tui/components/messages-panel.tsx +71 -0
  41. package/src/tui/components/status-badge.tsx +20 -0
  42. package/src/tui/components/status-bar.tsx +39 -0
  43. package/src/tui/components/styled-span.tsx +24 -0
  44. package/src/tui/components/timeline.tsx +223 -0
  45. package/src/tui/components/toast.tsx +104 -0
  46. package/src/tui/theme/index.ts +21 -0
  47. package/src/tui/theme/tokens.ts +180 -0
package/README.md CHANGED
@@ -26,6 +26,69 @@ triagent
26
26
 
27
27
  # Run webhook server only
28
28
  triagent --webhook-only
29
+
30
+ # Direct incident investigation
31
+ triagent --incident "API gateway returning 503 errors"
32
+ ```
33
+
34
+ ## Execution Modes
35
+
36
+ Triagent supports three execution modes for running commands:
37
+
38
+ ### Sandbox Mode (Default)
39
+
40
+ Commands run inside a Docker container via [Bashlet](https://github.com/anthropics/bashlet). This provides isolation and safety - the agent cannot accidentally modify your host system.
41
+
42
+ ```bash
43
+ triagent
44
+ ```
45
+
46
+ Codebases are mounted at `/workspace/<name>` and kubeconfig at `/root/.kube`.
47
+
48
+ ### Host Mode
49
+
50
+ Commands run directly on your local machine. Use this when you need access to tools not available in the sandbox.
51
+
52
+ ```bash
53
+ triagent --host
54
+ ```
55
+
56
+ ### Remote Mode
57
+
58
+ Commands run on a remote server via SSH. This is useful for connecting to a Docker container or VM with pre-installed debugging tools.
59
+
60
+ ```bash
61
+ triagent --remote user@host
62
+ triagent -r root@debug-container.local
63
+ ```
64
+
65
+ On startup, triagent creates a session workspace on the remote:
66
+ ```
67
+ 🌐 Running in remote mode: root@debug-container.local
68
+ Workspace: /tmp/triagent-a3b4c5d6 (session: a3b4c5d6)
69
+ ```
70
+
71
+ **Requirements:**
72
+ - SSH key-based authentication (no password prompts)
73
+ - The remote must have the necessary CLI tools (kubectl, etc.)
74
+
75
+ **Example: Debug container setup**
76
+
77
+ ```bash
78
+ # Run a container with SSH and debugging tools
79
+ docker run -d --name debug-tools \
80
+ -p 2222:22 \
81
+ -v ~/.kube:/root/.kube:ro \
82
+ your-debug-image:latest
83
+
84
+ # Add to ~/.ssh/config for easy access
85
+ # Host debug-tools
86
+ # HostName localhost
87
+ # Port 2222
88
+ # User root
89
+
90
+ # Connect triagent to it
91
+ triagent --remote debug-tools
29
92
  ```
30
93
 
31
94
  ## Configuration
@@ -57,9 +120,46 @@ triagent config path
57
120
  | `apiKey` | API key for the provider | - |
58
121
  | `baseUrl` | Custom API base URL (for proxies or local models) | - |
59
122
  | `webhookPort` | Webhook server port | `3000` |
60
- | `codebasePath` | Path to codebase | `./` |
123
+ | `codebasePath` | Path to single codebase (legacy) | `./` |
61
124
  | `kubeConfigPath` | Kubernetes config path | `~/.kube` |
62
125
 
126
+ ### Multiple Codebases
127
+
128
+ For applications spanning multiple repositories, configure `codebasePaths` in `~/.config/triagent/config.json`:
129
+
130
+ ```json
131
+ {
132
+ "codebasePaths": [
133
+ { "name": "frontend", "path": "/path/to/frontend-repo" },
134
+ { "name": "backend", "path": "/path/to/backend-repo" },
135
+ { "name": "infra", "path": "/path/to/infrastructure" }
136
+ ]
137
+ }
138
+ ```
139
+
140
+ Each codebase is mounted at `/workspace/<name>` in the sandbox. The model can access any codebase as needed during investigation.
141
+
142
+ ### Custom Instructions (TRIAGENT.md)
143
+
144
+ Create `~/.config/triagent/TRIAGENT.md` to provide custom instructions to the model. These instructions are prepended to the default system prompt.
145
+
146
+ Example `TRIAGENT.md`:
147
+
148
+ ```markdown
149
+ ## Project Context
150
+
151
+ This is a microservices e-commerce platform with the following services:
152
+ - frontend: Next.js app in /workspace/frontend
153
+ - api: Go backend in /workspace/backend
154
+ - infra: Terraform configs in /workspace/infra
155
+
156
+ ## Investigation Priorities
157
+
158
+ 1. Always check the api service logs first for 5xx errors
159
+ 2. The frontend service talks to api via internal DNS: api.default.svc.cluster.local
160
+ 3. Common issues: Redis connection timeouts, PostgreSQL connection pool exhaustion
161
+ ```
162
+
63
163
  ### Environment Variables
64
164
 
65
165
  | Variable | Description |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triagent",
3
- "version": "0.1.0-alpha8",
3
+ "version": "0.1.0-beta2",
4
4
  "description": "AI-powered Kubernetes debugging agent with terminal UI",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -27,7 +27,11 @@
27
27
  ],
28
28
  "repository": {
29
29
  "type": "git",
30
- "url": "git+https://github.com/OWNER/triagent.git"
30
+ "url": "git+https://github.com/ServiceWeave/triagent.git"
31
+ },
32
+ "homepage": "https://github.com/ServiceWeave/triagent#readme",
33
+ "bugs": {
34
+ "url": "https://github.com/ServiceWeave/triagent/issues"
31
35
  },
32
36
  "license": "MIT",
33
37
  "scripts": {
@@ -41,8 +45,10 @@
41
45
  "@ai-sdk/anthropic": "^1.2.0",
42
46
  "@ai-sdk/google": "^1.2.0",
43
47
  "@ai-sdk/openai": "^1.3.0",
44
- "@bashlet/sdk": "^0.1.0-alpha1",
48
+ "@bashlet/sdk": "1.3.2",
45
49
  "@mastra/core": "^1.0.0-beta.21",
50
+ "@opentui-ui/dialog": "^0.1.2",
51
+ "@opentui-ui/toast": "^0.0.5",
46
52
  "@opentui/core": "^0.1.72",
47
53
  "@opentui/solid": "^0.1.72",
48
54
  "hono": "^4.6.0",
package/src/cli/config.ts CHANGED
@@ -3,23 +3,139 @@ import { homedir } from "os";
3
3
  import { join } from "path";
4
4
  import type { AIProvider } from "../config.js";
5
5
 
6
+ export interface CodebaseEntry {
7
+ name: string;
8
+ path: string;
9
+ }
10
+
11
+ export interface ClusterConfig {
12
+ name: string;
13
+ context: string;
14
+ kubeConfigPath?: string;
15
+ environment?: "development" | "staging" | "production";
16
+ }
17
+
18
+ export interface PrometheusConfig {
19
+ url: string;
20
+ auth?: { token: string };
21
+ }
22
+
23
+ export interface GrafanaConfig {
24
+ url: string;
25
+ apiKey: string;
26
+ }
27
+
28
+ export interface ElasticsearchConfig {
29
+ url: string;
30
+ index: string;
31
+ auth?: { apiKey: string };
32
+ }
33
+
34
+ export interface LokiConfig {
35
+ url: string;
36
+ }
37
+
38
+ export interface CloudWatchConfig {
39
+ region: string;
40
+ logGroupPrefix?: string;
41
+ }
42
+
43
+ export interface SlackConfig {
44
+ webhookUrl: string;
45
+ botToken?: string;
46
+ defaultChannel?: string;
47
+ }
48
+
49
+ export interface TeamsConfig {
50
+ webhookUrl: string;
51
+ }
52
+
53
+ export interface RunbookConfig {
54
+ paths: string[];
55
+ gitRepos?: string[];
56
+ }
57
+
58
+ export interface CostAnalysisConfig {
59
+ provider?: "aws" | "gcp" | "azure";
60
+ hourlyRates?: {
61
+ cpu: number;
62
+ memory: number;
63
+ storage: number;
64
+ };
65
+ businessImpact?: {
66
+ revenuePerMinute?: number;
67
+ };
68
+ }
69
+
6
70
  export interface StoredConfig {
7
71
  aiProvider?: AIProvider;
8
72
  aiModel?: string;
9
73
  apiKey?: string;
10
74
  baseUrl?: string;
11
75
  webhookPort?: number;
12
- codebasePath?: string;
76
+ codebasePath?: string; // Deprecated: use codebasePaths instead
77
+ codebasePaths?: CodebaseEntry[];
13
78
  kubeConfigPath?: string;
79
+
80
+ // Phase 1: Foundation
81
+ historyRetentionDays?: number;
82
+ clusters?: ClusterConfig[];
83
+ activeCluster?: string;
84
+
85
+ // Phase 2: Observability
86
+ prometheus?: PrometheusConfig;
87
+ grafana?: GrafanaConfig;
88
+ logProvider?: "elasticsearch" | "loki" | "cloudwatch";
89
+ elasticsearch?: ElasticsearchConfig;
90
+ loki?: LokiConfig;
91
+ cloudwatch?: CloudWatchConfig;
92
+
93
+ // Phase 3: Operations
94
+ runbooks?: RunbookConfig;
95
+
96
+ // Phase 4: Communication & Cost
97
+ notifications?: {
98
+ slack?: SlackConfig;
99
+ teams?: TeamsConfig;
100
+ };
101
+ costAnalysis?: CostAnalysisConfig;
14
102
  }
15
103
 
16
104
  const CONFIG_DIR = join(homedir(), ".config", "triagent");
17
105
  const CONFIG_FILE = join(CONFIG_DIR, "config.json");
106
+ const TRIAGENT_MD_FILE = join(CONFIG_DIR, "TRIAGENT.md");
107
+ const RUNBOOK_MD_FILE = join(CONFIG_DIR, "RUNBOOK.md");
18
108
 
19
109
  export async function getConfigPath(): Promise<string> {
20
110
  return CONFIG_FILE;
21
111
  }
22
112
 
113
+ export async function getTriagentMdPath(): Promise<string> {
114
+ return TRIAGENT_MD_FILE;
115
+ }
116
+
117
+ export async function loadTriagentMd(): Promise<string | null> {
118
+ try {
119
+ const content = await readFile(TRIAGENT_MD_FILE, "utf-8");
120
+ return content.trim();
121
+ } catch {
122
+ return null;
123
+ }
124
+ }
125
+
126
+ export async function loadRunbookMd(): Promise<string | null> {
127
+ try {
128
+ const content = await readFile(RUNBOOK_MD_FILE, "utf-8");
129
+ return content.trim();
130
+ } catch {
131
+ return null;
132
+ }
133
+ }
134
+
135
+ export async function getRunbookMdPath(): Promise<string> {
136
+ return RUNBOOK_MD_FILE;
137
+ }
138
+
23
139
  export async function loadStoredConfig(): Promise<StoredConfig> {
24
140
  try {
25
141
  const content = await readFile(CONFIG_FILE, "utf-8");
@@ -40,7 +156,7 @@ export async function setConfigValue(key: keyof StoredConfig, value: string | nu
40
156
  await saveStoredConfig(config);
41
157
  }
42
158
 
43
- export async function getConfigValue(key: keyof StoredConfig): Promise<string | number | undefined> {
159
+ export async function getConfigValue(key: keyof StoredConfig): Promise<StoredConfig[keyof StoredConfig]> {
44
160
  const config = await loadStoredConfig();
45
161
  return config[key];
46
162
  }
package/src/config.ts CHANGED
@@ -1,22 +1,28 @@
1
1
  import { z } from "zod";
2
2
  import { resolve } from "path";
3
3
  import { homedir } from "os";
4
- import { loadStoredConfig, type StoredConfig } from "./cli/config.js";
4
+ import { loadStoredConfig, type StoredConfig, type CodebaseEntry } from "./cli/config.js";
5
5
 
6
6
  const AIProviderSchema = z.enum(["openai", "anthropic", "google"]);
7
7
  export type AIProvider = z.infer<typeof AIProviderSchema>;
8
8
 
9
+ const CodebaseEntrySchema = z.object({
10
+ name: z.string().min(1),
11
+ path: z.string().min(1),
12
+ });
13
+
9
14
  const ConfigSchema = z.object({
10
15
  aiProvider: AIProviderSchema,
11
16
  aiModel: z.string().min(1),
12
17
  apiKey: z.string().min(1),
13
18
  baseUrl: z.string().url().optional(),
14
19
  webhookPort: z.number().int().positive().default(3000),
15
- codebasePath: z.string().min(1).default("./"),
20
+ codebasePaths: z.array(CodebaseEntrySchema).min(1),
16
21
  kubeConfigPath: z.string().min(1).default("~/.kube"),
17
22
  });
18
23
 
19
24
  export type Config = z.infer<typeof ConfigSchema>;
25
+ export type { CodebaseEntry };
20
26
 
21
27
  function expandPath(path: string): string {
22
28
  if (path.startsWith("~")) {
@@ -39,6 +45,20 @@ function getApiKey(provider: AIProvider, stored: StoredConfig): string {
39
45
  }
40
46
  }
41
47
 
48
+ function resolveCodebasePaths(stored: StoredConfig): CodebaseEntry[] {
49
+ // Priority: codebasePaths array > legacy codebasePath > default
50
+ if (stored.codebasePaths && stored.codebasePaths.length > 0) {
51
+ return stored.codebasePaths.map((entry) => ({
52
+ name: entry.name,
53
+ path: expandPath(entry.path),
54
+ }));
55
+ }
56
+
57
+ // Backward compatibility: convert single codebasePath to array
58
+ const legacyPath = process.env.CODEBASE_PATH || stored.codebasePath || "./";
59
+ return [{ name: "workspace", path: expandPath(legacyPath) }];
60
+ }
61
+
42
62
  export async function loadConfig(): Promise<Config> {
43
63
  const stored = await loadStoredConfig();
44
64
 
@@ -50,7 +70,7 @@ export async function loadConfig(): Promise<Config> {
50
70
  apiKey: getApiKey(provider, stored),
51
71
  baseUrl: process.env.AI_BASE_URL || stored.baseUrl || undefined,
52
72
  webhookPort: parseInt(process.env.WEBHOOK_PORT || String(stored.webhookPort || 3000), 10),
53
- codebasePath: expandPath(process.env.CODEBASE_PATH || stored.codebasePath || "./"),
73
+ codebasePaths: resolveCodebasePaths(stored),
54
74
  kubeConfigPath: expandPath(process.env.KUBE_CONFIG_PATH || stored.kubeConfigPath || "~/.kube"),
55
75
  };
56
76