Repository navigation
Web UI: keep retrying + specific errors; watcher pre-check fix - #57
Merged
Merged
Conversation
Web UI: After 5 failed reconnect attempts, show specific causes (GEMINI_API_KEY, startup.sh, port) but keep retrying indefinitely. Previously stopped after 5 attempts. Watcher: Remove pgrep dedup guard that prevented the pre-check from running. Pre-check now correctly finds existing .txt files before entering fswatch wait loop. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
liususan091219
pushed a commit
to liususan091219/sutando-fork
that referenced
this pull request
May 28, 2026
…ck (sonichi#57) Web UI: After 5 failed reconnect attempts, show specific causes (GEMINI_API_KEY, startup.sh, port) but keep retrying indefinitely. Previously stopped after 5 attempts. Watcher: Remove pgrep dedup guard that prevented the pre-check from running. Pre-check now correctly finds existing .txt files before entering fswatch wait loop. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
john-the-dev
added a commit
to john-the-dev/sutando
that referenced
this pull request
Jul 7, 2026
…l body Addresses review on sonichi#2010 (qingyun-wu): - SKILL.md: instruct the agent to ANNOUNCE the log attachment before sending ("I'll attach recent diagnostic logs unless you'd rather I didn't") and honor --no-logs — an informed opt-out for voice/chat, matching sonichi#57's checkbox. - report-feedback.py: add _redact() backstop over the log excerpt (Bearer tokens, token=/api_key=/secret= values, common key formats sk-/xoxb-/ghp_/ AKIA, and the home-dir username). Private-Slack mirror makes this non-blocking, but /api/feedback can also open GitHub issues, so scrub before it leaves. - SKILL.md access-tier: state explicitly that non-owner Discord/Slack/Telegram route to sandboxed codex (no cloud token) and cannot reach this skill, so a non-owner can't ship the owner's logs into an issue. Also fixes a real bug found while testing: an empty --body sent body:null, which /api/feedback rejects (400 invalid_payload, 'Expected string, received null'). Now sends a string (falls back to the title), matching the desktop form. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DmsnVs5ffwHXdrQsLoZUCb
qingyun-wu
added a commit
that referenced
this pull request
Jul 9, 2026
…2010) * feat(skills): report-feedback skill (single reporting path) The user can report a bug / feature request / feedback by asking Sutando from ANY surface — chat, Discord, Telegram, or voice (via task delegation). One skill, not a per-surface tool, so there's no duplication. - skills/report-feedback/report-feedback.py posts to the cloud /api/feedback route (mirrors into GitHub issues) with kind/severity/title/body + a diagnostic context (platform + a tail of recent <workspace>/logs). - Reads cloud-auth.json on disk (falls back to the metering env the desktop injects); prints NOT_SIGNED_IN + exits 2 when signed out, ERROR on API failure — never leaves the agent guessing. - Owner-tier only. Replaces the earlier voice-only report_issue inline tool (dropped) per owner: the skill covers voice too via delegation, avoiding a duplicate capability. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Zp1T5KEdGsfNRFSLhY3fK * fix(report-feedback): read the cloud token the desktop actually writes The auth probe used the wrong path (state/auth/) and required a `signedIn` field. The desktop's readCloudAuth (electron/ipc.cjs) stores the record at <workspace>/cloud-auth.json and treats "signed in" as simply having a `token` (no signedIn field). Match that: read <workspace>/cloud-auth.json, also probe the packaged-app canonical workspace (~/.sutando/repo/workspace) so the skill finds the token across the dev/packaged split, and gate on token presence. Verified end-to-end: a signed-in submission returns 200 and lands in the feedback funnel + mirrored GitHub issue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Zp1T5KEdGsfNRFSLhY3fK * fix(report-feedback): announce+redact logs, harden tier note, fix null body Addresses review on #2010 (qingyun-wu): - SKILL.md: instruct the agent to ANNOUNCE the log attachment before sending ("I'll attach recent diagnostic logs unless you'd rather I didn't") and honor --no-logs — an informed opt-out for voice/chat, matching #57's checkbox. - report-feedback.py: add _redact() backstop over the log excerpt (Bearer tokens, token=/api_key=/secret= values, common key formats sk-/xoxb-/ghp_/ AKIA, and the home-dir username). Private-Slack mirror makes this non-blocking, but /api/feedback can also open GitHub issues, so scrub before it leaves. - SKILL.md access-tier: state explicitly that non-owner Discord/Slack/Telegram route to sandboxed codex (no cloud token) and cannot reach this skill, so a non-owner can't ship the owner's logs into an issue. Also fixes a real bug found while testing: an empty --body sent body:null, which /api/feedback rejects (400 invalid_payload, 'Expected string, received null'). Now sends a string (falls back to the title), matching the desktop form. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DmsnVs5ffwHXdrQsLoZUCb * fix(report-feedback): probe state/auth/cloud-auth.json (M1 canonical) first Post-M1 the cloud auth record lives at <workspace>/state/auth/cloud-auth.json. Add it as the first probe; retain the pre-M1 root path as a 30-day reader fallback per the workspace contract. Mirror the same M1+fallback pair for the packaged-app workspace so the skill finds the token regardless of checkout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EL2XBcV3RsWhevzTPtW4jw * fix(report-feedback): match real AWS access keys in _redact() (no separator) AKIA keys have no separator between the prefix and body (AKIAIOSFODNN7EXAMPLE), so the shared [_-] pattern never matched them. Split out a dedicated AKIA[A-Z0-9]{16} pattern that fires correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EL2XBcV3RsWhevzTPtW4jw * fix(report-feedback): redact Slack app tokens * test(report-feedback): cover main flow, cloud-auth branches, and logs excerpt Raises diff coverage on report-feedback.py from 35% to 100%, clearing the Coverage Gate on #2010. Adds tests for resolve_workspace (import + fallback), the read_cloud_auth metering-env / dedup / malformed-file branches, logs_excerpt (present / absent / unreadable), and main() (blank-title exit, not-signed-in exit, successful POST with and without logs, HTTPError and generic-error exits) via mocked urlopen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DC4oxV4ZwnKTKvE6Hb28Yo * fix(report-feedback): redact Google API keys --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Qingyun Wu <qingyun0327@gmail.com>
john-the-dev
added a commit
to john-the-dev/sutando
that referenced
this pull request
Jul 9, 2026
…onichi#2010) * feat(skills): report-feedback skill (single reporting path) The user can report a bug / feature request / feedback by asking Sutando from ANY surface — chat, Discord, Telegram, or voice (via task delegation). One skill, not a per-surface tool, so there's no duplication. - skills/report-feedback/report-feedback.py posts to the cloud /api/feedback route (mirrors into GitHub issues) with kind/severity/title/body + a diagnostic context (platform + a tail of recent <workspace>/logs). - Reads cloud-auth.json on disk (falls back to the metering env the desktop injects); prints NOT_SIGNED_IN + exits 2 when signed out, ERROR on API failure — never leaves the agent guessing. - Owner-tier only. Replaces the earlier voice-only report_issue inline tool (dropped) per owner: the skill covers voice too via delegation, avoiding a duplicate capability. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Zp1T5KEdGsfNRFSLhY3fK * fix(report-feedback): read the cloud token the desktop actually writes The auth probe used the wrong path (state/auth/) and required a `signedIn` field. The desktop's readCloudAuth (electron/ipc.cjs) stores the record at <workspace>/cloud-auth.json and treats "signed in" as simply having a `token` (no signedIn field). Match that: read <workspace>/cloud-auth.json, also probe the packaged-app canonical workspace (~/.sutando/repo/workspace) so the skill finds the token across the dev/packaged split, and gate on token presence. Verified end-to-end: a signed-in submission returns 200 and lands in the feedback funnel + mirrored GitHub issue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Zp1T5KEdGsfNRFSLhY3fK * fix(report-feedback): announce+redact logs, harden tier note, fix null body Addresses review on sonichi#2010 (qingyun-wu): - SKILL.md: instruct the agent to ANNOUNCE the log attachment before sending ("I'll attach recent diagnostic logs unless you'd rather I didn't") and honor --no-logs — an informed opt-out for voice/chat, matching sonichi#57's checkbox. - report-feedback.py: add _redact() backstop over the log excerpt (Bearer tokens, token=/api_key=/secret= values, common key formats sk-/xoxb-/ghp_/ AKIA, and the home-dir username). Private-Slack mirror makes this non-blocking, but /api/feedback can also open GitHub issues, so scrub before it leaves. - SKILL.md access-tier: state explicitly that non-owner Discord/Slack/Telegram route to sandboxed codex (no cloud token) and cannot reach this skill, so a non-owner can't ship the owner's logs into an issue. Also fixes a real bug found while testing: an empty --body sent body:null, which /api/feedback rejects (400 invalid_payload, 'Expected string, received null'). Now sends a string (falls back to the title), matching the desktop form. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DmsnVs5ffwHXdrQsLoZUCb * fix(report-feedback): probe state/auth/cloud-auth.json (M1 canonical) first Post-M1 the cloud auth record lives at <workspace>/state/auth/cloud-auth.json. Add it as the first probe; retain the pre-M1 root path as a 30-day reader fallback per the workspace contract. Mirror the same M1+fallback pair for the packaged-app workspace so the skill finds the token regardless of checkout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EL2XBcV3RsWhevzTPtW4jw * fix(report-feedback): match real AWS access keys in _redact() (no separator) AKIA keys have no separator between the prefix and body (AKIAIOSFODNN7EXAMPLE), so the shared [_-] pattern never matched them. Split out a dedicated AKIA[A-Z0-9]{16} pattern that fires correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EL2XBcV3RsWhevzTPtW4jw * fix(report-feedback): redact Slack app tokens * test(report-feedback): cover main flow, cloud-auth branches, and logs excerpt Raises diff coverage on report-feedback.py from 35% to 100%, clearing the Coverage Gate on sonichi#2010. Adds tests for resolve_workspace (import + fallback), the read_cloud_auth metering-env / dedup / malformed-file branches, logs_excerpt (present / absent / unreadable), and main() (blank-title exit, not-signed-in exit, successful POST with and without logs, HTTPError and generic-error exits) via mocked urlopen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DC4oxV4ZwnKTKvE6Hb28Yo * fix(report-feedback): redact Google API keys --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Qingyun Wu <qingyun0327@gmail.com>
liususan091219
added a commit
that referenced
this pull request
Jul 13, 2026
…ezone-aware now(UTC) (#2003) * fix(friction-detector): replace deprecated datetime.utcnow() with timezone-aware now(UTC) datetime.utcnow() was deprecated in Python 3.12 and scheduled for removal. Both `now` and the parsed `updatedAt` are now timezone-aware (UTC), so their difference is still correct. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QB3qZQrwponfum2JFNvHeh * test(friction-detector): cover check_github_issues() datetime comparison for diff-coverage gate Adds two tests that mock subprocess.run to exercise the timezone-aware datetime comparison path without a live gh token, satisfying the >= 95% diff-coverage gate on PR #2003. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QB3qZQrwponfum2JFNvHeh * fix(tests): fix start-cli, startup-env-token-persist, sutando-migrate for Ubuntu CI (#2029) * test(shell-ci): fix two ubuntu-portability failures from shell-ci-known-failures Two tests fail on ubuntu-latest CI (wired in by #1948) due to macOS-specific tooling; both pass on macOS today but gate CI on Ubuntu. start-cli-claude-config-dir.test.sh (2 failures): 1. Ambient CLAUDE_CONFIG_DIR leak — the test runner session has CLAUDE_CONFIG_DIR set, which leaked into the subprocess and broke "not set" assertion. Fix: add `unset CLAUDE_CONFIG_DIR` in setup_sandbox() alongside the HOME/SUTANDO_WORKSPACE exports that already clear the environment. 2. /var/folders vs /private/var symlink mismatch — macOS mktemp returns /var/folders/… but cd+pwd -P resolves to /private/var/folders/…; sutando-config.sh uses the physical path, so the expected vs actual CLAUDE_CONFIG_DIR differed. Fix: resolve SANDBOX to its real path (cd+pwd -P) immediately after mktemp. 3. Stale expected path — expected was keyed on $SUTANDO_WORKSPACE (deprecated v0.8 env-var, no longer read by the resolver) instead of the config-derived $REPO_FAKE/workspace. Fix: align expected to what sutando-config.sh actually computes. startup-env-token-persist.test.sh (would fail on Ubuntu): - stat -f '%Lp' (BSD mode format) and stat -f '%m' (BSD mtime) are macOS-only. Fix: replace both with equivalent Python one-liners (stat.S_IMODE + os.stat.st_mtime) that work on macOS and Linux without any extra deps. After this PR, both suites should be removable from tests/shell-ci-known-failures.txt once that file lands (via #1948). Closes two items from #1961. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EL2XBcV3RsWhevzTPtW4jw * test(shell-ci): fix sutando-migrate.test.sh — wrong DEST + BSD stat + pipefail exit Three bugs caused all 7 assertions to fail: 1. Wrong DEST — test used `SUTANDO_WORKSPACE="$DEST" --respect-env` but the workspace resolver ignores $SUTANDO_WORKSPACE since #1440. The backup landed in the real workspace instead of the temp fixture. Fix: use SUTANDO_MIGRATE_DEST (the documented E2E test hook symmetric with SUTANDO_MIGRATE_SRC_{A,B,C}). 2. Early exit via set -e — `grep -E "^sutando-migrate: backup"` exits 1 when the dest is empty (the backup line reads "dest empty; placeholder backup → …"). With `set -uo pipefail`, the INITIAL_BACKUP_ID assignment aborted the script before the assertion block ran. Fix: broaden the pattern to "migration-backup-.*\.tar\.gz" and add `|| true`. 3. BSD-only stat — `stat -f %m` is macOS-only; GNU stat uses `-c '%Y'`. Affects mtime comparison for notes/shared.md on Ubuntu. Fix: Python `os.stat().st_mtime` (portable, already used in startup-env-token-persist and start-cli fixes on this branch). All 14 assertions now pass locally. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EL2XBcV3RsWhevzTPtW4jw --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Qingyun Wu <qingyun0327@gmail.com> * feat(skills): report-feedback skill (report issues from any surface) (#2010) * feat(skills): report-feedback skill (single reporting path) The user can report a bug / feature request / feedback by asking Sutando from ANY surface — chat, Discord, Telegram, or voice (via task delegation). One skill, not a per-surface tool, so there's no duplication. - skills/report-feedback/report-feedback.py posts to the cloud /api/feedback route (mirrors into GitHub issues) with kind/severity/title/body + a diagnostic context (platform + a tail of recent <workspace>/logs). - Reads cloud-auth.json on disk (falls back to the metering env the desktop injects); prints NOT_SIGNED_IN + exits 2 when signed out, ERROR on API failure — never leaves the agent guessing. - Owner-tier only. Replaces the earlier voice-only report_issue inline tool (dropped) per owner: the skill covers voice too via delegation, avoiding a duplicate capability. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Zp1T5KEdGsfNRFSLhY3fK * fix(report-feedback): read the cloud token the desktop actually writes The auth probe used the wrong path (state/auth/) and required a `signedIn` field. The desktop's readCloudAuth (electron/ipc.cjs) stores the record at <workspace>/cloud-auth.json and treats "signed in" as simply having a `token` (no signedIn field). Match that: read <workspace>/cloud-auth.json, also probe the packaged-app canonical workspace (~/.sutando/repo/workspace) so the skill finds the token across the dev/packaged split, and gate on token presence. Verified end-to-end: a signed-in submission returns 200 and lands in the feedback funnel + mirrored GitHub issue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Zp1T5KEdGsfNRFSLhY3fK * fix(report-feedback): announce+redact logs, harden tier note, fix null body Addresses review on #2010 (qingyun-wu): - SKILL.md: instruct the agent to ANNOUNCE the log attachment before sending ("I'll attach recent diagnostic logs unless you'd rather I didn't") and honor --no-logs — an informed opt-out for voice/chat, matching #57's checkbox. - report-feedback.py: add _redact() backstop over the log excerpt (Bearer tokens, token=/api_key=/secret= values, common key formats sk-/xoxb-/ghp_/ AKIA, and the home-dir username). Private-Slack mirror makes this non-blocking, but /api/feedback can also open GitHub issues, so scrub before it leaves. - SKILL.md access-tier: state explicitly that non-owner Discord/Slack/Telegram route to sandboxed codex (no cloud token) and cannot reach this skill, so a non-owner can't ship the owner's logs into an issue. Also fixes a real bug found while testing: an empty --body sent body:null, which /api/feedback rejects (400 invalid_payload, 'Expected string, received null'). Now sends a string (falls back to the title), matching the desktop form. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DmsnVs5ffwHXdrQsLoZUCb * fix(report-feedback): probe state/auth/cloud-auth.json (M1 canonical) first Post-M1 the cloud auth record lives at <workspace>/state/auth/cloud-auth.json. Add it as the first probe; retain the pre-M1 root path as a 30-day reader fallback per the workspace contract. Mirror the same M1+fallback pair for the packaged-app workspace so the skill finds the token regardless of checkout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EL2XBcV3RsWhevzTPtW4jw * fix(report-feedback): match real AWS access keys in _redact() (no separator) AKIA keys have no separator between the prefix and body (AKIAIOSFODNN7EXAMPLE), so the shared [_-] pattern never matched them. Split out a dedicated AKIA[A-Z0-9]{16} pattern that fires correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EL2XBcV3RsWhevzTPtW4jw * fix(report-feedback): redact Slack app tokens * test(report-feedback): cover main flow, cloud-auth branches, and logs excerpt Raises diff coverage on report-feedback.py from 35% to 100%, clearing the Coverage Gate on #2010. Adds tests for resolve_workspace (import + fallback), the read_cloud_auth metering-env / dedup / malformed-file branches, logs_excerpt (present / absent / unreadable), and main() (blank-title exit, not-signed-in exit, successful POST with and without logs, HTTPError and generic-error exits) via mocked urlopen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DC4oxV4ZwnKTKvE6Hb28Yo * fix(report-feedback): redact Google API keys --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Qingyun Wu <qingyun0327@gmail.com> * fix(voice+phone): add explicit local-machine identity to prevent cloud fabrication (#2014) * fix(voice): add explicit local-machine identity to prevent cloud fabrication Addresses #2011 — when asked where Sutando runs, Gemini was defaulting to its training-data answer ("cloud-based AI"). Add an authoritative system-instruction line so the model answers correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RUA5TZgDa7VmFcNtPka38t * test: update voice-behavior-anchors fixture for identity instruction Regenerate the instructions hash after adding the local-machine identity line to buildInstructions() (deliberate prompt change for #2011). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RUA5TZgDa7VmFcNtPka38t * fix(phone): add explicit local-machine identity to prevent cloud fabrication Same issue as voice (#2011) — phone agent also lacked an authoritative statement about running locally. Adds the same system-instruction line to the inbound/outbound phone session path in buildPhoneInstructions(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RUA5TZgDa7VmFcNtPka38t * test: update phone-behavior-anchors fixture for identity instruction Regenerate hashes after adding the local-machine identity line to buildPhoneInstructions() (deliberate prompt change for #2011). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RUA5TZgDa7VmFcNtPka38t --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Qingyun Wu <qingyun0327@gmail.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Qingyun Wu <qingyun0327@gmail.com> Co-authored-by: Susan Xueqing Liu <xliu127@stevens.edu>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Test plan
🤖 Generated with Claude Code