hippo-memory 1.69.0 → 1.71.0
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/README.md +5 -2
- package/dist/agent-memories/apply.d.ts +3 -3
- package/dist/agent-memories/apply.js +16 -31
- package/dist/agent-memories/claude-code.d.ts +5 -5
- package/dist/agent-memories/claude-code.js +25 -12
- package/dist/agent-memories/copilot.js +3 -2
- package/dist/agent-memories/files.js +26 -14
- package/dist/agent-memories/gemini.js +6 -5
- package/dist/agent-memories/keys.js +3 -2
- package/dist/agent-memories/legacy.d.ts +2 -2
- package/dist/agent-memories/legacy.js +2 -2
- package/dist/agent-memories/markdown.d.ts +1 -3
- package/dist/agent-memories/markdown.js +1 -3
- package/dist/agent-memories/report.d.ts +1 -1
- package/dist/agent-memories/report.js +3 -2
- package/dist/agent-memories/source.d.ts +1 -1
- package/dist/agent-memories/source.js +6 -5
- package/dist/agent-memories/sync.d.ts +5 -3
- package/dist/agent-memories/sync.js +34 -38
- package/dist/agent-memories/types.d.ts +1 -1
- package/dist/api/assemble.d.ts +9 -46
- package/dist/api/assemble.js +44 -54
- package/dist/api/audit.d.ts +8 -7
- package/dist/api/audit.js +15 -21
- package/dist/api/auth.d.ts +17 -39
- package/dist/api/auth.js +76 -216
- package/dist/api/auto-sleep.d.ts +13 -0
- package/dist/api/auto-sleep.js +32 -0
- package/dist/{availability.d.ts → api/availability.d.ts} +4 -17
- package/dist/{availability.js → api/availability.js} +14 -44
- package/dist/{context-auto.js → api/context-auto.js} +2 -1
- package/dist/{context-render.d.ts → api/context-render.d.ts} +4 -4
- package/dist/{context-render.js → api/context-render.js} +7 -6
- package/dist/api/context-select.d.ts +5 -5
- package/dist/api/context-select.js +39 -48
- package/dist/api/context-types.d.ts +15 -40
- package/dist/api/context.d.ts +7 -26
- package/dist/api/context.js +67 -97
- package/dist/api/dormant.d.ts +7 -23
- package/dist/api/dormant.js +40 -128
- package/dist/api/drill-down.d.ts +9 -48
- package/dist/api/drill-down.js +41 -90
- package/dist/api/forget.d.ts +5 -33
- package/dist/api/forget.js +17 -73
- package/dist/api/goals.d.ts +1 -1
- package/dist/api/goals.js +2 -2
- package/dist/api/graph.d.ts +8 -0
- package/dist/api/graph.js +7 -0
- package/dist/api/index.d.ts +28 -0
- package/dist/api/index.js +31 -0
- package/dist/api/instruction-detect.js +85 -0
- package/dist/api/learn.js +63 -55
- package/dist/api/ledger-db.d.ts +21 -0
- package/dist/api/ledger-db.js +81 -0
- package/dist/api/memories.d.ts +5 -0
- package/dist/api/memories.js +7 -0
- package/dist/api/objects.d.ts +9 -0
- package/dist/api/objects.js +11 -0
- package/dist/api/on-store.d.ts +16 -0
- package/dist/api/on-store.js +38 -0
- package/dist/api/outcome.d.ts +5 -42
- package/dist/api/outcome.js +15 -80
- package/dist/{pilot-arm.d.ts → api/pilot-arm.d.ts} +1 -1
- package/dist/{pilot-arm.js → api/pilot-arm.js} +15 -23
- package/dist/api/predictions.d.ts +9 -0
- package/dist/api/predictions.js +18 -0
- package/dist/api/promote.d.ts +16 -31
- package/dist/api/promote.js +38 -123
- package/dist/{prompt-hook.d.ts → api/prompt-hook.d.ts} +11 -11
- package/dist/{prompt-hook.js → api/prompt-hook.js} +42 -60
- package/dist/api/quarantine.d.ts +5 -5
- package/dist/api/quarantine.js +30 -106
- package/dist/api/recall-cli.d.ts +55 -0
- package/dist/api/recall-cli.js +94 -0
- package/dist/api/recall-core.d.ts +4 -0
- package/dist/api/recall-core.js +120 -0
- package/dist/api/recall-finish.d.ts +53 -0
- package/dist/api/recall-finish.js +30 -0
- package/dist/api/recall-history.d.ts +64 -0
- package/dist/api/recall-history.js +159 -0
- package/dist/{recall-pipeline.d.ts → api/recall-pipeline.d.ts} +7 -8
- package/dist/{recall-pipeline.js → api/recall-pipeline.js} +60 -48
- package/dist/api/recall-record.d.ts +10 -13
- package/dist/api/recall-record.js +10 -30
- package/dist/api/recall-request.d.ts +2 -2
- package/dist/api/recall-request.js +3 -3
- package/dist/api/recall-types.d.ts +101 -316
- package/dist/api/recall.d.ts +5 -33
- package/dist/api/recall.js +92 -91
- package/dist/api/refine.d.ts +7 -0
- package/dist/api/refine.js +10 -0
- package/dist/api/remember-local.d.ts +57 -0
- package/dist/api/remember-local.js +90 -0
- package/dist/api/remember.d.ts +27 -12
- package/dist/api/remember.js +42 -46
- package/dist/api/session-owners.d.ts +4 -0
- package/dist/api/session-owners.js +17 -0
- package/dist/api/sleep-run.d.ts +7 -9
- package/dist/api/sleep-run.js +78 -94
- package/dist/api/sleep.d.ts +10 -29
- package/dist/api/tokens.d.ts +5 -12
- package/dist/api/tokens.js +12 -31
- package/dist/api/types.d.ts +8 -31
- package/dist/api/types.js +5 -23
- package/dist/capture/caller-session.d.ts +2 -2
- package/dist/capture/caller-session.js +8 -8
- package/dist/{capture-error.d.ts → capture/capture-error.d.ts} +2 -2
- package/dist/{capture-error.js → capture/capture-error.js} +7 -7
- package/dist/{codex-patch.js → capture/codex-patch.js} +1 -1
- package/dist/capture/command.d.ts +5 -13
- package/dist/capture/command.js +57 -60
- package/dist/capture/compact-resume-caller.d.ts +1 -1
- package/dist/capture/compact-resume-caller.js +7 -7
- package/dist/capture/compact.d.ts +8 -16
- package/dist/capture/compact.js +56 -47
- package/dist/capture/compaction-items-caller.d.ts +1 -1
- package/dist/capture/compaction-items-caller.js +7 -5
- package/dist/{compaction-items.js → capture/compaction-items.js} +27 -3
- package/dist/{compaction-record.d.ts → capture/compaction-record.d.ts} +10 -6
- package/dist/{compaction-record.js → capture/compaction-record.js} +143 -157
- package/dist/{compaction-spool.d.ts → capture/compaction-spool.d.ts} +1 -1
- package/dist/{compaction-spool.js → capture/compaction-spool.js} +18 -14
- package/dist/capture/copilot-transcript.js +1 -1
- package/dist/capture/extract.js +2 -2
- package/dist/capture/failure-caller.d.ts +2 -2
- package/dist/capture/failure-caller.js +6 -6
- package/dist/capture/failure-reading.d.ts +2 -5
- package/dist/capture/failure-reading.js +11 -12
- package/dist/{handoff-evidence.d.ts → capture/handoff-evidence.d.ts} +1 -1
- package/dist/{handoff-evidence.js → capture/handoff-evidence.js} +2 -1
- package/dist/capture/pre-compact-caller.d.ts +1 -1
- package/dist/capture/pre-compact-caller.js +2 -2
- package/dist/{session-digest.d.ts → capture/session-digest.d.ts} +2 -6
- package/dist/{session-digest.js → capture/session-digest.js} +29 -29
- package/dist/capture/session-end-caller.d.ts +2 -2
- package/dist/capture/session-end-caller.js +1 -1
- package/dist/capture/session-texts.d.ts +1 -1
- package/dist/capture/session-texts.js +5 -5
- package/dist/capture/session-worker.js +6 -4
- package/dist/{share-scrub.js → capture/share-scrub.js} +2 -2
- package/dist/capture/transcript.d.ts +2 -10
- package/dist/capture/transcript.js +72 -92
- package/dist/capture/working-state.d.ts +0 -3
- package/dist/capture/working-state.js +7 -7
- package/dist/cli/api-context.d.ts +3 -0
- package/dist/cli/api-context.js +6 -0
- package/dist/cli/audit-prune.d.ts +28 -0
- package/dist/cli/audit-prune.js +40 -0
- package/dist/cli/audit.d.ts +2 -2
- package/dist/cli/audit.js +92 -88
- package/dist/cli/auth.d.ts +2 -1
- package/dist/cli/auth.js +69 -53
- package/dist/cli/briefs.d.ts +4 -3
- package/dist/cli/briefs.js +76 -202
- package/dist/cli/card.d.ts +2 -1
- package/dist/cli/card.js +55 -54
- package/dist/cli/client.d.ts +28 -0
- package/dist/{client.js → cli/client.js} +25 -44
- package/dist/cli/compact-resume-payload.d.ts +8 -0
- package/dist/cli/compact-resume-payload.js +40 -0
- package/dist/cli/context.d.ts +1 -13
- package/dist/cli/context.js +51 -77
- package/dist/cli/continuity.d.ts +6 -5
- package/dist/cli/continuity.js +153 -145
- package/dist/cli/correction-latency.d.ts +24 -0
- package/dist/{correction-latency.js → cli/correction-latency.js} +2 -14
- package/dist/cli/curate.d.ts +13 -17
- package/dist/cli/curate.js +226 -222
- package/dist/cli/dag.d.ts +4 -4
- package/dist/cli/dag.js +70 -52
- package/dist/cli/decisions.d.ts +4 -3
- package/dist/cli/decisions.js +129 -253
- package/dist/cli/eval.d.ts +1 -3
- package/dist/cli/eval.js +56 -42
- package/dist/cli/exit.d.ts +6 -0
- package/dist/cli/exit.js +11 -0
- package/dist/cli/explain.d.ts +1 -2
- package/dist/cli/explain.js +39 -21
- package/dist/cli/flag-values.d.ts +30 -0
- package/dist/cli/flag-values.js +120 -0
- package/dist/cli/flags.d.ts +304 -0
- package/dist/cli/flags.js +169 -0
- package/dist/cli/github.d.ts +14 -0
- package/dist/cli/github.js +184 -0
- package/dist/cli/goals.d.ts +2 -1
- package/dist/cli/goals.js +76 -75
- package/dist/cli/hook-runtime.d.ts +31 -0
- package/dist/cli/hook-runtime.js +139 -0
- package/dist/cli/init.d.ts +2 -1
- package/dist/cli/init.js +153 -126
- package/dist/cli/install-steps.d.ts +7 -0
- package/dist/cli/install-steps.js +101 -0
- package/dist/cli/last-sleep.d.ts +3 -1
- package/dist/cli/last-sleep.js +12 -7
- package/dist/cli/maintenance.d.ts +5 -4
- package/dist/cli/maintenance.js +41 -42
- package/dist/cli/object-verbs.d.ts +40 -0
- package/dist/cli/object-verbs.js +66 -0
- package/dist/cli/owner-validation.d.ts +18 -0
- package/dist/cli/owner-validation.js +33 -0
- package/dist/cli/playbooks.d.ts +4 -3
- package/dist/cli/playbooks.js +98 -306
- package/dist/cli/print.d.ts +15 -0
- package/dist/cli/print.js +82 -0
- package/dist/cli/projects.d.ts +2 -3
- package/dist/cli/projects.js +56 -51
- package/dist/cli/provenance-coverage.d.ts +14 -0
- package/dist/{provenance-coverage.js → cli/provenance-coverage.js} +2 -6
- package/dist/cli/quality-repair-once.js +4 -4
- package/dist/cli/recall.d.ts +2 -6
- package/dist/cli/recall.js +128 -253
- package/dist/cli/refine-llm.d.ts +35 -0
- package/dist/cli/refine-llm.js +128 -0
- package/dist/cli/remember.d.ts +4 -6
- package/dist/cli/remember.js +193 -230
- package/dist/{scheduler.d.ts → cli/scheduler.d.ts} +4 -17
- package/dist/{scheduler.js → cli/scheduler.js} +22 -22
- package/dist/cli/serve.d.ts +1 -1
- package/dist/cli/serve.js +46 -7
- package/dist/cli/session-hooks.d.ts +11 -19
- package/dist/cli/session-hooks.js +263 -258
- package/dist/cli/setup.d.ts +4 -3
- package/dist/cli/setup.js +55 -37
- package/dist/cli/shared.d.ts +14 -118
- package/dist/cli/shared.js +26 -400
- package/dist/cli/slack.d.ts +2 -1
- package/dist/cli/slack.js +46 -74
- package/dist/cli/sleep-render.d.ts +4 -0
- package/dist/cli/sleep-render.js +61 -0
- package/dist/cli/sleep.d.ts +3 -8
- package/dist/cli/sleep.js +34 -91
- package/dist/cli/status.d.ts +9 -12
- package/dist/cli/status.js +86 -131
- package/dist/{stdin.d.ts → cli/stdin.d.ts} +4 -6
- package/dist/{stdin.js → cli/stdin.js} +8 -9
- package/dist/cli/transfer.d.ts +9 -9
- package/dist/cli/transfer.js +125 -122
- package/dist/cli/usage.js +13 -5
- package/dist/cli.d.ts +69 -167
- package/dist/cli.js +187 -296
- package/dist/connectors/dlq.d.ts +44 -0
- package/dist/connectors/dlq.js +30 -0
- package/dist/connectors/github/backfill.d.ts +6 -26
- package/dist/connectors/github/backfill.js +42 -114
- package/dist/connectors/github/deletion.d.ts +6 -21
- package/dist/connectors/github/deletion.js +17 -64
- package/dist/connectors/github/dlq.d.ts +12 -91
- package/dist/connectors/github/dlq.js +23 -140
- package/dist/connectors/github/ingest.d.ts +4 -37
- package/dist/connectors/github/ingest.js +24 -93
- package/dist/connectors/github/octokit-client.d.ts +5 -17
- package/dist/connectors/github/octokit-client.js +29 -25
- package/dist/connectors/github/ratelimit.d.ts +3 -14
- package/dist/connectors/github/ratelimit.js +3 -14
- package/dist/connectors/github/scope.d.ts +3 -5
- package/dist/connectors/github/scope.js +3 -5
- package/dist/connectors/github/signature.d.ts +4 -26
- package/dist/connectors/github/signature.js +4 -26
- package/dist/connectors/github/tenant-routing.d.ts +4 -26
- package/dist/connectors/github/tenant-routing.js +13 -61
- package/dist/connectors/github/transform.d.ts +1 -1
- package/dist/connectors/github/transform.js +2 -16
- package/dist/connectors/github/types.d.ts +4 -16
- package/dist/connectors/github/types.js +17 -27
- package/dist/connectors/github/webhook.d.ts +5 -18
- package/dist/connectors/github/webhook.js +86 -105
- package/dist/connectors/slack/backfill.d.ts +5 -18
- package/dist/connectors/slack/backfill.js +9 -42
- package/dist/connectors/slack/deletion.d.ts +4 -7
- package/dist/connectors/slack/deletion.js +20 -49
- package/dist/connectors/slack/dlq.d.ts +9 -68
- package/dist/connectors/slack/dlq.js +26 -136
- package/dist/connectors/slack/ingest.d.ts +4 -18
- package/dist/connectors/slack/ingest.js +26 -91
- package/dist/connectors/slack/scope.d.ts +2 -8
- package/dist/connectors/slack/scope.js +2 -8
- package/dist/connectors/slack/signature.d.ts +1 -6
- package/dist/connectors/slack/signature.js +2 -1
- package/dist/connectors/slack/tenant-routing.d.ts +4 -18
- package/dist/connectors/slack/tenant-routing.js +11 -31
- package/dist/connectors/slack/transform.d.ts +3 -19
- package/dist/connectors/slack/transform.js +2 -18
- package/dist/connectors/slack/types.d.ts +4 -10
- package/dist/connectors/slack/types.js +5 -16
- package/dist/connectors/slack/web-client.js +34 -28
- package/dist/connectors/slack/webhook.d.ts +5 -21
- package/dist/connectors/slack/webhook.js +80 -108
- package/dist/connectors/slack/workspaces.d.ts +8 -41
- package/dist/connectors/slack/workspaces.js +8 -58
- package/dist/consolidate/conflicts.d.ts +1 -1
- package/dist/consolidate/conflicts.js +10 -18
- package/dist/consolidate/dag-summary.d.ts +8 -0
- package/dist/consolidate/dag-summary.js +41 -0
- package/dist/{dag.d.ts → consolidate/dag.d.ts} +6 -29
- package/dist/{dag.js → consolidate/dag.js} +49 -131
- package/dist/consolidate/decay.d.ts +2 -2
- package/dist/consolidate/decay.js +35 -41
- package/dist/consolidate/dedupe.d.ts +29 -0
- package/dist/consolidate/dedupe.js +103 -0
- package/dist/consolidate/flush-units.d.ts +5 -6
- package/dist/consolidate/flush-units.js +3 -3
- package/dist/consolidate/half-life-migration.d.ts +34 -0
- package/dist/consolidate/half-life-migration.js +89 -0
- package/dist/consolidate/llm-passes.js +12 -29
- package/dist/consolidate/memory-value-weights.d.ts +8 -0
- package/dist/{memory-value-weights.js → consolidate/memory-value-weights.js} +2 -13
- package/dist/consolidate/memory-value.d.ts +45 -0
- package/dist/consolidate/memory-value.js +164 -0
- package/dist/consolidate/merge.js +65 -83
- package/dist/{merged-row.d.ts → consolidate/merged-row.d.ts} +1 -1
- package/dist/{merged-row.js → consolidate/merged-row.js} +4 -3
- package/dist/consolidate/physics-pass.js +46 -48
- package/dist/consolidate/replay.d.ts +9 -0
- package/dist/consolidate/replay.js +84 -0
- package/dist/consolidate/run.d.ts +7 -13
- package/dist/consolidate/run.js +6 -37
- package/dist/consolidate/sleep.d.ts +2 -9
- package/dist/consolidate/sleep.js +69 -151
- package/dist/consolidate/trace.d.ts +19 -0
- package/dist/{trace.js → consolidate/trace.js} +7 -24
- package/dist/consolidate/traces.js +40 -51
- package/dist/core/ablation.d.ts +15 -0
- package/dist/core/ablation.js +49 -0
- package/dist/{agent-memories/tools.d.ts → core/agent-memory-tools.d.ts} +1 -1
- package/dist/{agent-memories/tools.js → core/agent-memory-tools.js} +1 -1
- package/dist/{ambient.d.ts → core/ambient.d.ts} +1 -8
- package/dist/{ambient.js → core/ambient.js} +13 -18
- package/dist/{capture-contract.d.ts → core/capture-contract.d.ts} +16 -3
- package/dist/{capture-contract.js → core/capture-contract.js} +67 -1
- package/dist/{card.d.ts → core/card.d.ts} +4 -1
- package/dist/{card.js → core/card.js} +8 -0
- package/dist/core/compare.d.ts +32 -0
- package/dist/core/compare.js +70 -0
- package/dist/{config.d.ts → core/config.d.ts} +14 -28
- package/dist/{config.js → core/config.js} +101 -75
- package/dist/{handoff.d.ts → core/handoff.d.ts} +1 -7
- package/dist/core/handoff.js +81 -0
- package/dist/{memory-quality.js → core/memory-quality.js} +2 -2
- package/dist/core/memory.d.ts +183 -0
- package/dist/core/memory.js +380 -0
- package/dist/core/package-root.d.ts +3 -0
- package/dist/core/package-root.js +5 -0
- package/dist/{physics-config.d.ts → core/physics-config.d.ts} +1 -4
- package/dist/{physics-config.js → core/physics-config.js} +3 -8
- package/dist/{physics.d.ts → core/physics.d.ts} +16 -62
- package/dist/{physics.js → core/physics.js} +60 -122
- package/dist/{project-identity.d.ts → core/project-identity.d.ts} +17 -63
- package/dist/{project-identity.js → core/project-identity.js} +22 -55
- package/dist/{project-remote.js → core/project-remote.js} +17 -6
- package/dist/{prompt-recall.js → core/prompt-recall.js} +1 -1
- package/dist/core/raw-append-only.d.ts +5 -0
- package/dist/core/raw-append-only.js +7 -0
- package/dist/{salience.d.ts → core/salience.d.ts} +1 -7
- package/dist/{salience.js → core/salience.js} +36 -27
- package/dist/{search/types.d.ts → core/search-types.d.ts} +2 -2
- package/dist/{search/types.js → core/search-types.js} +1 -1
- package/dist/core/session-digest-row.d.ts +5 -0
- package/dist/core/session-digest-row.js +6 -0
- package/dist/{dashboard-actions.d.ts → dashboard/dashboard-actions.d.ts} +1 -1
- package/dist/{dashboard-actions.js → dashboard/dashboard-actions.js} +7 -7
- package/dist/{dashboard-queries.d.ts → dashboard/dashboard-queries.d.ts} +7 -2
- package/dist/{dashboard-queries.js → dashboard/dashboard-queries.js} +6 -10
- package/dist/{dashboard-snapshot.d.ts → dashboard/dashboard-snapshot.d.ts} +11 -10
- package/dist/{dashboard-snapshot.js → dashboard/dashboard-snapshot.js} +110 -104
- package/dist/{dashboard-types.d.ts → dashboard/dashboard-types.d.ts} +2 -1
- package/dist/{dashboard.d.ts → dashboard/dashboard.d.ts} +1 -0
- package/dist/{dashboard.js → dashboard/dashboard.js} +172 -55
- package/dist/db/backup.js +1 -1
- package/dist/db/busy.d.ts +19 -2
- package/dist/db/busy.js +88 -14
- package/dist/db/connect.d.ts +11 -2
- package/dist/db/connect.js +43 -10
- package/dist/db/index.d.ts +9 -0
- package/dist/db/index.js +8 -0
- package/dist/db/meta.d.ts +4 -0
- package/dist/db/meta.js +11 -1
- package/dist/db/migrate.d.ts +8 -1
- package/dist/db/migrate.js +28 -33
- package/dist/db/migrations/types.d.ts +2 -5
- package/dist/db/migrations/v08.js +1 -1
- package/dist/db/migrations/v14.js +39 -36
- package/dist/db/migrations/v15.js +2 -8
- package/dist/db/migrations/v16.js +31 -29
- package/dist/db/migrations/v17.js +2 -3
- package/dist/db/migrations/v18.js +6 -5
- package/dist/db/migrations/v19.js +2 -5
- package/dist/db/migrations/v20.js +5 -8
- package/dist/db/migrations/v22.js +28 -38
- package/dist/db/migrations/v23.js +3 -14
- package/dist/db/migrations/v24.js +25 -22
- package/dist/db/migrations/v25.js +3 -8
- package/dist/db/migrations/v26.js +2 -13
- package/dist/db/migrations/v27.js +3 -11
- package/dist/db/migrations/v28.js +2 -14
- package/dist/db/migrations/v29.js +23 -32
- package/dist/db/migrations/v30.js +4 -16
- package/dist/db/migrations/v31.js +23 -31
- package/dist/db/migrations/v32.js +4 -22
- package/dist/db/migrations/v33.js +2 -15
- package/dist/db/migrations/v34.js +2 -11
- package/dist/db/migrations/v35.js +2 -11
- package/dist/db/migrations/v36.js +2 -11
- package/dist/db/migrations/v37.js +8 -33
- package/dist/db/migrations/v38.js +5 -38
- package/dist/db/migrations/v39.js +10 -21
- package/dist/db/migrations/v40.js +8 -14
- package/dist/db/migrations/v41.js +2 -24
- package/dist/db/migrations/v43.js +7 -6
- package/dist/db/migrations/v44.js +2 -8
- package/dist/db/migrations/v45.js +2 -7
- package/dist/db/migrations/v46.js +1 -1
- package/dist/db/migrations/v47.js +2 -3
- package/dist/db/migrations/v48.js +1 -1
- package/dist/db/migrations/v49.js +1 -1
- package/dist/db/migrations/v50.js +8 -7
- package/dist/db/migrations/v51.js +1 -1
- package/dist/db/migrations/v52.js +1 -1
- package/dist/db/open.d.ts +11 -1
- package/dist/db/open.js +37 -4
- package/dist/db/physics-state.d.ts +23 -0
- package/dist/{physics-state.js → db/physics-state.js} +34 -52
- package/dist/db/raw-archive-mirror-cleanup.d.ts +5 -0
- package/dist/{raw-archive-mirror-cleanup.js → db/raw-archive-mirror-cleanup.js} +10 -20
- package/dist/db/request-stores.d.ts +15 -2
- package/dist/db/request-stores.js +37 -15
- package/dist/db/tables.d.ts +2 -0
- package/dist/db/tables.js +6 -0
- package/dist/{vector-store.d.ts → db/vector-store.d.ts} +6 -2
- package/dist/{vector-store.js → db/vector-store.js} +83 -51
- package/dist/db/wal-checkpointer.d.ts +11 -0
- package/dist/db/wal-checkpointer.js +92 -0
- package/dist/doctor.js +176 -152
- package/dist/entry/json-hooks.d.ts +1 -1
- package/dist/entry/json-hooks.js +1 -1
- package/dist/entry/project-identity.d.ts +1 -1
- package/dist/entry/project-identity.js +1 -1
- package/dist/entry/session-text.d.ts +3 -3
- package/dist/entry/session-text.js +5 -4
- package/dist/{eval-stats.d.ts → eval/eval-stats.d.ts} +15 -53
- package/dist/{eval-stats.js → eval/eval-stats.js} +24 -51
- package/dist/{eval-suite.d.ts → eval/eval-suite.d.ts} +3 -14
- package/dist/{eval-suite.js → eval/eval-suite.js} +99 -57
- package/dist/{eval.d.ts → eval/eval.d.ts} +7 -25
- package/dist/{eval.js → eval/eval.js} +35 -55
- package/dist/{judgment.d.ts → eval/judgment.d.ts} +1 -6
- package/dist/{judgment.js → eval/judgment.js} +23 -20
- package/dist/extensions/openclaw-plugin/index.js +2 -3
- package/dist/graph/delta.d.ts +3 -2
- package/dist/graph/delta.js +9 -23
- package/dist/{graph-extract.d.ts → graph/extract.d.ts} +10 -32
- package/dist/{graph-extract.js → graph/extract.js} +40 -81
- package/dist/{graph-recall.d.ts → graph/recall.d.ts} +9 -22
- package/dist/{graph-recall.js → graph/recall.js} +65 -108
- package/dist/graph/stream.d.ts +38 -0
- package/dist/{graph-stream.js → graph/stream.js} +20 -41
- package/dist/graph/view.d.ts +50 -0
- package/dist/graph/view.js +265 -0
- package/dist/graph/write.d.ts +4 -76
- package/dist/graph/write.js +13 -397
- package/dist/hooks/codex-session.js +1 -1
- package/dist/hooks/codex-wrapper.js +16 -17
- package/dist/hooks/copilot.d.ts +1 -8
- package/dist/hooks/copilot.js +16 -16
- package/dist/{cli → hooks}/hook-blocks.d.ts +6 -2
- package/dist/{cli → hooks}/hook-blocks.js +38 -125
- package/dist/hooks/json-hooks.d.ts +5 -37
- package/dist/hooks/json-hooks.js +49 -179
- package/dist/hooks/opencode.d.ts +0 -38
- package/dist/hooks/opencode.js +44 -103
- package/dist/hooks/shared.d.ts +8 -21
- package/dist/hooks/shared.js +30 -25
- package/dist/importers/core.d.ts +13 -42
- package/dist/importers/core.js +48 -64
- package/dist/importers/markdown-parse.d.ts +9 -32
- package/dist/importers/markdown-parse.js +10 -44
- package/dist/importers/markdown.js +5 -13
- package/dist/importers/sources.js +57 -81
- package/dist/importers/vault.d.ts +2 -8
- package/dist/importers/vault.js +95 -172
- package/dist/index.d.ts +26 -22
- package/dist/index.js +25 -21
- package/dist/learn/autolearn.d.ts +29 -0
- package/dist/{autolearn.js → learn/autolearn.js} +26 -51
- package/dist/{churn-git.d.ts → learn/churn-git.d.ts} +1 -1
- package/dist/{churn-git.js → learn/churn-git.js} +6 -4
- package/dist/{extract.d.ts → learn/extract.d.ts} +1 -1
- package/dist/learn/extract.js +123 -0
- package/dist/learn/forward-claim-detector.d.ts +12 -0
- package/dist/learn/forward-claim-detector.js +60 -0
- package/dist/{invalidation.d.ts → learn/invalidation.d.ts} +10 -18
- package/dist/{invalidation.js → learn/invalidation.js} +92 -125
- package/dist/mcp/admin-tools.d.ts +1 -1
- package/dist/mcp/admin-tools.js +39 -64
- package/dist/mcp/format.d.ts +5 -5
- package/dist/mcp/format.js +7 -6
- package/dist/mcp/framing.d.ts +6 -0
- package/dist/mcp/framing.js +11 -3
- package/dist/mcp/memory-tools.js +10 -35
- package/dist/mcp/protocol.d.ts +14 -36
- package/dist/mcp/protocol.js +10 -19
- package/dist/mcp/recall-tools.d.ts +2 -2
- package/dist/mcp/recall-tools.js +70 -52
- package/dist/mcp/request.d.ts +3 -11
- package/dist/mcp/request.js +71 -85
- package/dist/mcp/server.d.ts +3 -9
- package/dist/mcp/server.js +6 -17
- package/dist/mcp/session-state.d.ts +2 -3
- package/dist/mcp/session-state.js +7 -13
- package/dist/mcp/shared-gate.js +1 -1
- package/dist/mcp/stdio.d.ts +2 -6
- package/dist/mcp/stdio.js +112 -38
- package/dist/mcp/tool-args.js +2 -1
- package/dist/mcp/tools.js +7 -5
- package/dist/objects/customer-notes.d.ts +44 -0
- package/dist/objects/customer-notes.js +71 -0
- package/dist/objects/decisions.d.ts +41 -0
- package/dist/objects/decisions.js +63 -0
- package/dist/objects/descriptor.d.ts +52 -0
- package/dist/objects/descriptor.js +4 -0
- package/dist/objects/fields.d.ts +15 -0
- package/dist/objects/fields.js +21 -0
- package/dist/objects/incidents.d.ts +45 -0
- package/dist/objects/incidents.js +96 -0
- package/dist/objects/lifecycle.d.ts +27 -0
- package/dist/objects/lifecycle.js +96 -0
- package/dist/objects/policies.d.ts +60 -0
- package/dist/objects/policies.js +102 -0
- package/dist/objects/processes.d.ts +43 -0
- package/dist/objects/processes.js +92 -0
- package/dist/objects/project-briefs.d.ts +63 -0
- package/dist/objects/project-briefs.js +194 -0
- package/dist/objects/skills.d.ts +51 -0
- package/dist/objects/skills.js +110 -0
- package/dist/postinstall.js +10 -21
- package/dist/predictions/planning-fallacy.d.ts +14 -109
- package/dist/predictions/planning-fallacy.js +4 -129
- package/dist/rerankers/clef.d.ts +2 -6
- package/dist/rerankers/clef.js +28 -76
- package/dist/rerankers/cross-encoder.d.ts +0 -7
- package/dist/rerankers/cross-encoder.js +18 -28
- package/dist/rerankers/index.js +8 -7
- package/dist/rerankers/jev.d.ts +1 -4
- package/dist/rerankers/jev.js +36 -52
- package/dist/rerankers/llm.d.ts +2 -13
- package/dist/rerankers/llm.js +42 -59
- package/dist/rerankers/outage-warning.d.ts +8 -0
- package/dist/rerankers/outage-warning.js +32 -0
- package/dist/rerankers/remote.d.ts +4 -0
- package/dist/rerankers/remote.js +25 -0
- package/dist/rerankers/types.d.ts +3 -15
- package/dist/search/as-of.d.ts +1 -1
- package/dist/search/bm25-search.d.ts +2 -2
- package/dist/search/bm25-search.js +7 -7
- package/dist/search/bm25.d.ts +1 -1
- package/dist/search/bm25.js +1 -1
- package/dist/search/boosts.d.ts +5 -3
- package/dist/search/boosts.js +36 -15
- package/dist/search/breakdown.d.ts +2 -2
- package/dist/search/breakdown.js +2 -1
- package/dist/search/explain.d.ts +1 -1
- package/dist/search/explain.js +3 -2
- package/dist/search/finalize.d.ts +2 -2
- package/dist/search/finalize.js +4 -3
- package/dist/search/fusion.d.ts +1 -1
- package/dist/search/fusion.js +3 -3
- package/dist/search/goal-boost.d.ts +23 -0
- package/dist/search/goal-boost.js +90 -0
- package/dist/search/hybrid-score.d.ts +2 -2
- package/dist/search/hybrid-score.js +1 -1
- package/dist/search/hybrid.d.ts +6 -4
- package/dist/search/hybrid.js +11 -8
- package/dist/{multihop.d.ts → search/multihop.d.ts} +2 -2
- package/dist/{multihop.js → search/multihop.js} +9 -8
- package/dist/search/path-context.d.ts +11 -0
- package/dist/{path-context.js → search/path-context.js} +8 -19
- package/dist/search/physics-search.d.ts +4 -4
- package/dist/search/physics-search.js +25 -22
- package/dist/search/rerank.d.ts +3 -3
- package/dist/search/rerank.js +4 -3
- package/dist/search/rrf.d.ts +15 -0
- package/dist/search/rrf.js +36 -0
- package/dist/search/temporal.d.ts +1 -1
- package/dist/search/temporal.js +5 -2
- package/dist/search/vector.d.ts +10 -3
- package/dist/search/vector.js +51 -15
- package/dist/server/auth.d.ts +10 -29
- package/dist/server/auth.js +57 -42
- package/dist/server/boot.d.ts +5 -0
- package/dist/server/boot.js +432 -0
- package/dist/server/client-ip.d.ts +8 -20
- package/dist/server/client-ip.js +23 -34
- package/dist/server/cursor.d.ts +1 -1
- package/dist/server/cursor.js +2 -3
- package/dist/server/deadline.d.ts +13 -0
- package/dist/server/deadline.js +45 -0
- package/dist/server/defaults.d.ts +3 -0
- package/dist/server/defaults.js +4 -0
- package/dist/server/key-check-bounds.d.ts +9 -0
- package/dist/server/key-check-bounds.js +46 -0
- package/dist/server/lifecycle.d.ts +5 -4
- package/dist/server/lifecycle.js +20 -5
- package/dist/server/mcp-http.d.ts +1 -4
- package/dist/server/mcp-http.js +44 -68
- package/dist/server/rate-limit.d.ts +21 -0
- package/dist/{rate-limit.js → server/rate-limit.js} +4 -13
- package/dist/server/ready.d.ts +7 -0
- package/dist/server/ready.js +38 -0
- package/dist/server/request.d.ts +14 -12
- package/dist/server/request.js +71 -28
- package/dist/server/route-table.d.ts +15 -0
- package/dist/server/route-table.js +202 -0
- package/dist/server/routes/admin.js +29 -30
- package/dist/server/routes/customer-notes.d.ts +5 -5
- package/dist/server/routes/customer-notes.js +35 -105
- package/dist/server/routes/decisions.d.ts +5 -5
- package/dist/server/routes/decisions.js +45 -115
- package/dist/server/routes/incidents.d.ts +5 -5
- package/dist/server/routes/incidents.js +44 -105
- package/dist/server/routes/memories.js +21 -43
- package/dist/server/routes/object-routes.d.ts +35 -0
- package/dist/server/routes/object-routes.js +72 -0
- package/dist/server/routes/policies.d.ts +6 -6
- package/dist/server/routes/policies.js +42 -134
- package/dist/server/routes/predictions.js +35 -63
- package/dist/server/routes/processes.d.ts +5 -5
- package/dist/server/routes/processes.js +44 -134
- package/dist/server/routes/project-briefs.d.ts +6 -6
- package/dist/server/routes/project-briefs.js +41 -120
- package/dist/server/routes/recall.js +21 -44
- package/dist/server/routes/skills.d.ts +6 -6
- package/dist/server/routes/skills.js +40 -138
- package/dist/server/server-detect.d.ts +28 -0
- package/dist/server/server-detect.js +156 -0
- package/dist/server/sleep-child.d.ts +2 -0
- package/dist/server/sleep-child.js +19 -0
- package/dist/server/sleep-offload.d.ts +20 -0
- package/dist/server/sleep-offload.js +73 -0
- package/dist/server/tls.d.ts +6 -0
- package/dist/server/tls.js +24 -0
- package/dist/server/types.d.ts +35 -14
- package/dist/server/validation.d.ts +6 -5
- package/dist/server/validation.js +16 -34
- package/dist/server.d.ts +39 -61
- package/dist/server.js +26 -569
- package/dist/sharing/global-store.d.ts +19 -0
- package/dist/sharing/global-store.js +75 -0
- package/dist/sharing/global-sync.d.ts +6 -0
- package/dist/sharing/global-sync.js +80 -0
- package/dist/{project-merge.d.ts → sharing/project-merge.d.ts} +5 -6
- package/dist/{project-merge.js → sharing/project-merge.js} +34 -51
- package/dist/sharing/scope.d.ts +7 -0
- package/dist/{scope.js → sharing/scope.js} +5 -12
- package/dist/sharing/search-both.d.ts +60 -0
- package/dist/sharing/search-both.js +138 -0
- package/dist/sharing/share.d.ts +38 -0
- package/dist/sharing/share.js +226 -0
- package/dist/{ambient-store.d.ts → store/ambient.d.ts} +2 -2
- package/dist/{ambient-store.js → store/ambient.js} +11 -16
- package/dist/store/audit-event.d.ts +15 -17
- package/dist/store/audit-event.js +9 -16
- package/dist/{audit.d.ts → store/audit.d.ts} +29 -8
- package/dist/{audit.js → store/audit.js} +95 -18
- package/dist/{auth.d.ts → store/auth.d.ts} +19 -36
- package/dist/{auth.js → store/auth.js} +108 -99
- package/dist/store/candidates.d.ts +41 -13
- package/dist/store/candidates.js +141 -60
- package/dist/{card-detail.d.ts → store/card-detail.d.ts} +5 -4
- package/dist/{card-detail.js → store/card-detail.js} +3 -2
- package/dist/{store-cards.d.ts → store/cards.d.ts} +26 -16
- package/dist/{store-cards.js → store/cards.js} +133 -248
- package/dist/store/compactions.d.ts +78 -0
- package/dist/store/compactions.js +93 -0
- package/dist/store/conflicts.d.ts +26 -26
- package/dist/store/conflicts.js +194 -222
- package/dist/store/connectors/github.d.ts +84 -0
- package/dist/store/connectors/github.js +180 -0
- package/dist/store/connectors/slack.d.ts +69 -0
- package/dist/store/connectors/slack.js +177 -0
- package/dist/store/dashboard-reads.d.ts +27 -0
- package/dist/store/dashboard-reads.js +88 -0
- package/dist/store/delete-and-batch.d.ts +27 -51
- package/dist/store/delete-and-batch.js +100 -92
- package/dist/{delivery-recorder.d.ts → store/delivery-recorder.d.ts} +16 -5
- package/dist/{delivery-recorder.js → store/delivery-recorder.js} +60 -38
- package/dist/store/descendant-page.d.ts +12 -0
- package/dist/store/descendant-page.js +99 -0
- package/dist/store/diagnostics.d.ts +47 -0
- package/dist/store/diagnostics.js +44 -0
- package/dist/store/doctor-reads.d.ts +16 -0
- package/dist/store/doctor-reads.js +19 -0
- package/dist/store/dormant.d.ts +105 -0
- package/dist/store/dormant.js +241 -0
- package/dist/store/embeddings/index.d.ts +36 -0
- package/dist/{embeddings.js → store/embeddings/index.js} +104 -224
- package/dist/{local-embedding.d.ts → store/embeddings/local.d.ts} +1 -1
- package/dist/{local-embedding.js → store/embeddings/local.js} +69 -75
- package/dist/store/embeddings/provider.d.ts +40 -0
- package/dist/{embedding-provider.js → store/embeddings/provider.js} +78 -91
- package/dist/store/entry-reads.d.ts +66 -63
- package/dist/store/entry-reads.js +159 -121
- package/dist/store/entry-row.d.ts +14 -37
- package/dist/store/entry-row.js +37 -45
- package/dist/store/entry-writes.d.ts +40 -17
- package/dist/store/entry-writes.js +105 -41
- package/dist/{failure-log.d.ts → store/failure-log.d.ts} +7 -2
- package/dist/{failure-log.js → store/failure-log.js} +9 -2
- package/dist/store/goals.d.ts +96 -0
- package/dist/store/goals.js +260 -0
- package/dist/store/graph-queue.d.ts +13 -0
- package/dist/store/graph-queue.js +70 -0
- package/dist/store/graph-reads.d.ts +41 -0
- package/dist/{graph/read.js → store/graph-reads.js} +72 -162
- package/dist/{graph/types.d.ts → store/graph-rows.d.ts} +71 -14
- package/dist/{graph/rows.js → store/graph-rows.js} +20 -12
- package/dist/store/graph-view-rows.d.ts +13 -0
- package/dist/store/graph-view-rows.js +68 -0
- package/dist/store/graph-writes.d.ts +33 -0
- package/dist/store/graph-writes.js +232 -0
- package/dist/store/half-life.d.ts +35 -0
- package/dist/store/half-life.js +75 -0
- package/dist/store/handoffs.d.ts +6 -12
- package/dist/store/handoffs.js +54 -63
- package/dist/store/index-and-stats.d.ts +14 -20
- package/dist/store/index-and-stats.js +74 -119
- package/dist/store/index.d.ts +4 -0
- package/dist/store/index.js +3 -0
- package/dist/store/key-audit.d.ts +2 -2
- package/dist/store/key-audit.js +4 -4
- package/dist/store/key-writes.d.ts +12 -5
- package/dist/store/key-writes.js +30 -4
- package/dist/store/ledger-turn.d.ts +17 -0
- package/dist/store/ledger-turn.js +27 -0
- package/dist/store/markdown.d.ts +3 -7
- package/dist/store/markdown.js +25 -23
- package/dist/store/mirrors.d.ts +7 -13
- package/dist/store/mirrors.js +52 -47
- package/dist/store/object-types.d.ts +189 -0
- package/dist/store/object-types.js +4 -0
- package/dist/store/open.d.ts +6 -4
- package/dist/store/open.js +30 -36
- package/dist/store/planning-fallacy-evidence.d.ts +14 -0
- package/dist/store/planning-fallacy-evidence.js +52 -0
- package/dist/store/port.d.ts +649 -0
- package/dist/store/port.js +17 -0
- package/dist/{predictions/store.d.ts → store/predictions.d.ts} +25 -52
- package/dist/{predictions/store.js → store/predictions.js} +88 -184
- package/dist/{quality-repair.js → store/quality-repair.js} +27 -57
- package/dist/{quarantine.d.ts → store/quarantine.d.ts} +8 -8
- package/dist/{quarantine.js → store/quarantine.js} +64 -22
- package/dist/store/raw-archive.d.ts +14 -0
- package/dist/{raw-archive.js → store/raw-archive.js} +20 -31
- package/dist/store/recall-scope.d.ts +78 -0
- package/dist/{recall-scope.js → store/recall-scope.js} +23 -84
- package/dist/{recall-trace.d.ts → store/recall-trace.d.ts} +13 -81
- package/dist/{recall-trace.js → store/recall-trace.js} +41 -122
- package/dist/store/rejected-values.d.ts +55 -0
- package/dist/store/rejected-values.js +150 -0
- package/dist/store/rejection.d.ts +54 -0
- package/dist/{rejection.js → store/rejection.js} +16 -67
- package/dist/store/report-reads.d.ts +6 -0
- package/dist/store/report-reads.js +29 -0
- package/dist/store/rows.d.ts +11 -22
- package/dist/store/rows.js +52 -43
- package/dist/store/rule-sql.d.ts +5 -0
- package/dist/store/rule-sql.js +37 -0
- package/dist/store/search-rows.d.ts +22 -42
- package/dist/store/search-rows.js +55 -78
- package/dist/store/sessions.d.ts +23 -49
- package/dist/store/sessions.js +96 -118
- package/dist/store/sqlite/commit-watch.d.ts +4 -0
- package/dist/store/sqlite/commit-watch.js +29 -0
- package/dist/store/sqlite/connector-events-group.d.ts +5 -0
- package/dist/store/sqlite/connector-events-group.js +50 -0
- package/dist/store/sqlite/connector-writes-group.d.ts +6 -0
- package/dist/store/sqlite/connector-writes-group.js +65 -0
- package/dist/store/sqlite/dag-reads-group.d.ts +3 -0
- package/dist/store/sqlite/dag-reads-group.js +45 -0
- package/dist/store/sqlite/entry-writes-group.d.ts +18 -0
- package/dist/store/{entry-writes-group.js → sqlite/entry-writes-group.js} +73 -60
- package/dist/store/sqlite/error-codec.d.ts +17 -0
- package/dist/store/sqlite/error-codec.js +61 -0
- package/dist/store/sqlite/executor-counts.d.ts +10 -0
- package/dist/store/sqlite/executor-counts.js +11 -0
- package/dist/store/sqlite/executor.d.ts +29 -0
- package/dist/store/sqlite/executor.js +287 -0
- package/dist/store/sqlite/graph-reads-group.d.ts +5 -0
- package/dist/store/sqlite/graph-reads-group.js +18 -0
- package/dist/store/sqlite/local.d.ts +21 -0
- package/dist/store/sqlite/local.js +39 -0
- package/dist/store/sqlite/object-rows.d.ts +102 -0
- package/dist/store/sqlite/object-rows.js +221 -0
- package/dist/store/sqlite/objects-group.d.ts +20 -0
- package/dist/store/sqlite/objects-group.js +316 -0
- package/dist/store/sqlite/predictions-group.d.ts +5 -0
- package/dist/store/sqlite/predictions-group.js +28 -0
- package/dist/store/sqlite/quarantine-group.d.ts +5 -0
- package/dist/store/sqlite/quarantine-group.js +38 -0
- package/dist/store/sqlite/store.d.ts +23 -0
- package/dist/store/sqlite/store.js +294 -0
- package/dist/store/sqlite/vector-pack.d.ts +18 -0
- package/dist/store/sqlite/vector-pack.js +46 -0
- package/dist/store/sqlite/vector-reads-group.d.ts +3 -0
- package/dist/store/sqlite/vector-reads-group.js +23 -0
- package/dist/store/sqlite/worker-entry.d.ts +2 -0
- package/dist/store/sqlite/worker-entry.js +8 -0
- package/dist/store/sqlite/worker-jobs.d.ts +5 -0
- package/dist/store/sqlite/worker-jobs.js +116 -0
- package/dist/store/sqlite/worker-ops.d.ts +135 -0
- package/dist/store/sqlite/worker-ops.js +93 -0
- package/dist/store/sqlite/worker-store.d.ts +8 -0
- package/dist/store/sqlite/worker-store.js +79 -0
- package/dist/store/summaries.d.ts +11 -72
- package/dist/store/summaries.js +47 -186
- package/dist/{summary-dirty.d.ts → store/summary-dirty.d.ts} +1 -1
- package/dist/{summary-dirty.js → store/summary-dirty.js} +1 -1
- package/dist/store/tenant-lookup.d.ts +6 -1
- package/dist/store/tenant-lookup.js +7 -0
- package/dist/store/tenant.d.ts +11 -0
- package/dist/store/tenant.js +28 -0
- package/dist/store/token-ledger-rows.d.ts +61 -0
- package/dist/store/token-ledger-rows.js +68 -0
- package/dist/{token-ledger.d.ts → store/token-ledger.d.ts} +14 -51
- package/dist/{token-ledger.js → store/token-ledger.js} +51 -129
- package/dist/store/tombstone-checks.d.ts +12 -0
- package/dist/store/tombstone-checks.js +31 -0
- package/dist/store/usage-reports.d.ts +7 -0
- package/dist/store/usage-reports.js +18 -0
- package/dist/store/vector-index.d.ts +46 -0
- package/dist/store/vector-index.js +170 -0
- package/dist/{working-memory.d.ts → store/working-memory.d.ts} +11 -26
- package/dist/store/working-memory.js +118 -0
- package/dist/{yaml.d.ts → store/yaml.d.ts} +1 -5
- package/dist/{yaml.js → store/yaml.js} +2 -6
- package/dist/support-bundle.d.ts +1 -1
- package/dist/support-bundle.js +19 -37
- package/dist/{gated-write.d.ts → trust/gated-write.d.ts} +2 -2
- package/dist/{gated-write.js → trust/gated-write.js} +6 -6
- package/dist/trust/quarantine.d.ts +4 -0
- package/dist/trust/quarantine.js +8 -0
- package/dist/trust/reject-flow.d.ts +23 -0
- package/dist/trust/reject-flow.js +65 -0
- package/dist/util/anthropic-messages.d.ts +31 -0
- package/dist/util/anthropic-messages.js +58 -0
- package/dist/util/atomic-write.d.ts +4 -0
- package/dist/util/atomic-write.js +138 -0
- package/dist/util/capped-json.d.ts +6 -0
- package/dist/util/capped-json.js +46 -0
- package/dist/util/crash-handlers.d.ts +12 -0
- package/dist/util/crash-handlers.js +72 -0
- package/dist/{env.d.ts → util/env.d.ts} +27 -7
- package/dist/{env.js → util/env.js} +33 -8
- package/dist/{home-path.d.ts → util/home-path.d.ts} +2 -1
- package/dist/util/home-path.js +69 -0
- package/dist/{http-retry.d.ts → util/http-retry.d.ts} +8 -2
- package/dist/util/http-retry.js +103 -0
- package/dist/{http-util.d.ts → util/http-util.d.ts} +14 -3
- package/dist/{http-util.js → util/http-util.js} +31 -11
- package/dist/util/json.d.ts +17 -0
- package/dist/util/json.js +25 -0
- package/dist/{log.d.ts → util/log.d.ts} +7 -3
- package/dist/{log.js → util/log.js} +36 -11
- package/dist/util/real-path.d.ts +3 -0
- package/dist/util/real-path.js +15 -0
- package/dist/util/request-scope.d.ts +28 -0
- package/dist/util/request-scope.js +48 -0
- package/dist/{same-text.d.ts → util/same-text.d.ts} +1 -1
- package/dist/{secret-detect.d.ts → util/secret-detect.d.ts} +5 -30
- package/dist/{secret-detect.js → util/secret-detect.js} +17 -42
- package/dist/util/stored-json.d.ts +9 -0
- package/dist/util/stored-json.js +8 -0
- package/dist/util/time.d.ts +2 -0
- package/dist/util/time.js +3 -0
- package/dist/util/token-text.d.ts +15 -0
- package/dist/util/token-text.js +20 -0
- package/dist/{transcript-tail.d.ts → util/transcript-tail.d.ts} +2 -0
- package/dist/util/transcript-tail.js +49 -0
- package/dist/util/version.d.ts +11 -0
- package/dist/util/version.js +26 -0
- package/extensions/openclaw-plugin/index.ts +2 -6
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/dist/ablation.d.ts +0 -102
- package/dist/ablation.js +0 -136
- package/dist/api.d.ts +0 -26
- package/dist/api.js +0 -29
- package/dist/audit-prune.d.ts +0 -62
- package/dist/audit-prune.js +0 -107
- package/dist/autolearn.d.ts +0 -56
- package/dist/client.d.ts +0 -48
- package/dist/compare.d.ts +0 -86
- package/dist/compare.js +0 -117
- package/dist/connectors/github/cli-impl.d.ts +0 -25
- package/dist/connectors/github/cli-impl.js +0 -227
- package/dist/connectors/github/idempotency.d.ts +0 -19
- package/dist/connectors/github/idempotency.js +0 -27
- package/dist/connectors/slack/idempotency.d.ts +0 -17
- package/dist/connectors/slack/idempotency.js +0 -31
- package/dist/correction-latency.d.ts +0 -36
- package/dist/customer-notes.d.ts +0 -96
- package/dist/customer-notes.js +0 -336
- package/dist/db.d.ts +0 -9
- package/dist/db.js +0 -8
- package/dist/decisions.d.ts +0 -94
- package/dist/decisions.js +0 -326
- package/dist/dedupe.d.ts +0 -75
- package/dist/dedupe.js +0 -169
- package/dist/dormant.d.ts +0 -93
- package/dist/dormant.js +0 -137
- package/dist/embedding-provider.d.ts +0 -77
- package/dist/embeddings.d.ts +0 -101
- package/dist/extract.js +0 -133
- package/dist/forward-claim-detector.d.ts +0 -36
- package/dist/forward-claim-detector.js +0 -112
- package/dist/goals.d.ts +0 -173
- package/dist/goals.js +0 -448
- package/dist/graph/read.d.ts +0 -73
- package/dist/graph/rows.d.ts +0 -45
- package/dist/graph/types.js +0 -11
- package/dist/graph-stream.d.ts +0 -70
- package/dist/graph-view.d.ts +0 -73
- package/dist/graph-view.js +0 -333
- package/dist/half-life-migration.d.ts +0 -63
- package/dist/half-life-migration.js +0 -182
- package/dist/handoff.js +0 -68
- package/dist/home-path.js +0 -24
- package/dist/http-retry.js +0 -51
- package/dist/incidents.d.ts +0 -103
- package/dist/incidents.js +0 -349
- package/dist/instruction-detect.js +0 -27
- package/dist/json.d.ts +0 -7
- package/dist/json.js +0 -11
- package/dist/ledger-db.d.ts +0 -10
- package/dist/ledger-db.js +0 -42
- package/dist/memory-value-weights.d.ts +0 -19
- package/dist/memory-value.d.ts +0 -113
- package/dist/memory-value.js +0 -250
- package/dist/memory.d.ts +0 -294
- package/dist/memory.js +0 -522
- package/dist/owner-validation.d.ts +0 -42
- package/dist/owner-validation.js +0 -56
- package/dist/path-context.d.ts +0 -24
- package/dist/physics-state.d.ts +0 -39
- package/dist/policies.d.ts +0 -151
- package/dist/policies.js +0 -434
- package/dist/processes.d.ts +0 -108
- package/dist/processes.js +0 -371
- package/dist/project-briefs.d.ts +0 -128
- package/dist/project-briefs.js +0 -523
- package/dist/provenance-coverage.d.ts +0 -18
- package/dist/rate-limit.d.ts +0 -34
- package/dist/raw-archive-mirror-cleanup.d.ts +0 -20
- package/dist/raw-archive.d.ts +0 -24
- package/dist/recall-history.d.ts +0 -131
- package/dist/recall-history.js +0 -227
- package/dist/recall-scope.d.ts +0 -138
- package/dist/refine-llm.d.ts +0 -61
- package/dist/refine-llm.js +0 -167
- package/dist/reject-flow.d.ts +0 -69
- package/dist/reject-flow.js +0 -260
- package/dist/rejection.d.ts +0 -100
- package/dist/replay.d.ts +0 -41
- package/dist/replay.js +0 -122
- package/dist/rrf.d.ts +0 -43
- package/dist/rrf.js +0 -61
- package/dist/scope.d.ts +0 -14
- package/dist/server-detect.d.ts +0 -72
- package/dist/server-detect.js +0 -222
- package/dist/session-owners.d.ts +0 -7
- package/dist/session-owners.js +0 -54
- package/dist/shared.d.ts +0 -186
- package/dist/shared.js +0 -540
- package/dist/skills.d.ts +0 -101
- package/dist/skills.js +0 -367
- package/dist/store/entry-writes-group.d.ts +0 -9
- package/dist/store/vector-writes.d.ts +0 -6
- package/dist/store/vector-writes.js +0 -59
- package/dist/store-port.d.ts +0 -262
- package/dist/store-port.js +0 -178
- package/dist/tenant.d.ts +0 -25
- package/dist/tenant.js +0 -43
- package/dist/trace.d.ts +0 -34
- package/dist/transcript-tail.js +0 -48
- package/dist/version.d.ts +0 -26
- package/dist/version.js +0 -41
- package/dist/working-memory.js +0 -154
- /package/dist/{context-auto.d.ts → api/context-auto.d.ts} +0 -0
- /package/dist/{instruction-detect.d.ts → api/instruction-detect.d.ts} +0 -0
- /package/dist/{codex-patch.d.ts → capture/codex-patch.d.ts} +0 -0
- /package/dist/{compaction-items.d.ts → capture/compaction-items.d.ts} +0 -0
- /package/dist/{share-scrub.d.ts → capture/share-scrub.d.ts} +0 -0
- /package/dist/{overlap-index.d.ts → consolidate/overlap-index.d.ts} +0 -0
- /package/dist/{overlap-index.js → consolidate/overlap-index.js} +0 -0
- /package/dist/{api-errors.d.ts → core/api-errors.d.ts} +0 -0
- /package/dist/{api-errors.js → core/api-errors.js} +0 -0
- /package/dist/{memory-quality.d.ts → core/memory-quality.d.ts} +0 -0
- /package/dist/{project-remote.d.ts → core/project-remote.d.ts} +0 -0
- /package/dist/{prompt-recall.d.ts → core/prompt-recall.d.ts} +0 -0
- /package/dist/{dashboard-params.d.ts → dashboard/dashboard-params.d.ts} +0 -0
- /package/dist/{dashboard-params.js → dashboard/dashboard-params.js} +0 -0
- /package/dist/{dashboard-types.js → dashboard/dashboard-types.js} +0 -0
- /package/dist/{quality-repair.d.ts → store/quality-repair.d.ts} +0 -0
- /package/dist/{escape.d.ts → util/escape.d.ts} +0 -0
- /package/dist/{escape.js → util/escape.js} +0 -0
- /package/dist/{keyset.d.ts → util/keyset.d.ts} +0 -0
- /package/dist/{keyset.js → util/keyset.js} +0 -0
- /package/dist/{same-text.js → util/same-text.js} +0 -0
- /package/dist/{db → util}/sqlite-blocked.d.ts +0 -0
- /package/dist/{db → util}/sqlite-blocked.js +0 -0
- /package/dist/{tokenize.d.ts → util/tokenize.d.ts} +0 -0
- /package/dist/{tokenize.js → util/tokenize.js} +0 -0
- /package/dist/{write-budget.d.ts → util/write-budget.d.ts} +0 -0
- /package/dist/{write-budget.js → util/write-budget.js} +0 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import { envPort, envRequireAuth, envV1Rps } from '../util/env.js';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { detectServer, removePidfileIfOwned, writePidfile } from './server-detect.js';
|
|
4
|
+
import { closeHippoDb, getHippoDbPath, isStoreBusy, openHippoDb, outsideRequestStores, outsideSqliteOffLoop, runWithRequestStores, SERVER_DB_WAIT_MS, withSqliteBlocked } from '../db/index.js';
|
|
5
|
+
import { startWalCheckpointer } from '../db/wal-checkpointer.js';
|
|
6
|
+
import { requireGroup } from '../store/index.js';
|
|
7
|
+
import { workerSqliteStore } from '../store/sqlite/worker-store.js';
|
|
8
|
+
import { markSharedStore } from '../core/config.js';
|
|
9
|
+
import { auditWriteFailureCount } from '../store/audit.js';
|
|
10
|
+
import { PACKAGE_VERSION } from '../util/version.js';
|
|
11
|
+
import { errorFields, errorMessage, log } from '../util/log.js';
|
|
12
|
+
import { runWithRequestId } from '../util/request-scope.js';
|
|
13
|
+
import { createRateLimiter } from './rate-limit.js';
|
|
14
|
+
import { RecallContractError } from '../api/index.js';
|
|
15
|
+
import { handleSlackEventsWebhook } from '../connectors/slack/webhook.js';
|
|
16
|
+
import { handleGitHubEventsWebhook } from '../connectors/github/webhook.js';
|
|
17
|
+
import { BodyTimeoutError, BodyTooLargeError, closeAfterReply, DeadlineExceededError, HttpError, JSON_HEADERS, sendJson } from '../util/http-util.js';
|
|
18
|
+
import { workerCounts } from '../store/sqlite/executor-counts.js';
|
|
19
|
+
import { isLocalCaller, LIMITER_MAX_KEYS } from './auth.js';
|
|
20
|
+
import { enforceRateLimit, warnIfClientIpHeaderUnpinned } from './client-ip.js';
|
|
21
|
+
import { answerAtDeadline, handlerDeadlineCount, isAbandoned, requestDeadlineFor } from './deadline.js';
|
|
22
|
+
import { DEFAULT_SHUTDOWN_DRAIN_MS, drainAndClose, setKeepAliveTimeouts, shutdownBoundMs } from './lifecycle.js';
|
|
23
|
+
import { readyProbeFor } from './ready.js';
|
|
24
|
+
import { DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT } from './defaults.js';
|
|
25
|
+
import { installCrashHandlers, installSignalHandlers } from '../util/crash-handlers.js';
|
|
26
|
+
import { handleMcpPost, handleMcpStream } from './mcp-http.js';
|
|
27
|
+
import { MCP_PROJECT_SCOPED_HEADER } from '../core/project-identity.js';
|
|
28
|
+
import { accessRouteOf, logRequestFailure, noteAccess, openRequest, parseRequest, rejectEncodedSlash, replyFor, sendError } from './request.js';
|
|
29
|
+
import { createListener, warnIfCleartext } from './tls.js';
|
|
30
|
+
import { assertAddonRoutes, assertPublicJson, dispatchAddonRoute, dispatchPublicJson, dispatchV1Route, isPublicRoute } from './route-table.js';
|
|
31
|
+
// server.address() returns AddressInfo once a TCP socket is bound; null before
|
|
32
|
+
// listening, a string only for pipe/unix-socket listeners (never used here).
|
|
33
|
+
function isAddressInfo(a) {
|
|
34
|
+
return a !== null && typeof a !== 'string';
|
|
35
|
+
}
|
|
36
|
+
// Pinned at module load, bumped with package.json on releases; reading package.json synchronously here would couple the daemon to its install path,
|
|
37
|
+
// which tests that mkdtemp a hippoRoot avoid.
|
|
38
|
+
const VERSION = PACKAGE_VERSION;
|
|
39
|
+
const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);
|
|
40
|
+
async function handleRequest(req, res, opts, options) {
|
|
41
|
+
const { startedAt, streamSlots, limiter } = options;
|
|
42
|
+
// Pre-decode raw-URL slash check. Catches `%2F` / `%2f` before
|
|
43
|
+
// Node's URL parser collapses them and they slip past the route table.
|
|
44
|
+
rejectEncodedSlash(req.url ?? '/');
|
|
45
|
+
const { method, path, query } = parseRequest(req);
|
|
46
|
+
// An older core ignores X-Hippo-Project, so every /mcp reply, a 429 or 401 too, tells the client this one filters by it.
|
|
47
|
+
if (path === '/mcp')
|
|
48
|
+
res.setHeader(MCP_PROJECT_SCOPED_HEADER, '1');
|
|
49
|
+
if (method === 'GET' && path === '/health') {
|
|
50
|
+
noteAccess(req, { route: path });
|
|
51
|
+
sendHealth(req, res, startedAt);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (method === 'GET' && path === '/ready') {
|
|
55
|
+
noteAccess(req, { route: path });
|
|
56
|
+
await sendReady(res, opts.store);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
enforceRateLimit(req, path, limiter);
|
|
60
|
+
const routeRequest = { req, res, opts, query };
|
|
61
|
+
if (await runWithRequestStores(() => dispatchScopedRoute(routeRequest, method, path), { busyWaitMs: SERVER_DB_WAIT_MS }))
|
|
62
|
+
return;
|
|
63
|
+
// A scope of its own, which the heartbeat timer keeps after it closes, so the key check and every
|
|
64
|
+
// heartbeat wait the server's lock wait. Store-ready: the stream only authenticates, through the port.
|
|
65
|
+
if (method === 'GET' && path === '/mcp/stream') {
|
|
66
|
+
noteAccess(req, { route: path });
|
|
67
|
+
await runWithRequestStores(() => handleMcpStream(req, res, opts, streamSlots), { busyWaitMs: SERVER_DB_WAIT_MS });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
res.writeHead(404, JSON_HEADERS);
|
|
71
|
+
res.end(JSON.stringify({ error: 'not found' }));
|
|
72
|
+
}
|
|
73
|
+
const WEBHOOK_GROUPS = ['entryWrites', 'connectorWrites', 'connectorEvents'];
|
|
74
|
+
/** A store missing a group a delivery writes through refuses the webhook up front, so no delivery is acknowledged and then half stored. */
|
|
75
|
+
function requireWebhookGroups(store) {
|
|
76
|
+
for (const group of WEBHOOK_GROUPS)
|
|
77
|
+
requireGroup(store, group);
|
|
78
|
+
}
|
|
79
|
+
/** Every route that runs inside a request scope, so it opens each store once: the /v1 table, public JSON, add-on routes, the webhooks and POST /mcp. */
|
|
80
|
+
async function dispatchScopedRoute(r, method, path) {
|
|
81
|
+
if (await dispatchV1Route(r, method, path))
|
|
82
|
+
return true;
|
|
83
|
+
if (dispatchPublicJson(r, method, path))
|
|
84
|
+
return true;
|
|
85
|
+
if (await dispatchAddonRoute(r, method, path))
|
|
86
|
+
return true;
|
|
87
|
+
const { req, res, opts } = r;
|
|
88
|
+
if (method === 'POST' && path === '/v1/connectors/slack/events') {
|
|
89
|
+
noteAccess(req, { route: path });
|
|
90
|
+
// Bearer auth deliberately skipped: this route is in PUBLIC_ROUTES and authenticates via the Slack HMAC signature.
|
|
91
|
+
if (!isPublicRoute(method, path)) {
|
|
92
|
+
// Defensive: PUBLIC_ROUTES drift would land here. Fail closed.
|
|
93
|
+
throw new HttpError(401, 'auth required');
|
|
94
|
+
}
|
|
95
|
+
requireWebhookGroups(opts.store);
|
|
96
|
+
await handleSlackEventsWebhook({ req, res, opts }, opts.store);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (method === 'POST' && path === '/v1/connectors/github/events') {
|
|
100
|
+
noteAccess(req, { route: path });
|
|
101
|
+
if (!isPublicRoute(method, path)) {
|
|
102
|
+
throw new HttpError(401, 'auth required');
|
|
103
|
+
}
|
|
104
|
+
requireWebhookGroups(opts.store);
|
|
105
|
+
await handleGitHubEventsWebhook({ req, res, opts }, opts.store);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
// Store-ready: under another store the MCP layer lists and runs only the tools ported to the port.
|
|
109
|
+
if (method === 'POST' && path === '/mcp') {
|
|
110
|
+
noteAccess(req, { route: path });
|
|
111
|
+
await handleMcpPost(req, res, opts);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
function sendHealth(req, res, startedAt) {
|
|
117
|
+
// A caller on this machine (detectServer's probe reads version and pid) gets the full body; anyone else, even a proxied caller or a browser page on loopback,
|
|
118
|
+
// gets liveness only: the version would fingerprint the build and the pid is noise. Platform health checks only need the 200.
|
|
119
|
+
if (isLocalCaller(req)) {
|
|
120
|
+
sendJson(res, 200, {
|
|
121
|
+
ok: true,
|
|
122
|
+
version: VERSION,
|
|
123
|
+
started_at: startedAt,
|
|
124
|
+
pid: process.pid,
|
|
125
|
+
audit_write_failures: auditWriteFailureCount(),
|
|
126
|
+
store_queue_refusals: workerCounts.queueRefusals,
|
|
127
|
+
store_jobs_expired: workerCounts.jobsExpired,
|
|
128
|
+
store_workers_replaced: workerCounts.workersReplaced,
|
|
129
|
+
handler_deadlines: handlerDeadlineCount(),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
sendJson(res, 200, { ok: true });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** Readiness: one cheap read on the served store under the server's lock wait. /health stays
|
|
137
|
+
* liveness only, so a probe can tell a store that does not answer from a dead process. */
|
|
138
|
+
async function sendReady(res, store) {
|
|
139
|
+
const { readiness } = store;
|
|
140
|
+
if (readiness === undefined) {
|
|
141
|
+
sendJson(res, 200, { ok: true, store: 'unchecked' });
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (!(await readyProbeFor(store, readiness)())) {
|
|
145
|
+
sendJson(res, 503, { ok: false, error: 'store_unavailable' });
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
sendJson(res, 200, { ok: true });
|
|
149
|
+
}
|
|
150
|
+
function assertBindable(host) {
|
|
151
|
+
if (!LOOPBACK_HOSTS.has(host) && !envRequireAuth()) {
|
|
152
|
+
throw new Error(`Refusing to bind hippo serve to non-loopback host '${host}' without auth. ` +
|
|
153
|
+
`Set HIPPO_REQUIRE_AUTH=1 to bind non-loopback; every request then requires ` +
|
|
154
|
+
`a valid API key. Bind to 127.0.0.1 / ::1 / localhost otherwise.`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function assertNoLiveServer(hippoRoot) {
|
|
158
|
+
// Refuse to start if a live hippo server already serves this hippoRoot (detectServer probes the recorded /health and unlinks a stale pidfile):
|
|
159
|
+
// a concurrent `hippo serve` would race for the port and clobber the pidfile.
|
|
160
|
+
const existing = await detectServer(hippoRoot);
|
|
161
|
+
if (existing) {
|
|
162
|
+
throw new Error(`hippo serve: already running on port ${existing.port} (pid ${existing.pid}). ` +
|
|
163
|
+
`Stop that server before starting another on the same hippoRoot.`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// SHORTCUT: buckets live in this process and reset on restart or LRU eviction; move them to the store if serve ever runs as several processes.
|
|
167
|
+
function limiterFor({ ratePerSec, burst }) {
|
|
168
|
+
return createRateLimiter({ ratePerSec, burst, idleEvictMs: 60000, maxKeys: LIMITER_MAX_KEYS });
|
|
169
|
+
}
|
|
170
|
+
function bootRateLimiter(perAddress) {
|
|
171
|
+
if (perAddress === 'off')
|
|
172
|
+
return undefined;
|
|
173
|
+
if (perAddress !== undefined)
|
|
174
|
+
return limiterFor(perAddress);
|
|
175
|
+
// Per-IP rate limiter for /v1/* and /mcp*, built here (not at module scope) so HIPPO_V1_RPS is read at boot and a test can set it before serve().
|
|
176
|
+
// A non-positive or non-finite value disables limiting (the opt-out knob).
|
|
177
|
+
const v1Rps = Number(envV1Rps() ?? 20);
|
|
178
|
+
return Number.isFinite(v1Rps) && v1Rps > 0 ? limiterFor({ ratePerSec: v1Rps, burst: v1Rps * 2 }) : undefined;
|
|
179
|
+
}
|
|
180
|
+
const DEFAULT_FAILED_AUTH = { ratePerSec: 20, burst: 40 };
|
|
181
|
+
function assertRateLimitSpec(name, { ratePerSec, burst }) {
|
|
182
|
+
if (!Number.isFinite(ratePerSec) || ratePerSec <= 0)
|
|
183
|
+
throw new Error(`rateLimits.${name}.ratePerSec must be a finite number above 0`);
|
|
184
|
+
if (!Number.isFinite(burst) || burst < 1)
|
|
185
|
+
throw new Error(`rateLimits.${name}.burst must be a finite number of at least 1`);
|
|
186
|
+
}
|
|
187
|
+
/** Refuses a bad spec at boot, since a NaN or zero rate would otherwise turn a bucket off or refuse every request. */
|
|
188
|
+
function bootLimiters(rateLimits) {
|
|
189
|
+
const { perCaller, perAddress, failedAuthPerAddress = DEFAULT_FAILED_AUTH } = rateLimits ?? {};
|
|
190
|
+
if (perCaller !== undefined)
|
|
191
|
+
assertRateLimitSpec('perCaller', perCaller);
|
|
192
|
+
if (perAddress !== undefined && perAddress !== 'off')
|
|
193
|
+
assertRateLimitSpec('perAddress', perAddress);
|
|
194
|
+
assertRateLimitSpec('failedAuthPerAddress', failedAuthPerAddress);
|
|
195
|
+
warnIfClientIpHeaderUnpinned();
|
|
196
|
+
return {
|
|
197
|
+
perAddress: bootRateLimiter(perAddress),
|
|
198
|
+
callerLimiter: perCaller === undefined ? undefined : limiterFor(perCaller),
|
|
199
|
+
failedAuthLimiter: limiterFor(failedAuthPerAddress),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function createStoreHolder(hippoRoot, store) {
|
|
203
|
+
if (store.kind !== 'sqlite') {
|
|
204
|
+
log.info(`serve: the '${store.kind}' store serves ${hippoRoot}, so no hippo.db connection is held`);
|
|
205
|
+
return { hold: () => { }, afterResponse: () => { }, release: async () => { } };
|
|
206
|
+
}
|
|
207
|
+
// Handlers open and close their own connections; while this one is held, none of those closes is SQLite's last,
|
|
208
|
+
// which checkpoints and deletes the WAL. It opens only once the store exists, so serving never creates one.
|
|
209
|
+
let heldDb;
|
|
210
|
+
let checkpointer;
|
|
211
|
+
let stopHolding = false;
|
|
212
|
+
const hold = () => {
|
|
213
|
+
if (heldDb || stopHolding || !existsSync(getHippoDbPath(hippoRoot)))
|
|
214
|
+
return;
|
|
215
|
+
try {
|
|
216
|
+
// The 'finish' listener can fire inside a request scope (closing this connection with the request) and inside a `loop: 'off'` block (refusing the open).
|
|
217
|
+
// The server's lock wait: this open runs on the event loop, where SQLite's 5 s default and the 30 s journal-mode retry would stall every request.
|
|
218
|
+
heldDb = outsideSqliteOffLoop(() => outsideRequestStores(() => openHippoDb(hippoRoot, { busyWaitMs: SERVER_DB_WAIT_MS })));
|
|
219
|
+
checkpointer = startWalCheckpointer(getHippoDbPath(hippoRoot));
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
stopHolding = true;
|
|
223
|
+
log.warn(`serve: could not hold a store connection; requests still work, only slower: ${errorMessage(err)}`);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const afterResponse = () => {
|
|
227
|
+
hold();
|
|
228
|
+
checkpointer?.noteResponse();
|
|
229
|
+
};
|
|
230
|
+
const release = async () => {
|
|
231
|
+
stopHolding = true;
|
|
232
|
+
// The worker's connection closes first, so the held one is SQLite's last and its close checkpoints and deletes the WAL.
|
|
233
|
+
await checkpointer?.stop();
|
|
234
|
+
if (heldDb)
|
|
235
|
+
closeHippoDb(heldDb);
|
|
236
|
+
heldDb = undefined;
|
|
237
|
+
};
|
|
238
|
+
return { hold, afterResponse, release };
|
|
239
|
+
}
|
|
240
|
+
// A plugin's resolver may read hippo.db through this package's synchronous functions, so it runs outside the block of a `loop: 'off'` route.
|
|
241
|
+
function outsideRouteBlock(resolver) {
|
|
242
|
+
return resolver && ((token) => outsideSqliteOffLoop(() => resolver(token)));
|
|
243
|
+
}
|
|
244
|
+
function replyWithFailure(req, res, err, requestId) {
|
|
245
|
+
const mapped = replyFor(err);
|
|
246
|
+
logRequestFailure(req, err, mapped.status);
|
|
247
|
+
if (res.headersSent) {
|
|
248
|
+
try {
|
|
249
|
+
res.end();
|
|
250
|
+
}
|
|
251
|
+
catch { /* socket already gone */ }
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (isStoreBusy(err))
|
|
255
|
+
res.setHeader('Retry-After', '1');
|
|
256
|
+
else if (err instanceof HttpError && err.retryAfterSec !== undefined)
|
|
257
|
+
res.setHeader('Retry-After', String(err.retryAfterSec));
|
|
258
|
+
if (mapped.status === 500) {
|
|
259
|
+
// The id lets an operator find the logged cause without the client seeing internal text.
|
|
260
|
+
sendJson(res, 500, { error: mapped.message, requestId });
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
// RecallContractError keeps the shared {error} shape and adds `code` so clients branch without parsing prose.
|
|
264
|
+
if (err instanceof RecallContractError) {
|
|
265
|
+
sendJson(res, 400, { error: err.message, code: err.code });
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (err instanceof DeadlineExceededError) {
|
|
269
|
+
sendJson(res, 504, { error: err.message, code: err.code, requestId });
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
// readBody hit its cap or deadline, so close once the 413 or 408 is out rather than drain what the client keeps sending.
|
|
273
|
+
if (err instanceof BodyTooLargeError || err instanceof BodyTimeoutError)
|
|
274
|
+
res.once('finish', () => closeAfterReply(req));
|
|
275
|
+
sendError(res, mapped.status, mapped.message);
|
|
276
|
+
}
|
|
277
|
+
const HANDLER_LATE = 'the request did not finish by its deadline and was abandoned; a write it started may or may not be saved';
|
|
278
|
+
// The caller already has its 504, so this line is the only trace of the failure. The path and the error text can hold caller data, so it carries neither.
|
|
279
|
+
function logLateFailure(req, err, requestId) {
|
|
280
|
+
const fields = { requestId, method: req.method ?? 'GET', route: accessRouteOf(req), failureStatus: replyFor(err).status };
|
|
281
|
+
log.warn('request failed after its deadline reply', { ...fields, errorClass: errorFields(err).errorClass });
|
|
282
|
+
}
|
|
283
|
+
// A handler that ends after the 504 finds the response finished, so sending its own reply throws this: its work ran to the end.
|
|
284
|
+
function isDroppedLateReply(err) {
|
|
285
|
+
return err instanceof Error && 'code' in err && err.code === 'ERR_HTTP_HEADERS_SENT';
|
|
286
|
+
}
|
|
287
|
+
/** A GET only reads; every other method this server routes can write. */
|
|
288
|
+
const isRead = (method) => method === 'GET';
|
|
289
|
+
// A read that ends late changed nothing. A write that ends late ran to its end after its caller was told it may not have been saved.
|
|
290
|
+
function logLateFinish(req, requestId) {
|
|
291
|
+
const method = req.method ?? 'GET';
|
|
292
|
+
if (isRead(method))
|
|
293
|
+
return;
|
|
294
|
+
log.info('request finished after its deadline reply; its own reply was dropped', { requestId, method, route: accessRouteOf(req) });
|
|
295
|
+
}
|
|
296
|
+
function replyOrClose(req, res, err, requestId) {
|
|
297
|
+
try {
|
|
298
|
+
replyWithFailure(req, res, err, requestId);
|
|
299
|
+
}
|
|
300
|
+
catch (replyErr) {
|
|
301
|
+
// A throw here would be an unhandled rejection, which stops the daemon for every caller.
|
|
302
|
+
log.error(`serve: failure reply not sent, socket closed: ${errorMessage(replyErr)}`, errorFields(replyErr));
|
|
303
|
+
res.destroy();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/** Runs `handle` under the request's id and deadline; a failure becomes the reply, unless the deadline has already answered. */
|
|
307
|
+
function answerRequest(req, res, handle, slowWarnMs) {
|
|
308
|
+
const requestId = openRequest(req, res, slowWarnMs);
|
|
309
|
+
const deadline = requestDeadlineFor(req);
|
|
310
|
+
// Inside the scope, so the failure reply's log line carries the id too.
|
|
311
|
+
runWithRequestId(requestId, () => {
|
|
312
|
+
if (deadline)
|
|
313
|
+
answerAtDeadline(res, deadline, () => replyOrClose(req, res, new DeadlineExceededError(HANDLER_LATE), requestId));
|
|
314
|
+
handle().then(() => {
|
|
315
|
+
if (isAbandoned(res))
|
|
316
|
+
logLateFinish(req, requestId);
|
|
317
|
+
}, (err) => {
|
|
318
|
+
if (!isAbandoned(res))
|
|
319
|
+
replyOrClose(req, res, err, requestId);
|
|
320
|
+
else if (isDroppedLateReply(err))
|
|
321
|
+
logLateFinish(req, requestId);
|
|
322
|
+
else
|
|
323
|
+
logLateFailure(req, err, requestId);
|
|
324
|
+
});
|
|
325
|
+
}, deadline);
|
|
326
|
+
}
|
|
327
|
+
function listenOn(server, port, host) {
|
|
328
|
+
return new Promise((resolve, reject) => {
|
|
329
|
+
const onError = (err) => {
|
|
330
|
+
server.removeListener('listening', onListening);
|
|
331
|
+
reject(err);
|
|
332
|
+
};
|
|
333
|
+
const onListening = () => {
|
|
334
|
+
server.removeListener('error', onError);
|
|
335
|
+
// With no listener, an 'error' event after boot is an uncaught exception that stops the server for every caller.
|
|
336
|
+
server.on('error', (err) => log.error(`serve: listener error: ${err.message}`, errorFields(err)));
|
|
337
|
+
resolve();
|
|
338
|
+
};
|
|
339
|
+
server.once('error', onError);
|
|
340
|
+
server.once('listening', onListening);
|
|
341
|
+
server.listen(port, host);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function exitOnSignalOrCrash(stop, drainMs) {
|
|
345
|
+
const shutdown = { run: stop, boundMs: shutdownBoundMs(drainMs) };
|
|
346
|
+
installSignalHandlers('serve', shutdown);
|
|
347
|
+
installCrashHandlers('serve', shutdown);
|
|
348
|
+
}
|
|
349
|
+
/** Boots the HTTP daemon on host:port and writes the pidfile under hippoRoot; refuses non-loopback hosts unless HIPPO_REQUIRE_AUTH=1.
|
|
350
|
+
* Use port: 0 in tests for an ephemeral port. See docs/ARCHITECTURE.md#srcserverbootts. */
|
|
351
|
+
export async function serve(opts) {
|
|
352
|
+
const host = opts.host ?? DEFAULT_SERVER_HOST;
|
|
353
|
+
const requestedPort = opts.port ?? Number(envPort() ?? DEFAULT_SERVER_PORT);
|
|
354
|
+
const routes = frozenAddonRoutes(opts.routes);
|
|
355
|
+
const publicJsonBodies = assertPublicJson(opts.publicJson ?? {});
|
|
356
|
+
const limiters = bootLimiters(opts.rateLimits);
|
|
357
|
+
assertBindable(host);
|
|
358
|
+
await assertNoLiveServer(opts.hippoRoot);
|
|
359
|
+
// Single source of truth for the start time: returned by GET /health and written into the pidfile, so detectServer can prove a pid-reusing impostor is not
|
|
360
|
+
// this server.
|
|
361
|
+
const startedAt = new Date().toISOString();
|
|
362
|
+
// Open /mcp/stream count per client key, so the cap is per server rather than per process.
|
|
363
|
+
const streamSlots = new Map();
|
|
364
|
+
const served = servedOptsFor(opts, routes, publicJsonBodies, limiters);
|
|
365
|
+
// A store other than hippo.db is a team's central server, so its folder's config.json must not decide shared-ness.
|
|
366
|
+
if (served.store.kind !== 'sqlite')
|
|
367
|
+
markSharedStore(opts.hippoRoot);
|
|
368
|
+
const holder = createStoreHolder(opts.hippoRoot, served.store);
|
|
369
|
+
const inflight = new Set();
|
|
370
|
+
const server = acceptingServer(served, holder, inflight, { startedAt, streamSlots, limiter: limiters.perAddress });
|
|
371
|
+
setKeepAliveTimeouts(server);
|
|
372
|
+
const { port: actualPort, url } = await listenAndDescribe(server, requestedPort, host, opts.tls);
|
|
373
|
+
writePidfile(opts.hippoRoot, { port: actualPort, url, startedAt });
|
|
374
|
+
holder.hold();
|
|
375
|
+
let stopping = false;
|
|
376
|
+
const stop = async () => {
|
|
377
|
+
if (stopping)
|
|
378
|
+
return;
|
|
379
|
+
stopping = true;
|
|
380
|
+
await stopListening(opts, server, inflight, startedAt);
|
|
381
|
+
// The store's worker threads close their connections first, so the held one is still SQLite's last.
|
|
382
|
+
if (!opts.store)
|
|
383
|
+
await served.store.close();
|
|
384
|
+
await holder.release();
|
|
385
|
+
};
|
|
386
|
+
if (opts.handleSignals)
|
|
387
|
+
exitOnSignalOrCrash(stop, opts.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS);
|
|
388
|
+
return { port: actualPort, url, stop, server };
|
|
389
|
+
}
|
|
390
|
+
function frozenAddonRoutes(addonRoutes) {
|
|
391
|
+
// A frozen copy, so a route the caller adds or renames after boot never skips the check below.
|
|
392
|
+
const routes = Object.freeze((addonRoutes ?? []).map(({ path, handler, storeReady }) => Object.freeze(storeReady === undefined
|
|
393
|
+
? { path, handler }
|
|
394
|
+
: { path, handler, storeReady })));
|
|
395
|
+
assertAddonRoutes(routes);
|
|
396
|
+
return routes;
|
|
397
|
+
}
|
|
398
|
+
function servedOptsFor(opts, routes, publicJsonBodies, { callerLimiter, failedAuthLimiter }) {
|
|
399
|
+
return {
|
|
400
|
+
...opts, routes, publicJsonBodies, store: opts.store ?? workerSqliteStore(opts.hippoRoot), callerLimiter, failedAuthLimiter,
|
|
401
|
+
authResolver: outsideRouteBlock(opts.authResolver),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function acceptingServer(served, holder, inflight, options) {
|
|
405
|
+
const { kind } = served.store;
|
|
406
|
+
return createListener(served.tls, (req, res) => {
|
|
407
|
+
res.once('finish', holder.afterResponse);
|
|
408
|
+
inflight.add(res);
|
|
409
|
+
res.once('close', () => inflight.delete(res));
|
|
410
|
+
const run = () => handleRequest(req, res, served, options);
|
|
411
|
+
// A missed port under another store would otherwise create and write a hippo.db that store never reads.
|
|
412
|
+
answerRequest(req, res, () => (kind === 'sqlite' ? run() : withSqliteBlocked(kind, run)), served.slowRequestWarnMs);
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
async function listenAndDescribe(server, port, host, tls) {
|
|
416
|
+
await listenOn(server, port, host);
|
|
417
|
+
const address = server.address();
|
|
418
|
+
if (!isAddressInfo(address)) {
|
|
419
|
+
throw new Error('server.address() returned unexpected shape');
|
|
420
|
+
}
|
|
421
|
+
const addressInfo = address;
|
|
422
|
+
const actualPort = addressInfo.port;
|
|
423
|
+
const url = `${tls ? 'https' : 'http'}://${host.includes(':') ? `[${host}]` : host}:${actualPort}`;
|
|
424
|
+
warnIfCleartext(host, tls, LOOPBACK_HOSTS);
|
|
425
|
+
return { port: actualPort, url };
|
|
426
|
+
}
|
|
427
|
+
async function stopListening(opts, server, inflight, startedAt) {
|
|
428
|
+
// Remove the pidfile only if it still names this server: a newer server may have rewritten it, and an unconditional unlink would orphan it.
|
|
429
|
+
removePidfileIfOwned(opts.hippoRoot, { pid: process.pid, startedAt });
|
|
430
|
+
await drainAndClose(server, inflight, opts.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS);
|
|
431
|
+
}
|
|
432
|
+
//# sourceMappingURL=boot.js.map
|
|
@@ -1,27 +1,15 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'node:http';
|
|
2
|
-
import type { RateLimiter } from '
|
|
2
|
+
import type { RateLimiter } from './rate-limit.js';
|
|
3
3
|
/**
|
|
4
|
-
* Rate-limit key
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* carries the proxy's address, so per-IP buckets collapse into one global
|
|
8
|
-
* bucket that unauthenticated traffic can drain before auth runs. Set
|
|
9
|
-
* HIPPO_CLIENT_IP_HEADER to the header the proxy stamps with the real
|
|
10
|
-
* client address (fly-client-ip on Fly, which the edge always overwrites)
|
|
11
|
-
* to key buckets per client instead.
|
|
12
|
-
*
|
|
13
|
-
* Only set this when a trusted proxy fronts EVERY request: a directly
|
|
14
|
-
* reachable server honoring the header would let clients mint a fresh
|
|
15
|
-
* bucket per request and bypass the limiter entirely.
|
|
16
|
-
*
|
|
17
|
-
* HIPPO_TRUSTED_PROXIES (comma-separated IPs or CIDRs) pins which peers count
|
|
18
|
-
* as that proxy: the header is read only when the socket peer is listed, and
|
|
19
|
-
* listed hops are skipped. In a comma-joined chain (X-Forwarded-For) the key is
|
|
20
|
-
* the rightmost hop no trusted proxy added; entries to its left are whatever
|
|
21
|
-
* the client sent, so they never pick the bucket.
|
|
22
|
-
*/
|
|
4
|
+
* Rate-limit key: the socket's remote address, which behind a TLS proxy collapses into one bucket unauthenticated traffic can drain.
|
|
5
|
+
* Set HIPPO_CLIENT_IP_HEADER (fly-client-ip on Fly) only when a trusted proxy fronts EVERY request, or clients could mint a fresh bucket per request.
|
|
6
|
+
* HIPPO_TRUSTED_PROXIES (IPs or CIDRs) pins which peers count as that proxy; in a comma-joined chain the key is the rightmost hop no trusted proxy added. */
|
|
23
7
|
export declare function clientIpForRateLimit(req: IncomingMessage): string;
|
|
8
|
+
/** Said once at boot: with no proxy list, a caller that reaches the port without the proxy picks its own bucket by sending the header. */
|
|
9
|
+
export declare function warnIfClientIpHeaderUnpinned(): void;
|
|
24
10
|
/** The key for one subscriber: an IPv6 address's /64, since one host may hold the whole /64; any other key as is. */
|
|
25
11
|
export declare function subscriberKey(ip: string): string;
|
|
12
|
+
/** The one key every per-address limit and slot count uses, so rotating inside a /64 never buys a fresh bucket. */
|
|
13
|
+
export declare function clientLimitKey(req: IncomingMessage): string;
|
|
26
14
|
export declare function enforceRateLimit(req: IncomingMessage, path: string, limiter?: RateLimiter): void;
|
|
27
15
|
//# sourceMappingURL=client-ip.d.ts.map
|
package/dist/server/client-ip.js
CHANGED
|
@@ -1,28 +1,13 @@
|
|
|
1
1
|
// Client IP keying and the per-IP rate limit for /v1 and /mcp.
|
|
2
|
-
import { envClientIpHeader, envTrustedProxies } from '../env.js';
|
|
2
|
+
import { envClientIpHeader, envTrustedProxies } from '../util/env.js';
|
|
3
3
|
import { BlockList, isIP } from 'node:net';
|
|
4
|
-
import { log } from '../log.js';
|
|
5
|
-
import { HttpError } from '../http-util.js';
|
|
4
|
+
import { log } from '../util/log.js';
|
|
5
|
+
import { HttpError } from '../util/http-util.js';
|
|
6
|
+
const IPV6_PREFIX_GROUPS = 4;
|
|
6
7
|
/**
|
|
7
|
-
* Rate-limit key
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* carries the proxy's address, so per-IP buckets collapse into one global
|
|
11
|
-
* bucket that unauthenticated traffic can drain before auth runs. Set
|
|
12
|
-
* HIPPO_CLIENT_IP_HEADER to the header the proxy stamps with the real
|
|
13
|
-
* client address (fly-client-ip on Fly, which the edge always overwrites)
|
|
14
|
-
* to key buckets per client instead.
|
|
15
|
-
*
|
|
16
|
-
* Only set this when a trusted proxy fronts EVERY request: a directly
|
|
17
|
-
* reachable server honoring the header would let clients mint a fresh
|
|
18
|
-
* bucket per request and bypass the limiter entirely.
|
|
19
|
-
*
|
|
20
|
-
* HIPPO_TRUSTED_PROXIES (comma-separated IPs or CIDRs) pins which peers count
|
|
21
|
-
* as that proxy: the header is read only when the socket peer is listed, and
|
|
22
|
-
* listed hops are skipped. In a comma-joined chain (X-Forwarded-For) the key is
|
|
23
|
-
* the rightmost hop no trusted proxy added; entries to its left are whatever
|
|
24
|
-
* the client sent, so they never pick the bucket.
|
|
25
|
-
*/
|
|
8
|
+
* Rate-limit key: the socket's remote address, which behind a TLS proxy collapses into one bucket unauthenticated traffic can drain.
|
|
9
|
+
* Set HIPPO_CLIENT_IP_HEADER (fly-client-ip on Fly) only when a trusted proxy fronts EVERY request, or clients could mint a fresh bucket per request.
|
|
10
|
+
* HIPPO_TRUSTED_PROXIES (IPs or CIDRs) pins which peers count as that proxy; in a comma-joined chain the key is the rightmost hop no trusted proxy added. */
|
|
26
11
|
export function clientIpForRateLimit(req) {
|
|
27
12
|
const socketIp = req.socket.remoteAddress ?? 'unknown';
|
|
28
13
|
const header = envClientIpHeader();
|
|
@@ -67,6 +52,14 @@ function trustedProxyList(raw) {
|
|
|
67
52
|
trustedProxyCache = { raw, list };
|
|
68
53
|
return list;
|
|
69
54
|
}
|
|
55
|
+
/** Said once at boot: with no proxy list, a caller that reaches the port without the proxy picks its own bucket by sending the header. */
|
|
56
|
+
export function warnIfClientIpHeaderUnpinned() {
|
|
57
|
+
const header = envClientIpHeader();
|
|
58
|
+
if (!header || trustedProxyList(envTrustedProxies()))
|
|
59
|
+
return;
|
|
60
|
+
log.warn(`serve: HIPPO_CLIENT_IP_HEADER is ${header} and HIPPO_TRUSTED_PROXIES is unset, so any caller that reaches this port without passing the proxy can send that header and pick its own rate-limit bucket. ` +
|
|
61
|
+
"Set HIPPO_TRUSTED_PROXIES to the proxy's addresses or CIDRs, so the header is read from the proxy alone.");
|
|
62
|
+
}
|
|
70
63
|
/** The key for one subscriber: an IPv6 address's /64, since one host may hold the whole /64; any other key as is. */
|
|
71
64
|
export function subscriberKey(ip) {
|
|
72
65
|
const bare = ip.split('%')[0];
|
|
@@ -80,7 +73,11 @@ export function subscriberKey(ip) {
|
|
|
80
73
|
// A trailing dotted quad holds two groups; isIP has already checked the shape.
|
|
81
74
|
const rightGroups = right.length + (right.at(-1)?.includes('.') ? 1 : 0);
|
|
82
75
|
const groups = tail === undefined ? left : [...left, ...Array(8 - left.length - rightGroups).fill('0'), ...right];
|
|
83
|
-
return `${groups.slice(0,
|
|
76
|
+
return `${groups.slice(0, IPV6_PREFIX_GROUPS).map((g) => parseInt(g, 16).toString(16)).join(':')}::/64`;
|
|
77
|
+
}
|
|
78
|
+
/** The one key every per-address limit and slot count uses, so rotating inside a /64 never buys a fresh bucket. */
|
|
79
|
+
export function clientLimitKey(req) {
|
|
80
|
+
return subscriberKey(clientIpForRateLimit(req));
|
|
84
81
|
}
|
|
85
82
|
function isTrustedProxy(list, ip) {
|
|
86
83
|
const bare = ip.startsWith('::ffff:') && isIP(ip.slice(7)) === 4 ? ip.slice(7) : ip;
|
|
@@ -88,18 +85,10 @@ function isTrustedProxy(list, ip) {
|
|
|
88
85
|
return family !== 0 && list.check(bare, family === 6 ? 'ipv6' : 'ipv4');
|
|
89
86
|
}
|
|
90
87
|
export function enforceRateLimit(req, path, limiter) {
|
|
91
|
-
// Per-IP rate limit on /v1/* and /mcp* to bound api-key-id enumeration
|
|
92
|
-
// (
|
|
93
|
-
// here lands in the createServer catch like any other HttpError.
|
|
94
|
-
//
|
|
95
|
-
// Keyed on the socket's remote address by default. Behind a TLS-terminating
|
|
96
|
-
// proxy every socket carries the proxy's address, collapsing the per-IP
|
|
97
|
-
// buckets into one global bucket that pre-auth traffic can drain; set
|
|
98
|
-
// HIPPO_CLIENT_IP_HEADER there so each real client gets its own bucket
|
|
99
|
-
// (see clientIpForRateLimit).
|
|
88
|
+
// Per-IP rate limit on /v1/* and /mcp* to bound api-key-id enumeration; /health and other paths are never throttled, and a 429 lands in the createServer
|
|
89
|
+
// catch. Keyed per clientIpForRateLimit (set HIPPO_CLIENT_IP_HEADER behind a TLS-terminating proxy, or all clients share one bucket).
|
|
100
90
|
if (limiter && (path.startsWith('/v1/') || path === '/mcp' || path === '/mcp/stream')) {
|
|
101
|
-
|
|
102
|
-
if (!limiter.check(ip)) {
|
|
91
|
+
if (!limiter.check(clientLimitKey(req))) {
|
|
103
92
|
throw new HttpError(429, 'rate limit exceeded', limiter.retryAfterSec);
|
|
104
93
|
}
|
|
105
94
|
}
|
package/dist/server/cursor.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ServerResponse } from 'node:http';
|
|
2
|
-
import type { KeysetPosition } from '../keyset.js';
|
|
2
|
+
import type { KeysetPosition } from '../util/keyset.js';
|
|
3
3
|
type CursorPart = 'string' | 'integer';
|
|
4
4
|
/** Bare-array routes cannot grow a body field without breaking clients, so their next cursor rides this header. */
|
|
5
5
|
export declare const NEXT_CURSOR_HEADER = "X-Next-Cursor";
|
package/dist/server/cursor.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { HttpError } from '../http-util.js';
|
|
2
|
-
import { isJsonNumber } from '
|
|
3
|
-
import { isJsonString } from '../json.js';
|
|
1
|
+
import { HttpError } from '../util/http-util.js';
|
|
2
|
+
import { isJsonString, isJsonNumber } from '../util/json.js';
|
|
4
3
|
/** Bare-array routes cannot grow a body field without breaking clients, so their next cursor rides this header. */
|
|
5
4
|
export const NEXT_CURSOR_HEADER = 'X-Next-Cursor';
|
|
6
5
|
const MAX_CURSOR_CHARS = 1024;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { RequestDeadline } from '../util/request-scope.js';
|
|
3
|
+
/** Requests answered 504 by the handler deadline since the process started; /health reports it. */
|
|
4
|
+
export declare function handlerDeadlineCount(): number;
|
|
5
|
+
/** Whether `res` was answered at its deadline, so whatever its handler does later has no caller. */
|
|
6
|
+
export declare function isAbandoned(res: ServerResponse): boolean;
|
|
7
|
+
/** How long a request may run: 120 s, or HIPPO_REQUEST_DEADLINE_MS, where 0 turns the deadline off. */
|
|
8
|
+
export declare function requestDeadlineMs(): number;
|
|
9
|
+
/** The deadline of `req`, for every /v1 and /mcp route but the two exempt ones. */
|
|
10
|
+
export declare function requestDeadlineFor(req: IncomingMessage): RequestDeadline | undefined;
|
|
11
|
+
/** Calls `reply` when `res` has sent nothing by `deadline`. A store call still in flight answers first, since only the store knows what became of a write. */
|
|
12
|
+
export declare function answerAtDeadline(res: ServerResponse, deadline: RequestDeadline, reply: () => void): void;
|
|
13
|
+
//# sourceMappingURL=deadline.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { envRequestDeadlineMs } from '../util/env.js';
|
|
2
|
+
import { RequestDeadline } from '../util/request-scope.js';
|
|
3
|
+
const DEFAULT_REQUEST_DEADLINE_MS = 120_000;
|
|
4
|
+
// Each has a deadline of its own kind: sleep stops its child process, and the stream is meant to stay open.
|
|
5
|
+
const EXEMPT = new Set(['POST /v1/sleep', 'GET /mcp/stream']);
|
|
6
|
+
const abandoned = new WeakSet();
|
|
7
|
+
let handlerDeadlines = 0;
|
|
8
|
+
/** Requests answered 504 by the handler deadline since the process started; /health reports it. */
|
|
9
|
+
export function handlerDeadlineCount() {
|
|
10
|
+
return handlerDeadlines;
|
|
11
|
+
}
|
|
12
|
+
/** Whether `res` was answered at its deadline, so whatever its handler does later has no caller. */
|
|
13
|
+
export function isAbandoned(res) {
|
|
14
|
+
return abandoned.has(res);
|
|
15
|
+
}
|
|
16
|
+
/** How long a request may run: 120 s, or HIPPO_REQUEST_DEADLINE_MS, where 0 turns the deadline off. */
|
|
17
|
+
export function requestDeadlineMs() {
|
|
18
|
+
return envRequestDeadlineMs() ?? DEFAULT_REQUEST_DEADLINE_MS;
|
|
19
|
+
}
|
|
20
|
+
/** The deadline of `req`, for every /v1 and /mcp route but the two exempt ones. */
|
|
21
|
+
export function requestDeadlineFor(req) {
|
|
22
|
+
const ms = requestDeadlineMs();
|
|
23
|
+
const path = (req.url ?? '/').split('?')[0] ?? '/';
|
|
24
|
+
if (ms === 0 || EXEMPT.has(`${req.method ?? 'GET'} ${path}`))
|
|
25
|
+
return undefined;
|
|
26
|
+
return path.startsWith('/v1/') || path === '/mcp' ? new RequestDeadline(Date.now() + ms) : undefined;
|
|
27
|
+
}
|
|
28
|
+
/** Calls `reply` when `res` has sent nothing by `deadline`. A store call still in flight answers first, since only the store knows what became of a write. */
|
|
29
|
+
export function answerAtDeadline(res, deadline, reply) {
|
|
30
|
+
const expire = () => {
|
|
31
|
+
if (res.headersSent || res.destroyed)
|
|
32
|
+
return;
|
|
33
|
+
handlerDeadlines += 1;
|
|
34
|
+
abandoned.add(res);
|
|
35
|
+
reply();
|
|
36
|
+
};
|
|
37
|
+
// One turn of the loop later, so a handler that fails on the store's answer sends that answer itself.
|
|
38
|
+
const timer = setTimeout(() => deadline.onceIdle(() => setImmediate(expire)), deadline.at - Date.now());
|
|
39
|
+
// The open socket keeps the process alive; this timer alone must not.
|
|
40
|
+
timer.unref();
|
|
41
|
+
const clear = () => clearTimeout(timer);
|
|
42
|
+
res.once('finish', clear);
|
|
43
|
+
res.once('close', clear);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=deadline.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface KeyCheckBounds {
|
|
2
|
+
/** Throws the 429 a refused key check answers, before any derivation: no room left to
|
|
3
|
+
* wait (Retry-After 1), or `address` has spent its tries on `keyId` (Retry-After 12). */
|
|
4
|
+
admit(keyId: string, address: string): void;
|
|
5
|
+
/** Runs `derive` now or once a slot frees; `admit` in the same tick keeps the queue within its cap. */
|
|
6
|
+
run<T>(derive: () => Promise<T>): Promise<T>;
|
|
7
|
+
}
|
|
8
|
+
export declare function keyCheckBounds(maxKeys: number): KeyCheckBounds;
|
|
9
|
+
//# sourceMappingURL=key-check-bounds.d.ts.map
|