main
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
59a6b068bd |
feat(goose-native): live tool-call cards in the web chat UI (#1992)
* feat(goose-native): live tool-call cards in the web chat UI (issue #1876) goose_native_forwarder mirrored only assistant prose; tool calls were invisible in the web chat and the live-card spinner never appeared. Changes: - _extract_tool_calls(): parse toolreq parts from assistant content_json into (tool_id, name, args_json) triples. - _extract_tool_result(): parse toolresp parts from tool-role rows into (tool_id, output_text); tolerates both "id" and "tool_use_id" fields. - _message_to_items() replaces _message_to_item(): returns a list so one assistant row can produce a prose message + N function_call items; tool rows produce function_call_output items. _read_new_items() preserved for backward compat with existing tests. - _read_new_rows(): new thin helper that returns raw DB rows so the poll loop can track per-turn state while iterating. - forward_goose_store_to_session(): per-turn live-card state (in-memory): * current_turn_response_id minted on the first assistant/tool row of each turn ("goose:turn:{msg_id}"), reset on the next user row. * posted_running_response_id dedupe guard fires "running" + response_id exactly once per turn so the web UI enters the streaming lifecycle. * "idle" + response_id posted when the next user row arrives (turn closed), or after _IDLE_AFTER_QUIET_S (8 s) of transcript quiet (heuristic for the last turn with no following user message). - Tests: 9 new unit tests covering _extract_tool_calls, _extract_tool_result, and _message_to_items; existing 5 tests updated for the refactored API. Signed-off-by: gocoolp <go4java@gmail.com> * fix(goose-native): precise live-card close + restart replay for the turn lifecycle Address AI-review findings on the quiescence heuristic: - The 8s quiet window did double duty as the normal turn close and the dead-turn backstop, so it could not be both short enough for a snappy close and long enough to survive a real tool call: any call quieter than 8s flickered (idle then running again on the result row), and every final prose reply lingered in running for 8s. - Goose's agent loop ends a turn on an assistant reply with no tool calls, so the final prose row now posts the closing idle immediately; the quiet window survives only as a minutes-scale backstop (_STALLED_TURN_IDLE_S) for turns that died without a close (TUI interrupt, Goose crash). - Turn state is replayed from the store on restart (_replay_open_turn): resumed rows keep the original turn id instead of splitting the streaming group, and a running edge left unclosed by a crash is closed instead of spinning forever. Loop-level tests drive forward_goose_store_to_session end to end against a recording poster to pin the lifecycle edges. Co-authored-by: Isaac --------- Signed-off-by: gocoolp <go4java@gmail.com> Co-authored-by: Dhruv Gupta <dhruv.gupta@databricks.com> |
||
|
|
da5b06349a |
feat(harness): add goose-native harness (Block's Goose CLI) (#823) (#955)
* feat(goose): register goose-native harness (#823) Additive registration mirroring cursor-native: aliases, wrapper label, NativeCodingAgent metadata, harness module map, spec validation, and terminal role. No behavior yet; the harness module lands in later units. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * feat(goose): native executor, harness, and tmux bridge (#823) GooseNativeExecutor injects each web-UI turn into the running `goose session` TUI's tmux pane (no output streaming; supports mid-turn steering); goose_native_harness exposes create_app(); goose_native_bridge owns the tmux target handshake + bracketed-paste injection (single Enter) + spawn env (GOOSE_CLI_THEME=ansi, GOOSE_PROVIDER/MODEL). Mirrors cursor-native; drops the .cursor/mcp.json machinery (Goose MCP lives in config.yaml). Readiness uses a stable-pane settle since Goose has no sentinel prompt. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * feat(goose): session-store forwarder (#823) Tail Goose's SQLite session store (~/.local/share/goose/sessions/ sessions.db): resolve the session by the --name we launched with, poll messages past a monotonic id cursor, decode content_json (tolerant of str/list/dict part shapes), and POST new user/assistant rows as external_conversation_item. Persists the high-water id for restart-safe resume; supervisor restarts with bounded backoff. Verified against the real schema + a fixture (Goose 1.38.0). Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * feat(goose): runner wiring + CLI launch orchestration (#823) Runner: _auto_create_goose_terminal launches `goose session --name <id>` in a tmux pane (GOOSE_CLI_THEME=ansi), advertises the tmux target for the harness executor, and starts the session-store forwarder; spawn-env branches, ensure-locks, interrupt/stop handlers, status suppression, and cleanup all mirror cursor-native. goose_native.py owns the `omni goose` CLI orchestration (resolve binary, create/resume session, daemon bind, terminal-ready poll, direct tmux attach). Mirrors cursor, minus MCP. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * feat(goose): omni goose CLI command, resume dispatch, onboarding readiness (#823) Add the `omnigent goose` command (mirrors `omnigent cursor`: --server/ --resume/--session + raw goose args, daemon-spawned runner, tmux attach), register it in _CLICK_SUBCOMMANDS, route `omnigent resume` to run_goose_native for goose-native sessions, and teach onboarding to gate goose-native readiness on the `goose` binary (install hint: brew install block-goose-cli). Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * feat(goose): onboarding readiness/config reporter (#823) goose_auth.py is a read-only reporter (Omnigent manages no Goose credentials — Goose owns its auth via `goose configure`): confirms the `goose` binary and surfaces the configured provider/model (env overrides config, matching Goose's precedence) for setup display. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * feat(goose): web UI Goose icon + native-agent wiring (#823) Add GooseIcon (lobehub Goose glyph), register goose-native in the native-coding-agent registry (icon kind, harness alias, sort rank), widen the icon-kind unions, and resolve the Goose glyph in AgentCard + SubagentsPanel. Extends AgentCard tests with goose cases. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * test(goose): unit + e2e coverage for goose-native harness (#823) Unit tests for the forwarder (fixture DB matching the verified Goose 1.38 schema: discovery-by-name, content_json decode, attachment strip, role mapping, idempotent cursor), spawn env, executor injection, CLI resolve, and onboarding reporter — 25 tests, all green. Plus an opt-in e2e (OMNIGENT_E2E_GOOSE_NATIVE=1) smoke + cwd test mirroring cursor-native, skip-gated when goose/tmux are absent. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * fix(goose): suppress first-run telemetry prompt in the terminal (#823) Live e2e surfaced that a fresh Goose install blocks the headless pane on its interactive "share usage data?" prompt. Set GOOSE_TELEMETRY_OFF=1 on the goose terminal env (alongside GOOSE_CLI_THEME=ansi) so the first-run prompt never gates message injection. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * style(goose): wrap _message_to_item signature to satisfy ruff E501 (#823) Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * fix(goose): harden forwarder binding + lifecycle from codex/adversarial review (#823) Cross-model review (codex + adversarial subagent) converged on the forwarder's session binding and lifecycle: - Per-launch-unique goose session name (`<conv_id>-<ms>`): `goose session --name X` without --resume creates a NEW row each launch (verified, Goose 1.38), so the forwarder now binds to exactly this launch's row and can never replay an older same-conversation transcript on cold-resume. - Cancel the TUI->web forwarder on session teardown (was leaked): a deleted session no longer leaves a supervisor polling a dead store + POSTing forever. Covers cursor-native too (shared cleanup path). - Anchor the paste-confirm needle to the message's last line, not first, so on-screen echo of a prior turn can't trigger a premature Enter. - Surface persistent sqlite read errors once (deduped warning) instead of swallowing them into a silently-empty chat view. Re-verified live: goose-native e2e smoke + cwd still pass via OpenRouter. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * test(goose): add native goose render-parity e2e_ui test (#823) Mirror test_native_cursor_render_parity for goose-native: a native_goose_session fixture (auto-launches goose session on bind) + a render-parity Playwright test asserting composer-IN parity, a TUI-originated turn surfacing OUT via the forwarder, and no duplicate rendering. Skip-gated when goose/tmux/provider-config are absent (CI-safe). Satisfies the E2E UI Required gate for the ap-web Goose icon change. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * fix(goose): use os.environ.copy() in tmux attach to clear exfil-scan (#823) The exfil security-scan blocks the `dict(os.environ)` shape in added lines. os.environ.copy() is the identical plain-dict copy (drops TMUX before the local tmux attach) without tripping the wholesale-environ-dump pattern. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * style(goose): prettier-format ConversationIconKind union (#823) CI 'Check formatting' flagged the hand-wrapped union; prettier keeps it on one line (fits print width). Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * style(goose): apply pre-commit ruff-format (#823) Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * fix(goose): include goose-native in configured_harness_map (#823) The harness-coverage meta-test caught a real gap: configured_harness_map() added _CURSOR_NATIVE_HARNESSES but not _GOOSE_NATIVE_HARNESSES, so the canonical 'goose-native' spelling was absent from the hello-frame readiness map (the web UI 'needs setup' warning would have missed it). Add it, and cover goose in the readiness test's spelling lists. Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> * feat(goose): surface Goose in `omnigent setup` (configure harnesses) Wire onboarding/goose_auth.py (previously dead code) into the configure- harnesses menu: a "Goose" row that reports readiness (binary installed + provider configured via goose_config_summary) and a drill-in (_manage_goose_harness) that installs the CLI (brew/curl hint, non-npm) and launches `goose configure`. Goose owns its own auth (keyring / config.yaml), so Omnigent stores no key — mirrors the Qwen drill-in. Serves both the goose-native (TUI) and upcoming headless goose (ACP) harnesses. Adds 3 drill-in tests (missing-CLI hint, Back no-op, configure launch). Co-authored-by: Isaac * feat(goose): headless Goose ACP harness (GooseExecutor + wrap) Adds the chat-first `harness: goose` — the ACP counterpart to the terminal-first `goose-native` TUI. GooseExecutor drives `goose acp` over newline-delimited JSON-RPC 2.0 (initialize / session/new / session/prompt), streaming agent_message_chunk -> TextChunk and folding the system prompt into the first turn. Goose's mid-turn `session/request_permission` routes through Omnigent's generic TOOL_CALL policy + human-consent elicitation (ctx.elicit -> web ApprovalCard), so tool approvals surface as web elicitation cards rather than in-terminal prompts. Closes two qwen-harness gaps for Goose: token usage (TurnComplete.usage from the final result) and context window (max_context_tokens from usage_update). Modeled on QwenExecutor; verified end-to-end against a live goose 1.38 acp session (streaming + policy(ASK)->elicit->allow->tool-run + usage). goose_harness.create_app() wraps it via ExecutorAdapter (lazy build; provider/ model/cwd/builtins from HARNESS_GOOSE_* env). 19 unit tests. Co-authored-by: Isaac * feat(goose): register the headless `goose` harness across touchpoints Wires `harness: goose` into every registration site so it is runnable, selectable, and readiness-gated: - runtime/harnesses/__init__: goose -> omnigent.inner.goose_harness - workflow.AgentHarnessType += goose; new _build_goose_spawn_env (model + os_env only — Goose owns its auth via `goose configure`, so no gateway wiring; databricks-* models dropped) - runner/app: HARNESS_GOOSE_MODEL env key + spawn-env dispatch - onboarding/harness_install: goose -> GOOSE_KEY (gate on the goose binary) - onboarding/harness_readiness: headless goose gated on the binary + in the map - spec/_omnigent_compat: OMNIGENT_HARNESSES += goose (so --harness goose validates) - model_override: goose honors --model; cli: _OS_ENV_HARNESSES + help + prompt Tests: 3 _build_goose_spawn_env cases; configured_harness_map covers the new `goose` spelling. Co-authored-by: Isaac * feat(goose): web picker glyph for the headless goose harness The AgentCard harness fallback already maps any `harness` containing "goose" to GooseIcon, so a headless `harness: goose` agent renders with the Goose glyph in the new-session / add-agent pickers (better than qwen, which falls back to the bot icon). Adds a test case for the headless `goose` harness and refreshes the iconForAgent doc comment. Onboarding is served by the shared `omnigent setup` Goose row. Per-session brain-harness override (BRAIN_HARNESS_LABELS) is left for when Omnigent tools are exposed to Goose over ACP MCP, matching qwen. Co-authored-by: Isaac * test(goose): opt-in live e2e for the headless goose ACP harness tests/e2e/test_goose_acp_e2e.py drives GooseExecutor against a real `goose acp` process (isolated temp HOME, CI-safe skip behind OMNIGENT_E2E_GOOSE=1 + a configured provider): (1) a prose turn streams agent text and completes with token usage + a learned context window; (2) a shell tool call routes through policy(ASK) -> elicitation -> approve, then the tool runs and its marker reaches the transcript — the web ApprovalCard path. Both verified passing against goose 1.38 / claude-haiku-4-5. Co-authored-by: Isaac * fix(goose): web-UI duplicate, terminal switcher, and robust config detection Three fixes from live testing of the Goose harnesses: 1. Duplicate "Goose" in the new-chat picker: add "goose-native-ui" to NewChatDialog's BUILTIN_AGENTS so the server-persisted goose agent (created by `omnigent goose`) is deduped against the static NATIVE_CODING_AGENTS entry — matching claude/codex/cursor/pi. 2. Terminal view opened a plain shell and the Chat/Terminal pill vanished for native Goose: terminal_goose_main was missing from AGENT_TERMINAL_IDS, so goose's TUI pane wasn't recognized as the agent terminal (leaked into Shells, tripped isShellView). Add it — same omission/fix as the earlier pi/cursor regressions. Now goose-native switches chat<->terminal like the other natives. 3. `omnigent setup` showed Goose unconfigured even after `goose configure`: the old detector hand-parsed config.yaml for a top-level GOOSE_PROVIDER, which misses the keyring/format `goose configure` actually writes. Now detect via `goose info -v` (Goose's own resolved config — authoritative across platforms), with the file scan kept as a fallback when the binary can't be run. Tests: goose_info_config parse/precedence/fallback; useTerminals goose regression case; existing suites green (226 frontend, goose python). Co-authored-by: Isaac * chore(goose): snappier forwarder poll + lint/format + executor coverage - goose-native forwarder poll 0.7s → 0.4s: goose flushes a SQLite messages row per agentic step (verified), so a tighter cadence makes the mirrored chat track the terminal step-by-step on coding turns rather than lagging each one. - Apply ruff format/check across the goose modules (fixes Pre-commit CI). - Expand GooseExecutor unit tests (transport: _rpc/_read_stdout/_read_stderr, handshake/session lifecycle, _start_process reset, sandbox launch-path, run_turn boot-failure / ACP-error-reset / usage-update paths). Coverage 53% → 80%. Co-authored-by: Isaac * test(goose): cover goose_harness wrap + executor image/permission branches Lifts goose_executor + goose_harness coverage 80% → 89%: goose_harness was entirely uncovered (now ~95% — _resolve_os_env JSON/default/malformed, _build_goose_executor env reading + defaults, create_app), plus GooseExecutor branches for attachment/image handling (_inline_text_file_data variants, _image_blocks_from_content parse/SSRF-skip, image-marker toggle, run_turn image forwarding) and the _decide_permission edges (no-gates allow, ASK-without-handler deny, policy-exception fall-through, request-handler exception → JSON-RPC error). Co-authored-by: Isaac * test(e2e): exclude goose + goose-native from the live run-harness matrix test_run_harness_live_matrix_covers_registered_coding_harnesses asserts every registered coding harness has a live gateway round-trip row. Headless `goose` authenticates from its own `goose configure` config (no shared HARNESS_*_GATEWAY/DATABRICKS_PROFILE wiring — like qwen), and `goose-native` is a terminal-first TUI launched via `omni goose` (like claude-/cursor-native), so both are excluded from this gateway-driven matrix. Their live coverage lives in the dedicated test_goose_acp_e2e.py / test_goose_native_cli_e2e.py suites. Co-authored-by: Isaac * fix(ci): de-pollute ap-web/package-lock.json — drop databricks npm-proxy URL A merge carried a `resolved` URL pinned to the internal `npm-proxy.cloud.databricks.com` (the `yaml` dep) into the lockfile. `npm ci` fetches each package from its locked `resolved` URL regardless of NPM_CONFIG_REGISTRY, so every frontend CI job (pre-commit, npm test, UI Snapshot, E2E UI shards) failed at install with `ETIMEDOUT` against that internal proxy — which the public OSS CI can't reach. package.json is unchanged vs main, so the lock is restored to origin/main's clean state (all deps resolve from registry.npmjs.org). The npm analog of the uv.lock proxy-leak. Co-authored-by: Isaac --------- Signed-off-by: praneeth_paikray-data <praneeth.paikray@databricks.com> Co-authored-by: praneeth_paikray-data <praneeth.paikray@databricks.com> Co-authored-by: Dhruv Gupta <dhruv.gupta@databricks.com> |