hippo-memory 1.70.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 +1 -1
- package/dist/agent-memories/claude-code.d.ts +4 -2
- package/dist/agent-memories/claude-code.js +14 -5
- package/dist/agent-memories/files.js +23 -12
- package/dist/agent-memories/markdown.d.ts +1 -3
- package/dist/agent-memories/markdown.js +1 -3
- package/dist/agent-memories/report.js +2 -1
- package/dist/agent-memories/sync.d.ts +4 -2
- package/dist/agent-memories/sync.js +10 -5
- package/dist/api/assemble.d.ts +7 -44
- package/dist/api/assemble.js +2 -22
- package/dist/api/audit.d.ts +5 -1
- package/dist/api/audit.js +9 -1
- package/dist/api/auth.d.ts +13 -22
- package/dist/api/auth.js +34 -24
- package/dist/api/auto-sleep.js +2 -2
- package/dist/api/availability.d.ts +2 -15
- package/dist/api/availability.js +2 -15
- package/dist/api/context-select.js +3 -4
- package/dist/api/context-types.d.ts +10 -35
- package/dist/api/context.d.ts +5 -22
- package/dist/api/context.js +21 -38
- package/dist/api/dormant.d.ts +5 -21
- package/dist/api/dormant.js +7 -26
- package/dist/api/drill-down.d.ts +7 -46
- package/dist/api/drill-down.js +5 -26
- package/dist/api/forget.d.ts +5 -24
- package/dist/api/forget.js +5 -28
- package/dist/api/graph.d.ts +8 -0
- package/dist/api/graph.js +7 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/instruction-detect.js +16 -5
- package/dist/api/ledger-db.d.ts +2 -1
- package/dist/api/ledger-db.js +5 -3
- 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.js +5 -3
- package/dist/api/predictions.d.ts +9 -0
- package/dist/api/predictions.js +18 -0
- package/dist/api/promote.d.ts +13 -17
- package/dist/api/promote.js +11 -19
- package/dist/api/prompt-hook.d.ts +4 -4
- package/dist/api/prompt-hook.js +10 -9
- package/dist/api/recall-cli.d.ts +55 -0
- package/dist/api/recall-cli.js +94 -0
- package/dist/api/recall-core.js +14 -6
- package/dist/api/recall-finish.d.ts +53 -0
- package/dist/api/recall-finish.js +30 -0
- package/dist/api/recall-history.d.ts +17 -84
- package/dist/api/recall-history.js +15 -88
- package/dist/api/recall-pipeline.d.ts +2 -3
- package/dist/api/recall-pipeline.js +16 -9
- package/dist/api/recall-types.d.ts +64 -304
- package/dist/api/recall.d.ts +5 -33
- package/dist/api/recall.js +59 -60
- 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 +18 -1
- package/dist/api/remember.js +13 -1
- package/dist/api/sleep-run.d.ts +1 -3
- package/dist/api/sleep-run.js +17 -21
- package/dist/api/sleep.d.ts +9 -28
- package/dist/api/tokens.d.ts +3 -10
- package/dist/api/tokens.js +3 -10
- package/dist/api/types.d.ts +6 -29
- package/dist/api/types.js +4 -22
- package/dist/capture/command.d.ts +4 -12
- package/dist/capture/command.js +6 -22
- package/dist/capture/compact.d.ts +4 -15
- package/dist/capture/compact.js +16 -25
- package/dist/capture/compaction-record.js +101 -69
- package/dist/capture/compaction-spool.d.ts +1 -1
- package/dist/capture/compaction-spool.js +1 -1
- package/dist/capture/session-digest.d.ts +1 -1
- package/dist/capture/session-digest.js +13 -7
- package/dist/capture/transcript.d.ts +2 -10
- package/dist/capture/transcript.js +4 -18
- package/dist/cli/api-context.d.ts +3 -0
- package/dist/cli/api-context.js +6 -0
- package/dist/cli/audit-prune.d.ts +6 -39
- package/dist/cli/audit-prune.js +6 -39
- package/dist/cli/audit.d.ts +1 -1
- package/dist/cli/audit.js +33 -34
- package/dist/cli/auth.d.ts +2 -2
- package/dist/cli/auth.js +57 -39
- package/dist/cli/briefs.d.ts +4 -4
- package/dist/cli/briefs.js +18 -22
- package/dist/cli/card.d.ts +2 -2
- package/dist/cli/card.js +31 -32
- package/dist/cli/client.d.ts +7 -27
- package/dist/cli/client.js +24 -43
- package/dist/cli/compact-resume-payload.js +3 -2
- package/dist/cli/context.d.ts +1 -2
- package/dist/cli/context.js +12 -8
- package/dist/cli/continuity.d.ts +6 -6
- package/dist/cli/continuity.js +62 -64
- package/dist/cli/correction-latency.d.ts +2 -14
- package/dist/cli/correction-latency.js +2 -14
- package/dist/cli/curate.d.ts +13 -17
- package/dist/cli/curate.js +68 -92
- package/dist/cli/dag.d.ts +4 -4
- package/dist/cli/dag.js +33 -27
- package/dist/cli/decisions.d.ts +4 -4
- package/dist/cli/decisions.js +33 -47
- package/dist/cli/eval.js +15 -13
- 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 +6 -4
- package/dist/cli/flag-values.d.ts +6 -1
- package/dist/cli/flag-values.js +27 -10
- package/dist/cli/flags.d.ts +1 -0
- package/dist/cli/flags.js +1 -1
- package/dist/cli/github.d.ts +14 -0
- package/dist/{connectors/github/cli-impl.js → cli/github.js} +43 -71
- package/dist/cli/goals.d.ts +2 -2
- package/dist/cli/goals.js +46 -50
- package/dist/cli/hook-runtime.d.ts +9 -27
- package/dist/cli/hook-runtime.js +15 -31
- package/dist/cli/init.d.ts +2 -2
- package/dist/cli/init.js +6 -12
- package/dist/cli/install-steps.js +3 -12
- package/dist/cli/last-sleep.d.ts +2 -1
- package/dist/cli/last-sleep.js +3 -0
- package/dist/cli/maintenance.d.ts +4 -4
- package/dist/cli/maintenance.js +14 -19
- package/dist/cli/object-verbs.js +4 -3
- package/dist/cli/owner-validation.d.ts +5 -28
- package/dist/cli/owner-validation.js +5 -28
- package/dist/cli/playbooks.d.ts +4 -4
- package/dist/cli/playbooks.js +23 -28
- package/dist/cli/print.d.ts +2 -5
- package/dist/cli/print.js +1 -4
- package/dist/cli/projects.d.ts +2 -2
- package/dist/cli/projects.js +1 -3
- package/dist/cli/provenance-coverage.d.ts +2 -6
- package/dist/cli/provenance-coverage.js +2 -6
- package/dist/cli/quality-repair-once.js +2 -2
- package/dist/cli/recall.d.ts +2 -4
- package/dist/cli/recall.js +55 -131
- package/dist/cli/refine-llm.d.ts +5 -31
- package/dist/cli/refine-llm.js +9 -36
- package/dist/cli/remember.d.ts +3 -3
- package/dist/cli/remember.js +122 -180
- package/dist/cli/scheduler.d.ts +2 -17
- package/dist/cli/scheduler.js +12 -18
- package/dist/cli/serve.js +21 -12
- package/dist/cli/session-hooks.d.ts +10 -18
- package/dist/cli/session-hooks.js +112 -113
- package/dist/cli/setup.d.ts +4 -4
- package/dist/cli/setup.js +19 -21
- package/dist/cli/shared.d.ts +9 -23
- package/dist/cli/shared.js +9 -32
- package/dist/cli/slack.d.ts +2 -2
- package/dist/cli/slack.js +19 -23
- package/dist/cli/sleep.d.ts +3 -2
- package/dist/cli/sleep.js +15 -16
- package/dist/cli/status.d.ts +9 -12
- package/dist/cli/status.js +55 -98
- package/dist/cli/stdin.d.ts +4 -6
- package/dist/cli/stdin.js +2 -3
- package/dist/cli/transfer.d.ts +9 -9
- package/dist/cli/transfer.js +66 -89
- package/dist/cli/usage.js +11 -5
- package/dist/cli.d.ts +66 -448
- package/dist/cli.js +87 -263
- package/dist/connectors/github/backfill.d.ts +5 -25
- package/dist/connectors/github/backfill.js +14 -49
- package/dist/connectors/github/deletion.d.ts +4 -18
- package/dist/connectors/github/deletion.js +2 -15
- package/dist/connectors/github/dlq.d.ts +8 -33
- package/dist/connectors/github/dlq.js +12 -29
- package/dist/connectors/github/ingest.d.ts +2 -6
- package/dist/connectors/github/ingest.js +4 -10
- package/dist/connectors/github/octokit-client.d.ts +4 -16
- package/dist/connectors/github/octokit-client.js +27 -24
- 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 +2 -24
- package/dist/connectors/github/tenant-routing.js +4 -26
- package/dist/connectors/github/transform.js +2 -16
- package/dist/connectors/github/types.d.ts +3 -15
- package/dist/connectors/github/types.js +2 -9
- package/dist/connectors/github/webhook.d.ts +2 -16
- package/dist/connectors/github/webhook.js +6 -22
- package/dist/connectors/slack/backfill.d.ts +4 -17
- package/dist/connectors/slack/backfill.js +4 -13
- package/dist/connectors/slack/dlq.d.ts +2 -19
- package/dist/connectors/slack/dlq.js +2 -19
- package/dist/connectors/slack/ingest.d.ts +2 -3
- package/dist/connectors/slack/ingest.js +2 -3
- 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/tenant-routing.d.ts +2 -19
- package/dist/connectors/slack/tenant-routing.js +2 -21
- package/dist/connectors/slack/transform.d.ts +2 -18
- package/dist/connectors/slack/transform.js +2 -18
- package/dist/connectors/slack/types.d.ts +3 -9
- package/dist/connectors/slack/types.js +2 -4
- package/dist/connectors/slack/web-client.js +33 -27
- package/dist/connectors/slack/webhook.d.ts +2 -19
- package/dist/connectors/slack/webhook.js +5 -26
- package/dist/connectors/slack/workspaces.d.ts +4 -24
- package/dist/connectors/slack/workspaces.js +4 -24
- package/dist/consolidate/conflicts.js +6 -14
- package/dist/consolidate/dag-summary.d.ts +8 -0
- package/dist/consolidate/dag-summary.js +41 -0
- package/dist/consolidate/dag.d.ts +5 -28
- package/dist/consolidate/dag.js +14 -75
- package/dist/consolidate/decay.js +7 -19
- package/dist/consolidate/dedupe.d.ts +6 -52
- package/dist/consolidate/dedupe.js +13 -71
- package/dist/consolidate/flush-units.d.ts +3 -4
- package/dist/consolidate/flush-units.js +3 -3
- package/dist/consolidate/half-life-migration.d.ts +3 -26
- package/dist/consolidate/half-life-migration.js +11 -28
- package/dist/consolidate/llm-passes.js +4 -21
- package/dist/consolidate/memory-value-weights.d.ts +2 -13
- package/dist/consolidate/memory-value-weights.js +2 -13
- package/dist/consolidate/memory-value.d.ts +11 -84
- package/dist/consolidate/memory-value.js +16 -104
- package/dist/consolidate/merge.js +10 -31
- package/dist/consolidate/merged-row.js +2 -1
- package/dist/consolidate/physics-pass.js +1 -3
- package/dist/consolidate/replay.d.ts +6 -38
- package/dist/consolidate/replay.js +14 -52
- package/dist/consolidate/run.js +1 -1
- package/dist/consolidate/sleep.d.ts +1 -8
- package/dist/consolidate/sleep.js +5 -17
- package/dist/consolidate/trace.d.ts +3 -18
- package/dist/consolidate/trace.js +3 -18
- package/dist/consolidate/traces.js +6 -22
- package/dist/core/ablation.d.ts +4 -91
- package/dist/core/ablation.js +4 -91
- package/dist/core/ambient.d.ts +1 -8
- package/dist/core/ambient.js +1 -8
- package/dist/core/capture-contract.d.ts +4 -2
- package/dist/core/capture-contract.js +4 -2
- package/dist/core/card.d.ts +4 -1
- package/dist/core/card.js +8 -0
- package/dist/core/compare.d.ts +11 -67
- package/dist/core/compare.js +9 -63
- package/dist/core/config.d.ts +10 -24
- package/dist/core/config.js +13 -14
- package/dist/core/handoff.d.ts +1 -7
- package/dist/core/handoff.js +1 -7
- package/dist/core/memory-quality.js +1 -1
- package/dist/core/memory.d.ts +41 -164
- package/dist/core/memory.js +51 -221
- package/dist/core/package-root.d.ts +3 -0
- package/dist/core/package-root.js +5 -0
- package/dist/core/physics-config.d.ts +1 -4
- package/dist/core/physics-config.js +3 -8
- package/dist/core/physics.d.ts +15 -61
- package/dist/core/physics.js +59 -122
- package/dist/core/project-identity.d.ts +17 -58
- package/dist/core/project-identity.js +17 -38
- package/dist/core/project-remote.js +13 -2
- package/dist/core/raw-append-only.d.ts +5 -0
- package/dist/core/raw-append-only.js +7 -0
- package/dist/core/salience.d.ts +1 -7
- package/dist/core/salience.js +1 -7
- package/dist/dashboard/dashboard-actions.js +2 -2
- package/dist/dashboard/dashboard-snapshot.d.ts +2 -1
- package/dist/dashboard/dashboard-snapshot.js +11 -2
- package/dist/dashboard/dashboard-types.d.ts +2 -1
- package/dist/dashboard/dashboard.d.ts +1 -0
- package/dist/dashboard/dashboard.js +50 -4
- package/dist/db/busy.d.ts +1 -1
- package/dist/db/busy.js +17 -8
- package/dist/db/connect.d.ts +1 -0
- package/dist/db/connect.js +3 -3
- package/dist/db/migrate.js +3 -3
- package/dist/db/migrations/types.d.ts +2 -5
- package/dist/db/migrations/v14.js +2 -3
- package/dist/db/migrations/v15.js +2 -8
- package/dist/db/migrations/v16.js +2 -3
- package/dist/db/migrations/v17.js +2 -3
- package/dist/db/migrations/v19.js +2 -5
- package/dist/db/migrations/v20.js +2 -7
- package/dist/db/migrations/v22.js +4 -17
- package/dist/db/migrations/v23.js +3 -14
- package/dist/db/migrations/v24.js +2 -4
- 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 +4 -18
- package/dist/db/migrations/v30.js +4 -16
- package/dist/db/migrations/v31.js +2 -15
- 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 +9 -20
- package/dist/db/migrations/v40.js +2 -9
- package/dist/db/migrations/v41.js +2 -24
- package/dist/db/migrations/v43.js +1 -1
- package/dist/db/migrations/v44.js +2 -8
- package/dist/db/migrations/v45.js +2 -7
- package/dist/db/migrations/v47.js +2 -3
- package/dist/db/migrations/v51.js +1 -1
- package/dist/db/open.d.ts +2 -0
- package/dist/db/open.js +17 -1
- package/dist/db/physics-state.d.ts +6 -22
- package/dist/db/physics-state.js +25 -36
- package/dist/db/raw-archive-mirror-cleanup.d.ts +2 -17
- package/dist/db/raw-archive-mirror-cleanup.js +2 -17
- package/dist/db/vector-store.d.ts +2 -0
- package/dist/db/vector-store.js +8 -1
- package/dist/doctor.js +74 -19
- package/dist/entry/session-text.js +2 -1
- package/dist/eval/eval-stats.d.ts +15 -53
- package/dist/eval/eval-stats.js +12 -42
- package/dist/eval/eval-suite.d.ts +2 -13
- package/dist/eval/eval-suite.js +74 -34
- package/dist/eval/eval.d.ts +6 -24
- package/dist/eval/eval.js +7 -34
- package/dist/eval/judgment.d.ts +0 -3
- package/dist/eval/judgment.js +19 -11
- package/dist/extensions/openclaw-plugin/index.js +1 -1
- package/dist/graph/extract.d.ts +5 -27
- package/dist/graph/extract.js +7 -33
- package/dist/graph/recall.d.ts +7 -20
- package/dist/graph/recall.js +14 -76
- package/dist/graph/stream.d.ts +7 -39
- package/dist/graph/stream.js +11 -34
- package/dist/graph/view.d.ts +9 -35
- package/dist/graph/view.js +11 -39
- package/dist/hooks/codex-wrapper.js +2 -2
- package/dist/hooks/copilot.js +2 -1
- package/dist/hooks/hook-blocks.d.ts +6 -2
- package/dist/hooks/hook-blocks.js +37 -124
- package/dist/hooks/json-hooks.d.ts +4 -36
- package/dist/hooks/json-hooks.js +18 -57
- package/dist/hooks/opencode.d.ts +0 -38
- package/dist/hooks/opencode.js +9 -76
- package/dist/hooks/shared.d.ts +8 -17
- package/dist/hooks/shared.js +29 -20
- package/dist/importers/core.d.ts +12 -37
- package/dist/importers/core.js +9 -27
- package/dist/importers/markdown-parse.d.ts +8 -22
- package/dist/importers/markdown-parse.js +8 -25
- package/dist/importers/markdown.js +3 -12
- package/dist/importers/sources.js +7 -35
- package/dist/importers/vault.d.ts +2 -8
- package/dist/importers/vault.js +25 -81
- package/dist/index.d.ts +6 -2
- package/dist/index.js +6 -2
- package/dist/learn/autolearn.d.ts +14 -41
- package/dist/learn/autolearn.js +14 -41
- package/dist/learn/extract.js +2 -4
- package/dist/learn/forward-claim-detector.d.ts +6 -30
- package/dist/learn/forward-claim-detector.js +14 -66
- package/dist/learn/invalidation.d.ts +6 -18
- package/dist/learn/invalidation.js +9 -26
- package/dist/mcp/admin-tools.js +3 -5
- package/dist/mcp/memory-tools.js +4 -9
- package/dist/mcp/protocol.d.ts +8 -26
- package/dist/mcp/protocol.js +3 -6
- package/dist/mcp/recall-tools.js +3 -3
- package/dist/mcp/request.d.ts +3 -11
- package/dist/mcp/request.js +10 -26
- package/dist/mcp/server.d.ts +3 -9
- package/dist/mcp/server.js +5 -16
- package/dist/mcp/session-state.d.ts +1 -1
- package/dist/mcp/session-state.js +5 -11
- package/dist/mcp/stdio.d.ts +2 -6
- package/dist/mcp/stdio.js +48 -12
- package/dist/mcp/tools.js +5 -3
- package/dist/objects/customer-notes.d.ts +7 -42
- package/dist/objects/customer-notes.js +7 -48
- package/dist/objects/decisions.d.ts +12 -50
- package/dist/objects/decisions.js +15 -53
- package/dist/objects/incidents.d.ts +7 -38
- package/dist/objects/incidents.js +7 -41
- package/dist/objects/policies.d.ts +11 -89
- package/dist/objects/policies.js +11 -98
- package/dist/objects/processes.d.ts +7 -52
- package/dist/objects/processes.js +7 -61
- package/dist/objects/project-briefs.d.ts +11 -55
- package/dist/objects/project-briefs.js +14 -75
- package/dist/objects/skills.d.ts +7 -42
- package/dist/objects/skills.js +8 -54
- package/dist/postinstall.js +6 -19
- package/dist/predictions/planning-fallacy.d.ts +11 -34
- package/dist/rerankers/clef.js +3 -31
- package/dist/rerankers/cross-encoder.js +4 -6
- package/dist/rerankers/jev.js +9 -6
- package/dist/rerankers/llm.d.ts +2 -11
- package/dist/rerankers/llm.js +14 -14
- package/dist/rerankers/outage-warning.d.ts +2 -1
- package/dist/rerankers/outage-warning.js +2 -1
- package/dist/rerankers/remote.d.ts +1 -0
- package/dist/rerankers/remote.js +2 -0
- package/dist/rerankers/types.d.ts +2 -14
- package/dist/search/boosts.d.ts +0 -2
- package/dist/search/boosts.js +2 -2
- package/dist/search/goal-boost.d.ts +23 -0
- package/dist/search/goal-boost.js +90 -0
- package/dist/search/hybrid.d.ts +2 -0
- package/dist/search/multihop.js +2 -3
- package/dist/search/path-context.d.ts +5 -18
- package/dist/search/path-context.js +5 -18
- package/dist/search/physics-search.js +11 -8
- package/dist/search/rrf.d.ts +7 -35
- package/dist/search/rrf.js +5 -30
- package/dist/search/vector.d.ts +7 -0
- package/dist/search/vector.js +35 -5
- package/dist/server/auth.d.ts +9 -28
- package/dist/server/auth.js +32 -25
- package/dist/server/boot.d.ts +2 -21
- package/dist/server/boot.js +63 -96
- package/dist/server/client-ip.d.ts +3 -19
- package/dist/server/client-ip.js +5 -28
- package/dist/server/defaults.d.ts +3 -0
- package/dist/server/defaults.js +4 -0
- package/dist/server/key-check-bounds.d.ts +2 -1
- package/dist/server/lifecycle.d.ts +5 -4
- package/dist/server/lifecycle.js +19 -4
- package/dist/server/mcp-http.js +10 -44
- package/dist/server/rate-limit.d.ts +5 -18
- package/dist/server/rate-limit.js +4 -13
- package/dist/server/request.d.ts +4 -8
- package/dist/server/request.js +9 -17
- package/dist/server/route-table.js +42 -49
- package/dist/server/routes/admin.js +22 -28
- package/dist/server/routes/customer-notes.js +7 -12
- package/dist/server/routes/decisions.js +10 -17
- package/dist/server/routes/incidents.js +12 -20
- package/dist/server/routes/memories.js +12 -32
- package/dist/server/routes/object-routes.d.ts +0 -5
- package/dist/server/routes/object-routes.js +7 -15
- package/dist/server/routes/policies.js +10 -18
- package/dist/server/routes/predictions.js +13 -25
- package/dist/server/routes/processes.js +16 -26
- package/dist/server/routes/project-briefs.js +9 -16
- package/dist/server/routes/recall.js +10 -35
- package/dist/server/routes/skills.js +8 -18
- package/dist/server/server-detect.d.ts +9 -53
- package/dist/server/server-detect.js +24 -90
- package/dist/server/sleep-offload.js +4 -1
- package/dist/server/types.d.ts +12 -10
- package/dist/server/validation.d.ts +3 -1
- package/dist/server/validation.js +10 -23
- package/dist/server.d.ts +2 -2
- package/dist/server.js +3 -2
- 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/sharing/project-merge.d.ts +4 -5
- package/dist/sharing/project-merge.js +9 -7
- package/dist/sharing/scope.d.ts +4 -11
- package/dist/sharing/scope.js +4 -11
- 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/store/ambient.js +4 -9
- package/dist/store/audit-event.d.ts +4 -12
- package/dist/store/audit-event.js +4 -12
- package/dist/store/audit.d.ts +2 -5
- package/dist/store/audit.js +4 -12
- package/dist/store/auth.d.ts +6 -6
- package/dist/store/auth.js +10 -9
- package/dist/store/candidates.d.ts +20 -3
- package/dist/store/candidates.js +77 -48
- package/dist/store/card-detail.d.ts +2 -1
- package/dist/store/card-detail.js +2 -1
- package/dist/store/cards.d.ts +14 -9
- package/dist/store/cards.js +72 -126
- package/dist/store/conflicts.d.ts +4 -21
- package/dist/store/conflicts.js +71 -150
- package/dist/store/connectors/github.d.ts +10 -10
- package/dist/store/connectors/github.js +10 -10
- package/dist/store/dashboard-reads.js +4 -8
- package/dist/store/delete-and-batch.d.ts +11 -31
- package/dist/store/delete-and-batch.js +48 -64
- package/dist/store/delivery-recorder.d.ts +8 -4
- package/dist/store/delivery-recorder.js +25 -15
- package/dist/store/diagnostics.js +6 -13
- package/dist/store/dormant.d.ts +7 -29
- package/dist/store/dormant.js +15 -56
- package/dist/store/embeddings/index.d.ts +11 -80
- package/dist/store/embeddings/index.js +71 -207
- package/dist/store/embeddings/local.js +15 -32
- package/dist/store/embeddings/provider.d.ts +16 -53
- package/dist/store/embeddings/provider.js +41 -69
- package/dist/store/entry-reads.d.ts +21 -57
- package/dist/store/entry-reads.js +65 -126
- package/dist/store/entry-row.d.ts +6 -35
- package/dist/store/entry-row.js +9 -37
- package/dist/store/entry-writes.d.ts +10 -16
- package/dist/store/entry-writes.js +17 -35
- package/dist/store/goals.d.ts +3 -40
- package/dist/store/goals.js +21 -160
- package/dist/store/graph-queue.d.ts +6 -22
- package/dist/store/graph-queue.js +14 -45
- package/dist/store/graph-reads.d.ts +11 -35
- package/dist/store/graph-reads.js +20 -58
- package/dist/store/graph-rows.d.ts +3 -6
- package/dist/store/graph-rows.js +6 -9
- package/dist/store/graph-view-rows.d.ts +2 -1
- package/dist/store/graph-view-rows.js +2 -1
- package/dist/store/graph-writes.d.ts +6 -31
- package/dist/store/graph-writes.js +13 -37
- package/dist/store/half-life.d.ts +2 -1
- package/dist/store/half-life.js +14 -16
- package/dist/store/handoffs.d.ts +5 -11
- package/dist/store/handoffs.js +16 -34
- package/dist/store/index-and-stats.d.ts +6 -19
- package/dist/store/index-and-stats.js +34 -82
- package/dist/store/markdown.d.ts +2 -6
- package/dist/store/markdown.js +21 -20
- package/dist/store/mirrors.d.ts +2 -6
- package/dist/store/mirrors.js +14 -23
- package/dist/store/open.js +6 -13
- package/dist/store/planning-fallacy-evidence.d.ts +2 -30
- package/dist/store/planning-fallacy-evidence.js +8 -37
- package/dist/store/port.d.ts +82 -79
- package/dist/store/predictions.d.ts +11 -51
- package/dist/store/predictions.js +38 -119
- package/dist/{cli → store}/quality-repair.js +15 -31
- package/dist/store/raw-archive.d.ts +4 -16
- package/dist/store/raw-archive.js +14 -29
- package/dist/store/recall-scope.d.ts +19 -79
- package/dist/store/recall-scope.js +21 -82
- package/dist/store/recall-trace.d.ts +10 -77
- package/dist/store/recall-trace.js +19 -95
- package/dist/store/rejected-values.js +7 -19
- package/dist/store/rejection.d.ts +12 -58
- package/dist/store/rejection.js +13 -65
- package/dist/store/report-reads.d.ts +6 -0
- package/dist/store/report-reads.js +29 -0
- package/dist/store/rows.d.ts +9 -19
- package/dist/store/rows.js +36 -34
- package/dist/store/rule-sql.d.ts +5 -0
- package/dist/store/rule-sql.js +37 -0
- package/dist/store/search-rows.d.ts +7 -38
- package/dist/store/search-rows.js +14 -50
- package/dist/store/sessions.d.ts +10 -47
- package/dist/store/sessions.js +17 -73
- package/dist/store/sqlite/connector-events-group.js +2 -2
- package/dist/store/sqlite/entry-writes-group.js +2 -2
- package/dist/store/sqlite/error-codec.js +4 -2
- package/dist/store/sqlite/executor.d.ts +2 -1
- package/dist/store/sqlite/executor.js +2 -1
- package/dist/store/sqlite/local.d.ts +3 -3
- package/dist/store/sqlite/local.js +1 -0
- package/dist/store/sqlite/object-rows.js +6 -3
- package/dist/store/sqlite/objects-group.js +4 -2
- package/dist/store/sqlite/quarantine-group.js +2 -2
- package/dist/store/sqlite/store.d.ts +1 -4
- package/dist/store/sqlite/store.js +2 -3
- package/dist/store/sqlite/vector-reads-group.js +2 -2
- package/dist/store/sqlite/worker-jobs.js +1 -1
- package/dist/store/sqlite/worker-ops.d.ts +13 -2
- package/dist/store/sqlite/worker-ops.js +13 -2
- package/dist/store/sqlite/worker-store.js +5 -2
- package/dist/store/summaries.d.ts +10 -62
- package/dist/store/summaries.js +34 -146
- package/dist/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 +2 -16
- package/dist/store/tenant.js +4 -19
- package/dist/store/token-ledger.d.ts +13 -43
- package/dist/store/token-ledger.js +19 -50
- package/dist/store/usage-reports.js +5 -13
- package/dist/store/vector-index.d.ts +46 -0
- package/dist/store/vector-index.js +170 -0
- package/dist/store/working-memory.d.ts +6 -25
- package/dist/store/working-memory.js +18 -52
- package/dist/store/yaml.d.ts +1 -5
- package/dist/store/yaml.js +2 -6
- package/dist/support-bundle.js +7 -5
- package/dist/trust/reject-flow.js +1 -3
- package/dist/util/anthropic-messages.js +14 -2
- package/dist/util/atomic-write.d.ts +2 -1
- package/dist/util/atomic-write.js +35 -4
- package/dist/util/capped-json.d.ts +6 -0
- package/dist/util/capped-json.js +46 -0
- package/dist/util/crash-handlers.d.ts +8 -1
- package/dist/util/crash-handlers.js +52 -10
- package/dist/util/env.d.ts +13 -6
- package/dist/util/env.js +14 -7
- package/dist/util/home-path.d.ts +2 -1
- package/dist/util/home-path.js +4 -2
- package/dist/util/http-retry.d.ts +3 -1
- package/dist/util/http-retry.js +26 -5
- package/dist/util/log.d.ts +3 -2
- package/dist/util/log.js +25 -9
- package/dist/util/request-scope.d.ts +2 -1
- package/dist/util/request-scope.js +2 -1
- package/dist/util/secret-detect.d.ts +5 -30
- package/dist/util/secret-detect.js +13 -39
- package/dist/util/stored-json.d.ts +1 -1
- package/dist/util/stored-json.js +1 -1
- package/dist/util/token-text.d.ts +1 -4
- package/dist/util/token-text.js +1 -4
- package/dist/util/transcript-tail.d.ts +2 -0
- package/dist/util/transcript-tail.js +24 -23
- package/dist/util/version.d.ts +5 -20
- package/dist/util/version.js +5 -20
- package/extensions/openclaw-plugin/index.ts +1 -1
- 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 +1 -1
- package/dist/connectors/github/cli-impl.d.ts +0 -25
- package/dist/sharing/shared.d.ts +0 -187
- package/dist/sharing/shared.js +0 -563
- package/dist/store/vector-writes.d.ts +0 -13
- package/dist/store/vector-writes.js +0 -89
- /package/dist/{cli → store}/quality-repair.d.ts +0 -0
package/dist/server/request.js
CHANGED
|
@@ -27,7 +27,8 @@ export function replyFor(err) {
|
|
|
27
27
|
}
|
|
28
28
|
// Above the 30 s body deadline and the 5 s resolver deadline, so a request those govern is answered before it is called slow.
|
|
29
29
|
const SLOW_REQUEST_WARN_MS = 60_000;
|
|
30
|
-
/** Logs one warn when `res` has sent nothing after `afterMs`. It never ends the request
|
|
30
|
+
/** Logs one warn when `res` has sent nothing after `afterMs`. It never ends the request
|
|
31
|
+
* or touches the reply; the timer goes when the reply finishes or the socket closes. */
|
|
31
32
|
function watchSlowRequest(req, res, requestId, afterMs = SLOW_REQUEST_WARN_MS) {
|
|
32
33
|
const startedAt = Date.now();
|
|
33
34
|
const timer = setTimeout(() => {
|
|
@@ -51,6 +52,10 @@ export function noteAccess(req, note) {
|
|
|
51
52
|
if (held)
|
|
52
53
|
Object.assign(held, note);
|
|
53
54
|
}
|
|
55
|
+
/** The route name the request's access line carries, for a line about the same request logged after its reply. */
|
|
56
|
+
export function accessRouteOf(req) {
|
|
57
|
+
return accessNotes.get(req)?.route ?? UNMATCHED_ROUTE;
|
|
58
|
+
}
|
|
54
59
|
/** One info line per finished request. A path id and a query string can both hold caller data, so the line carries neither. */
|
|
55
60
|
function logAccessWhenFinished(req, res, requestId) {
|
|
56
61
|
const note = {};
|
|
@@ -97,14 +102,7 @@ export function parseRequest(req) {
|
|
|
97
102
|
query: url.searchParams,
|
|
98
103
|
};
|
|
99
104
|
}
|
|
100
|
-
/**
|
|
101
|
-
* Lightweight pattern matcher for /v1/memories/:id/<action>. Avoids pulling
|
|
102
|
-
* in a router dependency for the half-dozen patterns we actually use.
|
|
103
|
-
*
|
|
104
|
-
* Returns null if `path` does not match `pattern`. Otherwise returns an object
|
|
105
|
-
* mapping each :param name to its value. Path segments are exact-matched
|
|
106
|
-
* except for parameter slots.
|
|
107
|
-
*/
|
|
105
|
+
/** Pattern matcher for /v1/memories/:id/<action> (no router dependency); null if `path` does not match, else each :param name mapped to its value. */
|
|
108
106
|
function decodePathSegment(segment) {
|
|
109
107
|
try {
|
|
110
108
|
return decodeURIComponent(segment);
|
|
@@ -135,14 +133,8 @@ export function matchPath(pattern, path) {
|
|
|
135
133
|
}
|
|
136
134
|
return params;
|
|
137
135
|
}
|
|
138
|
-
/**
|
|
139
|
-
*
|
|
140
|
-
* them — otherwise `%2F` becomes `/`, path-split runs, and the route either
|
|
141
|
-
* silently 404s or matches the wrong template.
|
|
142
|
-
*
|
|
143
|
-
* Only the PATHNAME is scanned (split on the first `?`), so recall queries
|
|
144
|
-
* containing URLs like `?q=https%3A%2F%2Fexample.com` are not rejected.
|
|
145
|
-
*/
|
|
136
|
+
/** Rejects URL-encoded slashes in path segments BEFORE the URL parser decodes them (`%2F` would become `/` and 404 or match the wrong template).
|
|
137
|
+
* Only the PATHNAME is scanned, so recall queries like `?q=https%3A%2F%2Fexample.com` are not rejected. */
|
|
146
138
|
export function rejectEncodedSlash(rawUrl) {
|
|
147
139
|
const queryIdx = rawUrl.indexOf('?');
|
|
148
140
|
const pathname = queryIdx === -1 ? rawUrl : rawUrl.slice(0, queryIdx);
|
|
@@ -16,16 +16,8 @@ import { handleCloseProjectBrief, handleCreateProjectBrief, handleGetProjectBrie
|
|
|
16
16
|
import { handleAssembleSession, handleDrillRecall, handleGetContext, handleRecallMemories } from './routes/recall.js';
|
|
17
17
|
import { handleCloseSkill, handleCreateSkill, handleExportSkills, handleGetSkill, handleListSkills, handleSupersedeSkill } from './routes/skills.js';
|
|
18
18
|
import { parseJsonBody } from './validation.js';
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// tests/server-bearer-lockdown.test.ts. Do not gate auth elsewhere by
|
|
22
|
-
// `path.startsWith` — pattern-positional auth is bypass-by-accident.
|
|
23
|
-
//
|
|
24
|
-
// The route handlers consult `isPublicRoute` before invoking
|
|
25
|
-
// `buildContextWithAuth` / `requireAuth`. Adding a route here without
|
|
26
|
-
// adding the corresponding `isPublicRoute` short-circuit in a handler is
|
|
27
|
-
// a no-op (auth still applies), so the failure mode is fail-closed.
|
|
28
|
-
// The only other unauthenticated paths are ServeOpts.publicJson's: GET only, checked at boot by assertPublicJson.
|
|
19
|
+
// Explicit allow-list for unauthenticated /v1/* routes: add a new one here AND in tests/server-bearer-lockdown.test.ts; never gate auth by `path.startsWith`.
|
|
20
|
+
// Handlers check `isPublicRoute` before auth, so an entry without that check is a no-op (fail-closed); other open paths are ServeOpts.publicJson GETs.
|
|
29
21
|
const PUBLIC_ROUTES = new Set([
|
|
30
22
|
'POST /v1/connectors/slack/events',
|
|
31
23
|
'POST /v1/connectors/github/events',
|
|
@@ -59,44 +51,44 @@ const V1_ROUTES = [
|
|
|
59
51
|
{ method: 'GET', path: '/v1/predictions/stats', storeReady: 'predictions', loop: 'off', handler: handlePredictionStats },
|
|
60
52
|
{ method: 'GET', regex: /^\/v1\/predictions\/(\d+)$/, storeReady: 'predictions', loop: 'off', handler: handleGetPrediction },
|
|
61
53
|
{ method: 'POST', regex: /^\/v1\/predictions\/(\d+)\/close$/, storeReady: 'predictions', loop: 'off', handler: handleClosePrediction },
|
|
62
|
-
{ method: 'POST', path: '/v1/decisions', storeReady: 'objects', handler: handleCreateDecision },
|
|
63
|
-
{ method: 'GET', path: '/v1/decisions', storeReady: 'objects', handler: handleListDecisions },
|
|
64
|
-
{ method: 'POST', regex: /^\/v1\/decisions\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedeDecision },
|
|
65
|
-
{ method: 'POST', regex: /^\/v1\/decisions\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseDecision },
|
|
66
|
-
{ method: 'GET', regex: /^\/v1\/decisions\/(\d+)$/, storeReady: 'objects', handler: handleGetDecision },
|
|
67
|
-
{ method: 'POST', path: '/v1/incidents', storeReady: 'objects', handler: handleCreateIncident },
|
|
68
|
-
{ method: 'GET', path: '/v1/incidents', storeReady: 'objects', handler: handleListIncidents },
|
|
69
|
-
{ method: 'POST', regex: /^\/v1\/incidents\/(\d+)\/resolve$/, storeReady: 'objects', handler: handleResolveIncident },
|
|
70
|
-
{ method: 'POST', regex: /^\/v1\/incidents\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseIncident },
|
|
71
|
-
{ method: 'GET', regex: /^\/v1\/incidents\/(\d+)$/, storeReady: 'objects', handler: handleGetIncident },
|
|
72
|
-
{ method: 'POST', path: '/v1/processes', storeReady: 'objects', handler: handleCreateProcess },
|
|
73
|
-
{ method: 'GET', path: '/v1/processes', storeReady: 'objects', handler: handleListProcesses },
|
|
74
|
-
{ method: 'POST', regex: /^\/v1\/processes\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedeProcess },
|
|
75
|
-
{ method: 'POST', regex: /^\/v1\/processes\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseProcess },
|
|
76
|
-
{ method: 'GET', regex: /^\/v1\/processes\/(\d+)$/, storeReady: 'objects', handler: handleGetProcess },
|
|
77
|
-
{ method: 'POST', path: '/v1/policies', storeReady: 'objects', handler: handleCreatePolicy },
|
|
78
|
-
{ method: 'GET', path: '/v1/policies', storeReady: 'objects', handler: handleListPolicies },
|
|
79
|
-
{ method: 'GET', path: '/v1/policies/asof', storeReady: 'objects', handler: handlePoliciesAsOf },
|
|
80
|
-
{ method: 'POST', regex: /^\/v1\/policies\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedePolicy },
|
|
81
|
-
{ method: 'POST', regex: /^\/v1\/policies\/(\d+)\/close$/, storeReady: 'objects', handler: handleClosePolicy },
|
|
82
|
-
{ method: 'GET', regex: /^\/v1\/policies\/(\d+)$/, storeReady: 'objects', handler: handleGetPolicy },
|
|
83
|
-
{ method: 'POST', path: '/v1/skills', storeReady: 'objects', handler: handleCreateSkill },
|
|
84
|
-
{ method: 'GET', path: '/v1/skills', storeReady: 'objects', handler: handleListSkills },
|
|
85
|
-
{ method: 'GET', path: '/v1/skills/export', storeReady: 'objects', handler: handleExportSkills },
|
|
86
|
-
{ method: 'POST', regex: /^\/v1\/skills\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedeSkill },
|
|
87
|
-
{ method: 'POST', regex: /^\/v1\/skills\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseSkill },
|
|
88
|
-
{ method: 'GET', regex: /^\/v1\/skills\/(\d+)$/, storeReady: 'objects', handler: handleGetSkill },
|
|
89
|
-
{ method: 'POST', path: '/v1/project-briefs', storeReady: 'objects', handler: handleCreateProjectBrief },
|
|
90
|
-
{ method: 'GET', path: '/v1/project-briefs', storeReady: 'objects', handler: handleListProjectBriefs },
|
|
91
|
-
{ method: 'POST', path: '/v1/project-briefs/refresh', storeReady: 'objects', handler: handleRefreshProjectBrief },
|
|
92
|
-
{ method: 'POST', regex: /^\/v1\/project-briefs\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedeProjectBrief },
|
|
93
|
-
{ method: 'POST', regex: /^\/v1\/project-briefs\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseProjectBrief },
|
|
94
|
-
{ method: 'GET', regex: /^\/v1\/project-briefs\/(\d+)$/, storeReady: 'objects', handler: handleGetProjectBrief },
|
|
95
|
-
{ method: 'POST', path: '/v1/customer-notes', storeReady: 'objects', handler: handleCreateCustomerNote },
|
|
96
|
-
{ method: 'GET', path: '/v1/customer-notes', storeReady: 'objects', handler: handleListCustomerNotes },
|
|
97
|
-
{ method: 'POST', regex: /^\/v1\/customer-notes\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedeCustomerNote },
|
|
98
|
-
{ method: 'POST', regex: /^\/v1\/customer-notes\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseCustomerNote },
|
|
99
|
-
{ method: 'GET', regex: /^\/v1\/customer-notes\/(\d+)$/, storeReady: 'objects', handler: handleGetCustomerNote },
|
|
54
|
+
{ method: 'POST', path: '/v1/decisions', storeReady: 'objects', loop: 'off', handler: handleCreateDecision },
|
|
55
|
+
{ method: 'GET', path: '/v1/decisions', storeReady: 'objects', loop: 'off', handler: handleListDecisions },
|
|
56
|
+
{ method: 'POST', regex: /^\/v1\/decisions\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedeDecision },
|
|
57
|
+
{ method: 'POST', regex: /^\/v1\/decisions\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseDecision },
|
|
58
|
+
{ method: 'GET', regex: /^\/v1\/decisions\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetDecision },
|
|
59
|
+
{ method: 'POST', path: '/v1/incidents', storeReady: 'objects', loop: 'off', handler: handleCreateIncident },
|
|
60
|
+
{ method: 'GET', path: '/v1/incidents', storeReady: 'objects', loop: 'off', handler: handleListIncidents },
|
|
61
|
+
{ method: 'POST', regex: /^\/v1\/incidents\/(\d+)\/resolve$/, storeReady: 'objects', loop: 'off', handler: handleResolveIncident },
|
|
62
|
+
{ method: 'POST', regex: /^\/v1\/incidents\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseIncident },
|
|
63
|
+
{ method: 'GET', regex: /^\/v1\/incidents\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetIncident },
|
|
64
|
+
{ method: 'POST', path: '/v1/processes', storeReady: 'objects', loop: 'off', handler: handleCreateProcess },
|
|
65
|
+
{ method: 'GET', path: '/v1/processes', storeReady: 'objects', loop: 'off', handler: handleListProcesses },
|
|
66
|
+
{ method: 'POST', regex: /^\/v1\/processes\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedeProcess },
|
|
67
|
+
{ method: 'POST', regex: /^\/v1\/processes\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseProcess },
|
|
68
|
+
{ method: 'GET', regex: /^\/v1\/processes\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetProcess },
|
|
69
|
+
{ method: 'POST', path: '/v1/policies', storeReady: 'objects', loop: 'off', handler: handleCreatePolicy },
|
|
70
|
+
{ method: 'GET', path: '/v1/policies', storeReady: 'objects', loop: 'off', handler: handleListPolicies },
|
|
71
|
+
{ method: 'GET', path: '/v1/policies/asof', storeReady: 'objects', loop: 'off', handler: handlePoliciesAsOf },
|
|
72
|
+
{ method: 'POST', regex: /^\/v1\/policies\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedePolicy },
|
|
73
|
+
{ method: 'POST', regex: /^\/v1\/policies\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleClosePolicy },
|
|
74
|
+
{ method: 'GET', regex: /^\/v1\/policies\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetPolicy },
|
|
75
|
+
{ method: 'POST', path: '/v1/skills', storeReady: 'objects', loop: 'off', handler: handleCreateSkill },
|
|
76
|
+
{ method: 'GET', path: '/v1/skills', storeReady: 'objects', loop: 'off', handler: handleListSkills },
|
|
77
|
+
{ method: 'GET', path: '/v1/skills/export', storeReady: 'objects', loop: 'off', handler: handleExportSkills },
|
|
78
|
+
{ method: 'POST', regex: /^\/v1\/skills\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedeSkill },
|
|
79
|
+
{ method: 'POST', regex: /^\/v1\/skills\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseSkill },
|
|
80
|
+
{ method: 'GET', regex: /^\/v1\/skills\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetSkill },
|
|
81
|
+
{ method: 'POST', path: '/v1/project-briefs', storeReady: 'objects', loop: 'off', handler: handleCreateProjectBrief },
|
|
82
|
+
{ method: 'GET', path: '/v1/project-briefs', storeReady: 'objects', loop: 'off', handler: handleListProjectBriefs },
|
|
83
|
+
{ method: 'POST', path: '/v1/project-briefs/refresh', storeReady: 'objects', loop: 'off', handler: handleRefreshProjectBrief },
|
|
84
|
+
{ method: 'POST', regex: /^\/v1\/project-briefs\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedeProjectBrief },
|
|
85
|
+
{ method: 'POST', regex: /^\/v1\/project-briefs\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseProjectBrief },
|
|
86
|
+
{ method: 'GET', regex: /^\/v1\/project-briefs\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetProjectBrief },
|
|
87
|
+
{ method: 'POST', path: '/v1/customer-notes', storeReady: 'objects', loop: 'off', handler: handleCreateCustomerNote },
|
|
88
|
+
{ method: 'GET', path: '/v1/customer-notes', storeReady: 'objects', loop: 'off', handler: handleListCustomerNotes },
|
|
89
|
+
{ method: 'POST', regex: /^\/v1\/customer-notes\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedeCustomerNote },
|
|
90
|
+
{ method: 'POST', regex: /^\/v1\/customer-notes\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseCustomerNote },
|
|
91
|
+
{ method: 'GET', regex: /^\/v1\/customer-notes\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetCustomerNote },
|
|
100
92
|
];
|
|
101
93
|
/** The route's handler bound to this request's path params, or null when method or path differ. The matcher runs before the
|
|
102
94
|
* method check, as the inline route blocks did, so a malformed `%` escape still throws from matchPath on any method. */
|
|
@@ -199,7 +191,8 @@ export function assertSqliteStore(opts) {
|
|
|
199
191
|
if (opts.store.kind !== 'sqlite')
|
|
200
192
|
throw new HttpError(501, STORE_NOT_PORTED_MESSAGE);
|
|
201
193
|
}
|
|
202
|
-
/** Under another store, a route that names no group, or one the store lacks, answers
|
|
194
|
+
/** Under another store, a route that names no group, or one the store lacks, answers
|
|
195
|
+
* 501 without running; the caller is checked first, so a bad key is still a 401. */
|
|
203
196
|
async function refuseUnportedRoute(req, opts, group) {
|
|
204
197
|
if (opts.store.kind === 'sqlite' || (group !== undefined && hasGroup(opts.store, group)))
|
|
205
198
|
return;
|
|
@@ -2,25 +2,20 @@
|
|
|
2
2
|
import { AUDIT_OPS } from '../../store/audit.js';
|
|
3
3
|
import { auditList, authCreate, authListRows, authRevoke, quarantineApprove, quarantineList, quarantineReject } from '../../api/index.js';
|
|
4
4
|
import { HttpError, readBody, sendJson } from '../../util/http-util.js';
|
|
5
|
-
import { log } from '../../util/log.js';
|
|
6
5
|
import { assertCrossTenantAdmin, buildContextWithAuth } from '../auth.js';
|
|
7
6
|
import { pageOf, parseCursor, setNextCursorHeader } from '../cursor.js';
|
|
8
7
|
import { isSetMember, parseJsonObjectText, parseListLimit, validateIdSegment } from '../validation.js';
|
|
9
|
-
import { isJsonString } from '../../util/json.js';
|
|
8
|
+
import { isJsonBoolean, isJsonNumber, isJsonString } from '../../util/json.js';
|
|
10
9
|
const VALID_AUDIT_OPS = new Set(AUDIT_OPS);
|
|
11
10
|
// POST /v1/auth/keys reads its body before auth, so an unauthenticated caller can make the server buffer at most this many bytes and wait at most this many ms.
|
|
12
11
|
// ServeOpts.mintBodyDeadlineMs overrides the wait for tests only, so a 408 test need not sit through it.
|
|
13
12
|
const MINT_BODY_MAX_BYTES = 4 * 1024;
|
|
14
13
|
const MINT_BODY_DEADLINE_MS = 10_000;
|
|
15
|
-
// Cap on GET /v1/audit?limit
|
|
16
|
-
// enough to dump a small deployment's full audit log without paginating, but
|
|
17
|
-
// small enough that a malicious client can't ask for the world.
|
|
14
|
+
// Cap on GET /v1/audit?limit=: large enough to dump a small deployment's audit log unpaginated, small enough that a client cannot ask for the world.
|
|
18
15
|
const MAX_AUDIT_LIMIT = 10000;
|
|
19
16
|
// GET /v1/auth/keys had no limit, so its default page is the cap: a deployment below 1000 keys sees no change.
|
|
20
17
|
const MAX_AUTH_KEYS_PAGE = 1000;
|
|
21
|
-
// POST /v1/auth/keys
|
|
22
|
-
// body: the HTTP layer hands it to the client; the user-facing
|
|
23
|
-
// "store this somewhere safe" warning belongs in the CLI client, not here.
|
|
18
|
+
// POST /v1/auth/keys: mints a key; plaintext is in the response body (the CLI client, not this layer, carries the "store this somewhere safe" warning).
|
|
24
19
|
export async function handleCreateAuthKey({ req, res, opts }) {
|
|
25
20
|
// Body first, so the resolver's check (and any gate in it) runs right before the mint with no wait between.
|
|
26
21
|
const raw = await readBody(req, { maxBytes: MINT_BODY_MAX_BYTES, deadlineMs: opts.mintBodyDeadlineMs ?? MINT_BODY_DEADLINE_MS });
|
|
@@ -30,9 +25,8 @@ export async function handleCreateAuthKey({ req, res, opts }) {
|
|
|
30
25
|
if (labelRaw !== undefined && !isJsonString(labelRaw)) {
|
|
31
26
|
throw new HttpError(400, 'label must be a string');
|
|
32
27
|
}
|
|
33
|
-
// Optional body.role mirrors the --role CLI flag
|
|
34
|
-
//
|
|
35
|
-
// fallback to admin). authCreate refuses a member caller with a 403.
|
|
28
|
+
// Optional body.role mirrors the --role CLI flag and is validated strictly: anything but 'admin'|'member' is a 400; authCreate refuses a member caller
|
|
29
|
+
// with a 403.
|
|
36
30
|
const roleRaw = body['role'];
|
|
37
31
|
let role;
|
|
38
32
|
if (roleRaw !== undefined) {
|
|
@@ -41,24 +35,27 @@ export async function handleCreateAuthKey({ req, res, opts }) {
|
|
|
41
35
|
}
|
|
42
36
|
role = roleRaw;
|
|
43
37
|
}
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
// A null is a 400 too: a client that serialises an unset field as null must not get a key that never expires.
|
|
39
|
+
const ttlDays = body['ttlDays'];
|
|
40
|
+
if (ttlDays !== undefined && !isJsonNumber(ttlDays)) {
|
|
41
|
+
throw new HttpError(400, 'ttlDays must be a number');
|
|
42
|
+
}
|
|
43
|
+
const noExpiry = body['noExpiry'];
|
|
44
|
+
if (noExpiry !== undefined && !isJsonBoolean(noExpiry)) {
|
|
45
|
+
throw new HttpError(400, 'noExpiry must be true or false');
|
|
46
|
+
}
|
|
47
|
+
// Security: any body `tenantId` is IGNORED; the key is bound to the caller's authenticated tenant, or tenant A could mint a key for tenant B (see
|
|
48
|
+
// authCreate).
|
|
48
49
|
const result = await authCreate(ctx, {
|
|
49
50
|
label: labelRaw,
|
|
50
51
|
role,
|
|
52
|
+
ttlDays,
|
|
53
|
+
noExpiry,
|
|
51
54
|
});
|
|
52
|
-
// The reply cannot change shape, so the server log is where a defaulted admin key gets noticed.
|
|
53
|
-
if (role === undefined && result.role === 'admin') {
|
|
54
|
-
log.warn(`auth key ${result.keyId} was minted with no role in the body, so it is an admin key, and it never expires; send "role": "member" for a narrower one`);
|
|
55
|
-
}
|
|
56
55
|
sendJson(res, 200, result);
|
|
57
56
|
return;
|
|
58
57
|
}
|
|
59
|
-
// GET /v1/auth/keys?active=true&limit=&cursor=:
|
|
60
|
-
// `active` defaults to true so the common case (show me usable keys) is
|
|
61
|
-
// a single GET; ?active=false includes revoked and expired rows.
|
|
58
|
+
// GET /v1/auth/keys?active=true&limit=&cursor=: lists keys visible to ctx.tenantId; `active` defaults to true, ?active=false includes revoked and expired rows.
|
|
62
59
|
export async function handleListAuthKeys({ req, res, opts, query }) {
|
|
63
60
|
const activeRaw = query.get('active');
|
|
64
61
|
let active = true;
|
|
@@ -78,9 +75,8 @@ export async function handleListAuthKeys({ req, res, opts, query }) {
|
|
|
78
75
|
sendJson(res, 200, page.items.map((r) => r.key));
|
|
79
76
|
return;
|
|
80
77
|
}
|
|
81
|
-
// DELETE /v1/auth/keys/:keyId
|
|
82
|
-
//
|
|
83
|
-
// 200 with the body rather than 204 so the caller sees revokedAt.
|
|
78
|
+
// DELETE /v1/auth/keys/:keyId revokes: missing or cross-tenant keys are 404 (no leak); a member is 403 on any key but its own or, via the resolver, those it
|
|
79
|
+
// minted. Returns 200 with the body rather than 204 so the caller sees revokedAt.
|
|
84
80
|
export async function handleRevokeAuthKey({ req, res, opts }, keyMatch) {
|
|
85
81
|
validateIdSegment(keyMatch.keyId, 'key id');
|
|
86
82
|
const ctx = await buildContextWithAuth(req, opts);
|
|
@@ -124,9 +120,7 @@ export async function handleRejectQuarantine({ req, res, opts }, quarantineRejec
|
|
|
124
120
|
sendJson(res, 200, { rejected: quarantineRejectMatch.id });
|
|
125
121
|
return;
|
|
126
122
|
}
|
|
127
|
-
// GET /v1/audit?op=&since=&limit=&cursor=:
|
|
128
|
-
// validated at the route boundary so an invalid value lands a 400 before
|
|
129
|
-
// we hit the DB.
|
|
123
|
+
// GET /v1/audit?op=&since=&limit=&cursor=: all three filters are validated at the route boundary so an invalid value is a 400 before the DB is hit.
|
|
130
124
|
export async function handleListAudit({ req, res, opts, query }) {
|
|
131
125
|
const opRaw = query.get('op');
|
|
132
126
|
let op;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// /v1/customer-notes routes.
|
|
2
|
-
import { CUSTOMER_NOTE, MAX_CUSTOMER_LEN } from '../../objects/customer-notes.js';
|
|
2
|
+
import { CUSTOMER_NOTE, MAX_CHANGE_SUMMARY_LEN, MAX_CUSTOMER_LEN } from '../../objects/customer-notes.js';
|
|
3
3
|
import { sendJson } from '../../util/http-util.js';
|
|
4
4
|
import { buildContextWithAuth } from '../auth.js';
|
|
5
5
|
import { parseJsonBody } from '../validation.js';
|
|
6
|
-
import {
|
|
6
|
+
import { saveFor } from '../../api/objects.js';
|
|
7
|
+
import { closeRoute, getRoute, listRoute, optionalString, requiredString, supersedeRoute } from './object-routes.js';
|
|
7
8
|
const noteRoutes = {
|
|
8
9
|
noun: 'customer note',
|
|
9
10
|
field: 'note',
|
|
@@ -12,22 +13,16 @@ const noteRoutes = {
|
|
|
12
13
|
filterParam: 'customer',
|
|
13
14
|
revise: (body) => {
|
|
14
15
|
const note = requiredString(body, 'note', { max: 8192 });
|
|
15
|
-
const changeSummary = optionalString(body, 'changeSummary',
|
|
16
|
+
const changeSummary = optionalString(body, 'changeSummary', MAX_CHANGE_SUMMARY_LEN);
|
|
16
17
|
return (existing, id) => ({ customer: existing.customer, note, changeSummary, supersedesNoteId: id });
|
|
17
18
|
},
|
|
18
19
|
};
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// 5 routes (no assembler/refresh): POST /v1/customer-notes (new; body customer +
|
|
22
|
-
// note), GET /v1/customer-notes (list; status + customer filter; shared
|
|
23
|
-
// parseListLimit), GET /v1/customer-notes/:id, POST /v1/customer-notes/:id/supersede,
|
|
24
|
-
// POST /v1/customer-notes/:id/close. DoS caps: customer 256, note 8192,
|
|
25
|
-
// changeSummary 4096. The store validates + throws; the boundary maps validation ->
|
|
26
|
-
// 400, not-found -> 404, not-active -> 409. Mirrors /v1/project-briefs.
|
|
20
|
+
// Routes: customer notes under /v1/customer-notes (create, list, show, supersede, close), mirroring /v1/project-briefs. The store validates and throws;
|
|
21
|
+
// the boundary maps validation -> 400, not-found -> 404, not-active -> 409.
|
|
27
22
|
export async function handleCreateCustomerNote(rr) {
|
|
28
23
|
const ctx = await buildContextWithAuth(rr.req, rr.opts);
|
|
29
24
|
const body = await parseJsonBody(rr.req, ctx);
|
|
30
|
-
const customerNote = await saveFor(
|
|
25
|
+
const customerNote = await saveFor(ctx, CUSTOMER_NOTE, {
|
|
31
26
|
customer: requiredString(body, 'customer', { max: MAX_CUSTOMER_LEN }),
|
|
32
27
|
note: requiredString(body, 'note', { max: 8192 }),
|
|
33
28
|
});
|
|
@@ -4,10 +4,11 @@ import { HttpError, sendJson } from '../../util/http-util.js';
|
|
|
4
4
|
import { NotFoundError } from '../../core/api-errors.js';
|
|
5
5
|
import { isJsonNumber } from '../../util/json.js';
|
|
6
6
|
import { buildContextWithAuth } from '../auth.js';
|
|
7
|
-
import { parseJsonBody } from '../validation.js';
|
|
8
|
-
import {
|
|
7
|
+
import { MAX_SHORT_FIELD_LEN, parseJsonBody } from '../validation.js';
|
|
8
|
+
import { saveFor } from '../../api/objects.js';
|
|
9
|
+
import { closeRoute, getRoute, listRoute, optionalString, requiredString } from './object-routes.js';
|
|
9
10
|
const decisionRoutes = { noun: 'decision', field: 'decision', listField: 'decisions', object: DECISION };
|
|
10
|
-
const TEXT = { max:
|
|
11
|
+
const TEXT = { max: MAX_SHORT_FIELD_LEN, untrimmed: true };
|
|
11
12
|
function supersededId(body) {
|
|
12
13
|
const raw = body['supersedesDecisionId'];
|
|
13
14
|
if (raw === undefined || raw === null)
|
|
@@ -17,27 +18,19 @@ function supersededId(body) {
|
|
|
17
18
|
}
|
|
18
19
|
return raw;
|
|
19
20
|
}
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// 5 routes: POST /v1/decisions (create, optional supersedesDecisionId),
|
|
23
|
-
// GET /v1/decisions (list, status filter), GET /v1/decisions/:id (show),
|
|
24
|
-
// POST /v1/decisions/:id/supersede (create a successor + supersede :id),
|
|
25
|
-
// POST /v1/decisions/:id/close (retire). Bearer-authed + tenant-scoped via
|
|
26
|
-
// buildContextWithAuth. status validated against VALID_DECISION_STATES.
|
|
27
|
-
// DoS caps: text 4096, context 4096. The HTTP surface is
|
|
28
|
-
// new (no legacy --supersedes <memory-id> constraint), so it supersedes by
|
|
29
|
-
// table id and never weakens a memory mirror.
|
|
21
|
+
// Routes: /v1/decisions (create, list, show, supersede, close), Bearer-authed and tenant-scoped; status is validated against VALID_DECISION_STATES.
|
|
22
|
+
// The HTTP surface has no legacy --supersedes <memory-id> constraint, so it supersedes by table id and never weakens a memory mirror.
|
|
30
23
|
export async function handleCreateDecision(rr) {
|
|
31
24
|
const { req, res } = rr;
|
|
32
25
|
const ctx = await buildContextWithAuth(req, rr.opts);
|
|
33
26
|
const body = await parseJsonBody(req, ctx);
|
|
34
27
|
const write = {
|
|
35
28
|
decisionText: requiredString(body, 'text', TEXT),
|
|
36
|
-
context: optionalString(body, 'context',
|
|
29
|
+
context: optionalString(body, 'context', MAX_SHORT_FIELD_LEN),
|
|
37
30
|
supersedesDecisionId: supersededId(body),
|
|
38
31
|
};
|
|
39
32
|
try {
|
|
40
|
-
const decision = await saveFor(
|
|
33
|
+
const decision = await saveFor(ctx, DECISION, write);
|
|
41
34
|
sendJson(res, 201, { decision });
|
|
42
35
|
}
|
|
43
36
|
catch (e) {
|
|
@@ -55,9 +48,9 @@ export async function handleSupersedeDecision(rr, match) {
|
|
|
55
48
|
const oldId = parseInt(match[1], 10);
|
|
56
49
|
const ctx = await buildContextWithAuth(rr.req, rr.opts);
|
|
57
50
|
const body = await parseJsonBody(rr.req, ctx);
|
|
58
|
-
const decision = await saveFor(
|
|
51
|
+
const decision = await saveFor(ctx, DECISION, {
|
|
59
52
|
decisionText: requiredString(body, 'text', TEXT),
|
|
60
|
-
context: optionalString(body, 'context',
|
|
53
|
+
context: optionalString(body, 'context', MAX_SHORT_FIELD_LEN),
|
|
61
54
|
supersedesDecisionId: oldId,
|
|
62
55
|
});
|
|
63
56
|
sendJson(rr.res, 201, { decision });
|
|
@@ -3,9 +3,10 @@ import { INCIDENT, openIncident, resolveOpenIncident } from '../../objects/incid
|
|
|
3
3
|
import { HttpError, sendJson } from '../../util/http-util.js';
|
|
4
4
|
import { NotFoundError } from '../../core/api-errors.js';
|
|
5
5
|
import { buildContextWithAuth } from '../auth.js';
|
|
6
|
-
import { parseJsonBody } from '../validation.js';
|
|
6
|
+
import { MAX_SHORT_FIELD_LEN, parseJsonBody } from '../validation.js';
|
|
7
7
|
import { isJsonString } from '../../util/json.js';
|
|
8
|
-
import {
|
|
8
|
+
import { objectsOf } from '../../api/objects.js';
|
|
9
|
+
import { closeRoute, getRoute, listRoute, optionalString, requiredString } from './object-routes.js';
|
|
9
10
|
const MAX_LINKED_MEMORY_IDS = 256;
|
|
10
11
|
const incidentRoutes = { noun: 'incident', field: 'incident', listField: 'incidents', object: INCIDENT };
|
|
11
12
|
function linkedMemoryIds(body) {
|
|
@@ -18,34 +19,25 @@ function linkedMemoryIds(body) {
|
|
|
18
19
|
if (raw.length > MAX_LINKED_MEMORY_IDS) {
|
|
19
20
|
throw new HttpError(400, `linkedMemoryIds exceeds ${MAX_LINKED_MEMORY_IDS}-item cap`);
|
|
20
21
|
}
|
|
21
|
-
const isValidMemoryId = (item) => isJsonString(item) && item.length > 0 && item.length <=
|
|
22
|
+
const isValidMemoryId = (item) => isJsonString(item) && item.length > 0 && item.length <= MAX_SHORT_FIELD_LEN;
|
|
22
23
|
if (!raw.every(isValidMemoryId)) {
|
|
23
|
-
throw new HttpError(400,
|
|
24
|
+
throw new HttpError(400, `each linkedMemoryIds entry must be a non-empty string <= ${MAX_SHORT_FIELD_LEN} chars`);
|
|
24
25
|
}
|
|
25
26
|
return raw;
|
|
26
27
|
}
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// 5 routes: POST /v1/incidents (open; body text + context + linkedMemoryIds[]),
|
|
30
|
-
// GET /v1/incidents (list, status filter), GET /v1/incidents/:id (show),
|
|
31
|
-
// POST /v1/incidents/:id/resolve (open -> resolved; body resolutionText),
|
|
32
|
-
// POST /v1/incidents/:id/close (open|resolved -> closed). Bearer-authed +
|
|
33
|
-
// tenant-scoped via buildContextWithAuth. status validated against
|
|
34
|
-
// VALID_INCIDENT_STATES. DoS caps: text 4096, context 4096, resolutionText
|
|
35
|
-
// 4096. Mirrors /v1/decisions; lifecycle is
|
|
36
|
-
// open->resolved->closed (no supersede), so linkedMemoryIds replaces
|
|
37
|
-
// supersedesDecisionId on create.
|
|
28
|
+
// Routes: /v1/incidents (open, list, show, resolve, close), mirroring /v1/decisions; status is validated against VALID_INCIDENT_STATES.
|
|
29
|
+
// Lifecycle is open->resolved->closed (no supersede), so linkedMemoryIds replaces supersedesDecisionId on create.
|
|
38
30
|
export async function handleCreateIncident(rr) {
|
|
39
31
|
const { req, res, opts } = rr;
|
|
40
32
|
const ctx = await buildContextWithAuth(req, opts);
|
|
41
33
|
const body = await parseJsonBody(req, ctx);
|
|
42
34
|
const write = {
|
|
43
|
-
incidentText: requiredString(body, 'text', { max:
|
|
44
|
-
context: optionalString(body, 'context',
|
|
35
|
+
incidentText: requiredString(body, 'text', { max: MAX_SHORT_FIELD_LEN, untrimmed: true }),
|
|
36
|
+
context: optionalString(body, 'context', MAX_SHORT_FIELD_LEN),
|
|
45
37
|
linkedMemoryIds: linkedMemoryIds(body),
|
|
46
38
|
};
|
|
47
39
|
try {
|
|
48
|
-
const incident = await openIncident(objectsOf(
|
|
40
|
+
const incident = await openIncident(objectsOf(ctx), opts.hippoRoot, ctx.tenantId, write, ctx.actor.subject);
|
|
49
41
|
sendJson(res, 201, { incident });
|
|
50
42
|
}
|
|
51
43
|
catch (e) {
|
|
@@ -62,8 +54,8 @@ export async function handleResolveIncident(rr, match) {
|
|
|
62
54
|
const { req, res, opts } = rr;
|
|
63
55
|
const id = parseInt(match[1], 10);
|
|
64
56
|
const ctx = await buildContextWithAuth(req, opts);
|
|
65
|
-
const resolutionText = requiredString(await parseJsonBody(req, ctx), 'resolutionText', { max:
|
|
66
|
-
const incident = await resolveOpenIncident(objectsOf(
|
|
57
|
+
const resolutionText = requiredString(await parseJsonBody(req, ctx), 'resolutionText', { max: MAX_SHORT_FIELD_LEN });
|
|
58
|
+
const incident = await resolveOpenIncident(objectsOf(ctx), ctx.tenantId, id, resolutionText, ctx.actor.subject);
|
|
67
59
|
sendJson(res, 200, { incident });
|
|
68
60
|
}
|
|
69
61
|
export function handleCloseIncident(rr, match) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Memory write routes: create, graph, archive, supersede, promote, forget, outcome, sleep.
|
|
2
|
+
import { graphRows } from '../../api/graph.js';
|
|
2
3
|
import { archiveRaw, forget, outcome, outcomeForLastRecall, promote, remember, supersede } from '../../api/index.js';
|
|
3
4
|
import { graphModelOf } from '../../graph/view.js';
|
|
4
5
|
import { MAX_ENTITY_NAME_LEN } from '../../store/graph-rows.js';
|
|
5
6
|
import { HttpError, sendJson } from '../../util/http-util.js';
|
|
6
|
-
import { requireGroup } from '../../store/port.js';
|
|
7
7
|
import { assertCrossTenantAdmin, buildContextWithAuth, isLoopback } from '../auth.js';
|
|
8
8
|
import { sleepInChild } from '../sleep-offload.js';
|
|
9
9
|
import { getCallerProject, getString, getStringArray, isSetMember, parseJsonBody, parseListLimit, validateIdSegment } from '../validation.js';
|
|
@@ -53,8 +53,7 @@ export async function handleGetGraph({ req, res, opts, query }) {
|
|
|
53
53
|
}
|
|
54
54
|
const limit = parseListLimit(query.get('limit'));
|
|
55
55
|
const ctx = await buildContextWithAuth(req, opts);
|
|
56
|
-
const
|
|
57
|
-
const rows = await requireGroup(opts.store, 'graphReads').graphRows(ctx.tenantId, { entity: entityRaw ?? undefined, limit, reader: { role, scopes, owner } });
|
|
56
|
+
const rows = await graphRows(ctx, { entity: entityRaw ?? undefined, limit });
|
|
58
57
|
sendJson(res, 200, graphModelOf(rows));
|
|
59
58
|
return;
|
|
60
59
|
}
|
|
@@ -97,12 +96,8 @@ export async function handleForgetMemory({ req, res, opts }, idMatch) {
|
|
|
97
96
|
sendJson(res, 200, result);
|
|
98
97
|
return;
|
|
99
98
|
}
|
|
100
|
-
// POST /v1/outcome
|
|
101
|
-
//
|
|
102
|
-
// the last-recall path (api.outcomeForLastRecall); returned shape is
|
|
103
|
-
// {applied, ids} in that case so callers can disambiguate "no recent
|
|
104
|
-
// recall" from "all ids skipped". Each applied id writes one audit_log
|
|
105
|
-
// row (op='outcome', actor from Bearer).
|
|
99
|
+
// POST /v1/outcome: applies a positive/negative outcome to memory ids; without ids it falls back to the last-recall path (api.outcomeForLastRecall)
|
|
100
|
+
// and returns {applied, ids}, so callers can tell "no recent recall" from "all ids skipped". Each applied id writes one audit_log row (op='outcome').
|
|
106
101
|
export async function handleApplyOutcome({ req, res, opts }) {
|
|
107
102
|
const ctx = await buildContextWithAuth(req, opts);
|
|
108
103
|
const body = await parseJsonBody(req, ctx);
|
|
@@ -120,10 +115,8 @@ export async function handleApplyOutcome({ req, res, opts }) {
|
|
|
120
115
|
if (!idsRaw.every(isNonEmptyId)) {
|
|
121
116
|
throw new HttpError(400, 'ids must be an array of non-empty strings');
|
|
122
117
|
}
|
|
123
|
-
// DoS cap on ids.length
|
|
124
|
-
//
|
|
125
|
-
// to sub-second wall time on SQLite hot path. Cap BEFORE buildContextWithAuth
|
|
126
|
-
// so attack traffic doesn't pay the api-key lookup cost.
|
|
118
|
+
// DoS cap on ids.length (each id costs ~3 DB ops): N=1000 keeps per-request work sub-second on SQLite.
|
|
119
|
+
// Checked BEFORE buildContextWithAuth so attack traffic does not pay the api-key lookup.
|
|
127
120
|
if (idsRaw.length > 1000) {
|
|
128
121
|
throw new HttpError(400, 'ids exceeds 1000-id cap');
|
|
129
122
|
}
|
|
@@ -142,29 +135,16 @@ export async function handleApplyOutcome({ req, res, opts }) {
|
|
|
142
135
|
}
|
|
143
136
|
return;
|
|
144
137
|
}
|
|
145
|
-
// POST /v1/sleep
|
|
146
|
-
//
|
|
147
|
-
// this per-request loopback assertion makes the host-wide semantic fail-
|
|
148
|
-
// closed regardless of any future serve() boot-config change. Body:
|
|
149
|
-
// {dry_run?, no_share?}. Returns SleepResult JSON.
|
|
150
|
-
//
|
|
151
|
-
// Tenant scope (Episode A follow-up tracked in TODOS.md): api.sleep operates
|
|
152
|
-
// on the WHOLE hippoRoot (cross-tenant by design, matching CLI cmdSleep).
|
|
153
|
-
// The loopback-only guard is the trust boundary today. Future non-loopback
|
|
154
|
-
// serving must also zero the cross-tenant counters for other tenants.
|
|
138
|
+
// POST /v1/sleep: host-wide consolidation (consolidate, dedup, audit, share, ambient); api.sleep spans the WHOLE hippoRoot, cross-tenant by design, so the
|
|
139
|
+
// loopback-only guard is the trust boundary; non-loopback serving must also zero cross-tenant counters. Body {dry_run?, no_share?}; returns SleepResult JSON.
|
|
155
140
|
export async function handleSleep({ req, res, opts }) {
|
|
156
|
-
// Defensive per-request loopback guard
|
|
157
|
-
//
|
|
158
|
-
// NAT64 prefixes) flows through without drift. serve()'s boot-time host
|
|
159
|
-
// check is the primary trust boundary; this is belt-and-suspenders.
|
|
141
|
+
// Defensive per-request loopback guard using isLoopback(), so future mapped/IPv6/NAT64 forms flow through without drift;
|
|
142
|
+
// serve()'s boot-time host check is the primary trust boundary.
|
|
160
143
|
if (!isLoopback(req.socket.remoteAddress)) {
|
|
161
144
|
throw new HttpError(403, '/v1/sleep is loopback-only (host-wide consolidation; see CHANGELOG v1.11.4)');
|
|
162
145
|
}
|
|
163
|
-
// Admin-role gate
|
|
164
|
-
//
|
|
165
|
-
// any Bearer-authed caller now carries an explicit role from the api_keys
|
|
166
|
-
// row). When non-loopback serving lands, this gate is the actual auth
|
|
167
|
-
// boundary on host-wide sleep.
|
|
146
|
+
// Admin-role gate, forward-defensive: loopback fallback is admin by default and Bearer callers carry an explicit role from the api_keys row;
|
|
147
|
+
// when non-loopback serving lands, this gate is the real auth boundary on host-wide sleep.
|
|
168
148
|
const sleepCtx = await buildContextWithAuth(req, opts);
|
|
169
149
|
// Sleep consolidates every tenant under hippoRoot, so it is a cross-tenant action.
|
|
170
150
|
assertCrossTenantAdmin(sleepCtx, '/v1/sleep');
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type JsonValue } from '../../util/json.js';
|
|
2
2
|
import type { ObjectDescriptor, SavableDescriptor } from '../../objects/descriptor.js';
|
|
3
3
|
import type { ObjectByKind, ObjectKind, SavableKind } from '../../store/object-types.js';
|
|
4
|
-
import { type Objects } from '../../store/port.js';
|
|
5
4
|
import type { RouteRequest } from '../types.js';
|
|
6
5
|
export interface ObjectRouteConfig<K extends ObjectKind> {
|
|
7
6
|
/** Names the object in the 404 text, e.g. "customer note". */
|
|
@@ -29,10 +28,6 @@ export interface RequiredStringRule {
|
|
|
29
28
|
}
|
|
30
29
|
export declare function requiredString(body: Record<string, JsonValue>, key: string, rule: RequiredStringRule): string;
|
|
31
30
|
export declare function optionalString(body: Record<string, JsonValue>, key: string, max: number): string | undefined;
|
|
32
|
-
/** The request store's object group; the dispatcher has already answered 501 for a store without it. */
|
|
33
|
-
export declare function objectsOf({ opts }: RouteRequest): Objects;
|
|
34
|
-
/** Saves through the request's store as the authenticated caller. */
|
|
35
|
-
export declare function saveFor<K extends SavableKind, W>(rr: RouteRequest, d: SavableDescriptor<K, W>, tenantId: string, actor: string, write: W): Promise<ObjectByKind[K]>;
|
|
36
31
|
export declare function listRoute<K extends ObjectKind>(cfg: ObjectRouteConfig<K>, rr: RouteRequest): Promise<void>;
|
|
37
32
|
export declare function getRoute<K extends ObjectKind>(cfg: ObjectRouteConfig<K>, rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
|
|
38
33
|
export declare function closeRoute<K extends ObjectKind>(cfg: ObjectRouteConfig<K>, rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// List, get, close and supersede handlers shared by the typed-object routes; each route file passes one config.
|
|
2
2
|
import { HttpError, sendJson } from '../../util/http-util.js';
|
|
3
3
|
import { isJsonString } from '../../util/json.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { objectsOf, saveFor } from '../../api/objects.js';
|
|
5
|
+
import { closeObject, listObjects, objectById } from '../../objects/lifecycle.js';
|
|
6
6
|
import { buildContextWithAuth } from '../auth.js';
|
|
7
7
|
import { byCreatedAt, pageOf, parseCursor } from '../cursor.js';
|
|
8
8
|
import { isSetMember, parseJsonBody, parseListLimit } from '../validation.js';
|
|
@@ -26,14 +26,6 @@ export function optionalString(body, key, max) {
|
|
|
26
26
|
throw new HttpError(400, `${key} exceeds ${max}-character cap`);
|
|
27
27
|
return value;
|
|
28
28
|
}
|
|
29
|
-
/** The request store's object group; the dispatcher has already answered 501 for a store without it. */
|
|
30
|
-
export function objectsOf({ opts }) {
|
|
31
|
-
return requireGroup(opts.store, 'objects');
|
|
32
|
-
}
|
|
33
|
-
/** Saves through the request's store as the authenticated caller. */
|
|
34
|
-
export function saveFor(rr, d, tenantId, actor, write) {
|
|
35
|
-
return saveObject(objectsOf(rr), d, { hippoRoot: rr.opts.hippoRoot, tenantId, actor }, write);
|
|
36
|
-
}
|
|
37
29
|
async function found(cfg, objects, tenantId, id) {
|
|
38
30
|
const row = await objectById(objects, cfg.object, tenantId, id);
|
|
39
31
|
if (!row)
|
|
@@ -56,25 +48,25 @@ export async function listRoute(cfg, rr) {
|
|
|
56
48
|
}
|
|
57
49
|
listOpts.status = status;
|
|
58
50
|
}
|
|
59
|
-
const page = pageOf(await listObjects(objectsOf(
|
|
51
|
+
const page = pageOf(await listObjects(objectsOf(ctx), cfg.object, ctx.tenantId, listOpts), limit, byCreatedAt);
|
|
60
52
|
sendJson(res, 200, { [cfg.listField]: page.items, next_cursor: page.nextCursor });
|
|
61
53
|
}
|
|
62
54
|
export async function getRoute(cfg, rr, match) {
|
|
63
55
|
const id = parseInt(match[1], 10);
|
|
64
56
|
const ctx = await buildContextWithAuth(rr.req, rr.opts);
|
|
65
|
-
sendJson(rr.res, 200, { [cfg.field]: await found(cfg, objectsOf(
|
|
57
|
+
sendJson(rr.res, 200, { [cfg.field]: await found(cfg, objectsOf(ctx), ctx.tenantId, id) });
|
|
66
58
|
}
|
|
67
59
|
export async function closeRoute(cfg, rr, match) {
|
|
68
60
|
const id = parseInt(match[1], 10);
|
|
69
61
|
const ctx = await buildContextWithAuth(rr.req, rr.opts);
|
|
70
|
-
sendJson(rr.res, 200, { [cfg.field]: await closeObject(objectsOf(
|
|
62
|
+
sendJson(rr.res, 200, { [cfg.field]: await closeObject(objectsOf(ctx), cfg.object, ctx.tenantId, id, ctx.actor.subject) });
|
|
71
63
|
}
|
|
72
64
|
export async function supersedeRoute(cfg, rr, match) {
|
|
73
65
|
const id = parseInt(match[1], 10);
|
|
74
66
|
const ctx = await buildContextWithAuth(rr.req, rr.opts);
|
|
75
67
|
const successor = cfg.revise(await parseJsonBody(rr.req, ctx));
|
|
76
|
-
const existing = await found(cfg, objectsOf(
|
|
77
|
-
const saved = await saveFor(
|
|
68
|
+
const existing = await found(cfg, objectsOf(ctx), ctx.tenantId, id);
|
|
69
|
+
const saved = await saveFor(ctx, cfg.object, successor(existing, id));
|
|
78
70
|
sendJson(rr.res, 200, { [cfg.field]: saved });
|
|
79
71
|
}
|
|
80
72
|
//# sourceMappingURL=object-routes.js.map
|