hippo-memory 1.40.0 → 1.42.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.
Files changed (54) hide show
  1. package/README.md +8 -4
  2. package/dist/card.d.ts +3 -1
  3. package/dist/card.d.ts.map +1 -1
  4. package/dist/card.js +4 -1
  5. package/dist/card.js.map +1 -1
  6. package/dist/cli.d.ts +1 -1
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +106 -24
  9. package/dist/cli.js.map +1 -1
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +2 -2
  13. package/dist/index.js.map +1 -1
  14. package/dist/rerankers/cross-encoder.d.ts +1 -8
  15. package/dist/rerankers/cross-encoder.d.ts.map +1 -1
  16. package/dist/rerankers/cross-encoder.js +58 -52
  17. package/dist/rerankers/cross-encoder.js.map +1 -1
  18. package/dist/rerankers/index.d.ts.map +1 -1
  19. package/dist/rerankers/index.js +2 -0
  20. package/dist/rerankers/index.js.map +1 -1
  21. package/dist/rerankers/jev.d.ts +9 -0
  22. package/dist/rerankers/jev.d.ts.map +1 -0
  23. package/dist/rerankers/jev.js +116 -0
  24. package/dist/rerankers/jev.js.map +1 -0
  25. package/dist/rerankers/types.d.ts +7 -4
  26. package/dist/rerankers/types.d.ts.map +1 -1
  27. package/dist/src/card.js +4 -1
  28. package/dist/src/card.js.map +1 -1
  29. package/dist/src/cli.js +106 -24
  30. package/dist/src/cli.js.map +1 -1
  31. package/dist/src/index.js +2 -2
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/rerankers/cross-encoder.js +58 -52
  34. package/dist/src/rerankers/cross-encoder.js.map +1 -1
  35. package/dist/src/rerankers/index.js +2 -0
  36. package/dist/src/rerankers/index.js.map +1 -1
  37. package/dist/src/rerankers/jev.js +116 -0
  38. package/dist/src/rerankers/jev.js.map +1 -0
  39. package/dist/src/store.js +122 -22
  40. package/dist/src/store.js.map +1 -1
  41. package/dist/src/version.js +2 -15
  42. package/dist/src/version.js.map +1 -1
  43. package/dist/store.d.ts +14 -8
  44. package/dist/store.d.ts.map +1 -1
  45. package/dist/store.js +122 -22
  46. package/dist/store.js.map +1 -1
  47. package/dist/version.d.ts +2 -15
  48. package/dist/version.d.ts.map +1 -1
  49. package/dist/version.js +2 -15
  50. package/dist/version.js.map +1 -1
  51. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  52. package/extensions/openclaw-plugin/package.json +1 -1
  53. package/openclaw.plugin.json +1 -1
  54. package/package.json +1 -1
package/README.md CHANGED
@@ -622,10 +622,12 @@ hippo watch "npm run build"
622
622
  | `hippo card create --title "..."` | Create a work-queue card (`--repo`, `--contract`, `--budget`, repeatable `--depends-on <id>`) |
623
623
  | `hippo card show <id>` | Show a card, its deps, runs, comments and latest handoff |
624
624
  | `hippo card list [--status <status>]` | List cards, newest-updated first |
625
- | `hippo card claim <id> --runtime <name>` | Claim a ready or blocked card |
626
- | `hippo card block <id> --reason "<why>"` | Block a running card; the reason is recorded as a comment |
627
- | `hippo card review <id>` | Move a running card to review |
628
- | `hippo card complete <id> --outcome <success\|failure\|partial>` | Complete a card in review: `success` marks it done and promotes children whose parents are all done; `failure` or `partial` shelves it |
625
+ | `hippo card claim <id> --runtime <name>` | Claim a ready or blocked card; prints its run id and the time its 4-hour lease expires |
626
+ | `hippo card heartbeat <id> --run <n>` | Extend a claimed card's lease |
627
+ | `hippo card block <id> --reason "<why>" [--run <n>]` | Block a running card; the reason is recorded as a comment |
628
+ | `hippo card review <id> [--run <n>]` | Move a running card to review |
629
+ | `hippo card complete <id> --outcome <success\|failure\|partial> [--run <n>]` | Complete a card in review: `success` marks it done and promotes children whose parents are all done; `failure` or `partial` shelves it |
630
+ | `hippo card reclaim` | Sweep every card whose lease has passed and return it to ready |
629
631
  | `hippo card comment <id> --body "..."` | Add a comment to a card |
630
632
  | `hippo wm push --scope <s> --content "..."` | Push to working memory |
631
633
  | `hippo wm read --scope <s>` | Read working memory entries |
@@ -635,6 +637,8 @@ hippo watch "npm run build"
635
637
  | `hippo dashboard --port <n>` | Use custom port |
636
638
  | `hippo mcp` | Start MCP server (stdio transport) |
637
639
 
640
+ On `heartbeat`, `block`, `review` and `complete`, a given `--run` is checked against the card's live run and the command is refused, unchanged, if the two do not match.
641
+
638
642
  ---
639
643
 
640
644
  ## Framework Integrations
package/dist/card.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  export type CardStatus = 'backlog' | 'ready' | 'running' | 'blocked' | 'review' | 'done' | 'shelved';
3
3
  /** Narrows a raw string, such as a CLI flag value, to a CardStatus. */
4
4
  export declare function isCardStatus(v: string): v is CardStatus;
5
- /** A card row; leaseUntil and heartbeatAt are reserved for W2b's lease and heartbeat, W2a never writes them and they stay null. */
5
+ /** A card row; leaseUntil and heartbeatAt are null unless the card is running, and a card claimed under 1.40.0 runs with both null until a heartbeat sets them. */
6
6
  export interface Card {
7
7
  id: string;
8
8
  title: string;
@@ -42,4 +42,6 @@ export type CardTransitions = {
42
42
  };
43
43
  /** transitionCard checks every status write against this map. */
44
44
  export declare const CARD_TRANSITIONS: CardTransitions;
45
+ /** How long a claim or a heartbeat keeps a running card's lease before reclaimExpiredCards may return it to ready. */
46
+ export declare const CARD_LEASE_MS: number;
45
47
  //# sourceMappingURL=card.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../src/card.ts"],"names":[],"mappings":"AACA,gGAAgG;AAChG,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAIrG,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,UAAU,CAEvD;AAED,mIAAmI;AACnI,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,yDAAyD;AACzD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,gCAAgC;AAChC,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mDAAmD;AACnD,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,EAAE,CAAC,IAAI,UAAU,GAAG,SAAS,UAAU,EAAE;CAAE,CAAC;AAEpF,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,EAAE,eAQ9B,CAAC"}
1
+ {"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../src/card.ts"],"names":[],"mappings":"AACA,gGAAgG;AAChG,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAIrG,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,UAAU,CAEvD;AAED,mKAAmK;AACnK,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,yDAAyD;AACzD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,gCAAgC;AAChC,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mDAAmD;AACnD,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,EAAE,CAAC,IAAI,UAAU,GAAG,SAAS,UAAU,EAAE;CAAE,CAAC;AAEpF,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,EAAE,eAQ9B,CAAC;AAGF,sHAAsH;AACtH,eAAO,MAAM,aAAa,QAAqB,CAAC"}
package/dist/card.js CHANGED
@@ -7,10 +7,13 @@ export function isCardStatus(v) {
7
7
  export const CARD_TRANSITIONS = {
8
8
  backlog: ['ready'],
9
9
  ready: ['running'],
10
- running: ['blocked', 'review'],
10
+ running: ['blocked', 'review', 'ready'],
11
11
  blocked: ['running'],
12
12
  review: ['done', 'shelved'],
13
13
  done: [],
14
14
  shelved: [],
15
15
  };
16
+ // SHORTCUT: one fixed lease for every claim; add a per-claim lease when the W3 dispatcher heartbeats on a timer.
17
+ /** How long a claim or a heartbeat keeps a running card's lease before reclaimExpiredCards may return it to ready. */
18
+ export const CARD_LEASE_MS = 4 * 60 * 60 * 1000;
16
19
  //# sourceMappingURL=card.js.map
package/dist/card.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"card.js","sourceRoot":"","sources":["../src/card.ts"],"names":[],"mappings":"AAIA,MAAM,aAAa,GAA0B,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAErH,uEAAuE;AACvE,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5C,CAAC;AA0CD,iEAAiE;AACjE,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C,OAAO,EAAE,CAAC,OAAO,CAAC;IAClB,KAAK,EAAE,CAAC,SAAS,CAAC;IAClB,OAAO,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC;IAC9B,OAAO,EAAE,CAAC,SAAS,CAAC;IACpB,MAAM,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,EAAE;CACZ,CAAC"}
1
+ {"version":3,"file":"card.js","sourceRoot":"","sources":["../src/card.ts"],"names":[],"mappings":"AAIA,MAAM,aAAa,GAA0B,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAErH,uEAAuE;AACvE,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5C,CAAC;AA0CD,iEAAiE;AACjE,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C,OAAO,EAAE,CAAC,OAAO,CAAC;IAClB,KAAK,EAAE,CAAC,SAAS,CAAC;IAClB,OAAO,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC;IACvC,OAAO,EAAE,CAAC,SAAS,CAAC;IACpB,MAAM,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,EAAE;CACZ,CAAC;AAEF,iHAAiH;AACjH,sHAAsH;AACtH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC"}
package/dist/cli.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * hippo snapshot <save|show|clear>
14
14
  * hippo session <log|show|latest|resume|complete>
15
15
  * hippo handoff <create|latest|show>
16
- * hippo card <create|show|list|claim|block|review|complete|comment>
16
+ * hippo card <create|show|list|claim|heartbeat|block|review|complete|reclaim|comment>
17
17
  * hippo current <show>
18
18
  * hippo forget <id> [--archive --reason "<why>"]
19
19
  * hippo reject <id>|--value "<text>" --reason "<why>"
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA2BH,OAAO,EAWL,WAAW,EAEZ,MAAM,aAAa,CAAC;AA2HrB,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AA4ChC,kFAAkF;AAClF,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AAsJD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAA;CAAE,CAwCjI;AAqxED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,MAAqB,GAAG,MAAM,CAyF3F;AAyGD;;;GAGG;AACH,oHAAoH;AACpH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAuD/D;AA87GD,oHAAoH;AACpH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,EACtF,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,MAAkB,GAC1B,IAAI,CA8BN"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA2BH,OAAO,EAWL,WAAW,EAEZ,MAAM,aAAa,CAAC;AA6HrB,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AA4ChC,kFAAkF;AAClF,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AAuJD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAA;CAAE,CAwCjI;AAqxED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,MAAqB,GAAG,MAAM,CAyF3F;AAyGD;;;GAGG;AACH,oHAAoH;AACpH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAuD/D;AAigHD,oHAAoH;AACpH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,EACtF,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,MAAkB,GAC1B,IAAI,CA8BN"}
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * hippo snapshot <save|show|clear>
14
14
  * hippo session <log|show|latest|resume|complete>
15
15
  * hippo handoff <create|latest|show>
16
- * hippo card <create|show|list|claim|block|review|complete|comment>
16
+ * hippo card <create|show|list|claim|heartbeat|block|review|complete|reclaim|comment>
17
17
  * hippo current <show>
18
18
  * hippo forget <id> [--archive --reason "<why>"]
19
19
  * hippo reject <id>|--value "<text>" --reason "<why>"
@@ -38,7 +38,7 @@ import { execFileSync, execSync, spawn } from 'child_process';
38
38
  import { installJsonHooks, uninstallJsonHooks, resolveJsonHookPaths, detectInstalledTools, defaultSleepLogPath, ensureCodexWrapperInstalled, installCodexWrapper, isCodexWrapperInstalled, repairCodexWrapperIfInstalled, uninstallCodexWrapper, resolveCodexSessionTranscript, resolveCodexWrapperPaths, installOpencodePlugin, uninstallOpencodePlugin, resolveOpencodePluginPath, } from './hooks.js';
39
39
  import { createMemory, calculateStrength, calculateRewardFactor, deriveHalfLife, resolveConfidence, confidenceFacets, confidenceLabel, computeSchemaFit, Layer, } from './memory.js';
40
40
  import { detectSecret } from './secret-detect.js';
41
- import { getHippoRoot, isInitialized, initStore, writeEntry, readEntry, deleteEntry, loadAllEntries, loadSearchEntries, loadRecallSearchEntries, loadIndex, saveIndex, loadStats, updateStats, saveActiveTaskSnapshot, loadActiveTaskSnapshot, closeTaskSnapshotsForSession, clearActiveTaskSnapshot, appendSessionEvent, listSessionEvents, listMemoryConflicts, resolveConflict, saveSessionHandoff, loadLatestHandoff, loadHandoffById, stampHandoffOutcome, writeSessionEndHandoff, createCard, loadCard, listCards, loadCardDeps, loadCardRuns, loadCardComments, claimCard, blockCard, reviewCard, completeCard, addCardComment, loadLatestHandoffForCard, } from './store.js';
41
+ import { getHippoRoot, isInitialized, initStore, writeEntry, readEntry, deleteEntry, loadAllEntries, loadSearchEntries, loadRecallSearchEntries, loadIndex, saveIndex, loadStats, updateStats, saveActiveTaskSnapshot, loadActiveTaskSnapshot, closeTaskSnapshotsForSession, clearActiveTaskSnapshot, appendSessionEvent, listSessionEvents, listMemoryConflicts, resolveConflict, saveSessionHandoff, loadLatestHandoff, loadHandoffById, stampHandoffOutcome, writeSessionEndHandoff, createCard, loadCard, listCards, loadCardDeps, loadCardRuns, loadCardComments, claimCard, heartbeatCard, blockCard, reviewCard, completeCard, reclaimExpiredCards, addCardComment, loadLatestHandoffForCard, } from './store.js';
42
42
  import { rejectValue, unrejectValue, listRejectionsForTenant } from './reject-flow.js';
43
43
  import { RejectedValueError } from './rejection.js';
44
44
  import { isHandoffOutcome, formatHandoffEvidenceLine } from './handoff.js';
@@ -117,6 +117,7 @@ import { multihopSearch } from './multihop.js';
117
117
  import { graphExpandRecall, MAX_HOPS, DEFAULT_MAX_NEIGHBORS } from './graph-recall.js';
118
118
  import { DEFAULT_GRAPH_STREAM_WEIGHT } from './graph-stream.js';
119
119
  import { getReranker } from './rerankers/index.js';
120
+ import { JEV_DEFAULT_TOP_K } from './rerankers/jev.js';
120
121
  import { computeSalience } from './salience.js';
121
122
  import { renderAmbientSummary } from './ambient.js';
122
123
  import { validateOwner, isStrictOwnerEnv } from './owner-validation.js';
@@ -1213,10 +1214,10 @@ async function cmdRecall(hippoRoot, query, flags) {
1213
1214
  // --reranker <name> is set, look up the reranker fn from the registry
1214
1215
  // (src/rerankers/index.ts) and apply it to the top-K candidates. The
1215
1216
  // reranker reorders (and may rescale) results; the post-budget set is
1216
- // returned. Default off; opt-in via --reranker <cross-encoder|llm>. The
1217
+ // returned. Default off; opt-in via --reranker <cross-encoder|jev|llm>. The
1217
1218
  // structurally similar --rerank-utility block above is the OFC MVP and is
1218
1219
  // independent — both can run in the same recall, with --rerank-utility
1219
- // applied first. Available rerankers: cross-encoder, llm (see
1220
+ // applied first. Available rerankers: cross-encoder, jev, llm (see
1220
1221
  // src/rerankers/index.ts). The Track 1 `features` reranker was removed in
1221
1222
  // v1.9.1 per the F10 HARD RETRACTION; it is no longer a valid value.
1222
1223
  const rerankerName = flags['reranker'] !== undefined ? String(flags['reranker']).trim() : '';
@@ -1225,7 +1226,7 @@ async function cmdRecall(hippoRoot, query, flags) {
1225
1226
  if (rerankerFn) {
1226
1227
  const topK = flags['reranker-top-k'] !== undefined
1227
1228
  ? parseInt(String(flags['reranker-top-k']), 10)
1228
- : 50;
1229
+ : rerankerName === 'jev' ? JEV_DEFAULT_TOP_K : 50;
1229
1230
  const head = results.slice(0, topK);
1230
1231
  const tail = results.slice(topK);
1231
1232
  const rerankInput = head.map((r, i) => ({ ...r, preRerankRank: i + 1 }));
@@ -4034,6 +4035,10 @@ function printCard(hippoRoot, tenantId, card) {
4034
4035
  console.log(`- Status: ${card.status}`);
4035
4036
  if (card.assigneeRuntime)
4036
4037
  console.log(`- Assignee: ${card.assigneeRuntime}`);
4038
+ if (card.leaseUntil)
4039
+ console.log(`- Lease until: ${card.leaseUntil}`);
4040
+ if (card.heartbeatAt)
4041
+ console.log(`- Heartbeat: ${card.heartbeatAt}`);
4037
4042
  if (card.repo)
4038
4043
  console.log(`- Repo: ${card.repo}`);
4039
4044
  if (card.contract)
@@ -4050,7 +4055,7 @@ function printCard(hippoRoot, tenantId, card) {
4050
4055
  if (runs.length > 0) {
4051
4056
  console.log('\n### Runs');
4052
4057
  for (const run of runs) {
4053
- console.log(`- ${run.runtime} started ${run.started}${run.ended ? ` ended ${run.ended} (${run.outcome})` : ' (open)'}`);
4058
+ console.log(`- run ${run.id}: ${run.runtime} started ${run.started}${run.ended ? ` ended ${run.ended} (${run.outcome})` : ' (open)'}`);
4054
4059
  }
4055
4060
  }
4056
4061
  const comments = loadCardComments(hippoRoot, tenantId, card.id);
@@ -4080,14 +4085,34 @@ function cardStringFlag(flags, key) {
4080
4085
  }
4081
4086
  return v.trim();
4082
4087
  }
4088
+ // A too-large --run would silently round to a different id (mirrors parsePositiveIncidentId).
4089
+ function cardRunFlag(flags) {
4090
+ const raw = cardStringFlag(flags, 'run');
4091
+ if (raw === undefined)
4092
+ return undefined;
4093
+ const n = Number(raw);
4094
+ if (!/^\d+$/.test(raw) || !Number.isSafeInteger(n) || n <= 0) {
4095
+ console.error(`Invalid --run: "${raw}" (expected a positive integer).`);
4096
+ process.exit(1);
4097
+ }
4098
+ return n;
4099
+ }
4100
+ // Reads after the store already refused, so this only explains the refusal, never changes it.
4101
+ function cardRefusal(hippoRoot, tenantId, id) {
4102
+ const card = loadCard(hippoRoot, tenantId, id);
4103
+ const liveRun = loadCardRuns(hippoRoot, tenantId, id).find((r) => !r.ended);
4104
+ return `status ${card?.status ?? 'unknown'}, live run ${liveRun?.id ?? 'none'}`;
4105
+ }
4083
4106
  const CARD_SUBCOMMAND_FLAGS = {
4084
4107
  create: ['title', 'repo', 'contract', 'budget', 'depends-on'],
4085
4108
  show: ['json'],
4086
4109
  list: ['status', 'json'],
4087
4110
  claim: ['runtime', 'session'],
4088
- block: ['reason'],
4089
- review: new Array(),
4090
- complete: ['outcome'],
4111
+ heartbeat: ['run'],
4112
+ block: ['reason', 'run'],
4113
+ review: ['run'],
4114
+ complete: ['outcome', 'run'],
4115
+ reclaim: new Array(),
4091
4116
  comment: ['body', 'author'],
4092
4117
  };
4093
4118
  function cmdCard(hippoRoot, args, flags) {
@@ -4205,7 +4230,29 @@ function cmdCard(hippoRoot, args, flags) {
4205
4230
  console.error(`Could not claim card ${id} (not ready/blocked, or already claimed).`);
4206
4231
  process.exit(1);
4207
4232
  }
4208
- console.log(`Claimed card ${card.id} for ${runtime}`);
4233
+ console.log(`Claimed card ${card.id} for ${runtime} (run ${card.runId}, lease until ${card.leaseUntil})`);
4234
+ return;
4235
+ }
4236
+ if (subcommand === 'heartbeat') {
4237
+ const id = args[1];
4238
+ const runId = cardRunFlag(flags);
4239
+ if (!id || runId === undefined) {
4240
+ console.error('Usage: hippo card heartbeat <id> --run <n>');
4241
+ process.exit(1);
4242
+ }
4243
+ let card;
4244
+ try {
4245
+ card = heartbeatCard(hippoRoot, tenantId, id, runId);
4246
+ }
4247
+ catch (error) {
4248
+ console.error(error instanceof Error ? error.message : String(error));
4249
+ process.exit(1);
4250
+ }
4251
+ if (!card) {
4252
+ console.error(`Could not heartbeat card ${id} (${cardRefusal(hippoRoot, tenantId, id)}).`);
4253
+ process.exit(1);
4254
+ }
4255
+ console.log(`Heartbeat card ${card.id}: lease until ${card.leaseUntil}`);
4209
4256
  return;
4210
4257
  }
4211
4258
  if (subcommand === 'block') {
@@ -4215,16 +4262,18 @@ function cmdCard(hippoRoot, args, flags) {
4215
4262
  console.error(CARD_BLOCK_REASON_REQUIRED);
4216
4263
  process.exit(1);
4217
4264
  }
4265
+ const runId = cardRunFlag(flags);
4218
4266
  let card;
4219
4267
  try {
4220
- card = blockCard(hippoRoot, tenantId, id, reason);
4268
+ card = blockCard(hippoRoot, tenantId, id, reason, runId);
4221
4269
  }
4222
4270
  catch (error) {
4223
4271
  console.error(error instanceof Error ? error.message : String(error));
4224
4272
  process.exit(1);
4225
4273
  }
4226
4274
  if (!card) {
4227
- console.error(`Could not block card ${id} (not running).`);
4275
+ const why = runId === undefined ? 'not running' : cardRefusal(hippoRoot, tenantId, id);
4276
+ console.error(`Could not block card ${id} (${why}).`);
4228
4277
  process.exit(1);
4229
4278
  }
4230
4279
  console.log(`Blocked card ${card.id}`);
@@ -4233,19 +4282,21 @@ function cmdCard(hippoRoot, args, flags) {
4233
4282
  if (subcommand === 'review') {
4234
4283
  const id = args[1];
4235
4284
  if (!id) {
4236
- console.error('Usage: hippo card review <id>');
4285
+ console.error('Usage: hippo card review <id> [--run <n>]');
4237
4286
  process.exit(1);
4238
4287
  }
4288
+ const runId = cardRunFlag(flags);
4239
4289
  let card;
4240
4290
  try {
4241
- card = reviewCard(hippoRoot, tenantId, id);
4291
+ card = reviewCard(hippoRoot, tenantId, id, runId);
4242
4292
  }
4243
4293
  catch (error) {
4244
4294
  console.error(error instanceof Error ? error.message : String(error));
4245
4295
  process.exit(1);
4246
4296
  }
4247
4297
  if (!card) {
4248
- console.error(`Could not move card ${id} to review (not running).`);
4298
+ const why = runId === undefined ? 'not running' : cardRefusal(hippoRoot, tenantId, id);
4299
+ console.error(`Could not move card ${id} to review (${why}).`);
4249
4300
  process.exit(1);
4250
4301
  }
4251
4302
  console.log(`Card ${card.id} moved to review`);
@@ -4255,19 +4306,21 @@ function cmdCard(hippoRoot, args, flags) {
4255
4306
  const id = args[1];
4256
4307
  const outcomeRaw = flags['outcome'];
4257
4308
  if (!id || !isHandoffOutcome(outcomeRaw)) {
4258
- console.error('Usage: hippo card complete <id> --outcome <success|failure|partial>');
4309
+ console.error('Usage: hippo card complete <id> --outcome <success|failure|partial> [--run <n>]');
4259
4310
  process.exit(1);
4260
4311
  }
4312
+ const runId = cardRunFlag(flags);
4261
4313
  let result;
4262
4314
  try {
4263
- result = completeCard(hippoRoot, tenantId, id, outcomeRaw);
4315
+ result = completeCard(hippoRoot, tenantId, id, outcomeRaw, runId);
4264
4316
  }
4265
4317
  catch (error) {
4266
4318
  console.error(error instanceof Error ? error.message : String(error));
4267
4319
  process.exit(1);
4268
4320
  }
4269
4321
  if (!result) {
4270
- console.error(`Could not complete card ${id} (not in review).`);
4322
+ const why = runId === undefined ? 'not in review' : cardRefusal(hippoRoot, tenantId, id);
4323
+ console.error(`Could not complete card ${id} (${why}).`);
4271
4324
  process.exit(1);
4272
4325
  }
4273
4326
  console.log(`Completed card ${result.card.id} (status: ${result.card.status})`);
@@ -4276,13 +4329,28 @@ function cmdCard(hippoRoot, args, flags) {
4276
4329
  }
4277
4330
  return;
4278
4331
  }
4332
+ if (subcommand === 'reclaim') {
4333
+ if (args.length > 1) {
4334
+ console.error('Usage: hippo card reclaim (sweeps every expired lease; use hippo card block <id> for one card)');
4335
+ process.exit(1);
4336
+ }
4337
+ const ids = reclaimExpiredCards(hippoRoot, tenantId);
4338
+ if (ids.length === 0) {
4339
+ console.log('No expired leases.');
4340
+ return;
4341
+ }
4342
+ for (const id of ids) {
4343
+ console.log(`Reclaimed card ${id} (now ready)`);
4344
+ }
4345
+ return;
4346
+ }
4279
4347
  if (subcommand === 'comment') {
4280
4348
  const id = args[1];
4281
4349
  if (!id) {
4282
4350
  console.error('Usage: hippo card comment <id> --body "..." [--author <name>]');
4283
4351
  process.exit(1);
4284
4352
  }
4285
- // Only show and comment look the card up directly; claim/block/review/complete throw from the store instead.
4353
+ // Only show and comment look the card up directly; claim/heartbeat/block/review/complete throw from the store instead.
4286
4354
  const card = loadCard(hippoRoot, tenantId, id);
4287
4355
  if (!card) {
4288
4356
  console.error(`No card found with id ${id}.`);
@@ -4298,7 +4366,7 @@ function cmdCard(hippoRoot, args, flags) {
4298
4366
  console.log(`Added comment ${comment.id} to card ${id}`);
4299
4367
  return;
4300
4368
  }
4301
- console.error('Usage: hippo card <create|show|list|claim|block|review|complete|comment>');
4369
+ console.error('Usage: hippo card <create|show|list|claim|heartbeat|block|review|complete|reclaim|comment>');
4302
4370
  process.exit(1);
4303
4371
  }
4304
4372
  // ---------------------------------------------------------------------------
@@ -8047,11 +8115,17 @@ Commands:
8047
8115
  where cost_factor = min(0.3, tokens / 10000). Re-sorts
8048
8116
  results by utility. Default off. RESEARCH.md §PFC.OFC.
8049
8117
  --reranker <name> Apply a reranker pass after retrieval
8050
- (cross-encoder|llm). Looks up the named
8118
+ (cross-encoder|jev|llm). Looks up the named
8051
8119
  reranker from src/rerankers/index.ts and re-orders
8052
8120
  the top-K candidates. Default unset (no reranker).
8053
- See docs/plans/2026-05-10-f6-reranker-hardening.md.
8054
- --reranker-top-k <n> Cap candidates passed to the reranker (default 50).
8121
+ jev calls the hosted TypeSafe Jev API: it needs
8122
+ TYPESAFE_API_KEY, sends the query and candidate
8123
+ text to that API, costs about 0.0004 USD a recall,
8124
+ and falls back to cross-encoder on any failure.
8125
+ See docs/evals/2026-09-19-jev-reranker.md and
8126
+ docs/plans/2026-05-10-f6-reranker-hardening.md.
8127
+ --reranker-top-k <n> Cap candidates passed to the reranker (default 50;
8128
+ 40 for jev).
8055
8129
  --goal <tag> dlPFC goal-conditioned recall: memories tagged with
8056
8130
  the goal tag get a 1.5x score boost and results are
8057
8131
  re-sorted. Default off. RESEARCH.md §PFC.dlPFC.
@@ -8224,14 +8298,20 @@ Commands:
8224
8298
  --json Output as JSON
8225
8299
  card list List cards, newest-updated first
8226
8300
  --status <status> Filter by status
8227
- card claim <id> Claim a ready or blocked card
8301
+ card claim <id> Claim a ready or blocked card; prints its run id and lease
8228
8302
  --runtime <name> Claiming runtime (required)
8229
8303
  --session <id> Session ID
8304
+ card heartbeat <id> Extend a running card's lease
8305
+ --run <n> Your run id, as card claim printed it (required)
8230
8306
  card block <id> Block a running card
8231
8307
  --reason "<why>" Reason recorded as a card comment (required)
8308
+ --run <n> Refuse unless <n> is the card's live run
8232
8309
  card review <id> Move a running card to review
8310
+ --run <n> Refuse unless <n> is the card's live run
8233
8311
  card complete <id> Complete a card in review
8234
8312
  --outcome <o> success | failure | partial (required)
8313
+ --run <n> Refuse unless <n> is the card's live run
8314
+ card reclaim Return every running card whose lease has expired to ready
8235
8315
  card comment <id> Add a comment to a card
8236
8316
  --body <text> Comment body (required)
8237
8317
  --author <name> Comment author (default: cli)
@@ -8463,6 +8543,8 @@ Examples:
8463
8543
  hippo card create --title "Add cards table" --repo hippo --depends-on card_abc
8464
8544
  hippo card claim card_abc --runtime codex
8465
8545
  hippo card complete card_abc --outcome success
8546
+ hippo card heartbeat card_abc --run 7
8547
+ hippo card reclaim
8466
8548
  hippo embed --status
8467
8549
  hippo watch "npm run build"
8468
8550
  hippo learn --git --days 30