heyspex 0.1.0 → 0.2.1
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.
- package/.claude-plugin/marketplace.json +14 -0
- package/README.md +21 -0
- package/bin/hs.mjs +1089 -104
- package/bin/spexcode.mjs +262 -0
- package/dev-factory/engine/worktree.mjs +740 -0
- package/dev-factory/lib/code-repo-root.mjs +67 -0
- package/dev-factory/lib/context-budget.mjs +84 -0
- package/dev-factory/lib/epic-detect.mjs +190 -0
- package/dev-factory/lib/fleet-queue-file.mjs +846 -0
- package/dev-factory/lib/fleet-queue.mjs +426 -0
- package/dev-factory/lib/fleet-state.mjs +14 -18
- package/dev-factory/lib/model-catalog.mjs +338 -0
- package/dev-factory/lib/seat-context.mjs +199 -0
- package/dev-factory/lib/spec-io.mjs +93 -0
- package/dev-factory/lib/sufficiency-score.mjs +8 -2
- package/dev-factory/lib/worktree-allocator.mjs +744 -0
- package/dev-factory/model-catalog.json +222 -0
- package/dist/cli/hs-check.mjs +1127 -103
- package/dist/cli/hs-claim.mjs +981 -63
- package/dist/cli/hs-connect.mjs +10059 -1729
- package/dist/cli/hs-defer.mjs +5507 -3072
- package/dist/cli/hs-dev.mjs +576 -0
- package/dist/cli/hs-env.mjs +416 -302
- package/dist/cli/hs-index-rebuild.mjs +7334 -0
- package/dist/cli/hs-index-reconcile.mjs +8138 -0
- package/dist/cli/hs-init.mjs +2840 -149
- package/dist/cli/hs-install.mjs +7214 -594
- package/dist/cli/hs-join.mjs +10640 -2421
- package/dist/cli/hs-vault-pull.mjs +8157 -6370
- package/dist/cli/hs-vault-push.mjs +6433 -0
- package/dist/cli/hs-vault.mjs +4029 -1555
- package/dist/cli/hs-workspace.mjs +2702 -538
- package/dist/cli-templates/customer-vault/specs/entity-model.md +594 -601
- package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +110 -177
- package/dist/cli-templates/customer-vault/specs/protocols/destructive-action-checklist.md +122 -0
- package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +13 -15
- package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +91 -0
- package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +150 -168
- package/dist/cli-templates/customer-vault/specs/protocols/spec-summary-authoring.md +83 -0
- package/dist/cli-templates/harness-commands/claude/hs-auto.md +36 -0
- package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -0
- package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -0
- package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -0
- package/dist/cli-templates/harness-commands/claude/hs-dev.md +20 -0
- package/dist/cli-templates/harness-commands/claude/hs-doctor.md +32 -0
- package/dist/cli-templates/harness-commands/claude/hs-ground.md +30 -0
- package/dist/cli-templates/harness-commands/claude/hs-handoff.md +33 -0
- package/dist/cli-templates/harness-commands/claude/hs-log.md +27 -0
- package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -0
- package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -0
- package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -0
- package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -0
- package/dist/cli-templates/harness-commands/claude/hs-res.md +63 -0
- package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -0
- package/dist/cli-templates/harness-commands/claude/hs-review.md +23 -0
- package/dist/cli-templates/harness-commands/claude/hs-view.md +28 -0
- package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -0
- package/dist/cli-templates/harness-commands/claude/prototype.md +91 -0
- package/dist/cli-templates/harness-commands/cursor/hs-auto.md +40 -0
- package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +31 -0
- package/dist/cli-templates/harness-commands/cursor/hs-capture.md +65 -0
- package/dist/cli-templates/harness-commands/cursor/hs-defer.md +32 -0
- package/dist/cli-templates/harness-commands/cursor/hs-dev.md +24 -0
- package/dist/cli-templates/harness-commands/cursor/hs-doctor.md +36 -0
- package/dist/cli-templates/harness-commands/cursor/hs-ground.md +33 -0
- package/dist/cli-templates/harness-commands/cursor/hs-handoff.md +37 -0
- package/dist/cli-templates/harness-commands/cursor/hs-log.md +31 -0
- package/dist/cli-templates/harness-commands/cursor/hs-next.md +32 -0
- package/dist/cli-templates/harness-commands/cursor/hs-promote.md +73 -0
- package/dist/cli-templates/harness-commands/cursor/hs-push.md +38 -0
- package/dist/cli-templates/harness-commands/cursor/hs-qc.md +30 -0
- package/dist/cli-templates/harness-commands/cursor/hs-res.md +67 -0
- package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +26 -0
- package/dist/cli-templates/harness-commands/cursor/hs-review.md +26 -0
- package/dist/cli-templates/harness-commands/cursor/hs-view.md +32 -0
- package/dist/cli-templates/harness-commands/cursor/kill-ports.md +35 -0
- package/dist/cli-templates/harness-commands/cursor/prototype.md +95 -0
- package/docs/copilot-plugin-install.md +162 -0
- package/docs/driving-heyspex-from-copilot.md +165 -0
- package/docs/mcp-client-config.md +259 -6
- package/package.json +249 -124
- package/plugins/heyspex/.claude-plugin/plugin.json +12 -0
- package/plugins/heyspex/.cursor-plugin/plugin.json +15 -0
- package/plugins/heyspex/.mcp.json +12 -0
- package/plugins/heyspex/README.md +309 -0
- package/plugins/heyspex/bin/launch-mcp.mjs +95 -0
- package/plugins/heyspex/bin/run-hook.mjs +80 -0
- package/plugins/heyspex/hooks/cursor-hooks.json +308 -0
- package/plugins/heyspex/hooks/hooks.json +418 -0
- package/plugins/heyspex/mcp.json +13 -0
- package/scripts/check-mcp-freshness.mjs +17 -9
- package/scripts/claude-hooks/_env-vault-events.mjs +68 -0
- package/scripts/claude-hooks/_guardrail-events.mjs +96 -0
- package/scripts/claude-hooks/_guardrail-loader.mjs +494 -0
- package/scripts/claude-hooks/_intent-classifier-router.mjs +456 -0
- package/scripts/claude-hooks/_intent-classifier-semantic.mjs +356 -0
- package/scripts/claude-hooks/_run.mjs +413 -0
- package/scripts/claude-hooks/_transcript-text.mjs +60 -0
- package/scripts/claude-hooks/_vault-retrieval.mjs +322 -0
- package/scripts/claude-hooks/agent-spawn-delegation-gate.mjs +133 -0
- package/scripts/claude-hooks/capture-execution-gate.mjs +51 -0
- package/scripts/claude-hooks/capture-workflow-gate.mjs +11 -0
- package/scripts/claude-hooks/check-auto-memory-budget.mjs +396 -0
- package/scripts/claude-hooks/check-claude-md-budget.mjs +327 -0
- package/scripts/claude-hooks/check-mcp-freshness-hook.mjs +14 -0
- package/scripts/claude-hooks/claim-before-code-gate.mjs +11 -0
- package/scripts/claude-hooks/claim-liveness-shared.mjs +577 -0
- package/scripts/claude-hooks/close-terminal-on-ship.mjs +64 -0
- package/scripts/claude-hooks/coo-denylist-gate.mjs +11 -0
- package/scripts/claude-hooks/coo-guard-check.mjs +11 -0
- package/scripts/claude-hooks/deep-research-cost-gate.mjs +11 -0
- package/scripts/claude-hooks/dev-server-task-reminder.mjs +15 -0
- package/scripts/claude-hooks/diagnose-capture-reminder.mjs +11 -0
- package/scripts/claude-hooks/directive-obligation-gate.mjs +11 -0
- package/scripts/claude-hooks/emit-claim-status.mjs +41 -0
- package/scripts/claude-hooks/enforce-session-sequence.mjs +11 -0
- package/scripts/claude-hooks/enforce-vault-naming.mjs +11 -0
- package/scripts/claude-hooks/env-vault-guard.mjs +11 -0
- package/scripts/claude-hooks/epic-milestone-phrasing-gate.mjs +11 -0
- package/scripts/claude-hooks/git-identity-guard.mjs +11 -0
- package/scripts/claude-hooks/git-reflog-audit.mjs +15 -0
- package/scripts/claude-hooks/git-stash-gate.mjs +11 -0
- package/scripts/claude-hooks/guard-auto-memory-writes.mjs +137 -0
- package/scripts/claude-hooks/id-display-format-gate.mjs +11 -0
- package/scripts/claude-hooks/inject-communication-style.mjs +86 -0
- package/scripts/claude-hooks/inject-coo-seat.mjs +78 -0
- package/scripts/claude-hooks/inject-dev-server-awareness.mjs +11 -0
- package/scripts/claude-hooks/inject-doctrine.mjs +13 -0
- package/scripts/claude-hooks/inject-vault-context.mjs +14 -0
- package/scripts/claude-hooks/intent-classifier-trigger.mjs +279 -0
- package/scripts/claude-hooks/log-session-activity.mjs +341 -0
- package/scripts/claude-hooks/naughty-list-gate.mjs +11 -0
- package/scripts/claude-hooks/notify-status-flip.mjs +72 -0
- package/scripts/claude-hooks/plan-exit-ingest-gate.mjs +170 -0
- package/scripts/claude-hooks/plan-ingest-close-reminder.mjs +66 -0
- package/scripts/claude-hooks/plan-review-gate.mjs +11 -0
- package/scripts/claude-hooks/preflight-reminder.mjs +262 -0
- package/scripts/claude-hooks/protected-action-intent.mjs +221 -0
- package/scripts/claude-hooks/research-method-guard.mjs +398 -0
- package/scripts/claude-hooks/research-protocol-gate.mjs +11 -0
- package/scripts/claude-hooks/session-end.mjs +451 -0
- package/scripts/claude-hooks/settings-write-gate.mjs +11 -0
- package/scripts/claude-hooks/settlement-check-gate.mjs +39 -0
- package/scripts/claude-hooks/status-flip-detect.mjs +17 -0
- package/scripts/claude-hooks/terminal-close-shared.mjs +426 -0
- package/scripts/claude-hooks/terminal-close-watcher.mjs +122 -0
- package/scripts/claude-hooks/vault-mirror-catchup.mjs +11 -0
- package/scripts/claude-hooks/vault-write-tier-guard.mjs +11 -0
- package/scripts/claude-hooks/wip-checkpoint.mjs +302 -0
- package/scripts/cursor-hooks/manifest.mjs +281 -0
- package/scripts/dev-server-status.mjs +34 -13
- package/scripts/dev-server-stop.mjs +313 -0
- package/scripts/heyspex-mode.mjs +288 -0
- package/scripts/hs-auto-resolve.mjs +240 -0
- package/scripts/hs-auto-source.mjs +398 -0
- package/scripts/hs-auto.mjs +134 -0
- package/scripts/hs-capture-cli.mjs +326 -0
- package/scripts/hs-classifier.mjs +136 -0
- package/scripts/hs-companion.mjs +88 -0
- package/scripts/hs-gateway.mjs +115 -0
- package/scripts/hs-mode.mjs +125 -0
- package/scripts/hs-next.mjs +441 -0
- package/scripts/hs-settings.mjs +338 -0
- package/scripts/hs-view.mjs +496 -0
- package/scripts/hs-worktree.mjs +201 -0
- package/scripts/install-claude-code-hooks.mjs +272 -0
- package/scripts/install-cursor-hooks.mjs +471 -0
- package/scripts/lib/agent-env-markers.mjs +38 -0
- package/scripts/lib/console-url-resolver.mjs +199 -61
- package/scripts/lib/customer-kit.mjs +1203 -0
- package/scripts/lib/dev-mode.mjs +249 -32
- package/scripts/lib/directive-obligations.mjs +349 -0
- package/scripts/lib/heyspex-home.mjs +72 -0
- package/scripts/lib/hook-dispositions.mjs +210 -0
- package/scripts/lib/hook-vault-header.mjs +30 -0
- package/scripts/lib/hs-command.mjs +277 -0
- package/scripts/lib/hs-companion-settings.mjs +472 -0
- package/scripts/lib/hs-source-branch.mjs +196 -0
- package/scripts/lib/hs-source-commit.mjs +59 -5
- package/scripts/lib/hs-source-identity.mjs +526 -0
- package/scripts/lib/hs-source-log.mjs +13 -22
- package/scripts/lib/hs-source-preflight.mjs +18 -4
- package/scripts/lib/hs-source-push.mjs +386 -307
- package/scripts/lib/hs-worktree-args.mjs +115 -0
- package/scripts/lib/shared-path-helpers.mjs +134 -0
- package/scripts/lib/standalone-doctor.mjs +540 -3
- package/scripts/lib/subagent-git-lock.mjs +109 -0
- package/scripts/lib/template-pin.mjs +34 -0
- package/scripts/lib/window-placement.mjs +109 -0
- package/scripts/provenance-lib.mjs +91 -0
- package/scripts/stamp-provenance-on-edit.mjs +222 -0
- package/scripts/wip-ledger-health.mjs +653 -0
- package/spexharness/doctrine/baseline/SPEXCODE-DOCTRINE.baseline.json +29 -0
- package/spexharness/src/adapters/audit.mjs +197 -0
- package/spexharness/src/adapters/claude-code.mjs +449 -0
- package/spexharness/src/adapters/copilot.mjs +236 -0
- package/spexharness/src/adapters/detect-harness.mjs +34 -0
- package/spexharness/src/chat/tui/ansi.mjs +448 -0
- package/spexharness/src/chat/tui/palette.mjs +22 -0
- package/spexharness/src/chat/tui/select.mjs +208 -0
- package/spexharness/src/config-root.mjs +54 -0
- package/spexharness/src/context-budget.mjs +141 -0
- package/spexharness/src/dev-server-control.mjs +389 -0
- package/spexharness/src/doctrine/behaviour.mjs +371 -0
- package/spexharness/src/doctrine/corpus.mjs +733 -0
- package/spexharness/src/doctrine/inject.mjs +338 -0
- package/spexharness/src/doctrine/merge.mjs +184 -0
- package/spexharness/src/doctrine/sources.mjs +252 -0
- package/spexharness/src/doctrine/synthesize.mjs +401 -0
- package/spexharness/src/doctrine/vault-source.mjs +574 -0
- package/spexharness/src/doctrine/writer.mjs +176 -0
- package/spexharness/src/events.mjs +84 -0
- package/spexharness/src/gates/_shell-write-targets.mjs +77 -0
- package/spexharness/src/gates/capture-workflow-gate.mjs +202 -0
- package/spexharness/src/gates/claim-before-code-gate.decide.mjs +33 -0
- package/spexharness/src/gates/claim-before-code-gate.mjs +129 -0
- package/spexharness/src/gates/deep-research-cost-gate.mjs +275 -0
- package/spexharness/src/gates/diagnose-capture-reminder.mjs +112 -0
- package/spexharness/src/gates/directive-obligation-gate.decide.mjs +132 -0
- package/spexharness/src/gates/directive-obligation-gate.mjs +183 -0
- package/spexharness/src/gates/enforce-session-sequence.mjs +895 -0
- package/spexharness/src/gates/enforce-vault-naming.mjs +215 -0
- package/spexharness/src/gates/epic-milestone-phrasing-gate.mjs +233 -0
- package/spexharness/src/gates/git-identity-guard.decide.mjs +436 -0
- package/spexharness/src/gates/git-identity-guard.mjs +61 -0
- package/spexharness/src/gates/git-reflog-audit.mjs +151 -0
- package/spexharness/src/gates/git-stash-gate.decide.mjs +361 -0
- package/spexharness/src/gates/git-stash-gate.mjs +159 -0
- package/spexharness/src/gates/id-display-format-gate.mjs +190 -0
- package/spexharness/src/gates/inject-doctrine-gate.mjs +89 -0
- package/spexharness/src/gates/inject-vault-context.mjs +239 -0
- package/spexharness/src/gates/naughty-list-gate.mjs +213 -0
- package/spexharness/src/gates/plan-review-gate.mjs +233 -0
- package/spexharness/src/gates/preflight-reminder-gate.mjs +265 -0
- package/spexharness/src/gates/research-protocol-gate.mjs +175 -0
- package/spexharness/src/gates/settings-write-gate.decide.mjs +96 -0
- package/spexharness/src/gates/settings-write-gate.mjs +104 -0
- package/spexharness/src/gates/vault-mirror-catchup.mjs +124 -0
- package/spexharness/src/gates/vault-write-tier-guard.decide.mjs +125 -0
- package/spexharness/src/gates/vault-write-tier-guard.mjs +42 -0
- package/spexharness/src/git-reflog-audit.mjs +294 -0
- package/spexharness/src/intent-classifier.mjs +196 -0
- package/spexharness/src/intent-directive.mjs +626 -0
- package/spexharness/src/profile/learn.mjs +551 -0
- package/spexharness/src/profile/proposals.mjs +357 -0
- package/spexharness/src/profile/vault-inclusion.mjs +386 -0
- package/spexharness/src/redact.mjs +151 -0
- package/spexharness/src/rule-table.mjs +849 -0
- package/spexharness/src/seat-context.mjs +122 -0
- package/spexharness/src/session-bootstrap.mjs +512 -0
- package/spexharness/src/session-record.mjs +815 -0
- package/spexharness/src/token-inventory.mjs +207 -0
- package/vendor/heyspex-mcp/account-switch.mjs +96 -0
- package/vendor/heyspex-mcp/alignment-gate.mjs +315 -1
- package/vendor/heyspex-mcp/blocker-resolver.mjs +37 -2
- package/vendor/heyspex-mcp/breakdown-ladder.mjs +119 -4
- package/vendor/heyspex-mcp/breakdown-source.mjs +271 -0
- package/vendor/heyspex-mcp/breakdown-writer.mjs +23 -3
- package/vendor/heyspex-mcp/capture-buffer-patches.mjs +77 -8
- package/vendor/heyspex-mcp/capture-channel.mjs +55 -5
- package/vendor/heyspex-mcp/capture-console-client.mjs +117 -3
- package/vendor/heyspex-mcp/capture-focus.mjs +1061 -31
- package/vendor/heyspex-mcp/capture-outbox.mjs +15 -5
- package/vendor/heyspex-mcp/capture-sources.mjs +195 -0
- package/vendor/heyspex-mcp/capture-tools.mjs +1264 -0
- package/vendor/heyspex-mcp/claim-tools.mjs +1147 -0
- package/vendor/heyspex-mcp/cli-verbs.mjs +2074 -0
- package/vendor/heyspex-mcp/cli.mjs +207 -15524
- package/vendor/heyspex-mcp/close-branch-report.mjs +102 -0
- package/vendor/heyspex-mcp/cloud-tool-surface.mjs +128 -0
- package/vendor/heyspex-mcp/cloud-vault-writes.mjs +602 -0
- package/vendor/heyspex-mcp/config-seed.yaml +15 -0
- package/vendor/heyspex-mcp/config-settings.mjs +290 -7
- package/vendor/heyspex-mcp/console-fetch.mjs +822 -17
- package/vendor/heyspex-mcp/console-transport.mjs +172 -0
- package/vendor/heyspex-mcp/credentials.mjs +585 -0
- package/vendor/heyspex-mcp/defer-spec.mjs +44 -4
- package/vendor/heyspex-mcp/deferred-reclaim.mjs +29 -3
- package/vendor/heyspex-mcp/design-handlers.mjs +31 -0
- package/vendor/heyspex-mcp/design-tools.mjs +4 -1
- package/vendor/heyspex-mcp/dev-factory-bridge.mjs +4 -4
- package/vendor/heyspex-mcp/dev-mode-core.mjs +45 -0
- package/vendor/heyspex-mcp/dev-tools.mjs +131 -0
- package/vendor/heyspex-mcp/diagnose-handlers.mjs +54 -0
- package/vendor/heyspex-mcp/events-tools.mjs +207 -0
- package/vendor/heyspex-mcp/filing-tools.mjs +723 -0
- package/vendor/heyspex-mcp/fleet-handlers.mjs +37 -0
- package/vendor/heyspex-mcp/fleet-queue-tools.mjs +71 -15
- package/vendor/heyspex-mcp/focus-tools.mjs +190 -0
- package/vendor/heyspex-mcp/folder-trust-store.mjs +60 -0
- package/vendor/heyspex-mcp/http-transport.mjs +302 -0
- package/vendor/heyspex-mcp/list-tools.mjs +257 -0
- package/vendor/heyspex-mcp/login-handoff.mjs +15 -1
- package/vendor/heyspex-mcp/mcp-config.mjs +107 -0
- package/vendor/heyspex-mcp/memory-tools.mjs +523 -0
- package/vendor/heyspex-mcp/mvp-mode.mjs +53 -6
- package/vendor/heyspex-mcp/obligation-backstop.mjs +103 -0
- package/vendor/heyspex-mcp/occupied-keys.mjs +142 -0
- package/vendor/heyspex-mcp/on-deck-brief.mjs +3377 -0
- package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -0
- package/vendor/heyspex-mcp/package-root.mjs +32 -0
- package/vendor/heyspex-mcp/peer-presence.mjs +8 -1
- package/vendor/heyspex-mcp/plan-ground.mjs +621 -652
- package/vendor/heyspex-mcp/plan-push.mjs +908 -22
- package/vendor/heyspex-mcp/plan-review.mjs +124 -30
- package/vendor/heyspex-mcp/preflight-gate.mjs +0 -0
- package/vendor/heyspex-mcp/procedure-handlers.mjs +327 -0
- package/vendor/heyspex-mcp/procedure-tools.mjs +1181 -970
- package/vendor/heyspex-mcp/promote-capture.mjs +388 -69
- package/vendor/heyspex-mcp/promote-diagnose.mjs +74 -0
- package/vendor/heyspex-mcp/promote-research-to-diagnose.mjs +9 -2
- package/vendor/heyspex-mcp/promote-research.mjs +666 -53
- package/vendor/heyspex-mcp/promotion-helpers.mjs +251 -0
- package/vendor/heyspex-mcp/prompts.mjs +1460 -0
- package/vendor/heyspex-mcp/read-path.mjs +191 -15
- package/vendor/heyspex-mcp/receipt-shape.mjs +146 -0
- package/vendor/heyspex-mcp/recipe-handlers.mjs +117 -0
- package/vendor/heyspex-mcp/regen-tasks.mjs +210 -3
- package/vendor/heyspex-mcp/register-spec.mjs +266 -54
- package/vendor/heyspex-mcp/request-context.mjs +83 -0
- package/vendor/heyspex-mcp/research-focus.mjs +207 -4
- package/vendor/heyspex-mcp/research-handlers.mjs +625 -0
- package/vendor/heyspex-mcp/research-tools.mjs +4 -4
- package/vendor/heyspex-mcp/response-budget.mjs +348 -0
- package/vendor/heyspex-mcp/review-focus.mjs +25 -1
- package/vendor/heyspex-mcp/roadmap-default.mjs +70 -0
- package/vendor/heyspex-mcp/search-specs.mjs +280 -0
- package/vendor/heyspex-mcp/seat-block-ledger.mjs +113 -0
- package/vendor/heyspex-mcp/seat-identity.mjs +2 -6
- package/vendor/heyspex-mcp/session-owner.mjs +109 -0
- package/vendor/heyspex-mcp/session-tools.mjs +162 -0
- package/vendor/heyspex-mcp/settings-registry.mjs +232 -0
- package/vendor/heyspex-mcp/settings-tools.mjs +85 -12
- package/vendor/heyspex-mcp/spec-edit-tools.mjs +453 -0
- package/vendor/heyspex-mcp/spec-id-lib.mjs +93 -17
- package/vendor/heyspex-mcp/split-research.mjs +304 -56
- package/vendor/heyspex-mcp/stdio-rpc.mjs +22 -0
- package/vendor/heyspex-mcp/submit-for-review.mjs +150 -7
- package/vendor/heyspex-mcp/summary-contract.mjs +105 -0
- package/vendor/heyspex-mcp/summary-write-tools.mjs +228 -0
- package/vendor/heyspex-mcp/symptom-query.mjs +146 -0
- package/vendor/heyspex-mcp/test-helpers.mjs +123 -1
- package/vendor/heyspex-mcp/tool-catalog.mjs +2819 -0
- package/vendor/heyspex-mcp/tool-dispatch.mjs +657 -0
- package/vendor/heyspex-mcp/tool-helpers.mjs +325 -0
- package/vendor/heyspex-mcp/validate-linkage.mjs +414 -27
- package/vendor/heyspex-mcp/vault-io.mjs +720 -0
- package/vendor/heyspex-mcp/vault-less-endpoints.mjs +52 -0
- package/vendor/heyspex-mcp/vault-resolve.mjs +554 -68
- package/vendor/heyspex-mcp/verify-reproduction.mjs +270 -30
- package/vendor/heyspex-mcp/whoami.mjs +92 -0
- package/vendor/heyspex-mcp/write-outbox.mjs +512 -0
|
@@ -24,12 +24,20 @@
|
|
|
24
24
|
* 2. **Append, not overwrite.** The console PATCH route stores top-level keys
|
|
25
25
|
* verbatim (last-write-wins), so multi-beat `user_quote` calls would clobber
|
|
26
26
|
* each other. We read the prior buffer and append, sending the full array.
|
|
27
|
+
* The same rule applies to the five single-object timestamped sections
|
|
28
|
+
* (`details`/`example`/`userStories`/`doneWhen`/`jtbd`): a second call on
|
|
29
|
+
* the same field combines onto the prior call's text (blank-line joined)
|
|
30
|
+
* instead of replacing it — [[HS-OSCT-77P9-DIAG-heyspex-update-capture-overwrites-agent]].
|
|
31
|
+
* `coreIdea`/`summary` are deliberately NOT combined — a capture's core
|
|
32
|
+
* idea/summary is its current best statement, not an accumulating log.
|
|
27
33
|
*
|
|
28
34
|
* `confirmed: true` is set on every agent-authored section because the MCP write
|
|
29
35
|
* is an explicit commit — the agent only calls this after the conversational
|
|
30
36
|
* confirm in the /hs-capture four-beat protocol.
|
|
31
37
|
*/
|
|
32
38
|
|
|
39
|
+
import { buildSourcesPatches, normalizeCaptureSources } from './capture-sources.mjs';
|
|
40
|
+
|
|
33
41
|
/** Keep only well-formed `{ at, text }` quote entries from a prior buffer. */
|
|
34
42
|
function priorUserQuotes(currentBuffer) {
|
|
35
43
|
const raw = currentBuffer?.userQuotes ?? currentBuffer?.user_quotes;
|
|
@@ -48,10 +56,49 @@ function priorChatTurns(currentBuffer) {
|
|
|
48
56
|
);
|
|
49
57
|
}
|
|
50
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Read the prior confirmed text for a single-object timestamped section
|
|
61
|
+
* (`details`/`example`/`userStories`/`doneWhen`/`jtbd`) so a second
|
|
62
|
+
* `heyspex_update_capture` call can accumulate onto it instead of
|
|
63
|
+
* overwriting it ([[HS-OSCT-77P9-DIAG-heyspex-update-capture-overwrites-agent]]).
|
|
64
|
+
* The console PATCH route stores whatever this module sends verbatim
|
|
65
|
+
* (last-write-wins on the whole key), so accumulation has to happen here,
|
|
66
|
+
* on the read-before-write side — the same reason userQuotes/chatTurns
|
|
67
|
+
* already read the prior buffer before building their new array value.
|
|
68
|
+
*/
|
|
69
|
+
function priorTimestampedText(currentBuffer, key) {
|
|
70
|
+
const raw = currentBuffer?.[key];
|
|
71
|
+
if (raw && typeof raw === 'object' && !Array.isArray(raw) && typeof raw.text === 'string') {
|
|
72
|
+
return raw.text.trim();
|
|
73
|
+
}
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Combine a prior section's text with a new call's text so neither call's
|
|
79
|
+
* content is lost — joined with a blank line so the materialized section
|
|
80
|
+
* reads as separate paragraphs, not run together.
|
|
81
|
+
*/
|
|
82
|
+
function combineTimestampedText(currentBuffer, key, nextText) {
|
|
83
|
+
const prior = priorTimestampedText(currentBuffer, key);
|
|
84
|
+
return prior ? `${prior}\n\n${nextText}` : nextText;
|
|
85
|
+
}
|
|
86
|
+
|
|
51
87
|
function collapse(value) {
|
|
52
88
|
return String(value).trim().replace(/\n+/g, ' ');
|
|
53
89
|
}
|
|
54
90
|
|
|
91
|
+
/**
|
|
92
|
+
* A user's quote is stored STRICTLY VERBATIM ([[HS-TGHU-ST07-TASK-store-and-render-capture-quotes]],
|
|
93
|
+
* owner 2026-10-10: "always verbatim words in notes"): no trim, no newline
|
|
94
|
+
* flattening, no whitespace collapsing. Only CRLF/CR are normalized to LF.
|
|
95
|
+
* A whitespace-only quote is dropped (nothing was said).
|
|
96
|
+
*/
|
|
97
|
+
function verbatimQuote(value) {
|
|
98
|
+
const raw = String(value).replace(/\r\n?/g, '\n');
|
|
99
|
+
return raw.trim() ? raw : '';
|
|
100
|
+
}
|
|
101
|
+
|
|
55
102
|
/**
|
|
56
103
|
* @param {object} args - { user_quote, agent_note, core_idea, summary (alias: concept), agent_expansion, example, user_stories, kind, done_when, jtbd }
|
|
57
104
|
* @param {object} currentBuffer - the session's existing content_buffer (may be {})
|
|
@@ -61,9 +108,17 @@ function collapse(value) {
|
|
|
61
108
|
export function buildCaptureBufferPatches(args = {}, currentBuffer = {}, iso) {
|
|
62
109
|
const patches = [];
|
|
63
110
|
|
|
64
|
-
if (args.user_quote !== undefined &&
|
|
65
|
-
const
|
|
66
|
-
|
|
111
|
+
if (args.user_quote !== undefined && verbatimQuote(args.user_quote)) {
|
|
112
|
+
const entry = { at: iso, text: verbatimQuote(args.user_quote) };
|
|
113
|
+
// op 'append' + items: a current console appends under its row lock (no
|
|
114
|
+
// stale read-modify-write); an older console ignores both and stores the
|
|
115
|
+
// full prior-plus-new value.
|
|
116
|
+
patches.push({
|
|
117
|
+
path: 'userQuotes',
|
|
118
|
+
value: [...priorUserQuotes(currentBuffer), entry],
|
|
119
|
+
op: 'append',
|
|
120
|
+
items: [entry],
|
|
121
|
+
});
|
|
67
122
|
}
|
|
68
123
|
|
|
69
124
|
if (args.agent_note !== undefined && collapse(args.agent_note)) {
|
|
@@ -85,13 +140,17 @@ export function buildCaptureBufferPatches(args = {}, currentBuffer = {}, iso) {
|
|
|
85
140
|
}
|
|
86
141
|
|
|
87
142
|
// agent_expansion is the `## Expanded Concept` deep-dive section → buffer.details.
|
|
143
|
+
// Accumulates onto the prior call's text (HS-OSCT) instead of replacing it.
|
|
88
144
|
if (args.agent_expansion !== undefined && String(args.agent_expansion).trim()) {
|
|
89
|
-
|
|
145
|
+
const text = combineTimestampedText(currentBuffer, 'details', String(args.agent_expansion).trim());
|
|
146
|
+
patches.push({ path: 'details', value: { at: iso, text, confirmed: true } });
|
|
90
147
|
}
|
|
91
148
|
|
|
92
149
|
// example is the `## Example` section (real or AI-illustrative) → buffer.example.
|
|
150
|
+
// Accumulates onto the prior call's text (HS-OSCT) instead of replacing it.
|
|
93
151
|
if (args.example !== undefined && String(args.example).trim()) {
|
|
94
|
-
|
|
152
|
+
const text = combineTimestampedText(currentBuffer, 'example', String(args.example).trim());
|
|
153
|
+
patches.push({ path: 'example', value: { at: iso, text, confirmed: true } });
|
|
95
154
|
}
|
|
96
155
|
|
|
97
156
|
// user_stories is the `## User Stories` section → buffer.userStories. Root cause
|
|
@@ -100,9 +159,10 @@ export function buildCaptureBufferPatches(args = {}, currentBuffer = {}, iso) {
|
|
|
100
159
|
// console content_buffer (and therefore the materialized file) never saw it —
|
|
101
160
|
// silent data loss, unlike example which has always had this push.
|
|
102
161
|
if (args.user_stories !== undefined && String(args.user_stories).trim()) {
|
|
162
|
+
const text = combineTimestampedText(currentBuffer, 'userStories', String(args.user_stories).trim());
|
|
103
163
|
patches.push({
|
|
104
164
|
path: 'userStories',
|
|
105
|
-
value: { at: iso, text
|
|
165
|
+
value: { at: iso, text, confirmed: true },
|
|
106
166
|
});
|
|
107
167
|
}
|
|
108
168
|
|
|
@@ -122,9 +182,10 @@ export function buildCaptureBufferPatches(args = {}, currentBuffer = {}, iso) {
|
|
|
122
182
|
// {at,text,confirmed} shape as the other agent-authored sections (example,
|
|
123
183
|
// user_stories, details) — sibling half of the HS-NR43 fix above.
|
|
124
184
|
if (args.done_when !== undefined && String(args.done_when).trim()) {
|
|
185
|
+
const text = combineTimestampedText(currentBuffer, 'doneWhen', String(args.done_when).trim());
|
|
125
186
|
patches.push({
|
|
126
187
|
path: 'doneWhen',
|
|
127
|
-
value: { at: iso, text
|
|
188
|
+
value: { at: iso, text, confirmed: true },
|
|
128
189
|
});
|
|
129
190
|
}
|
|
130
191
|
|
|
@@ -137,11 +198,19 @@ export function buildCaptureBufferPatches(args = {}, currentBuffer = {}, iso) {
|
|
|
137
198
|
// is refused by name at the handler's first hop, never silently dropped here
|
|
138
199
|
// — [[additionalproperties-false-in-an-mcp]].
|
|
139
200
|
if (args.jtbd !== undefined && String(args.jtbd).trim()) {
|
|
201
|
+
const text = combineTimestampedText(currentBuffer, 'jtbd', String(args.jtbd).trim());
|
|
140
202
|
patches.push({
|
|
141
203
|
path: 'jtbd',
|
|
142
|
-
value: { at: iso, text
|
|
204
|
+
value: { at: iso, text, confirmed: true },
|
|
143
205
|
});
|
|
144
206
|
}
|
|
145
207
|
|
|
208
|
+
// sources: links + original files saved with the capture (HS-9K12). The
|
|
209
|
+
// handler pre-validates via normalizeCaptureSources; an invalid bag emits nothing.
|
|
210
|
+
if (args.sources !== undefined) {
|
|
211
|
+
const normalized = normalizeCaptureSources(args.sources, iso);
|
|
212
|
+
if (normalized.ok) patches.push(...buildSourcesPatches(normalized, currentBuffer));
|
|
213
|
+
}
|
|
214
|
+
|
|
146
215
|
return patches;
|
|
147
216
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { existsSync, watch as fsWatch } from 'node:fs';
|
|
14
14
|
import { basename as pathBasename, dirname, join } from 'node:path';
|
|
15
|
-
import {
|
|
15
|
+
import { loadCaptureFocusOffline, captureFocusPath } from './capture-focus.mjs';
|
|
16
16
|
import { parseSpecId } from './spec-id-lib.mjs';
|
|
17
17
|
|
|
18
18
|
// Exponential reconnect backoff (ms). Capped at 30s; jittered ±50% on use.
|
|
@@ -205,6 +205,54 @@ export function createCaptureChannelWatcher({
|
|
|
205
205
|
return body?.session ?? null;
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Resolve the LIVE capture-on-deck pin, the same way `readCaptureFocusVendor`
|
|
210
|
+
* does elsewhere in the vendor MCP (`capture-tools.mjs`): GET the Neon
|
|
211
|
+
* control-plane pin + selection, falling back to the offline file
|
|
212
|
+
* (`loadCaptureFocusOffline`) only when the console can't be reached, and only
|
|
213
|
+
* when that file pin is not itself stale. Kept local
|
|
214
|
+
* (own `fetchImpl`/`getConsoleUrl`, mirroring `fetchSessionSnapshot` above)
|
|
215
|
+
* rather than importing capture-tools.mjs, so this watcher's boot-time pin
|
|
216
|
+
* read stays on the SAME injectable transport its tests already stub.
|
|
217
|
+
*
|
|
218
|
+
* Fixes [[HS-O4U6-2ZRO-DIAG-the-offline-capture-pin-file-is-months]]: on a
|
|
219
|
+
* console-backed vault the pin lives in Neon, not the offline file — every
|
|
220
|
+
* MCP server start used to `loadCaptureFocus(vaultRoot)` (the file) here,
|
|
221
|
+
* so on this repo it printed "pin → HS-CAP-VDPB is already materialized"
|
|
222
|
+
* for a capture promoted two months earlier, on every single boot.
|
|
223
|
+
*/
|
|
224
|
+
async function fetchLiveCaptureFocus() {
|
|
225
|
+
try {
|
|
226
|
+
const consoleUrl = getConsoleUrl().replace(/\/$/, '');
|
|
227
|
+
const [pinRes, selRes] = await Promise.all([
|
|
228
|
+
fetchImpl(`${consoleUrl}/api/control/capture-focus-pin`, { headers: { accept: 'application/json' } }),
|
|
229
|
+
fetchImpl(`${consoleUrl}/api/control/capture-focus-selection`, { headers: { accept: 'application/json' } }),
|
|
230
|
+
]);
|
|
231
|
+
if (pinRes.ok) {
|
|
232
|
+
const pinBody = await pinRes.json();
|
|
233
|
+
const pin = pinBody?.pin ?? null;
|
|
234
|
+
if (!pin) return null; // Neon authoritative — no pin means nothing on deck.
|
|
235
|
+
const selBody = selRes.ok ? await selRes.json() : null;
|
|
236
|
+
const sel = selBody?.selection ?? null;
|
|
237
|
+
const base = (p) => String(p || '').replace(/^.*[/\\]/, '');
|
|
238
|
+
const selMatches = sel?.specPath ? base(sel.specPath).startsWith(pin.specId) : false;
|
|
239
|
+
const focus = {
|
|
240
|
+
spec_path: selMatches ? sel.specPath : `specs/captures/${pin.specId}.md`,
|
|
241
|
+
pinned_at: pin.pinnedAt,
|
|
242
|
+
};
|
|
243
|
+
if (pin.expiresAt) focus.expires = pin.expiresAt;
|
|
244
|
+
focus.kind = 'idea';
|
|
245
|
+
return focus;
|
|
246
|
+
}
|
|
247
|
+
} catch {
|
|
248
|
+
// Fall through to the offline file seam.
|
|
249
|
+
}
|
|
250
|
+
// Offline seam — refuse a rotted file pin instead of starting a watcher on it.
|
|
251
|
+
// This watcher spent two months announcing a June capture read off this file
|
|
252
|
+
// ([[HS-O4U6-2ZRO-DIAG-the-offline-capture-pin-file-is-months]]).
|
|
253
|
+
return loadCaptureFocusOffline(vaultRoot).focus;
|
|
254
|
+
}
|
|
255
|
+
|
|
208
256
|
async function openAndConsumeStream(sessionId, controller) {
|
|
209
257
|
const consoleUrl = getConsoleUrl();
|
|
210
258
|
const url = `${consoleUrl.replace(/\/$/, '')}/api/capture/sessions/${encodeURIComponent(sessionId)}/events`;
|
|
@@ -447,13 +495,14 @@ export function createCaptureChannelWatcher({
|
|
|
447
495
|
if (pinDebounceTimer) clearTimeout(pinDebounceTimer);
|
|
448
496
|
pinDebounceTimer = setTimeout(() => {
|
|
449
497
|
pinDebounceTimer = null;
|
|
450
|
-
reevaluatePin();
|
|
498
|
+
reevaluatePin().catch((err) => log(`reevaluatePin failed: ${err?.message ?? err}`));
|
|
451
499
|
}, PIN_WATCH_DEBOUNCE_MS);
|
|
452
500
|
}
|
|
453
501
|
|
|
454
|
-
function reevaluatePin() {
|
|
502
|
+
async function reevaluatePin() {
|
|
455
503
|
if (stopped) return;
|
|
456
|
-
const focus =
|
|
504
|
+
const focus = await fetchLiveCaptureFocus();
|
|
505
|
+
if (stopped) return; // may have been stopped while the pin fetch was in flight
|
|
457
506
|
const nextSessionId = focus ? deriveSessionIdFromFocus(focus) : null;
|
|
458
507
|
|
|
459
508
|
if (nextSessionId === activeSessionId) {
|
|
@@ -533,7 +582,8 @@ export function createCaptureChannelWatcher({
|
|
|
533
582
|
return;
|
|
534
583
|
}
|
|
535
584
|
startPinWatcher();
|
|
536
|
-
|
|
585
|
+
// Initial open if pin already exists — fire-and-forget (async network read).
|
|
586
|
+
reevaluatePin().catch((err) => log(`reevaluatePin failed: ${err?.message ?? err}`));
|
|
537
587
|
}
|
|
538
588
|
|
|
539
589
|
function stop() {
|
|
@@ -19,6 +19,17 @@ import {
|
|
|
19
19
|
removeOutboxEntry,
|
|
20
20
|
stashCaptureArgs,
|
|
21
21
|
} from "./capture-outbox.mjs";
|
|
22
|
+
import {
|
|
23
|
+
CONSOLE_NOT_SIGNED_IN,
|
|
24
|
+
consoleAuthErrorMessage,
|
|
25
|
+
consoleUnauthorizedMessage,
|
|
26
|
+
} from "./console-fetch.mjs";
|
|
27
|
+
// The runnable form of a HeySpex command on THIS machine — `hs login` when the
|
|
28
|
+
// bin is on PATH, else an absolute-path `node "<install>/bin/hs.mjs" login`.
|
|
29
|
+
// Never an `npx` form: npx resolves from the READER's working directory, and
|
|
30
|
+
// outside a checkout `npx hs` installs the unrelated public package hs@2.0.1
|
|
31
|
+
// ([[HS-I6HT-EKYU-DIAG-every-message-tells-the-user-to-run-hs]]).
|
|
32
|
+
import { hsCommand } from "../../scripts/lib/hs-command.mjs";
|
|
22
33
|
|
|
23
34
|
/**
|
|
24
35
|
* Error taxonomy for capture-session console calls ([[HS-K27H-DIAG-0G86]]).
|
|
@@ -39,14 +50,28 @@ import {
|
|
|
39
50
|
* message verbatim. (Entries ALREADY in the outbox from a genuine
|
|
40
51
|
* transport failure still pause the drain on 5xx and retry later — the
|
|
41
52
|
* ban is on adding new entries for an answered request.)
|
|
53
|
+
* - `CONSOLE_LOCAL_URL_IN_CLOUD_MODE` / `CONSOLE_NOT_SIGNED_IN`
|
|
54
|
+
* ([[HS-9R6S-N8VP-DIAG-capture-writes-report-the-console-as]]): the call
|
|
55
|
+
* never had a chance — cloud mode pointed at a local console, no bearer
|
|
56
|
+
* token on this machine, or an answered HTTP 401. NEVER stash: nothing
|
|
57
|
+
* becomes reachable until `hs login` runs, so a "your words are buffered
|
|
58
|
+
* and will sync" promise would be false AND would hide the real cause.
|
|
59
|
+
* console-fetch stamps `authProblem` on the transport result; the 401 form
|
|
60
|
+
* stays inside the 4xx "the server ANSWERED, never stash" family and only
|
|
61
|
+
* swaps the generic CAPTURE_CONFLICT code for an actionable one.
|
|
42
62
|
*/
|
|
43
63
|
export const CAPTURE_ERROR_UNREACHABLE = "CONSOLE_UNREACHABLE";
|
|
44
64
|
export const CAPTURE_ERROR_CONFLICT = "CAPTURE_CONFLICT";
|
|
45
65
|
export const CAPTURE_ERROR_FAILED = "PATCH_FAILED";
|
|
66
|
+
export const CAPTURE_ERROR_NOT_SIGNED_IN = CONSOLE_NOT_SIGNED_IN;
|
|
46
67
|
|
|
47
68
|
/** Classify a `consoleFetch` result by HTTP status range. */
|
|
48
69
|
export function classifyConsoleFetchError(result) {
|
|
70
|
+
// Sign-in problems are stamped by console-fetch on the status-0 result and
|
|
71
|
+
// outrank the generic transport class — they never buffer.
|
|
72
|
+
if (result.authProblem) return result.authProblem;
|
|
49
73
|
if (result.status === 0) return CAPTURE_ERROR_UNREACHABLE;
|
|
74
|
+
if (result.status === 401) return CONSOLE_NOT_SIGNED_IN;
|
|
50
75
|
if (result.status >= 400 && result.status < 500)
|
|
51
76
|
return CAPTURE_ERROR_CONFLICT;
|
|
52
77
|
return CAPTURE_ERROR_FAILED;
|
|
@@ -66,7 +91,7 @@ export function extractConsoleMessage(result) {
|
|
|
66
91
|
* - list/create helper failures: `{ ok:false, error, result }` (status on `result`)
|
|
67
92
|
*
|
|
68
93
|
* @returns {{
|
|
69
|
-
* kind: 'transport'|'client'|'server',
|
|
94
|
+
* kind: 'transport'|'auth'|'client'|'server',
|
|
70
95
|
* shouldStash: boolean,
|
|
71
96
|
* error: string,
|
|
72
97
|
* http_status: number,
|
|
@@ -86,6 +111,38 @@ export function describeCaptureWriteFailure(failure) {
|
|
|
86
111
|
const serverCode = typeof result?.body?.error === "string" ? result.body.error : null;
|
|
87
112
|
const serverMessage = typeof result?.body?.message === "string" ? result.body.message : null;
|
|
88
113
|
|
|
114
|
+
// Sign-in problems first ([[HS-9R6S-N8VP-DIAG]]). A refused connection to the
|
|
115
|
+
// WRONG console is not an outage, and a 401 is not a capture conflict —
|
|
116
|
+
// both are "run hs login", and neither may touch the offline buffer.
|
|
117
|
+
const authProblem =
|
|
118
|
+
typeof result?.authProblem === "string" ? result.authProblem : null;
|
|
119
|
+
if (status === 0 && authProblem) {
|
|
120
|
+
return {
|
|
121
|
+
kind: "auth",
|
|
122
|
+
shouldStash: false,
|
|
123
|
+
error: authProblem,
|
|
124
|
+
http_status: 0,
|
|
125
|
+
server_error_code: null,
|
|
126
|
+
server_message: null,
|
|
127
|
+
message:
|
|
128
|
+
result?.authMessage
|
|
129
|
+
|| consoleAuthErrorMessage(authProblem, result?.url)
|
|
130
|
+
|| null,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (status === 401) {
|
|
134
|
+
const verbatim401 = serverMessage ? ` The server said: ${serverMessage}` : "";
|
|
135
|
+
return {
|
|
136
|
+
kind: "auth",
|
|
137
|
+
shouldStash: false,
|
|
138
|
+
error: CONSOLE_NOT_SIGNED_IN,
|
|
139
|
+
http_status: 401,
|
|
140
|
+
server_error_code: serverCode,
|
|
141
|
+
server_message: serverMessage,
|
|
142
|
+
message: `${consoleUnauthorizedMessage(result?.url)}${verbatim401}`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
89
146
|
// status 0 ⇔ genuine transport failure (ECONNREFUSED / DNS / timeout /
|
|
90
147
|
// abort — console-fetch stamps status 0 on every non-response). Anything
|
|
91
148
|
// else is an HTTP response the server actually returned.
|
|
@@ -116,9 +173,14 @@ export function describeCaptureWriteFailure(failure) {
|
|
|
116
173
|
`The console answered — it is not down. This write was NOT saved and will NOT retry automatically; fix the request and send it again.`,
|
|
117
174
|
};
|
|
118
175
|
}
|
|
176
|
+
// [[HS-K27H]] decided a 5xx was NOT stashed because the OLD code reported
|
|
177
|
+
// server errors as "can't reach the server". The sin was that false
|
|
178
|
+
// connectivity claim, not the buffering: degrade-don't-die buffers the user's
|
|
179
|
+
// words on a 5xx too, but the message says truthfully that the console
|
|
180
|
+
// ANSWERED with an error (see respondToCaptureWriteFailure).
|
|
119
181
|
return {
|
|
120
182
|
kind: "server",
|
|
121
|
-
shouldStash:
|
|
183
|
+
shouldStash: true,
|
|
122
184
|
error: CAPTURE_ERROR_FAILED,
|
|
123
185
|
http_status: status,
|
|
124
186
|
server_error_code: serverCode,
|
|
@@ -141,9 +203,25 @@ export function describeCaptureWriteFailure(failure) {
|
|
|
141
203
|
* (AC4) — the drain path replays every OUTBOX_KINDS field in canonical
|
|
142
204
|
* structured shape.
|
|
143
205
|
* - 4xx → `status: "refused"` + the server's error/message verbatim (AC2/AC5).
|
|
144
|
-
* - 5xx → `status: "
|
|
206
|
+
* - 5xx → stash too, `status: "failed_buffered"`, `buffered: true`, truthful "console answered <status>" message (not "unreachable"); `status: "failed"` only when there was nothing to stash.
|
|
207
|
+
* - sign-in (`authProblem` on a status-0 result, or an answered 401) →
|
|
208
|
+
* `status: "blocked"`, `buffered: false`, `action` = the sign-in command in
|
|
209
|
+
* the form this machine can run (`hs login`, or `node "<abs>/bin/hs.mjs" login`),
|
|
210
|
+
* and a message
|
|
211
|
+
* that says plainly the words were NOT saved ([[HS-9R6S-N8VP-DIAG]]).
|
|
145
212
|
*/
|
|
146
213
|
export function respondToCaptureWriteFailure(vaultRoot, args, failure) {
|
|
214
|
+
const payload = respondToCaptureWriteFailureInner(vaultRoot, args, failure);
|
|
215
|
+
// sources (original files / links) are NOT stashed offline: say so honestly
|
|
216
|
+
// instead of letting the "your words are saved" message imply they were.
|
|
217
|
+
if (Array.isArray(args?.sources) && args.sources.length > 0) {
|
|
218
|
+
payload.sources_not_saved = true;
|
|
219
|
+
payload.message = `${payload.message} NOTE: the sources you attached were NOT saved offline. Re-send them with heyspex_update_capture once the console is reachable.`;
|
|
220
|
+
}
|
|
221
|
+
return payload;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function respondToCaptureWriteFailureInner(vaultRoot, args, failure) {
|
|
147
225
|
const d = describeCaptureWriteFailure(failure);
|
|
148
226
|
if (d.kind === "transport") {
|
|
149
227
|
const stashed = stashCaptureArgs(vaultRoot, args || {});
|
|
@@ -161,6 +239,42 @@ export function respondToCaptureWriteFailure(vaultRoot, args, failure) {
|
|
|
161
239
|
pending_sync: pending,
|
|
162
240
|
};
|
|
163
241
|
}
|
|
242
|
+
if (d.kind === "auth") {
|
|
243
|
+
// NOT buffered, and the payload must not imply otherwise ([[HS-9R6S-N8VP-DIAG]]).
|
|
244
|
+
return {
|
|
245
|
+
ok: false,
|
|
246
|
+
status: "blocked",
|
|
247
|
+
error: d.error,
|
|
248
|
+
http_status: d.http_status,
|
|
249
|
+
server_error: d.server_error_code,
|
|
250
|
+
console_error: d.server_message ?? d.server_error_code,
|
|
251
|
+
buffered: false,
|
|
252
|
+
stashed: [],
|
|
253
|
+
// The form that actually runs on this machine, never the bare `hs`
|
|
254
|
+
// spelling ([[HS-I6HT-EKYU-DIAG-every-message-tells-the-user-to-run-hs]]).
|
|
255
|
+
action: hsCommand("login"),
|
|
256
|
+
message: `This capture was not filed, and your words were not buffered. ${d.message}`,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
if (d.kind === "server") {
|
|
260
|
+
const stashed = stashCaptureArgs(vaultRoot, args || {});
|
|
261
|
+
if (stashed.length > 0) {
|
|
262
|
+
return {
|
|
263
|
+
ok: false,
|
|
264
|
+
status: "failed_buffered",
|
|
265
|
+
error: d.error,
|
|
266
|
+
http_status: d.http_status,
|
|
267
|
+
server_error: d.server_error_code,
|
|
268
|
+
console_error: d.server_message ?? d.server_error_code,
|
|
269
|
+
buffered: true,
|
|
270
|
+
source: "offline-buffer",
|
|
271
|
+
message:
|
|
272
|
+
`The console answered ${d.http_status} applying the write; your words are buffered locally in .heyspex/capture-outbox and will be replayed on the next successful capture call.`,
|
|
273
|
+
stashed: stashed.map((s) => ({ kind: s.kind })),
|
|
274
|
+
pending_sync: outboxCount(vaultRoot),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
164
278
|
return {
|
|
165
279
|
ok: false,
|
|
166
280
|
status: d.kind === "client" ? "refused" : "failed",
|