tinker-agent 2.1.0 → 2.2.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/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable user-facing changes to Tinker are documented here. The project follo
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [2.2.0] - 2026-08-29
9
+
10
+ ### Added
11
+
12
+ - Log raw vector cosine scores in `MemorySearch` diagnostics, making hybrid
13
+ recall ranking easier to inspect and tune.
14
+
15
+ ### Changed
16
+
17
+ - Restyle the prompt-input footer status line with a muted color palette:
18
+ model name, workspace path, Git branch, and cache hit rate each get a soft
19
+ accent color, and normal-pressure context usage now renders in a cool gray
20
+ instead of plain dim. Red and yellow remain reserved for context-pressure
21
+ warnings.
22
+
8
23
  ## [2.1.0] - 2026-08-29
9
24
 
10
25
  ### Added
@@ -249,7 +264,8 @@ All notable user-facing changes to Tinker are documented here. The project follo
249
264
  - First formal npm release under the `tinker-agent` package name with the `tinker`
250
265
  executable.
251
266
 
252
- [Unreleased]: https://github.com/ishowshao/tinker/compare/v2.1.0...HEAD
267
+ [Unreleased]: https://github.com/ishowshao/tinker/compare/v2.2.0...HEAD
268
+ [2.2.0]: https://github.com/ishowshao/tinker/releases/tag/v2.2.0
253
269
  [2.1.0]: https://github.com/ishowshao/tinker/releases/tag/v2.1.0
254
270
  [2.0.0]: https://github.com/ishowshao/tinker/releases/tag/v2.0.0
255
271
  [1.11.0]: https://github.com/ishowshao/tinker/releases/tag/v1.11.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tinker-agent",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "A personal coding agent with an interactive TUI and one-shot CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -14,6 +14,7 @@ export const MAX_MEMORY_KEYWORDS = 8;
14
14
  export const MAX_MEMORY_KEYWORD_BYTES = 128;
15
15
  export const MEMORY_RRF_K = 60;
16
16
  export const MEMORY_EXTRACTION_QUEUE_CAPACITY = 64;
17
+ export const MEMORY_SEARCH_DIAGNOSTIC_VECTOR_SCORES_LIMIT = 10;
17
18
 
18
19
  export type MemoryEmbeddingKind = "openai-compatible";
19
20
 
@@ -146,6 +147,7 @@ export type MemorySearchDiagnostic = {
146
147
  readonly ftsReturned: number;
147
148
  readonly degraded: MemoryRecallDegraded | null;
148
149
  readonly scores: readonly number[];
150
+ readonly vectorScores: readonly number[];
149
151
  readonly ms: number;
150
152
  };
151
153
 
@@ -21,6 +21,7 @@ import {
21
21
  MEMORY_GET_TOOL_NAME,
22
22
  MEMORY_RECALL_CANDIDATE_LIMIT,
23
23
  MEMORY_RRF_K,
24
+ MEMORY_SEARCH_DIAGNOSTIC_VECTOR_SCORES_LIMIT,
24
25
  MEMORY_SEARCH_LIMIT,
25
26
  MEMORY_SEARCH_TOOL_NAME,
26
27
  MemoryError,
@@ -470,6 +471,9 @@ export class MemoryCoordinator implements CompletedTurnHook {
470
471
  ftsReturned: ftsMatches.length,
471
472
  degraded,
472
473
  scores: fused.map((match) => roundScore(match.score)),
474
+ vectorScores: vectorMatches
475
+ .slice(0, MEMORY_SEARCH_DIAGNOSTIC_VECTOR_SCORES_LIMIT)
476
+ .map((match) => roundScore(match.score)),
473
477
  ms: elapsedMs(startedAt),
474
478
  }),
475
479
  );
@@ -688,6 +692,7 @@ function searchDiagnostic(input: {
688
692
  readonly ftsReturned?: number;
689
693
  readonly degraded?: MemoryRecallDegraded | null;
690
694
  readonly scores?: readonly number[];
695
+ readonly vectorScores?: readonly number[];
691
696
  readonly ms: number;
692
697
  }): MemorySearchDiagnostic {
693
698
  return Object.freeze({
@@ -704,6 +709,7 @@ function searchDiagnostic(input: {
704
709
  ftsReturned: input.ftsReturned ?? 0,
705
710
  degraded: input.degraded ?? null,
706
711
  scores: Object.freeze([...(input.scores ?? [])]),
712
+ vectorScores: Object.freeze([...(input.vectorScores ?? [])]),
707
713
  ms: input.ms,
708
714
  });
709
715
  }
@@ -741,30 +741,42 @@ export function PromptInput(props: PromptInputProps) {
741
741
  </Box>
742
742
  {showSuggestions ? null : (
743
743
  <Box>
744
- <Text dimColor>
745
- {props.modelName}
746
- {props.reasoningEffort === undefined ? null : ` ${props.reasoningEffort}`} ·{" "}
747
- {formatWorkspacePath(props.workspaceRoot)}
748
- {props.gitBranch === undefined ? null : ` · ${props.gitBranch}`}
749
- {state.phase.kind === "idle" ? null : ` · ${phaseLabel(state.phase)}`}
744
+ <Text>
745
+ <Text color={FOOTER_COLORS.model}>{props.modelName}</Text>
746
+ {props.reasoningEffort === undefined ? null : (
747
+ <Text dimColor> {props.reasoningEffort}</Text>
748
+ )}
749
+ <Text dimColor> · </Text>
750
+ <Text color={FOOTER_COLORS.workspacePath}>
751
+ {formatWorkspacePath(props.workspaceRoot)}
752
+ </Text>
753
+ {props.gitBranch === undefined ? null : (
754
+ <>
755
+ <Text dimColor> · </Text>
756
+ <Text color={FOOTER_COLORS.gitBranch}>{props.gitBranch}</Text>
757
+ </>
758
+ )}
759
+ {state.phase.kind === "idle" ? null : (
760
+ <>
761
+ <Text dimColor> · </Text>
762
+ <Text dimColor>{phaseLabel(state.phase)}</Text>
763
+ </>
764
+ )}
765
+ {props.contextUsage === undefined ? null : (
766
+ <>
767
+ <Text dimColor> · </Text>
768
+ <Text color={contextColor(props.contextUsage.pressure)}>
769
+ {formatContextUsageLine(props.contextUsage)}
770
+ </Text>
771
+ </>
772
+ )}
773
+ {cacheRate === undefined ? null : (
774
+ <>
775
+ <Text dimColor> · </Text>
776
+ <Text color={FOOTER_COLORS.cacheRate}>{cacheRate}</Text>
777
+ </>
778
+ )}
750
779
  </Text>
751
- {props.contextUsage === undefined ? null : (
752
- <>
753
- <Text dimColor> · </Text>
754
- <Text
755
- color={contextColor(props.contextUsage.pressure)}
756
- dimColor={props.contextUsage.pressure === "normal"}
757
- >
758
- {formatContextUsageLine(props.contextUsage)}
759
- </Text>
760
- </>
761
- )}
762
- {cacheRate === undefined ? null : (
763
- <>
764
- <Text dimColor> · </Text>
765
- <Text dimColor>{cacheRate}</Text>
766
- </>
767
- )}
768
780
  </Box>
769
781
  )}
770
782
  {showFileSuggestions ? (
@@ -962,14 +974,25 @@ function fileMentionReplacement(
962
974
  : `${filePath} `;
963
975
  }
964
976
 
977
+ // Decorative palette for the footer status line, kept muted so it reads
978
+ // softly against the dim separators. Red and yellow are deliberately
979
+ // excluded: they stay reserved for context-pressure warnings.
980
+ const FOOTER_COLORS = {
981
+ model: "#7b9cc4",
982
+ workspacePath: "#6fa8a8",
983
+ gitBranch: "#a08fbd",
984
+ cacheRate: "#7aa37e",
985
+ contextUsage: "#8a9199",
986
+ } as const;
987
+
965
988
  function contextColor(
966
989
  pressure: ContextUsageSnapshot["pressure"],
967
- ): "yellow" | "red" | undefined {
990
+ ): "yellow" | "red" | typeof FOOTER_COLORS.contextUsage {
968
991
  return pressure === "blocked"
969
992
  ? "red"
970
993
  : pressure === "triggered"
971
994
  ? "yellow"
972
- : undefined;
995
+ : FOOTER_COLORS.contextUsage;
973
996
  }
974
997
 
975
998
  function formatWorkspacePath(workspaceRoot: string): string {