Skip to content

Nod setup, Settings › Agents, agent menu and canvas cards - #589

Merged
scgopi merged 9 commits into
mainfrom
feat/nod-setup
Oct 2, 2026
Merged

scgopi merged 9 commits into
mainfrom
feat/nod-setup

Conversation

@scgopi

@scgopi scgopi commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Summary

Nod's setup, settings and place in the app (the NodSetup stream of GraphCode Nod), stacked on #583. You can set up Nod from the Welcome tour or from Settings. Settings gets a sidebar where Agents lists Nod and each CLI. The new-loop agent menu is grouped into Chat and Terminal, and canvas cards carry Nod's glyph, a goal progress bar and Allow once.

Changes

  • Setup (design 5a/5b): NodSetupView + NodSetupModel. You pick the engine, either Claude Agent SDK (Opus · Sonnet · Haiku) or GitHub Copilot SDK (GPT · Claude · Gemini), then sign in.
    • Claude: an Anthropic API key, checked and saved to the Keychain.
    • Copilot: the GitHub device flow (CopilotDeviceFlow).
      • It opens github.com/login/device, shows the code with Copy and a countdown, and handles authorization_pending, slow_down (+5 s), expired_token and access_denied.
      • The success state shows the account, plan, model count and premium requests used / limit.
  • Keychain: NodCredentialStore stores generic-password items under NodSettings.keychainService (app.graphcode.nod), one account per credential. An in-memory store is used for tests.
  • Claude sign-in policy: the Agent SDK docs say third-party products may not offer claude.ai login unless Anthropic approved it (code.claude.com/docs/en/agent-sdk/overview). So Continue with Claude and the Claude Code sign-in found on this Mac offer are built but gated off by NodClaudeSignIn.subscriptionLoginAllowed = false. The detector reads Keychain attributes only, so it never raises a prompt. API key is the path that ships.
  • Settings (design 7a): the single form becomes a sidebar: General · Agents (Nod, Claude Code, Codex, Copilot CLI, OpenCode, Pi) · Templates.
    • Each CLI page holds its permission picker, which moved out of General. Copilot's page also holds preferred versions.
    • NodSettingsPane is bound to GraphcodeSettings.nod and covers:
      • engines with sign-in state
      • the default model per loop type, plus Composite children
      • the goal evaluator
      • the permissions table
      • the shell allowlist editor
      • the spend cap
      • MCP servers: graphcode built in, then servers from recent projects' .mcp.json with an on/off switch, and Connect for remote servers that need sign-in.
  • Domain:
    • NodModelCatalog defines models per engine and per-type defaults (Main Sonnet, Goal Opus, Timed Haiku, Turn Sonnet, Composite Opus / children Sonnet, evaluator Haiku). Stale stored ids fall back to the default.
    • NodSettings.switchEngine drops choices the new engine can't run. Allowlist and spend-cap helpers are added.
    • New NodSettings.disabledMCPServers field, decoded tolerantly.
  • Agent menu (design 6a): RunsAsAgentMenu replaces the Agent picker. It is grouped into Chat (Nod with engine, sign-in and a Model submenu) and Terminal (the CLIs).
    • Greying is canHost, unchanged, with the missing capability written beside the row ("no sub-agents").
    • Nod's model is chosen in that menu, so the separate Model field hides for Nod.
  • Canvas cards (design 6b):
    • Nod cards show a monochrome node glyph in the meta row and keep LoopNode.activity as the live line.
    • Goal clauses draw as a bar ("goal 1 / 2").
    • A pending ask shows Allow once only when answerableFromCard; otherwise it shows Review in chat with "network · needs context".
    • The backend badge rule is unchanged.
  • Beta ramp: Nod's Welcome row, its Settings › Agents row and the agent menu's Chat group all follow FeatureRamps.nod from Add GraphCode Nod as a sixth backend kind, with its wire protocol #583 (beta 100, stable 0), as well as isSpiked.
  • Welcome: the Nod row on the agent page opens the setup sheet. It becomes the default only once Nod isSpiked.

Waiting on other streams / humans

  • Card data: cards read a NodCardDetail through NodCardWiring.provider/.answerer, which are no-ops on this branch, as agreed with NodLaunch. NodLaunch's follow-up adds LoopNode.nod: NodCardState? and points the answerer at NodControlClient.send(.resolvePermission(.allowOnce)).
  • Copilot OAuth app: the device flow needs GraphCode's own GitHub OAuth app client id in Info.plist key GraphCodeGitHubClientID. Without it the sheet says "This build has no GitHub sign-in configured." rather than borrowing another app's id.
  • MCP Connect: disabled with a tooltip. The OAuth handshake belongs to the runtime.
  • No wire-contract change: NodProtocol.swift and PROTOCOL.md are untouched.

Renders

NodSetupRenderTests draws every view headless with NSHostingView into .build/nod-renders/ and fails if a render is blank:

  • 5a-setup-claude
  • 5b-copilot-waiting, 5b-copilot-signed-in
  • 6a-agent-menu-goal, 6a-agent-menu-composite
  • 6b-canvas-cards
  • 7a-settings-nod, 7a-settings-copilot-cli

Test plan

RED: xcodebuild build-for-testing with graphcode/Sources and GraphcodeKit/Sources checked out from origin/feat/nod-foundation under the new NodSetupTests -> exit 65, NodSetupTests.swift:11:17 error: cannot find 'NodModelCatalog' in scope; :105:17 cannot find 'NodCredentialStore' in scope
GREEN: xcodebuild test -only-testing NodModelCatalogTests NodCredentialStoreTests NodClaudeSignInTests CopilotDeviceFlowTests AgentMenuSectionTests NodCardPresentationTests NodMCPServerTests NodSetupModelTests NodSetupRenderTests LoopCardPresentationTests -> pass, 67 tests in 10 suites
REGRESSION: xcodebuild -scheme graphcode test with private DerivedData -> pass, 2060 tests in 226 suites, 0 failed, 0 skipped (xcresult)

Gate on 1fca568 (= pushed head), private DerivedData:

  • xcodebuild test exit 0: 2060 tests / 226 suites / 0 failures (2056 before the ramp tests).
  • graphcode-cli build exit 0; graphcoded build exit 0.
  • scripts/cli-smoke.sh against a separate SwiftPM build: exit 0.
  • The portable Domain package (Tools/portable-prepare.py + swift test) compiles the new NodModelCatalog.swift: exit 0.
  • swiftlint 0 errors; swift format lint --strict clean.
  • Linux: see checks.

Checklist

  • I have signed off my commits (git commit -s) per the DCO
  • Tests pass locally
  • Code follows the existing style (swift format lint --strict, swiftlint 0 errors)
  • I added the tests before relying on the behaviour and observed RED against the base

scgopi and others added 6 commits October 1, 2026 21:50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scgopi <scgopireddy@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scgopi <scgopireddy@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scgopi <scgopireddy@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scgopi <scgopireddy@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scgopi <scgopireddy@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scgopi <scgopireddy@gmail.com>
Orange is the Needs-you state, which the pill and border already carry; the chat
pane's Allow once is action blue too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scgopi <scgopireddy@gmail.com>
@scgopi
scgopi changed the base branch from feat/nod-foundation to main October 2, 2026 14:50
Signed-off-by: scgopi <scgopireddy@gmail.com>
Signed-off-by: scgopi <scgopireddy@gmail.com>
@scgopi
scgopi merged commit e62583b into main Oct 2, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant