@cyanautomation/kaseki-agent 1.15.1 → 1.16.0

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.
@@ -0,0 +1,37 @@
1
+ /**
2
+ * github-operations-monitor.ts
3
+ *
4
+ * Monitoring module for analyzing kaseki github operations failures.
5
+ * Provides functions to detect, classify, and analyze github operations stage failures.
6
+ */
7
+ interface DiagnosticResult {
8
+ hasFailed: boolean;
9
+ failureType: string;
10
+ description: string;
11
+ exitCode: number;
12
+ stage: string;
13
+ apiError?: {
14
+ type: string;
15
+ message: string;
16
+ httpStatus: string;
17
+ };
18
+ logs: {
19
+ gitPushLogTail?: string[];
20
+ lastCommandLog?: string[];
21
+ healthCheckLog?: string[];
22
+ };
23
+ }
24
+ /**
25
+ * Detect if kaseki run had a github operations failure
26
+ */
27
+ export declare function isGithubOperationFailure(metadataPath: string): boolean;
28
+ /**
29
+ * Analyze a kaseki run for github operations failures
30
+ */
31
+ export declare function analyzeGithubOperations(resultsDir: string): DiagnosticResult;
32
+ /**
33
+ * Format diagnostic result as human-readable markdown
34
+ */
35
+ export declare function formatDiagnosticResult(result: DiagnosticResult): string;
36
+ export {};
37
+ //# sourceMappingURL=github-operations-monitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-operations-monitor.d.ts","sourceRoot":"","sources":["../src/github-operations-monitor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,UAAU,gBAAgB;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,IAAI,EAAE;QACJ,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAQtE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAoG5E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAoCvE"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * github-operations-monitor.ts
3
+ *
4
+ * Monitoring module for analyzing kaseki github operations failures.
5
+ * Provides functions to detect, classify, and analyze github operations stage failures.
6
+ */
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ /**
10
+ * Detect if kaseki run had a github operations failure
11
+ */
12
+ export function isGithubOperationFailure(metadataPath) {
13
+ try {
14
+ const content = fs.readFileSync(metadataPath, 'utf8');
15
+ const metadata = JSON.parse(content);
16
+ return metadata.current_stage === 'github operations' && (metadata.exit_code ?? 0) !== 0;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ /**
23
+ * Analyze a kaseki run for github operations failures
24
+ */
25
+ export function analyzeGithubOperations(resultsDir) {
26
+ const metadataPath = path.join(resultsDir, 'metadata.json');
27
+ const failurePath = path.join(resultsDir, 'failure.json');
28
+ const gitPushLogPath = path.join(resultsDir, 'git-push.log');
29
+ const lastCommandLogPath = path.join(resultsDir, 'last-command.log');
30
+ const healthCheckLogPath = path.join(resultsDir, 'github-health-check.log');
31
+ const result = {
32
+ hasFailed: false,
33
+ failureType: 'unknown',
34
+ description: 'No failure detected',
35
+ exitCode: 0,
36
+ stage: 'unknown',
37
+ logs: {},
38
+ };
39
+ // Read metadata
40
+ let metadata = {};
41
+ try {
42
+ const content = fs.readFileSync(metadataPath, 'utf8');
43
+ metadata = JSON.parse(content);
44
+ }
45
+ catch (e) {
46
+ result.description = `Failed to read metadata: ${e}`;
47
+ return result;
48
+ }
49
+ // Read failure.json for additional context
50
+ try {
51
+ if (fs.existsSync(failurePath)) {
52
+ const content = fs.readFileSync(failurePath, 'utf8');
53
+ JSON.parse(content); // Validate JSON format
54
+ }
55
+ }
56
+ catch (e) {
57
+ // Failure.json might not exist if success
58
+ }
59
+ result.stage = metadata.current_stage ?? 'unknown';
60
+ result.exitCode = metadata.exit_code ?? 0;
61
+ // Classify failure
62
+ if (metadata.current_stage !== 'github operations') {
63
+ result.hasFailed = false;
64
+ result.description = `Failure occurred in stage '${metadata.current_stage}', not github operations`;
65
+ return result;
66
+ }
67
+ if (result.exitCode === 0) {
68
+ result.hasFailed = false;
69
+ result.description = 'GitHub operations completed successfully';
70
+ return result;
71
+ }
72
+ result.hasFailed = true;
73
+ // Determine failure type
74
+ const pushExit = metadata.github_push_exit_code ?? 0;
75
+ const prExit = metadata.github_pr_exit_code ?? 0;
76
+ if (pushExit !== 0 && prExit === 0) {
77
+ result.failureType = 'git_push_failed';
78
+ result.description = `Git push failed with exit code ${pushExit}`;
79
+ }
80
+ else if (prExit !== 0) {
81
+ result.failureType = 'pr_creation_failed';
82
+ result.description = `GitHub PR creation failed with exit code ${prExit}`;
83
+ // Include API error details
84
+ if (metadata.github_api_error_type || metadata.github_api_error_message) {
85
+ result.apiError = {
86
+ type: metadata.github_api_error_type ?? 'unknown',
87
+ message: metadata.github_api_error_message ?? '',
88
+ httpStatus: metadata.github_api_http_status ?? '',
89
+ };
90
+ result.description += ` (${result.apiError.type}: ${result.apiError.message})`;
91
+ }
92
+ }
93
+ else if (pushExit === 0 && prExit === 0) {
94
+ result.failureType = 'post_github_ops_failure';
95
+ result.description =
96
+ 'GitHub operations succeeded but overall exit code is non-zero (failure in cleanup/trap)';
97
+ }
98
+ else {
99
+ result.failureType = 'unknown_failure_pattern';
100
+ result.description = `Unknown failure pattern: push_exit=${pushExit}, pr_exit=${prExit}, overall_exit=${result.exitCode}`;
101
+ }
102
+ // Collect logs
103
+ if (fs.existsSync(gitPushLogPath)) {
104
+ const content = fs.readFileSync(gitPushLogPath, 'utf8');
105
+ result.logs.gitPushLogTail = content.split('\n').slice(-30).filter((l) => l.trim());
106
+ }
107
+ if (fs.existsSync(lastCommandLogPath)) {
108
+ const content = fs.readFileSync(lastCommandLogPath, 'utf8');
109
+ result.logs.lastCommandLog = content.split('\n').filter((l) => l.trim());
110
+ }
111
+ if (fs.existsSync(healthCheckLogPath)) {
112
+ const content = fs.readFileSync(healthCheckLogPath, 'utf8');
113
+ result.logs.healthCheckLog = content.split('\n').filter((l) => l.trim());
114
+ }
115
+ return result;
116
+ }
117
+ /**
118
+ * Format diagnostic result as human-readable markdown
119
+ */
120
+ export function formatDiagnosticResult(result) {
121
+ let output = '# GitHub Operations Diagnostic\n\n';
122
+ output += `**Stage:** ${result.stage}\n`;
123
+ output += `**Exit code:** ${result.exitCode}\n`;
124
+ output += `**Failure detected:** ${result.hasFailed ? 'Yes' : 'No'}\n`;
125
+ output += `**Failure type:** ${result.failureType}\n\n`;
126
+ output += `## Summary\n\n${result.description}\n\n`;
127
+ if (result.apiError) {
128
+ output += '## API Error Details\n\n';
129
+ output += `- **Type:** ${result.apiError.type}\n`;
130
+ output += `- **Message:** ${result.apiError.message}\n`;
131
+ output += `- **HTTP Status:** ${result.apiError.httpStatus}\n\n`;
132
+ }
133
+ if (result.logs.gitPushLogTail && result.logs.gitPushLogTail.length > 0) {
134
+ output += '## Git Push Log (last 30 lines)\n\n```\n';
135
+ output += result.logs.gitPushLogTail.join('\n');
136
+ output += '\n```\n\n';
137
+ }
138
+ if (result.logs.lastCommandLog && result.logs.lastCommandLog.length > 0) {
139
+ output += '## Last Command Log\n\n```\n';
140
+ output += result.logs.lastCommandLog.join('\n');
141
+ output += '\n```\n\n';
142
+ }
143
+ if (result.logs.healthCheckLog && result.logs.healthCheckLog.length > 0) {
144
+ output += '## Health Check Log\n\n```\n';
145
+ output += result.logs.healthCheckLog.join('\n');
146
+ output += '\n```\n\n';
147
+ }
148
+ return output;
149
+ }
150
+ // CLI usage
151
+ if (require.main === module) {
152
+ const resultsDir = process.argv[2];
153
+ if (!resultsDir) {
154
+ console.error('Usage: npx ts-node github-operations-monitor.ts <results-directory>');
155
+ process.exit(1);
156
+ }
157
+ const result = analyzeGithubOperations(resultsDir);
158
+ const formatted = formatDiagnosticResult(result);
159
+ console.log(formatted);
160
+ process.exit(result.hasFailed && result.failureType === 'post_github_ops_failure' ? 1 : 0);
161
+ }
162
+ //# sourceMappingURL=github-operations-monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-operations-monitor.js","sourceRoot":"","sources":["../src/github-operations-monitor.ts"],"names":[],"mappings":"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"}
package/kaseki-agent.sh CHANGED
@@ -28,7 +28,7 @@ KASEKI_AGENT_GUARDRAILS="${KASEKI_AGENT_GUARDRAILS:-1}"
28
28
  KASEKI_RESTORE_DISALLOWED_CHANGES="${KASEKI_RESTORE_DISALLOWED_CHANGES:-1}"
29
29
  KASEKI_VALIDATION_FAIL_FAST="${KASEKI_VALIDATION_FAIL_FAST:-1}"
30
30
  KASEKI_STRICT_SCRIPT_CHECK="${KASEKI_STRICT_SCRIPT_CHECK:-0}"
31
- GITHUB_APP_ENABLED="${GITHUB_APP_ENABLED:-0}"
31
+ GITHUB_APP_ENABLED="${GITHUB_APP_ENABLED:-1}"
32
32
  KASEKI_PUBLISH_MODE="${KASEKI_PUBLISH_MODE:-auto}"
33
33
  KASEKI_GITHUB_PR_RETRIES="${KASEKI_GITHUB_PR_RETRIES:-3}"
34
34
  START_EPOCH="$(date +%s)"
@@ -84,6 +84,10 @@ REPO_MEMORY_FILE=""
84
84
  REPO_MEMORY_STATUS="disabled"
85
85
  REPO_MEMORY_COMMIT_SHA="unknown"
86
86
 
87
+ # Track last executed command for better error reporting
88
+ LAST_COMMAND=""
89
+ LAST_COMMAND_LOG="/results/last-command.log"
90
+
87
91
  # Signal handler for graceful termination
88
92
  handle_termination() {
89
93
  local signal="$1"
@@ -99,6 +103,9 @@ handle_termination() {
99
103
  trap 'handle_termination SIGTERM' SIGTERM
100
104
  trap 'handle_termination SIGINT' SIGINT
101
105
 
106
+ # DEBUG trap: capture last command before execution for better error diagnostics
107
+ trap 'LAST_COMMAND="$BASH_COMMAND"' DEBUG
108
+
102
109
  setup_host_logging_mirror() {
103
110
  local base_name="$1"
104
111
  local stamp host_log_file
@@ -160,6 +167,55 @@ case "$KASEKI_GIT_CACHE_MODE" in
160
167
  ;;
161
168
  esac
162
169
 
170
+ # Helper function to run Node.js subprocesses with comprehensive error logging
171
+ # Usage: run_node_subprocess <output_var_name> "<node_code>" [<input_data>] [<error_log_file>]
172
+ # Captures stderr, logs errors, and returns exit code for caller to check
173
+ run_node_subprocess() {
174
+ local output_var_name="$1"
175
+ local node_code="$2"
176
+ local input_data="${3:-}"
177
+ local error_log_file="${4:-/tmp/node-error.log}"
178
+ local node_stderr_tmp node_exit_code output_value
179
+
180
+ node_stderr_tmp="$(mktemp /tmp/node-stderr.XXXXXX)" || {
181
+ printf 'ERROR: Failed to create temp file for Node.js stderr\n' >&2
182
+ eval "$output_var_name=''"
183
+ return 1
184
+ }
185
+
186
+ # Run Node.js and capture both stdout and stderr
187
+ if [ -n "$input_data" ]; then
188
+ output_value=$(printf '%s' "$input_data" | node -e "$node_code" 2>"$node_stderr_tmp")
189
+ else
190
+ output_value=$(node -e "$node_code" 2>"$node_stderr_tmp")
191
+ fi
192
+ node_exit_code=$?
193
+
194
+ # Handle errors
195
+ if [ $node_exit_code -ne 0 ]; then
196
+ local stderr_content
197
+ stderr_content="$(cat "$node_stderr_tmp" 2>/dev/null || echo '<unable to read stderr>')"
198
+ {
199
+ printf '[node-subprocess-error] Command failed with exit code %d\n' "$node_exit_code"
200
+ if [ -n "$stderr_content" ]; then
201
+ printf '[node-subprocess-error] stderr: %s\n' "$stderr_content"
202
+ fi
203
+ printf '[node-subprocess-error] code: %.200s\n' "$node_code"
204
+ if [ -n "$input_data" ]; then
205
+ printf '[node-subprocess-error] input (first 150 chars): %.150s\n' "$input_data"
206
+ fi
207
+ } | tee -a "$error_log_file" >&2
208
+ rm -f "$node_stderr_tmp"
209
+ eval "$output_var_name=''"
210
+ return "$node_exit_code"
211
+ fi
212
+
213
+ # Success: store output in variable and return 0
214
+ eval "$output_var_name='$output_value'"
215
+ rm -f "$node_stderr_tmp"
216
+ return 0
217
+ }
218
+
163
219
  # Safely encode value as JSON string; fallback to empty string if node unavailable
164
220
  json_encode() {
165
221
  if ! command -v node &>/dev/null; then
@@ -712,8 +768,20 @@ check_secret_scan_allowlist() {
712
768
  finish() {
713
769
  local code=$?
714
770
  if [ "$code" -ne 0 ] && [ "$STATUS" -eq 0 ]; then
771
+ # Capture diagnostic context for the catch-all error
715
772
  STATUS="$code"
716
773
  FAILED_COMMAND="unexpected shell failure"
774
+ # Log the last command that was executed
775
+ {
776
+ printf '[unexpected-failure] Exit code: %d\n' "$code"
777
+ printf '[unexpected-failure] Last command: %s\n' "$LAST_COMMAND"
778
+ printf '[unexpected-failure] Current stage: %s\n' "$CURRENT_STAGE"
779
+ if [ -f /results/progress.log ]; then
780
+ printf '[unexpected-failure] Last 5 progress entries:\n'
781
+ tail -5 /results/progress.log | sed 's/^/ /'
782
+ fi
783
+ } | tee -a "$LAST_COMMAND_LOG" >&2
784
+ emit_error_event "unexpected_shell_failure" "Uncaught shell error (exit $code) in stage '$CURRENT_STAGE'. Last command: $LAST_COMMAND. See $LAST_COMMAND_LOG for context." "exit"
717
785
  fi
718
786
  # Authoritative call site: this runs at EXIT so artifacts reflect final repo state.
719
787
  collect_git_artifacts
@@ -1266,6 +1334,73 @@ $memory_section
1266
1334
  EOF
1267
1335
  }
1268
1336
 
1337
+ check_github_operations_health() {
1338
+ # Preflight health check for github operations before pi agent runs
1339
+ # Tests: GitHub App secrets, git config, Node.js token generation capability
1340
+ local health_log="/results/github-health-check.log"
1341
+ : > "$health_log"
1342
+
1343
+ printf '[preflight] github operations health check started\n' | tee -a "$health_log"
1344
+
1345
+ # Check 1: GitHub App secrets are readable
1346
+ if ! [ -r /run/secrets/github_app_id ]; then
1347
+ printf '[health-check] ERROR: Cannot read GitHub App ID from /run/secrets/github_app_id\n' | tee -a "$health_log" >&2
1348
+ return 1
1349
+ fi
1350
+ if ! [ -r /run/secrets/github_app_client_id ]; then
1351
+ printf '[health-check] ERROR: Cannot read GitHub App client ID from /run/secrets/github_app_client_id\n' | tee -a "$health_log" >&2
1352
+ return 1
1353
+ fi
1354
+ if ! [ -r /run/secrets/github_app_private_key ]; then
1355
+ printf '[health-check] ERROR: Cannot read GitHub App private key from /run/secrets/github_app_private_key\n' | tee -a "$health_log" >&2
1356
+ return 1
1357
+ fi
1358
+ printf '[health-check] ✓ GitHub App secrets are readable\n' | tee -a "$health_log"
1359
+
1360
+ # Check 2: Verify git is available
1361
+ if ! git --version >/dev/null 2>&1; then
1362
+ printf '[health-check] ERROR: git command is not available\n' | tee -a "$health_log" >&2
1363
+ return 1
1364
+ fi
1365
+ printf '[health-check] ✓ git is available\n' | tee -a "$health_log"
1366
+
1367
+ # Check 3: Test Node.js github-app-token helper script exists
1368
+ if ! [ -x /usr/local/bin/github-app-token ]; then
1369
+ printf '[health-check] ERROR: github-app-token helper not found at /usr/local/bin/github-app-token\n' | tee -a "$health_log" >&2
1370
+ return 1
1371
+ fi
1372
+ printf '[health-check] ✓ github-app-token helper is available\n' | tee -a "$health_log"
1373
+
1374
+ # Check 4: Test Node.js is available
1375
+ if ! command -v node >/dev/null 2>&1; then
1376
+ printf '[health-check] ERROR: Node.js is not available\n' | tee -a "$health_log" >&2
1377
+ return 1
1378
+ fi
1379
+ printf '[health-check] ✓ Node.js is available\n' | tee -a "$health_log"
1380
+
1381
+ # Check 5: Test Node.js JSON parsing
1382
+ local test_output
1383
+ test_output=$(printf '{"test":"value"}' | node -e "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.test);" 2>&1) || {
1384
+ printf '[health-check] ERROR: Node.js JSON parsing failed: %s\n' "$test_output" | tee -a "$health_log" >&2
1385
+ return 1
1386
+ }
1387
+ if [ "$test_output" != "value" ]; then
1388
+ printf '[health-check] ERROR: Node.js JSON parsing returned unexpected output: %s\n' "$test_output" | tee -a "$health_log" >&2
1389
+ return 1
1390
+ fi
1391
+ printf '[health-check] ✓ Node.js JSON parsing works\n' | tee -a "$health_log"
1392
+
1393
+ # Check 6: Test curl is available
1394
+ if ! command -v curl >/dev/null 2>&1; then
1395
+ printf '[health-check] ERROR: curl is not available\n' | tee -a "$health_log" >&2
1396
+ return 1
1397
+ fi
1398
+ printf '[health-check] ✓ curl is available\n' | tee -a "$health_log"
1399
+
1400
+ printf '[preflight] github operations health check PASSED\n' | tee -a "$health_log"
1401
+ return 0
1402
+ }
1403
+
1269
1404
  validate_github_api_response() {
1270
1405
  local http_status response log_file error_type error_message
1271
1406
  http_status="$1"
@@ -1393,13 +1528,19 @@ run_github_operations() {
1393
1528
  return 7
1394
1529
  }
1395
1530
 
1396
- token="$(printf '%s' "$token_data" | node -e "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.token || '')" 2>/dev/null)"
1397
- if [ -z "$token" ]; then
1531
+ # Use helper to extract token from JSON response
1532
+ if ! run_node_subprocess token "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.token || '')" "$token_data" /results/git-push.log; then
1398
1533
  printf -- 'Failed to extract token from response: %s\n' "$token_data" | tee -a /results/git-push.log >&2
1399
1534
  GITHUB_PUSH_EXIT=7
1400
1535
  return 7
1401
1536
  fi
1402
1537
 
1538
+ if [ -z "$token" ]; then
1539
+ printf -- 'Failed to extract token from response (empty result)\n' | tee -a /results/git-push.log >&2
1540
+ GITHUB_PUSH_EXIT=7
1541
+ return 7
1542
+ fi
1543
+
1403
1544
  printf 'Token generated successfully\n' | tee -a /results/git-push.log
1404
1545
 
1405
1546
  # Create and push feature branch
@@ -1510,6 +1651,19 @@ EOF
1510
1651
  printf 'Debug: Creating PR with head=%s, base=%s, draft=true\n' "$feature_branch" "$GIT_REF" | tee -a /results/git-push.log
1511
1652
  fi
1512
1653
 
1654
+ # Encode PR title and body as JSON strings
1655
+ local pr_title_json pr_body_json
1656
+ if ! run_node_subprocess pr_title_json "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))" "$pr_title" /results/git-push.log; then
1657
+ printf 'ERROR: Failed to JSON encode PR title\n' | tee -a /results/git-push.log >&2
1658
+ GITHUB_PR_EXIT=8
1659
+ return 8
1660
+ fi
1661
+ if ! run_node_subprocess pr_body_json "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))" "$pr_body" /results/git-push.log; then
1662
+ printf 'ERROR: Failed to JSON encode PR body\n' | tee -a /results/git-push.log >&2
1663
+ GITHUB_PR_EXIT=8
1664
+ return 8
1665
+ fi
1666
+
1513
1667
  # Use curl with -w to capture HTTP status separately
1514
1668
  # curl exit code: 0=success, non-0=failure
1515
1669
  local curl_exit
@@ -1517,7 +1671,7 @@ EOF
1517
1671
  -H "Authorization: token $token" \
1518
1672
  -H "Accept: application/vnd.github.v3+json" \
1519
1673
  "https://api.github.com/repos/$owner/$repo/pulls" \
1520
- -d "{\"title\": $(printf '%s' "$pr_title" | node -e "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))"), \"body\": $(printf '%s' "$pr_body" | node -e "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))"), \"head\": \"$feature_branch\", \"base\": \"$GIT_REF\", \"draft\": true}" > "$temp_status_file" 2>&1
1674
+ -d "{\"title\": $pr_title_json, \"body\": $pr_body_json, \"head\": \"$feature_branch\", \"base\": \"$GIT_REF\", \"draft\": true}" > "$temp_status_file" 2>&1
1521
1675
  curl_exit=$?
1522
1676
 
1523
1677
  # Split response and status code
@@ -1552,8 +1706,13 @@ EOF
1552
1706
 
1553
1707
  # Validate the API response
1554
1708
  if validate_github_api_response "$pr_http_status" "$pr_response" /results/git-push.log; then
1555
- # API returned success (201); now extract the URL
1556
- pr_url="$(printf '%s' "$pr_response" | node -e "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.html_url || '')" 2>/dev/null || true)"
1709
+ # API returned success (201); now extract the URL using helper
1710
+ if ! run_node_subprocess pr_url "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.html_url || '')" "$pr_response" /results/git-push.log; then
1711
+ printf 'ERROR: Failed to extract PR URL from API response\n' | tee -a /results/git-push.log >&2
1712
+ emit_error_event "github_pr_response_malformed" "Failed to parse PR API response to extract html_url" "exit"
1713
+ GITHUB_PR_EXIT=9
1714
+ pr_url=""
1715
+ fi
1557
1716
 
1558
1717
  if [ -n "$pr_url" ]; then
1559
1718
  GITHUB_PR_URL="$pr_url"
@@ -1612,6 +1771,17 @@ printf 'Provider: %s\n' "$KASEKI_PROVIDER"
1612
1771
  printf 'Model: %s\n' "$KASEKI_MODEL"
1613
1772
  printf 'Pi version: %s\n' "$PI_VERSION"
1614
1773
 
1774
+ # Run preflight health check for GitHub operations if enabled
1775
+ if [ "$GITHUB_APP_ENABLED" = "1" ]; then
1776
+ printf '\n==> github operations preflight health check\n'
1777
+ if ! check_github_operations_health; then
1778
+ printf 'ERROR: GitHub operations preflight health check failed\n' >&2
1779
+ printf 'GitHub App is enabled but configuration or dependencies are missing.\n' >&2
1780
+ printf 'Proceeding with kaseki run, but GitHub operations will be skipped or fail.\n' >&2
1781
+ emit_error_event "github_preflight_failed" "GitHub operations health check failed; check /results/github-health-check.log for details" "continue"
1782
+ fi
1783
+ fi
1784
+
1615
1785
  openrouter_api_key=""
1616
1786
  openrouter_api_key_source=""
1617
1787
  if [ -n "${OPENROUTER_API_KEY:-}" ]; then
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanautomation/kaseki-agent",
3
- "version": "1.15.1",
3
+ "version": "1.16.0",
4
4
  "description": "Ephemeral coding-agent runner: orchestrates Pi CLI via Docker for automated code modifications with validation",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env bash
2
+ # kaseki-diagnose-github-failure.sh
3
+ # Diagnostic script to analyze github operations failures in kaseki run artifacts
4
+ # Usage: ./kaseki-diagnose-github-failure.sh /agents/kaseki-results/kaseki-N
5
+
6
+ set -eo pipefail
7
+
8
+ RESULTS_DIR="${1:-.}"
9
+ SCRIPT_NAME="$(basename "$0")"
10
+
11
+ if [ ! -d "$RESULTS_DIR" ]; then
12
+ printf 'ERROR: Results directory not found: %s\n' "$RESULTS_DIR" >&2
13
+ exit 1
14
+ fi
15
+
16
+ if [ ! -f "$RESULTS_DIR/metadata.json" ] || [ ! -f "$RESULTS_DIR/failure.json" ]; then
17
+ printf 'ERROR: metadata.json or failure.json not found in %s\n' "$RESULTS_DIR" >&2
18
+ exit 1
19
+ fi
20
+
21
+ # Helper to extract JSON values
22
+ json_value() {
23
+ local file="$1"
24
+ local key="$2"
25
+ grep "\"$key\"" "$file" | head -1 | sed 's/.*"'$key'":\s*"\?//; s/".*$//'
26
+ }
27
+
28
+ # Read key fields from metadata
29
+ INSTANCE="$(json_value "$RESULTS_DIR/metadata.json" "instance")"
30
+ STAGE="$(json_value "$RESULTS_DIR/metadata.json" "current_stage")"
31
+ EXIT_CODE="$(json_value "$RESULTS_DIR/metadata.json" "exit_code")"
32
+ GITHUB_PUSH_EXIT="$(json_value "$RESULTS_DIR/metadata.json" "github_push_exit_code")"
33
+ GITHUB_PR_EXIT="$(json_value "$RESULTS_DIR/metadata.json" "github_pr_exit_code")"
34
+ GITHUB_API_ERROR_TYPE="$(json_value "$RESULTS_DIR/metadata.json" "github_api_error_type")"
35
+ GITHUB_API_ERROR_MESSAGE="$(json_value "$RESULTS_DIR/metadata.json" "github_api_error_message")"
36
+ GITHUB_API_HTTP_STATUS="$(json_value "$RESULTS_DIR/metadata.json" "github_api_http_status")"
37
+
38
+ printf '# GitHub Operations Failure Diagnostic Report\n\n'
39
+ printf '**Instance:** %s\n' "$INSTANCE"
40
+ printf '**Results directory:** %s\n' "$RESULTS_DIR"
41
+ printf '**Overall exit code:** %s\n\n' "$EXIT_CODE"
42
+
43
+ # ===== Stage-level diagnosis =====
44
+ printf '## Stage Status\n\n'
45
+ if [ "$STAGE" = "github operations" ]; then
46
+ printf '- Current stage: github operations (failure occurred in this stage)\n'
47
+ else
48
+ printf '- Current stage: %s (failure occurred before github operations)\n' "$STAGE"
49
+ fi
50
+ printf '- GitHub push exit code: %s\n' "$GITHUB_PUSH_EXIT"
51
+ printf '- GitHub PR exit code: %s\n' "$GITHUB_PR_EXIT"
52
+
53
+ # ===== Detailed diagnosis =====
54
+ printf '\n## Diagnosis\n\n'
55
+
56
+ # Check if failure occurred in github operations stage
57
+ if [ "$STAGE" != "github operations" ]; then
58
+ printf '**Failure occurred before github operations stage.**\n'
59
+ printf '\nGithub operations was never attempted. Check earlier stages:\n'
60
+ if [ -f "$RESULTS_DIR/stage-timings.tsv" ]; then
61
+ printf '\n```\n'
62
+ cat "$RESULTS_DIR/stage-timings.tsv"
63
+ printf '```\n'
64
+ fi
65
+ exit 0
66
+ fi
67
+
68
+ # Now we know stage is github operations
69
+ if [ "$GITHUB_PUSH_EXIT" -ne 0 ] && [ "$GITHUB_PR_EXIT" -eq 0 ]; then
70
+ printf '**Git push failed (exit code: %s)**\n\n' "$GITHUB_PUSH_EXIT"
71
+ printf 'The git push operation failed. Check git-push.log for details:\n'
72
+ if [ -f "$RESULTS_DIR/git-push.log" ]; then
73
+ printf '\n```\n'
74
+ tail -20 "$RESULTS_DIR/git-push.log"
75
+ printf '```\n'
76
+ fi
77
+ elif [ "$GITHUB_PR_EXIT" -ne 0 ]; then
78
+ printf '**GitHub PR creation failed (exit code: %s)**\n\n' "$GITHUB_PR_EXIT"
79
+
80
+ if [ -n "$GITHUB_API_ERROR_TYPE" ]; then
81
+ printf 'API error type: %s\n' "$GITHUB_API_ERROR_TYPE"
82
+ fi
83
+ if [ -n "$GITHUB_API_ERROR_MESSAGE" ]; then
84
+ printf 'API error message: %s\n' "$GITHUB_API_ERROR_MESSAGE"
85
+ fi
86
+ if [ -n "$GITHUB_API_HTTP_STATUS" ]; then
87
+ printf 'HTTP status: %s\n' "$GITHUB_API_HTTP_STATUS"
88
+ fi
89
+
90
+ printf '\nAPI error details from git-push.log:\n'
91
+ if [ -f "$RESULTS_DIR/git-push.log" ]; then
92
+ printf '\n```\n'
93
+ tail -30 "$RESULTS_DIR/git-push.log"
94
+ printf '```\n'
95
+ fi
96
+ elif [ "$GITHUB_PUSH_EXIT" -eq 0 ] && [ "$GITHUB_PR_EXIT" -eq 0 ]; then
97
+ printf '**GitHub operations completed successfully, but overall exit code is non-zero.**\n\n'
98
+ printf 'This suggests the failure occurred in the finish() trap handler or post-github-ops cleanup.\n'
99
+ printf 'Check the following:\n\n'
100
+
101
+ if [ -f "$RESULTS_DIR/last-command.log" ]; then
102
+ printf '- Last command log exists at: %s\n' "$RESULTS_DIR/last-command.log"
103
+ printf '\n```\n'
104
+ cat "$RESULTS_DIR/last-command.log"
105
+ printf '```\n'
106
+ fi
107
+
108
+ if [ -f "$RESULTS_DIR/github-health-check.log" ]; then
109
+ printf '\n- GitHub health check log:\n\n```\n'
110
+ cat "$RESULTS_DIR/github-health-check.log"
111
+ printf '```\n'
112
+ fi
113
+
114
+ if [ -f "$RESULTS_DIR/restoration-errors.log" ]; then
115
+ printf '\n- Restoration errors:\n\n```\n'
116
+ cat "$RESULTS_DIR/restoration-errors.log"
117
+ printf '```\n'
118
+ fi
119
+ else
120
+ printf '**Unknown failure pattern.**\n\n'
121
+ printf 'Push exit: %s, PR exit: %s\n' "$GITHUB_PUSH_EXIT" "$GITHUB_PR_EXIT"
122
+ fi
123
+
124
+ # ===== Check for subprocess errors =====
125
+ printf '\n## Node.js Subprocess Errors\n\n'
126
+ if [ -f "$RESULTS_DIR/git-push.log" ] && grep -q '\[node-subprocess-error\]' "$RESULTS_DIR/git-push.log"; then
127
+ printf 'Node.js subprocess errors detected:\n\n'
128
+ printf '```\n'
129
+ grep '\[node-subprocess-error\]' "$RESULTS_DIR/git-push.log" | head -20
130
+ printf '```\n'
131
+ else
132
+ printf 'No Node.js subprocess errors detected.\n'
133
+ fi
134
+
135
+ # ===== Helpful references =====
136
+ printf '\n## Helpful Resources\n\n'
137
+ printf '- **Exit codes reference:** See docs/EXIT_CODES.md\n'
138
+ printf '- **Git-push.log:** Full GitHub operations details at %s/git-push.log\n' "$RESULTS_DIR"
139
+ printf '- **Metadata:** Complete run metadata at %s/metadata.json\n' "$RESULTS_DIR"
140
+ printf '- **Failure details:** See %s/failure.json\n' "$RESULTS_DIR"
141
+
142
+ printf '\n---\n\n'
143
+ printf '**Generated by %s**\n' "$SCRIPT_NAME"