@sayknow-cli/coding-agent 0.6.3 → 0.6.5
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 +25 -1
- package/README.md +2 -2
- package/dist/types/config/keybindings.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +7 -3
- package/dist/types/i18n/messages/en.d.ts +16 -0
- package/dist/types/modes/components/session-selector.d.ts +7 -3
- package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
- package/dist/types/modes/components/user-message-selector.d.ts +5 -2
- package/dist/types/modes/components/user-message.d.ts +3 -1
- package/dist/types/modes/components/welcome.d.ts +49 -6
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +5 -0
- package/dist/types/modes/interactive-mode.d.ts +7 -1
- package/dist/types/modes/theme/defaults/index.d.ts +342 -0
- package/dist/types/modes/theme/theme.d.ts +5 -1
- package/dist/types/modes/types.d.ts +1 -1
- package/dist/types/session/session-manager.d.ts +9 -0
- package/dist/types/session/session-stars.d.ts +15 -0
- package/dist/types/tui/output-block.d.ts +12 -1
- package/dist/types/tui/utils.d.ts +1 -3
- package/dist/types/utils/session-title-generation.d.ts +6 -0
- package/dist/types/utils/title-generator.d.ts +11 -2
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +8 -0
- package/src/cli/session-picker.ts +4 -0
- package/src/config/keybindings.ts +1 -1
- package/src/config/settings-schema.ts +4 -3
- package/src/i18n/messages/de.messages.ts +4 -1
- package/src/i18n/messages/de.settings.ts +2 -0
- package/src/i18n/messages/de.ts +16 -0
- package/src/i18n/messages/en.ts +16 -0
- package/src/i18n/messages/es.messages.ts +4 -1
- package/src/i18n/messages/es.settings.ts +2 -0
- package/src/i18n/messages/es.ts +16 -0
- package/src/i18n/messages/fr.messages.ts +4 -1
- package/src/i18n/messages/fr.settings.ts +2 -0
- package/src/i18n/messages/fr.ts +16 -0
- package/src/i18n/messages/ja.messages.ts +4 -1
- package/src/i18n/messages/ja.settings.ts +2 -0
- package/src/i18n/messages/ja.ts +16 -0
- package/src/i18n/messages/ko.messages.ts +4 -1
- package/src/i18n/messages/ko.settings.ts +2 -0
- package/src/i18n/messages/ko.ts +16 -0
- package/src/i18n/messages/zh.messages.ts +2 -1
- package/src/i18n/messages/zh.settings.ts +2 -0
- package/src/i18n/messages/zh.ts +16 -0
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/lsp/render.ts +0 -1
- package/src/modes/components/session-selector.ts +71 -21
- package/src/modes/components/sessions-dashboard.ts +8 -5
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/presets.ts +5 -5
- package/src/modes/components/status-line/separators.ts +4 -0
- package/src/modes/components/tool-execution.ts +6 -19
- package/src/modes/components/tool-status-header.ts +103 -12
- package/src/modes/components/user-message-selector.ts +57 -15
- package/src/modes/components/user-message.ts +25 -15
- package/src/modes/components/welcome.ts +374 -478
- package/src/modes/controllers/command-controller.ts +56 -7
- package/src/modes/controllers/input-controller.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +91 -10
- package/src/modes/interactive-mode.ts +101 -12
- package/src/modes/theme/defaults/glow-octopus.json +114 -0
- package/src/modes/theme/defaults/index.ts +6 -0
- package/src/modes/theme/defaults/ink-octopus.json +114 -0
- package/src/modes/theme/defaults/violet-octopus.json +114 -0
- package/src/modes/theme/theme.ts +18 -2
- package/src/modes/types.ts +1 -1
- package/src/prompts/system/title-system.md +1 -1
- package/src/sdk/protocol/operation-inventory.generated.json +40 -0
- package/src/session/agent-session.ts +12 -0
- package/src/session/internal/managed-session-scope.ts +10 -0
- package/src/session/session-manager.ts +26 -1
- package/src/session/session-stars.ts +104 -0
- package/src/slash-commands/builtin-registry.ts +104 -4
- package/src/tools/debug.ts +0 -1
- package/src/tools/fetch.ts +0 -1
- package/src/tui/output-block.ts +29 -61
- package/src/tui/utils.ts +1 -8
- package/src/utils/session-title-generation.ts +29 -0
- package/src/utils/title-generator.ts +46 -2
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/jaybeyond/Sayknow_CLI/main/packages/coding-agent/theme-schema.json",
|
|
3
|
+
"name": "ink-octopus",
|
|
4
|
+
"vars": {
|
|
5
|
+
"ink": "#14110e",
|
|
6
|
+
"mantle": "#1b1712",
|
|
7
|
+
"surface": "#241e18",
|
|
8
|
+
"surfaceBright": "#322a21",
|
|
9
|
+
"text": "#ece4d6",
|
|
10
|
+
"muted": "#b3a794",
|
|
11
|
+
"dim": "#7d7264",
|
|
12
|
+
"accent": "#e8a23a",
|
|
13
|
+
"secondary": "#d4bd95",
|
|
14
|
+
"success": "#9fbf7a",
|
|
15
|
+
"error": "#e2664f",
|
|
16
|
+
"warning": "#f0c75e",
|
|
17
|
+
"link": "#e6b77c",
|
|
18
|
+
"keyword": "#e8a23a",
|
|
19
|
+
"function": "#e6cf9c",
|
|
20
|
+
"string": "#a9c58a",
|
|
21
|
+
"number": "#e59f78",
|
|
22
|
+
"type": "#d9b2d6",
|
|
23
|
+
"diffRemoved": "#d9695a",
|
|
24
|
+
"toolSuccessBg": "#1d2317",
|
|
25
|
+
"toolErrorBg": "#2d1915",
|
|
26
|
+
"think1": "#c9b28f",
|
|
27
|
+
"think2": "#e0b27a",
|
|
28
|
+
"think3": "#e8a23a",
|
|
29
|
+
"think4": "#e2664f"
|
|
30
|
+
},
|
|
31
|
+
"colors": {
|
|
32
|
+
"accent": "accent",
|
|
33
|
+
"border": "surfaceBright",
|
|
34
|
+
"borderAccent": "accent",
|
|
35
|
+
"borderMuted": "dim",
|
|
36
|
+
"success": "success",
|
|
37
|
+
"error": "error",
|
|
38
|
+
"warning": "warning",
|
|
39
|
+
"muted": "muted",
|
|
40
|
+
"dim": "dim",
|
|
41
|
+
"text": "text",
|
|
42
|
+
"thinkingText": "muted",
|
|
43
|
+
"selectedBg": "surfaceBright",
|
|
44
|
+
"userMessageBg": "surface",
|
|
45
|
+
"userMessageText": "text",
|
|
46
|
+
"customMessageBg": "mantle",
|
|
47
|
+
"customMessageText": "text",
|
|
48
|
+
"customMessageLabel": "accent",
|
|
49
|
+
"toolPendingBg": "mantle",
|
|
50
|
+
"toolSuccessBg": "toolSuccessBg",
|
|
51
|
+
"toolErrorBg": "toolErrorBg",
|
|
52
|
+
"toolTitle": "text",
|
|
53
|
+
"toolOutput": "muted",
|
|
54
|
+
"mdHeading": "accent",
|
|
55
|
+
"mdLink": "link",
|
|
56
|
+
"mdLinkUrl": "dim",
|
|
57
|
+
"mdCode": "secondary",
|
|
58
|
+
"mdCodeBlock": "string",
|
|
59
|
+
"mdCodeBlockBorder": "surfaceBright",
|
|
60
|
+
"mdQuote": "muted",
|
|
61
|
+
"mdQuoteBorder": "surfaceBright",
|
|
62
|
+
"mdHr": "dim",
|
|
63
|
+
"mdListBullet": "accent",
|
|
64
|
+
"toolDiffAdded": "success",
|
|
65
|
+
"toolDiffRemoved": "diffRemoved",
|
|
66
|
+
"toolDiffContext": "muted",
|
|
67
|
+
"link": "link",
|
|
68
|
+
"syntaxComment": "dim",
|
|
69
|
+
"syntaxKeyword": "keyword",
|
|
70
|
+
"syntaxFunction": "function",
|
|
71
|
+
"syntaxVariable": "text",
|
|
72
|
+
"syntaxString": "string",
|
|
73
|
+
"syntaxNumber": "number",
|
|
74
|
+
"syntaxType": "type",
|
|
75
|
+
"syntaxOperator": "muted",
|
|
76
|
+
"syntaxPunctuation": "dim",
|
|
77
|
+
"thinkingOff": "dim",
|
|
78
|
+
"thinkingMinimal": "muted",
|
|
79
|
+
"thinkingLow": "think1",
|
|
80
|
+
"thinkingMedium": "think2",
|
|
81
|
+
"thinkingHigh": "think3",
|
|
82
|
+
"thinkingXhigh": "think4",
|
|
83
|
+
"bashMode": "accent",
|
|
84
|
+
"statusLineBg": "surfaceBright",
|
|
85
|
+
"statusLineSep": "dim",
|
|
86
|
+
"statusLineModel": "accent",
|
|
87
|
+
"statusLinePath": "secondary",
|
|
88
|
+
"statusLineGitClean": "success",
|
|
89
|
+
"statusLineGitDirty": "warning",
|
|
90
|
+
"statusLineContext": "string",
|
|
91
|
+
"statusLineSpend": "warning",
|
|
92
|
+
"statusLineStaged": "success",
|
|
93
|
+
"statusLineDirty": "warning",
|
|
94
|
+
"statusLineUntracked": "diffRemoved",
|
|
95
|
+
"statusLineOutput": "text",
|
|
96
|
+
"statusLineCost": "accent",
|
|
97
|
+
"statusLineSubagents": "secondary",
|
|
98
|
+
"pythonMode": "function"
|
|
99
|
+
},
|
|
100
|
+
"export": {
|
|
101
|
+
"pageBg": "#14110e",
|
|
102
|
+
"cardBg": "#1b1712",
|
|
103
|
+
"infoBg": "#241e18"
|
|
104
|
+
},
|
|
105
|
+
"symbols": {
|
|
106
|
+
"preset": "unicode",
|
|
107
|
+
"overrides": {
|
|
108
|
+
"icon.pi": "🐙",
|
|
109
|
+
"icon.agents": "🦑",
|
|
110
|
+
"status.running": "◐",
|
|
111
|
+
"md.bullet": "›"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/jaybeyond/Sayknow_CLI/main/packages/coding-agent/theme-schema.json",
|
|
3
|
+
"name": "violet-octopus",
|
|
4
|
+
"vars": {
|
|
5
|
+
"ink": "#110c17",
|
|
6
|
+
"mantle": "#191220",
|
|
7
|
+
"surface": "#221829",
|
|
8
|
+
"surfaceBright": "#2e2138",
|
|
9
|
+
"text": "#efe7f7",
|
|
10
|
+
"muted": "#bba9cf",
|
|
11
|
+
"dim": "#7a6a8c",
|
|
12
|
+
"accent": "#b692ff",
|
|
13
|
+
"secondary": "#ffb482",
|
|
14
|
+
"success": "#8fd6a8",
|
|
15
|
+
"error": "#ff6f91",
|
|
16
|
+
"warning": "#ffcf70",
|
|
17
|
+
"link": "#d3b8ff",
|
|
18
|
+
"keyword": "#b692ff",
|
|
19
|
+
"function": "#ffb482",
|
|
20
|
+
"string": "#8fd6a8",
|
|
21
|
+
"number": "#ffcf70",
|
|
22
|
+
"type": "#d3b8ff",
|
|
23
|
+
"diffRemoved": "#ff6f91",
|
|
24
|
+
"toolSuccessBg": "#17241c",
|
|
25
|
+
"toolErrorBg": "#2c1522",
|
|
26
|
+
"think1": "#d3b8ff",
|
|
27
|
+
"think2": "#c5a4ff",
|
|
28
|
+
"think3": "#b692ff",
|
|
29
|
+
"think4": "#ffb482"
|
|
30
|
+
},
|
|
31
|
+
"colors": {
|
|
32
|
+
"accent": "accent",
|
|
33
|
+
"border": "surfaceBright",
|
|
34
|
+
"borderAccent": "accent",
|
|
35
|
+
"borderMuted": "dim",
|
|
36
|
+
"success": "success",
|
|
37
|
+
"error": "error",
|
|
38
|
+
"warning": "warning",
|
|
39
|
+
"muted": "muted",
|
|
40
|
+
"dim": "dim",
|
|
41
|
+
"text": "text",
|
|
42
|
+
"thinkingText": "muted",
|
|
43
|
+
"selectedBg": "surfaceBright",
|
|
44
|
+
"userMessageBg": "surface",
|
|
45
|
+
"userMessageText": "text",
|
|
46
|
+
"customMessageBg": "mantle",
|
|
47
|
+
"customMessageText": "text",
|
|
48
|
+
"customMessageLabel": "accent",
|
|
49
|
+
"toolPendingBg": "mantle",
|
|
50
|
+
"toolSuccessBg": "toolSuccessBg",
|
|
51
|
+
"toolErrorBg": "toolErrorBg",
|
|
52
|
+
"toolTitle": "text",
|
|
53
|
+
"toolOutput": "muted",
|
|
54
|
+
"mdHeading": "accent",
|
|
55
|
+
"mdLink": "link",
|
|
56
|
+
"mdLinkUrl": "dim",
|
|
57
|
+
"mdCode": "secondary",
|
|
58
|
+
"mdCodeBlock": "string",
|
|
59
|
+
"mdCodeBlockBorder": "surfaceBright",
|
|
60
|
+
"mdQuote": "muted",
|
|
61
|
+
"mdQuoteBorder": "surfaceBright",
|
|
62
|
+
"mdHr": "dim",
|
|
63
|
+
"mdListBullet": "accent",
|
|
64
|
+
"toolDiffAdded": "success",
|
|
65
|
+
"toolDiffRemoved": "diffRemoved",
|
|
66
|
+
"toolDiffContext": "muted",
|
|
67
|
+
"link": "link",
|
|
68
|
+
"syntaxComment": "dim",
|
|
69
|
+
"syntaxKeyword": "keyword",
|
|
70
|
+
"syntaxFunction": "function",
|
|
71
|
+
"syntaxVariable": "text",
|
|
72
|
+
"syntaxString": "string",
|
|
73
|
+
"syntaxNumber": "number",
|
|
74
|
+
"syntaxType": "type",
|
|
75
|
+
"syntaxOperator": "muted",
|
|
76
|
+
"syntaxPunctuation": "dim",
|
|
77
|
+
"thinkingOff": "dim",
|
|
78
|
+
"thinkingMinimal": "muted",
|
|
79
|
+
"thinkingLow": "think1",
|
|
80
|
+
"thinkingMedium": "think2",
|
|
81
|
+
"thinkingHigh": "think3",
|
|
82
|
+
"thinkingXhigh": "think4",
|
|
83
|
+
"bashMode": "accent",
|
|
84
|
+
"statusLineBg": "surfaceBright",
|
|
85
|
+
"statusLineSep": "dim",
|
|
86
|
+
"statusLineModel": "accent",
|
|
87
|
+
"statusLinePath": "secondary",
|
|
88
|
+
"statusLineGitClean": "success",
|
|
89
|
+
"statusLineGitDirty": "warning",
|
|
90
|
+
"statusLineContext": "string",
|
|
91
|
+
"statusLineSpend": "warning",
|
|
92
|
+
"statusLineStaged": "success",
|
|
93
|
+
"statusLineDirty": "warning",
|
|
94
|
+
"statusLineUntracked": "diffRemoved",
|
|
95
|
+
"statusLineOutput": "text",
|
|
96
|
+
"statusLineCost": "accent",
|
|
97
|
+
"statusLineSubagents": "secondary",
|
|
98
|
+
"pythonMode": "function"
|
|
99
|
+
},
|
|
100
|
+
"export": {
|
|
101
|
+
"pageBg": "#110c17",
|
|
102
|
+
"cardBg": "#191220",
|
|
103
|
+
"infoBg": "#221829"
|
|
104
|
+
},
|
|
105
|
+
"symbols": {
|
|
106
|
+
"preset": "unicode",
|
|
107
|
+
"overrides": {
|
|
108
|
+
"icon.pi": "🐙",
|
|
109
|
+
"icon.agents": "🦑",
|
|
110
|
+
"status.running": "◐",
|
|
111
|
+
"md.bullet": "▸"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -107,6 +107,7 @@ export type SymbolKey =
|
|
|
107
107
|
| "icon.output"
|
|
108
108
|
| "icon.host"
|
|
109
109
|
| "icon.session"
|
|
110
|
+
| "icon.star"
|
|
110
111
|
| "icon.package"
|
|
111
112
|
| "icon.warning"
|
|
112
113
|
| "icon.rewind"
|
|
@@ -142,6 +143,8 @@ export type SymbolKey =
|
|
|
142
143
|
| "md.quoteBorder"
|
|
143
144
|
| "md.hrChar"
|
|
144
145
|
| "md.bullet"
|
|
146
|
+
// Transcript rail marking the user's own prompts
|
|
147
|
+
| "rail.user"
|
|
145
148
|
// Language/file type icons
|
|
146
149
|
| "lang.default"
|
|
147
150
|
| "lang.typescript"
|
|
@@ -272,6 +275,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
272
275
|
"icon.output": "⤴",
|
|
273
276
|
"icon.host": "🖥",
|
|
274
277
|
"icon.session": "🆔",
|
|
278
|
+
"icon.star": "★",
|
|
275
279
|
"icon.package": "📦",
|
|
276
280
|
"icon.warning": "⚠",
|
|
277
281
|
"icon.rewind": "↶",
|
|
@@ -307,6 +311,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
307
311
|
"md.quoteBorder": "▏",
|
|
308
312
|
"md.hrChar": "─",
|
|
309
313
|
"md.bullet": "•",
|
|
314
|
+
"rail.user": "▌",
|
|
310
315
|
// Language/file icons (emoji-centric, no Nerd Font required)
|
|
311
316
|
"lang.default": "⌘",
|
|
312
317
|
"lang.typescript": "🟦",
|
|
@@ -507,6 +512,7 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
507
512
|
"icon.host": "\uf109",
|
|
508
513
|
// pick: | alt:
|
|
509
514
|
"icon.session": "\uf550",
|
|
515
|
+
"icon.star": "\uf005",
|
|
510
516
|
// pick: | alt:
|
|
511
517
|
"icon.package": "\uf487",
|
|
512
518
|
// pick: | alt:
|
|
@@ -567,6 +573,7 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
567
573
|
"md.hrChar": "─",
|
|
568
574
|
// pick: | alt: •
|
|
569
575
|
"md.bullet": "\uf111",
|
|
576
|
+
"rail.user": "▌",
|
|
570
577
|
// Language icons (nerd font devicons)
|
|
571
578
|
"lang.default": "",
|
|
572
579
|
"lang.typescript": "\u{E628}",
|
|
@@ -696,6 +703,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
696
703
|
"icon.output": "out:",
|
|
697
704
|
"icon.host": "host",
|
|
698
705
|
"icon.session": "id",
|
|
706
|
+
"icon.star": "*",
|
|
699
707
|
"icon.package": "[P]",
|
|
700
708
|
"icon.warning": "[!]",
|
|
701
709
|
"icon.rewind": "<-",
|
|
@@ -730,6 +738,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
730
738
|
"md.quoteBorder": "|",
|
|
731
739
|
"md.hrChar": "-",
|
|
732
740
|
"md.bullet": "*",
|
|
741
|
+
"rail.user": "|",
|
|
733
742
|
// Language icons (ASCII uses abbreviations)
|
|
734
743
|
"lang.default": "code",
|
|
735
744
|
"lang.typescript": "ts",
|
|
@@ -1471,6 +1480,7 @@ export class Theme {
|
|
|
1471
1480
|
output: this.#symbols["icon.output"],
|
|
1472
1481
|
host: this.#symbols["icon.host"],
|
|
1473
1482
|
session: this.#symbols["icon.session"],
|
|
1483
|
+
star: this.#symbols["icon.star"],
|
|
1474
1484
|
package: this.#symbols["icon.package"],
|
|
1475
1485
|
warning: this.#symbols["icon.warning"],
|
|
1476
1486
|
rewind: this.#symbols["icon.rewind"],
|
|
@@ -1516,6 +1526,12 @@ export class Theme {
|
|
|
1516
1526
|
};
|
|
1517
1527
|
}
|
|
1518
1528
|
|
|
1529
|
+
get rail() {
|
|
1530
|
+
return {
|
|
1531
|
+
user: this.#symbols["rail.user"],
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1519
1535
|
get md() {
|
|
1520
1536
|
return {
|
|
1521
1537
|
quoteBorder: this.#symbols["md.quoteBorder"],
|
|
@@ -1785,7 +1801,7 @@ var themeWatcher: fs.FSWatcher | undefined;
|
|
|
1785
1801
|
var themeReloadTimer: NodeJS.Timeout | undefined;
|
|
1786
1802
|
var sigwinchHandler: (() => void) | undefined;
|
|
1787
1803
|
var autoDetectedTheme: boolean = false;
|
|
1788
|
-
var autoDarkTheme: string = "
|
|
1804
|
+
var autoDarkTheme: string = "ink-octopus";
|
|
1789
1805
|
var autoLightTheme: string = "blue-octopus";
|
|
1790
1806
|
var onThemeChangeCallback: (() => void) | undefined;
|
|
1791
1807
|
var themeLoadRequestId: number = 0;
|
|
@@ -1806,7 +1822,7 @@ export async function initTheme(
|
|
|
1806
1822
|
lightTheme?: string,
|
|
1807
1823
|
): Promise<void> {
|
|
1808
1824
|
autoDetectedTheme = true;
|
|
1809
|
-
autoDarkTheme = darkTheme ?? "
|
|
1825
|
+
autoDarkTheme = darkTheme ?? "ink-octopus";
|
|
1810
1826
|
autoLightTheme = lightTheme ?? "blue-octopus";
|
|
1811
1827
|
const name = getDefaultTheme();
|
|
1812
1828
|
previewThemeActive = false;
|
package/src/modes/types.ts
CHANGED
|
@@ -289,7 +289,7 @@ export interface InteractiveModeContext {
|
|
|
289
289
|
handleHandoffCommand(customInstructions?: string): Promise<void>;
|
|
290
290
|
handleContributionPrepCommand(customInstructions?: string): Promise<void>;
|
|
291
291
|
handleMoveCommand(targetPath: string): Promise<void>;
|
|
292
|
-
handleRenameCommand(title
|
|
292
|
+
handleRenameCommand(title?: string): Promise<void>;
|
|
293
293
|
handleMemoryCommand(text: string): Promise<void>;
|
|
294
294
|
handleSTTToggle(): Promise<void>;
|
|
295
295
|
executeCompaction(
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Generate a 3-6 word title for a coding session from the user
|
|
1
|
+
Generate a 3-6 word title for a coding session from the supplied user message or conversation digest. If multiple messages are supplied, identify the primary/current task from the digest, weighting the newest substantive message highest; do not assume the first message is the task. If one message is supplied, capture its main task or topic.
|
|
2
2
|
Output ONLY the title. No quotes or trailing punctuation.
|
|
@@ -2177,6 +2177,28 @@
|
|
|
2177
2177
|
"testIds": "not_applicable"
|
|
2178
2178
|
}
|
|
2179
2179
|
},
|
|
2180
|
+
{
|
|
2181
|
+
"sourceId": "slash_command:star",
|
|
2182
|
+
"sourceFile": "packages/coding-agent/src/slash-commands/builtin-registry.ts",
|
|
2183
|
+
"sourceKind": "slash_command",
|
|
2184
|
+
"decision": "exclude",
|
|
2185
|
+
"rationale": "local session-discovery metadata command; no SDK operation counterpart or runtime/retention authority",
|
|
2186
|
+
"exclusionMetadata": {
|
|
2187
|
+
"adapterMappings": "not_applicable",
|
|
2188
|
+
"testIds": "not_applicable"
|
|
2189
|
+
}
|
|
2190
|
+
},
|
|
2191
|
+
{
|
|
2192
|
+
"sourceId": "slash_command:unstar",
|
|
2193
|
+
"sourceFile": "packages/coding-agent/src/slash-commands/builtin-registry.ts",
|
|
2194
|
+
"sourceKind": "slash_command",
|
|
2195
|
+
"decision": "exclude",
|
|
2196
|
+
"rationale": "local session-discovery metadata command; no SDK operation counterpart or runtime/retention authority",
|
|
2197
|
+
"exclusionMetadata": {
|
|
2198
|
+
"adapterMappings": "not_applicable",
|
|
2199
|
+
"testIds": "not_applicable"
|
|
2200
|
+
}
|
|
2201
|
+
},
|
|
2180
2202
|
{
|
|
2181
2203
|
"sourceId": "slash_command:resume",
|
|
2182
2204
|
"sourceFile": "packages/coding-agent/src/slash-commands/builtin-registry.ts",
|
|
@@ -2275,6 +2297,24 @@
|
|
|
2275
2297
|
"testIds": "not_applicable"
|
|
2276
2298
|
}
|
|
2277
2299
|
},
|
|
2300
|
+
{
|
|
2301
|
+
"sourceId": "slash_command:fork",
|
|
2302
|
+
"sourceFile": "packages/coding-agent/src/slash-commands/builtin-registry.ts",
|
|
2303
|
+
"sourceKind": "slash_command",
|
|
2304
|
+
"decision": "include",
|
|
2305
|
+
"sdkId": "session.branch",
|
|
2306
|
+
"adapterMappings": {
|
|
2307
|
+
"telegram": "generic_safe",
|
|
2308
|
+
"discord": "generic_safe",
|
|
2309
|
+
"slack": "generic_safe",
|
|
2310
|
+
"mcp": "generic_safe",
|
|
2311
|
+
"acp": "generic_safe",
|
|
2312
|
+
"daemonCli": "generic_safe"
|
|
2313
|
+
},
|
|
2314
|
+
"testIds": [
|
|
2315
|
+
"packages/coding-agent/test/sdk-operation-inventory.test.ts"
|
|
2316
|
+
]
|
|
2317
|
+
},
|
|
2278
2318
|
{
|
|
2279
2319
|
"sourceId": "slash_command:rename",
|
|
2280
2320
|
"sourceFile": "packages/coding-agent/src/slash-commands/builtin-registry.ts",
|
|
@@ -109,6 +109,7 @@ import {
|
|
|
109
109
|
type FallbackAttemptToken,
|
|
110
110
|
type FallbackTriggerClass,
|
|
111
111
|
} from "@sayknow-cli/ai/utils/fallback-transport";
|
|
112
|
+
import { REPETITION_GUARD_ERROR_CODE } from "@sayknow-cli/ai/utils/stream-repetition-guard";
|
|
112
113
|
import {
|
|
113
114
|
BTW_MAX_ANSWER_UTF8_BYTES,
|
|
114
115
|
BTW_MAX_QUESTION_UTF8_BYTES,
|
|
@@ -360,6 +361,7 @@ import { parseCommandArgs } from "../utils/command-args";
|
|
|
360
361
|
import { type EditMode, resolveEditMode } from "../utils/edit-mode";
|
|
361
362
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
362
363
|
import { extractFileMentions, generateFileMentionMessages } from "../utils/file-mentions";
|
|
364
|
+
import { invalidateSessionTitleGeneration } from "../utils/session-title-generation";
|
|
363
365
|
import { buildNamedToolChoice, buildNamedToolChoiceResult } from "../utils/tool-choice";
|
|
364
366
|
import { buildWorkflowIntentDiff, WORKFLOW_INTENT_DIFF_CUSTOM_TYPE } from "../workflow/workflow-intent-diff";
|
|
365
367
|
import { buildWorkspaceTree, type WorkspaceTree } from "../workspace-tree";
|
|
@@ -7717,6 +7719,9 @@ export class AgentSession {
|
|
|
7717
7719
|
}
|
|
7718
7720
|
|
|
7719
7721
|
async prompt(text: string, options?: PromptOptions): Promise<void> {
|
|
7722
|
+
// A newer human prompt makes any in-flight /rename regeneration stale.
|
|
7723
|
+
if (options?.synthetic !== true && options?.attribution !== "agent")
|
|
7724
|
+
invalidateSessionTitleGeneration(this.sessionManager);
|
|
7720
7725
|
this.#assertRecoveryHydrationPromoted();
|
|
7721
7726
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
7722
7727
|
|
|
@@ -8493,6 +8498,7 @@ export class AgentSession {
|
|
|
8493
8498
|
* Queue a steering message to interrupt the agent mid-run.
|
|
8494
8499
|
*/
|
|
8495
8500
|
async steer(text: string, images?: ImageContent[]): Promise<void> {
|
|
8501
|
+
invalidateSessionTitleGeneration(this.sessionManager);
|
|
8496
8502
|
this.#assertRecoveryHydrationPromoted();
|
|
8497
8503
|
if (text.startsWith("/")) {
|
|
8498
8504
|
this.#throwIfExtensionCommand(text);
|
|
@@ -8511,6 +8517,7 @@ export class AgentSession {
|
|
|
8511
8517
|
images?: ImageContent[],
|
|
8512
8518
|
options?: Pick<PromptOptions, "followUpQueuePolicy">,
|
|
8513
8519
|
): Promise<void> {
|
|
8520
|
+
invalidateSessionTitleGeneration(this.sessionManager);
|
|
8514
8521
|
this.#assertRecoveryHydrationPromoted();
|
|
8515
8522
|
if (text.startsWith("/")) {
|
|
8516
8523
|
this.#throwIfExtensionCommand(text);
|
|
@@ -13726,6 +13733,11 @@ export class AgentSession {
|
|
|
13726
13733
|
#classifyErrorForRetry(message: AssistantMessage): RetryErrorClassification {
|
|
13727
13734
|
if (message.stopReason !== "error") return "none";
|
|
13728
13735
|
if (message.errorKind === "provider_safety_stop") return "terminal";
|
|
13736
|
+
// A decode loop is deterministic for the submitted context: replaying the
|
|
13737
|
+
// identical conversation re-trips the guard and re-bills the full context.
|
|
13738
|
+
// Without this the message carries no transport facts and would fall
|
|
13739
|
+
// through to "unknown", which is admitted for bounded retry (#5627).
|
|
13740
|
+
if (message.errorCode === REPETITION_GUARD_ERROR_CODE) return "terminal";
|
|
13729
13741
|
if (!message.errorMessage) return "none";
|
|
13730
13742
|
const err = message.errorMessage;
|
|
13731
13743
|
// Provider safety refusals (e.g. Anthropic stop_reason "refusal" /
|
|
@@ -1215,6 +1215,16 @@ function receiptMatches(
|
|
|
1215
1215
|
tree?: unknown;
|
|
1216
1216
|
};
|
|
1217
1217
|
};
|
|
1218
|
+
// A receipt is keyed by its source and names exactly one destination. The
|
|
1219
|
+
// resume listing probes every (legacy source, v2 destination) pair, so reject
|
|
1220
|
+
// on these cheap receipt fields before hashing both transcripts and walking
|
|
1221
|
+
// the artifact manifest; the full conjunction below would be false anyway.
|
|
1222
|
+
if (
|
|
1223
|
+
record.destination?.path !== destination.path ||
|
|
1224
|
+
record.source?.path !== source.path ||
|
|
1225
|
+
record.source?.sha256 !== source.identity.sha256
|
|
1226
|
+
)
|
|
1227
|
+
return false;
|
|
1218
1228
|
const exact = (
|
|
1219
1229
|
recorded: { dev?: unknown; ino?: unknown; size?: unknown; mtimeNs?: unknown } | undefined,
|
|
1220
1230
|
candidate: ManagedCandidate,
|
|
@@ -35,6 +35,7 @@ import type { ManagedLegacyLocalMigrationSource } from "../internal-urls/local-p
|
|
|
35
35
|
import { assertSafePathComponent } from "../skc-runtime/session-layout";
|
|
36
36
|
import { writeTextAtomic } from "../skc-runtime/state-writer";
|
|
37
37
|
import * as git from "../utils/git";
|
|
38
|
+
import { invalidateSessionTitleGeneration } from "../utils/session-title-generation";
|
|
38
39
|
import { ArtifactManager } from "./artifacts";
|
|
39
40
|
import {
|
|
40
41
|
type BlobPutResult,
|
|
@@ -96,7 +97,6 @@ import {
|
|
|
96
97
|
memoryGuardCanonicalJson,
|
|
97
98
|
memoryGuardSha256Hex,
|
|
98
99
|
} from "./memory-guard-checkpoint-participant";
|
|
99
|
-
|
|
100
100
|
import {
|
|
101
101
|
type BashExecutionMessage,
|
|
102
102
|
type CustomMessage,
|
|
@@ -110,6 +110,7 @@ import {
|
|
|
110
110
|
stripInternalDetailsFields,
|
|
111
111
|
} from "./messages";
|
|
112
112
|
import { type SessionManagerReadAccess, sessionManagerReadCapability } from "./session-manager-internal";
|
|
113
|
+
import { isSessionIdStarred, setSessionIdStarred } from "./session-stars";
|
|
113
114
|
import type {
|
|
114
115
|
ManagedSessionSecurityContext,
|
|
115
116
|
SessionStorage,
|
|
@@ -889,6 +890,8 @@ export interface SessionInfo {
|
|
|
889
890
|
/** Working directory where the session was started. Empty string for old sessions. */
|
|
890
891
|
cwd: string;
|
|
891
892
|
title?: string;
|
|
893
|
+
/** Starred for discovery (see session-stars.ts); never affects resolution or retention. */
|
|
894
|
+
starred: boolean;
|
|
892
895
|
/** Path to the parent session (if this session was forked). */
|
|
893
896
|
parentSessionPath?: string;
|
|
894
897
|
created: Date;
|
|
@@ -3819,6 +3822,11 @@ async function collectProjectSessions(cwd: string, storage: FileSessionStorage):
|
|
|
3819
3822
|
return await collectSessionsFromFiles(listProjectSessionTranscriptFiles(cwd), storage);
|
|
3820
3823
|
}
|
|
3821
3824
|
|
|
3825
|
+
/** Starred sessions first, each group keeping its incoming (recency) order. */
|
|
3826
|
+
export function prioritizeStarredSessions(sessions: readonly SessionInfo[]): SessionInfo[] {
|
|
3827
|
+
return [...sessions.filter(session => session.starred), ...sessions.filter(session => !session.starred)];
|
|
3828
|
+
}
|
|
3829
|
+
|
|
3822
3830
|
function mergeSessionInventories(...inventories: SessionInfo[][]): SessionInfo[] {
|
|
3823
3831
|
const sessions = new Map<string, SessionInfo>();
|
|
3824
3832
|
for (const inventory of inventories) {
|
|
@@ -4229,6 +4237,7 @@ async function collectSessionFromFile(
|
|
|
4229
4237
|
id: header.id,
|
|
4230
4238
|
cwd: header.cwd ?? "",
|
|
4231
4239
|
title: header.title ?? shortSummary,
|
|
4240
|
+
starred: isSessionIdStarred(header.id),
|
|
4232
4241
|
parentSessionPath: header.parentSession,
|
|
4233
4242
|
created: new Date(header.timestamp ?? ""),
|
|
4234
4243
|
modified: stats.mtime,
|
|
@@ -6998,6 +7007,20 @@ export class SessionManager {
|
|
|
6998
7007
|
return this.#sessionName;
|
|
6999
7008
|
}
|
|
7000
7009
|
|
|
7010
|
+
isSessionStarred(): boolean {
|
|
7011
|
+
return isSessionIdStarred(this.getSessionId());
|
|
7012
|
+
}
|
|
7013
|
+
|
|
7014
|
+
/** Star or unstar this session. Returns `false` when it was already in that state. */
|
|
7015
|
+
async setSessionStarred(starred: boolean): Promise<boolean> {
|
|
7016
|
+
return setSessionIdStarred(this.getSessionId(), starred);
|
|
7017
|
+
}
|
|
7018
|
+
|
|
7019
|
+
/** Star or unstar a session listed in the resume picker (not necessarily this one). */
|
|
7020
|
+
async setSessionStarredForPicker(session: Pick<SessionInfo, "id">, starred: boolean): Promise<void> {
|
|
7021
|
+
await setSessionIdStarred(session.id, starred);
|
|
7022
|
+
}
|
|
7023
|
+
|
|
7001
7024
|
/** Strip C0/C1 control characters (includes ESC, so removes ANSI sequences) and collapse whitespace. */
|
|
7002
7025
|
static #sanitizeName(name: string): string {
|
|
7003
7026
|
return name
|
|
@@ -7023,6 +7046,7 @@ export class SessionManager {
|
|
|
7023
7046
|
const sanitized = SessionManager.#sanitizeName(name);
|
|
7024
7047
|
if (!sanitized) return false;
|
|
7025
7048
|
|
|
7049
|
+
invalidateSessionTitleGeneration(this);
|
|
7026
7050
|
this.#sessionName = sanitized;
|
|
7027
7051
|
this.#titleSource = source;
|
|
7028
7052
|
await this.#appendHeaderPatch({ title: sanitized, titleSource: source });
|
|
@@ -7219,6 +7243,7 @@ export class SessionManager {
|
|
|
7219
7243
|
| PythonExecutionMessage
|
|
7220
7244
|
| FileMentionMessage,
|
|
7221
7245
|
): string {
|
|
7246
|
+
if (message.role === "user") invalidateSessionTitleGeneration(this);
|
|
7222
7247
|
const entry: SessionMessageEntry = {
|
|
7223
7248
|
type: "message",
|
|
7224
7249
|
id: generateId(this.#byId),
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starred sessions: discovery-only metadata kept in one small index file under
|
|
3
|
+
* the agent directory, keyed by session id.
|
|
4
|
+
*
|
|
5
|
+
* Stars never touch transcripts. They sort sessions first in the resume picker
|
|
6
|
+
* and the sessions dashboard, and nothing else: `--continue`, ID-prefix
|
|
7
|
+
* resolution, deletion, and retention ignore them. A deleted session leaves a
|
|
8
|
+
* harmless stale id behind; a forked session gets a new id and starts unstarred.
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from "node:fs";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { getAgentDir, logger } from "@sayknow-cli/utils";
|
|
13
|
+
import { withFileLock } from "../config/file-lock";
|
|
14
|
+
|
|
15
|
+
const INDEX_FILE_NAME = "session-stars.json";
|
|
16
|
+
|
|
17
|
+
interface StarIndexFile {
|
|
18
|
+
version: 1;
|
|
19
|
+
/** Session id → ISO time the star was added. */
|
|
20
|
+
starred: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let cache: { path: string; mtimeMs: number; size: number; ids: ReadonlySet<string> } | undefined;
|
|
24
|
+
|
|
25
|
+
export function getSessionStarIndexPath(agentDir: string = getAgentDir()): string {
|
|
26
|
+
return path.join(agentDir, INDEX_FILE_NAME);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseIndex(text: string): StarIndexFile {
|
|
30
|
+
const parsed = JSON.parse(text) as Partial<StarIndexFile>;
|
|
31
|
+
const starred: Record<string, string> = {};
|
|
32
|
+
if (parsed && typeof parsed === "object" && parsed.starred && typeof parsed.starred === "object") {
|
|
33
|
+
for (const [id, at] of Object.entries(parsed.starred)) {
|
|
34
|
+
if (typeof at === "string") starred[id] = at;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { version: 1, starred };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Ids of starred sessions. Cheap to call per listed session: the parsed index is
|
|
42
|
+
* cached until the file's mtime or size changes. A missing or unreadable index
|
|
43
|
+
* means nothing is starred; listing never fails because of stars.
|
|
44
|
+
*/
|
|
45
|
+
export function readStarredSessionIds(agentDir?: string): ReadonlySet<string> {
|
|
46
|
+
const indexPath = getSessionStarIndexPath(agentDir);
|
|
47
|
+
let stat: fs.Stats;
|
|
48
|
+
try {
|
|
49
|
+
stat = fs.statSync(indexPath);
|
|
50
|
+
} catch {
|
|
51
|
+
cache = undefined;
|
|
52
|
+
return new Set();
|
|
53
|
+
}
|
|
54
|
+
if (cache?.path === indexPath && cache.mtimeMs === stat.mtimeMs && cache.size === stat.size) return cache.ids;
|
|
55
|
+
try {
|
|
56
|
+
const ids = new Set(Object.keys(parseIndex(fs.readFileSync(indexPath, "utf8")).starred));
|
|
57
|
+
cache = { path: indexPath, mtimeMs: stat.mtimeMs, size: stat.size, ids };
|
|
58
|
+
return ids;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
logger.warn("Ignoring unreadable session star index", { indexPath, error: String(error) });
|
|
61
|
+
return new Set();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function isSessionIdStarred(sessionId: string, agentDir?: string): boolean {
|
|
66
|
+
return readStarredSessionIds(agentDir).has(sessionId);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Star or unstar a session by id. Returns `true` when the index changed and
|
|
71
|
+
* `false` when the session was already in the requested state. The
|
|
72
|
+
* read-modify-write runs under a cross-process lock and lands with an atomic
|
|
73
|
+
* rename, so two CLIs toggling different sessions cannot drop each other's star.
|
|
74
|
+
*/
|
|
75
|
+
export async function setSessionIdStarred(sessionId: string, starred: boolean, agentDir?: string): Promise<boolean> {
|
|
76
|
+
if (!sessionId) throw new Error("A session id is required to change its star.");
|
|
77
|
+
const indexPath = getSessionStarIndexPath(agentDir);
|
|
78
|
+
await fs.promises.mkdir(path.dirname(indexPath), { recursive: true });
|
|
79
|
+
return withFileLock(indexPath, async () => {
|
|
80
|
+
let index: StarIndexFile;
|
|
81
|
+
try {
|
|
82
|
+
index = parseIndex(await fs.promises.readFile(indexPath, "utf8"));
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
85
|
+
// Refuse to overwrite an index we cannot read: that would silently drop every other star.
|
|
86
|
+
throw new Error(`Cannot update session stars: ${indexPath} is unreadable (${String(error)}).`);
|
|
87
|
+
}
|
|
88
|
+
index = { version: 1, starred: {} };
|
|
89
|
+
}
|
|
90
|
+
if (sessionId in index.starred === starred) return false;
|
|
91
|
+
if (starred) index.starred[sessionId] = new Date().toISOString();
|
|
92
|
+
else delete index.starred[sessionId];
|
|
93
|
+
const tmpPath = `${indexPath}.${process.pid}.${Date.now()}.tmp`;
|
|
94
|
+
try {
|
|
95
|
+
await fs.promises.writeFile(tmpPath, `${JSON.stringify(index, null, "\t")}\n`, "utf8");
|
|
96
|
+
await fs.promises.rename(tmpPath, indexPath);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
await fs.promises.rm(tmpPath, { force: true }).catch(() => undefined);
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
cache = undefined;
|
|
102
|
+
return true;
|
|
103
|
+
});
|
|
104
|
+
}
|