Feature/home assistant integration#17
Merged
Merged
Conversation
…sible to LLM When a tool call requires user approval in the non-blocking gateway path, the LLM previously received a result that was indistinguishable from a failed tool call (exit_code=-1, error=message). The LLM could not tell whether the tool was pending approval, had returned empty results, or had failed silently — causing it to burn context on wrong hypotheses. Fix changes the result format to include: - status: pending_approval (clear state name) - approval_pending: True (explicit boolean for LLMs to detect) - error: cleared to empty string (removes misleading error signal) This lets the LLM reason about approval latency vs actual errors, short-circuiting the previous silent failure mode. Fixes NousResearch#14806
GLM models via Ollama report finish_reason='stop' even when the response was truncated by max_tokens. The continuation mechanism uses _has_natural_response_ending() as one of the heuristics to detect whether the response was genuinely finished. Currently only ASCII punctuation and CJK punctuation are recognized. This means any response ending with an emoji (e.g. ⚡, 👍) or the caret character ^ (common in French ^^ smiley) is not recognized as naturally ended, triggering a false-positive continuation where the model receives 'Continue where you left off' and produces garbled output. Add: - ^ (caret) to the punctuation set - Unicode emoji range (codepoint >= 0x1F300) as natural ending This only affects GLM/Ollama users but the fix is safe for all backends since _has_natural_response_ending() is only consulted inside the continuation flow.
…ousResearch#28328) Pre-stages AUTHOR_MAP entries for 10 new contributors whose PRs are being salvaged in the May 2026 low-hanging-fruit batch (group 8). Lands ahead of the per-PR salvage PRs so they don't get blocked by AUTHOR_MAP CI. Contributors: - AceWattGit (NousResearch#28159 — _pool_may_recover_from_rate_limit NameError) - YuanHanzhong (NousResearch#28032 — x.com/status fallbacks link-like) - colin-chang (NousResearch#28245, NousResearch#28249, NousResearch#28251 — gateway + mattermost fixes) - felix-windsor (NousResearch#28019 — preserve cron asterisks in strip mode) - houenyang-momo (NousResearch#28205 — charizard completion menu contrast) - iqdoctor (NousResearch#28095 — windows installer docs) - joe102084 (NousResearch#28151 — whitespace-only cron responses) - jvinals (NousResearch#27936 — Slack U-IDs → DM channel) - maxmilian (NousResearch#28267 — ModelPickerDialog portal) - samggggflynn (NousResearch#27952 — dingtalk pre_start) Per references/batch-pr-salvage-may14-additions.md.
The dingtalk-stream SDK calls pre_start() on every registered handler before opening the WebSocket connection. Without this method, the SDK raises AttributeError and kills the stream connection, causing DingTalk to be unable to connect via Stream Mode.
Wraps _pt_print in try/except with a print() fallback. When a kanban worker's stdout is piped to a log file, prompt_toolkit raises NoConsoleScreenBufferError (Windows) or OSError (other) because there is no real console buffer. The fallback keeps worker output flowing instead of crashing.
…rch#28334) PR NousResearch#28330 was salvaged with a wrong noreply numeric ID (18091625 vs the correct 7065068). The commit on main is correctly authored to Grogger by username, but neither noreply form was in AUTHOR_MAP. Adds both so release-notes generation maps them to @Grogger.
resolve_xai_oauth_runtime_credentials() called _refresh_xai_oauth_tokens() with no try/except. A terminal refresh failure (HTTP 400/401/403 — invalid_grant, token revoked) propagated without clearing the dead access_token / refresh_token from auth.json, causing every subsequent session to retry the same doomed network request. Add a try/except around the refresh call that mirrors the existing credential_pool.py quarantine: when _is_terminal_xai_oauth_refresh_error identifies a non-retryable failure, clear the dead token fields from auth.json and write a last_auth_error diagnostic marker so future calls fail fast with a clear relogin_required error instead of hitting the network. active_provider is preserved (set_active=False) so multi-provider users whose chosen provider is not xai-oauth are unaffected. Tests: two new cases in test_auth_xai_oauth_provider.py cover terminal quarantine and transient pass-through.
…prompts (NousResearch#27644) The background review prompts (_SKILL_REVIEW_PROMPT and _COMBINED_REVIEW_PROMPT) now include explicit protection rules for bundled, hub-installed, and pinned skills — aligning with the curator's existing policy at curator.py L345/350. Before this change, bg-review could freely rewrite bundled skills like 'hermes-agent' or pinned skills, while the 7-day curator explicitly skips them. The review agent now sees: • Bundled skills (shipped with Hermes) • Hub-installed skills (installed via hermes skills install) • Pinned skills (marked via hermes curator pin) If only protected skills need updating, the review says 'Nothing to save.' and stops. Fixes NousResearch#27644
The dashboard's main column is `relative z-2` (App.tsx), which creates a stacking context that traps fixed descendants below the app sidebar (`z-50`). `ModelPickerDialog` renders `fixed inset-0 z-[100]` inline, so its z-100 is scoped to z-2 and the sidebar covers its left edge. The bug is visible across all themes but only obvious in the Large theme variants (Hermes Teal (Large), etc.) where the larger root font widens the dialog into the sidebar's column. Toast.tsx already documents the same trap and uses the same `createPortal(..., document.body)` escape. This commit ports the picker; the same pattern affects other inline z-[100] modals in the dashboard (OAuthLoginModal, Cron / Models / Profiles page modals) and is left for a follow-up — keeping this PR scoped to the reporter's specific case. Fixes NousResearch#28103 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the gateway receives SIGUSR1 (graceful restart via launchd_restart), the SIGUSR1 handler calls request_restart(via_service=True) and the gateway shuts down cleanly with exit code 0. However, the generated launchd plist uses KeepAlive → SuccessfulExit → false, meaning launchd only relaunches on *non-zero* exit codes. A clean exit(0) is treated as "successful, don't restart", so the gateway stays down after /restart, /update, or SIGUSR1. The systemd unit template already uses RestartForceExitStatus=75 for the same scenario. Mirror that convention: when _restart_via_service is True, raise SystemExit(75) so launchd's SuccessfulExit=false policy triggers a relaunch. Closes NousResearch#28135
Two code paths call json.loads() on output from external tools without catching JSONDecodeError. If the tool returns a non-JSON string (error message, empty string, or None), the entire call path crashes. 1. gateway/run.py — text_to_speech_tool() result in voice reply path. A TTS failure that returns an error string instead of JSON crashes the voice reply handler, killing the message response entirely. 2. cron/scheduler.py — skill_view() result when loading skills for cron jobs. A corrupted or missing skill file that returns an error string instead of JSON crashes the cron tick, preventing all jobs from executing that cycle. Both fixes catch (json.JSONDecodeError, TypeError), log a warning, and gracefully skip the failed operation instead of crashing.
…sections Two related bugs in gateway/config.py prevented per-platform gateway_restart_notification from working through config.yaml: 1. The shared-key bridging loop (load_gateway_config) omitted 'gateway_restart_notification', so the key never landed in platform_data['extra'] even when set under e.g. 'discord:' or 'mattermost:' sections. 2. PlatformConfig.from_dict() only read gateway_restart_notification from the top-level data dict, ignoring the 'extra' sub-dict where bridged keys are stored. Fix: add the key to the bridging loop, and add an 'extra' fallback in from_dict() so that round-tripped values (YAML → bridged → extra → from_dict) resolve correctly. Impact: users can now set gateway_restart_notification: false per platform in config.yaml instead of relying on env vars or the global platforms: block.
When the kanban auto-decomposer fans a triage task into child tasks, recompute_ready() immediately promotes parent-free children to 'ready' so the dispatcher picks them up. Some users want a manual workflow where children stay in 'todo' for review before dispatch. Add 'kanban.auto_promote_children' config key (default: true): - false: children stay in 'todo' after decomposition - true: existing behavior (auto-promote to 'ready') Changes: - kanban_db.py: decompose_triage_task() gains auto_promote param - kanban_decompose.py: reads auto_promote_children from config - kanban dashboard API: exposes the new setting in GET/PUT /orchestration Closes NousResearch#28016
…mespace The conversation_loop.py references _pool_may_recover_from_rate_limit which was defined in run_agent.py. After the conversation-loop extraction refactor, the helper was no longer in the same module scope. Wrap the call as _ra()._pool_may_recover_from_rate_limit() to route through the run_agent monkeypatch namespace where the helper is available. Adds regression test in test_gemini_fast_fallback.py. Fixes: MAILROOM Email Triage NameError, OPS Execution Monitor NameError.
Qwen3.x and DeepSeek-V3.x default to chatty/hallucinatory tool use without enforcement steering — agents narrate "calling tool X" without actually emitting a tool call, or run partial loops. Both model families fit the same failure pattern TOOL_USE_ENFORCEMENT_GUIDANCE was already injected for (gpt, codex, gemini, gemma, grok, glm). Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com> Squashed salvage of: - 403e567 fix(agent): add qwen and deepseek to TOOL_USE_ENFORCEMENT_MODELS - 9433eab test(agent): use realistic qwen-plus identifier in enforcement test Fixes NousResearch#28079.
The _SLACK_TARGET_RE regex only matched IDs starting with C (channel), G (group), or D (direct message). Slack user IDs start with U, causing 'Could not resolve' errors when trying to send DMs to specific users. Changes: - Expand _SLACK_TARGET_RE to accept U-prefixed IDs (user IDs) - Add conversations.open fallback to resolve user IDs to DM channel IDs before sending, since chat.postMessage requires a conversation ID Fixes #ISSUE_NUMBER
…t-found Three related fixes for the MEDIA:<path> extraction pipeline that caused 'file not found' noise in platform channels: 1. run.py — tighten tool-result MEDIA regex from \S+ (any non- whitespace) to require a path pattern with known extensions. Prevents LLM-generated placeholder paths like 'MEDIA:/path/to/example.mp4' from being captured as real media. 2. base.py — remove the |\S+ fallback in extract_media() that catches anything non-whitespace as a potential MEDIA path. This was the primary cause of false positives — strings like '' in tool output were captured as MEDIA: paths. 3. mattermost.py — replace the file-not-found error message sent to the channel with a silent logger.warning() skip. When a path extracted by MEDIA doesn't exist on disk, the channel no longer gets a noisy '(file not found: ...)' message. Impact: eliminates the persistent 'file not found' spam in Mattermost channels caused by over-broad MEDIA regex patterns matching non-path text in tool output.
…dpoint xAI's token endpoint returns HTTP 403 to the OAuth grant when the account isn't on the allowlist for API access (e.g. standard SuperGrok subscribers — see NousResearch#26847). Treating it like a stale-token 400/401 made ``format_auth_error`` append "Run ``hermes model`` to re-authenticate", which is misleading because re-login can't change xAI's tier decision. Split 403 off in both ``refresh_xai_oauth_pure`` and the loopback login token exchange: * New error code ``xai_oauth_tier_denied`` with ``relogin_required=False`` * Message explains the entitlement gate and points at the ``XAI_API_KEY`` + ``provider: xai`` fallback * 400/401 still set ``relogin_required=True`` as before * 5xx still set ``relogin_required=False`` as before
…resh-loop
The existing ``_is_entitlement_failure`` heuristic only fires when
the response body contains specific substrings ("do not have an
active Grok subscription", etc.). xAI has been seen to 403 standard
SuperGrok subscribers with a terser body that doesn't match those
keywords (NousResearch#26847), and the recovery path would then mint a fresh
token, get a fresh 403, and loop until Ctrl+C.
Add a defense-in-depth check at the recovery call site: any 403 on
``provider == "xai-oauth"`` short-circuits ``try_refresh_current``
so the error surfaces immediately with the friendly hint from
``_summarize_api_error``. Keeps the existing keyword path for all
other providers untouched.
…Research#26847 Tests: * ``test_refresh_xai_oauth_pure_403_marked_tier_denied_not_relogin`` — refresh-403 raises ``xai_oauth_tier_denied`` with ``relogin_required=False`` and the API-key fallback hint in body. * ``test_format_auth_error_tier_denied_does_not_suggest_relogin`` — the renderer does not append "Run ``hermes model``" for the new code. * ``test_recover_with_credential_pool_skips_refresh_on_bare_403_for_xai_oauth`` — bare ``{"reason":"forbidden","message":"Forbidden"}`` body (which does not match the existing keyword heuristic) still short-circuits ``try_refresh_current`` on xai-oauth. Docs: * Drop the "(any active tier)" claim from the xai-grok-oauth guide, add a top-of-page warning callout, and a Troubleshooting section for the 403-after-login case pointing at ``XAI_API_KEY`` + ``provider: xai`` as the documented fallback.
Two Mattermost thread-related bugs: 1. _resolve_root_id() — Mattermost CRT requires root_id to be the thread root post. Using any reply's own ID as root_id causes '400 Invalid RootId'. Add _resolve_root_id() that walks up the post chain via API to find the actual root, and apply it in send(), _send_url_as_file(), and _send_local_file(). 2. _progress_reply_to — The condition in run.py only checked Platform.FEISHU, missing Mattermost entirely. This caused tool progress messages to always land in the main channel instead of the thread. Add Platform.MATTERMOST to the condition so progress messages are routed to threads when reply_mode=thread. Impact: Tool progress messages now appear in Mattermost threads instead of flooding the main channel; thread replies no longer fail with Invalid RootId when the reply target is itself a reply.
Salvages NousResearch#19964 by @Beandon13. Adds `hermes kanban archive --rm` to permanently remove already-archived tasks with cascading cleanup of links, comments, events, runs, and notify-subs. Safety guard: only archived tasks can be deleted; active/blocked/done must be archived first. Cherry-picked from NousResearch#19964 onto current main (severe stale base, applied manually to preserve substance only).
…pter salvage
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.19.2 to 7.1.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](docker/build-push-action@10e90e3...bcafcac) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.3.0 to 6.2.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](actions/setup-python@v5.3.0...a309ff8) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
…ng (NousResearch#28683) Follow-up to NousResearch#28455. The respawn guard's blocker_auth rule (last error matched a quota/auth/429 pattern) was auto-blocking the task on first occurrence. That's too aggressive: transient rate limits typically clear in seconds to minutes, but the auto-block puts the task in 'blocked' status which requires manual unblock. Now treats blocker_auth the same as recent_success and active_pr: defer the spawn this tick, leave the task in 'ready', let the next tick try again. If the auth error genuinely persists, the existing consecutive_failures counter trips the auto-block circuit breaker after failure_limit failures via the normal path — so a persistent 401/403/quota-exhausted still ends up blocked, just not on first hit. Also documents the respawn_guarded event in kanban.md's events table with the three guard reasons. Updated test_dispatch_respawn_guard_auto_blocks_auth_error → renamed to test_dispatch_respawn_guard_defers_auth_error_without_auto_block; asserts task stays in 'ready' and the guard reason is recorded.
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.3.1 to 6.0.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@34e1148...de0fac2) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Austin Pickett <pickett.austin@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…ising KeyboardInterrupt (NousResearch#28688) The SIGTERM/SIGHUP handler raised KeyboardInterrupt() at the end of its agent-interrupt + grace-window sequence. Python delivers signals between bytecodes on the main thread, so when the signal hit mid-event-loop (typically inside prompt_toolkit's '_poll_output_size' coroutine's 'await asyncio.sleep()'), the KeyboardInterrupt unwound INTO that coroutine. prompt_toolkit's Task captured it as a BaseException; prompt_toolkit's '_handle_exception' then printed 'Unhandled exception in event loop' + the full asyncio traceback and parked the terminal on 'Press ENTER to continue...' before exiting. Same root cause as NousResearch#13710, different surface: there the failure was an EIO cascade after a logging-cache KeyError escaped the handler; here it's the KBI raise itself landing inside an asyncio Task. The fix is the same shape — let the event loop unwind on its own terms. Now: schedule 'app.exit()' via 'loop.call_soon_threadsafe()'. The prompt_toolkit Application returns normally from 'app.run()' and the existing '(EOFError, KeyboardInterrupt, BrokenPipeError)' handler in the input loop catches everything else. Fallback to 'raise KeyboardInterrupt()' preserved for contexts where prompt_toolkit isn't the active app (e.g. -q one-shot mode). The agent interrupt + 1.5 s grace window run unchanged before the new exit path, so subprocess-group cleanup ('os.killpg' on Linux) still gets its window. Tested live: external SIGTERM to the CLI (with 'kill <pid>') now exits cleanly with no traceback dump and no ENTER pause.
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.3.3 to 3.4.2. - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](cure53/DOMPurify@3.3.3...3.4.2) --- updated-dependencies: - dependency-name: dompurify dependency-version: 3.4.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.11 to 1.16.0. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](follow-redirects/follow-redirects@v1.15.11...v1.16.0) --- updated-dependencies: - dependency-name: follow-redirects dependency-version: 1.16.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](lodash/lodash@4.17.23...4.18.1) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.18.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [lodash-es](https://github.com/lodash/lodash) and [langium](https://github.com/eclipse-langium/langium/tree/HEAD/packages/langium). These dependencies needed to be updated together. Updates `lodash-es` from 4.17.23 to 4.18.1 - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](lodash/lodash@4.17.23...4.18.1) Updates `langium` from 4.2.1 to 4.2.3 - [Release notes](https://github.com/eclipse-langium/langium/releases) - [Changelog](https://github.com/eclipse-langium/langium/blob/main/packages/langium/CHANGELOG.md) - [Commits](https://github.com/eclipse-langium/langium/commits/HEAD/packages/langium) --- updated-dependencies: - dependency-name: lodash-es dependency-version: 4.18.1 dependency-type: indirect - dependency-name: langium dependency-version: 4.2.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [python-multipart](https://github.com/Kludex/python-multipart) from 0.0.22 to 0.0.27. - [Release notes](https://github.com/Kludex/python-multipart/releases) - [Changelog](https://github.com/Kludex/python-multipart/blob/main/CHANGELOG.md) - [Commits](Kludex/python-multipart@0.0.22...0.0.27) --- updated-dependencies: - dependency-name: python-multipart dependency-version: 0.0.27 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [python-dotenv](https://github.com/theskumar/python-dotenv) from 1.2.1 to 1.2.2. - [Release notes](https://github.com/theskumar/python-dotenv/releases) - [Changelog](https://github.com/theskumar/python-dotenv/blob/main/CHANGELOG.md) - [Commits](theskumar/python-dotenv@v1.2.1...v1.2.2) --- updated-dependencies: - dependency-name: python-dotenv dependency-version: 1.2.2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](fastify/fast-uri@v3.1.0...v3.1.2) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@babel/plugin-transform-modules-systemjs](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs) from 7.29.0 to 7.29.4. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.29.4/packages/babel-plugin-transform-modules-systemjs) --- updated-dependencies: - dependency-name: "@babel/plugin-transform-modules-systemjs" dependency-version: 7.29.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
* fix: update design system package, replace bg image, remove sync assets * fix(web): update bundled asset metadata * fix(web): normalize npm lockfile metadata * fix(nix): refresh npm lockfile hashes * chore(ci): trigger PR checks * fix(web): declare motion peer dependency * fix(nix): refresh npm lockfile hashes * chore(ci): trigger PR checks after dependency update * fix(web): restore cross-platform lockfile entries * fix(nix): refresh npm lockfile hashes * chore(ci): trigger PR checks after lockfile restore --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…ousResearch#28104) Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 5.2.3 to 5.2.4. - [Release notes](https://github.com/webpack/webpack-dev-server/releases) - [Changelog](https://github.com/webpack/webpack-dev-server/blob/main/CHANGELOG.md) - [Commits](webpack/webpack-dev-server@v5.2.3...v5.2.4) --- updated-dependencies: - dependency-name: webpack-dev-server dependency-version: 5.2.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [ws](https://github.com/websockets/ws) from 8.20.0 to 8.20.1. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](websockets/ws@8.20.0...8.20.1) --- updated-dependencies: - dependency-name: ws dependency-version: 8.20.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…search#24011) Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.13.0 to 11.15.0. - [Release notes](https://github.com/mermaid-js/mermaid/releases) - [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.13.0...mermaid@11.15.0) --- updated-dependencies: - dependency-name: mermaid dependency-version: 11.15.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Extends the previous commit to cover the remaining additive-column index that sits on the same migration trap: - ``task_events.run_id`` -> ``idx_events_run`` was still in SCHEMA_SQL. A legacy ``task_events`` table predating NousResearch#17805 (no ``run_id``) would still abort ``executescript`` before ``_migrate_add_optional_columns`` could add the column. Hoisted out of SCHEMA_SQL and made unconditional in the migration alongside the other three indexes. - Removed the now-redundant ``CREATE INDEX idx_tasks_idempotency`` that was nested inside the ``if "idempotency_key" not in cols`` branch. The unconditional create lower in the function makes it idempotent on both fresh and legacy DBs. - Strengthened the regression test to cover all four indexes (``idx_tasks_session_id``, ``idx_tasks_tenant``, ``idx_tasks_idempotency``, ``idx_events_run``) and to seed a pre-NousResearch#17805 ``task_events`` shape that exercises the ``run_id`` migration path. The result: every ``CREATE INDEX`` that depends on an additive column now runs after the migration ensures the column exists. Verified against a realistic pre-NousResearch#16081 board fixture (tasks + task_events both legacy shape) — origin/main reproduces ``no such column: session_id``; this branch migrates cleanly and creates all four indexes.
This was referenced May 30, 2026
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.
What does this PR do?
Related Issue
Fixes #
Type of Change
Changes Made
How to Test
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AFor New Skills
hermes --toolsets skills -q "Use the X skill to do Y"Screenshots / Logs