kimaki 0.4.95 → 0.4.96
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.
|
@@ -40,6 +40,14 @@ import { extractLeadingOpencodeCommand } from '../opencode-command-detection.js'
|
|
|
40
40
|
const logger = createLogger(LogPrefix.SESSION);
|
|
41
41
|
const discordLogger = createLogger(LogPrefix.DISCORD);
|
|
42
42
|
const DETERMINISTIC_CONTEXT_LIMIT = 100_000;
|
|
43
|
+
const TOAST_SESSION_ID_REGEX = /\b(ses_[A-Za-z0-9]+)\b\s*$/u;
|
|
44
|
+
function extractToastSessionId({ message }) {
|
|
45
|
+
const match = message.match(TOAST_SESSION_ID_REGEX);
|
|
46
|
+
return match?.[1];
|
|
47
|
+
}
|
|
48
|
+
function stripToastSessionId({ message }) {
|
|
49
|
+
return message.replace(TOAST_SESSION_ID_REGEX, '').trimEnd();
|
|
50
|
+
}
|
|
43
51
|
const shouldLogSessionEvents = process.env['KIMAKI_LOG_SESSION_EVENTS'] === '1' ||
|
|
44
52
|
process.env['KIMAKI_VITEST'] === '1';
|
|
45
53
|
// ── Registry ─────────────────────────────────────────────────────
|
|
@@ -943,6 +951,9 @@ export class ThreadSessionRuntime {
|
|
|
943
951
|
}
|
|
944
952
|
const sessionId = this.state?.sessionId;
|
|
945
953
|
const eventSessionId = getOpencodeEventSessionId(event);
|
|
954
|
+
const toastSessionId = event.type === 'tui.toast.show'
|
|
955
|
+
? extractToastSessionId({ message: event.properties.message })
|
|
956
|
+
: undefined;
|
|
946
957
|
if (shouldLogSessionEvents) {
|
|
947
958
|
const eventDetails = (() => {
|
|
948
959
|
if (event.type === 'session.error') {
|
|
@@ -970,6 +981,7 @@ export class ThreadSessionRuntime {
|
|
|
970
981
|
logger.log(`[EVENT] type=${event.type} eventSessionId=${eventSessionId || 'none'} activeSessionId=${sessionId || 'none'} ${this.formatRunStateForLog()}${eventDetails}`);
|
|
971
982
|
}
|
|
972
983
|
const isGlobalEvent = event.type === 'tui.toast.show';
|
|
984
|
+
const isScopedToastEvent = Boolean(toastSessionId);
|
|
973
985
|
// Drop events that don't match current session (stale events from
|
|
974
986
|
// previous sessions), unless it's a global event or a subtask session.
|
|
975
987
|
if (!isGlobalEvent && eventSessionId && eventSessionId !== sessionId) {
|
|
@@ -977,6 +989,11 @@ export class ThreadSessionRuntime {
|
|
|
977
989
|
return; // stale event from previous session
|
|
978
990
|
}
|
|
979
991
|
}
|
|
992
|
+
if (isScopedToastEvent && toastSessionId !== sessionId) {
|
|
993
|
+
if (!this.getSubtaskInfoForSession(toastSessionId)) {
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
980
997
|
if (isOpencodeSessionEventLogEnabled()) {
|
|
981
998
|
const eventLogResult = await appendOpencodeSessionEventLog({
|
|
982
999
|
threadId: this.threadId,
|
|
@@ -2023,7 +2040,7 @@ export class ThreadSessionRuntime {
|
|
|
2023
2040
|
if (properties.variant === 'warning') {
|
|
2024
2041
|
return;
|
|
2025
2042
|
}
|
|
2026
|
-
const toastMessage = properties.message.trim();
|
|
2043
|
+
const toastMessage = stripToastSessionId({ message: properties.message }).trim();
|
|
2027
2044
|
if (!toastMessage) {
|
|
2028
2045
|
return;
|
|
2029
2046
|
}
|
|
@@ -11,12 +11,16 @@ import { createPluginLogger, formatPluginErrorWithStack, setPluginLogFilePath }
|
|
|
11
11
|
import { initSentry, notifyError } from './sentry.js';
|
|
12
12
|
import { abbreviatePath } from './utils.js';
|
|
13
13
|
const logger = createPluginLogger('OPENCODE');
|
|
14
|
+
const TOAST_SESSION_MARKER_SEPARATOR = ' ';
|
|
14
15
|
function getSystemPromptDiffDir({ dataDir }) {
|
|
15
16
|
return path.join(dataDir, 'system-prompt-diffs');
|
|
16
17
|
}
|
|
17
18
|
function normalizeSystemPrompt({ system }) {
|
|
18
19
|
return system.join('\n');
|
|
19
20
|
}
|
|
21
|
+
function appendToastSessionMarker({ message, sessionId, }) {
|
|
22
|
+
return `${message}${TOAST_SESSION_MARKER_SEPARATOR}${sessionId}`;
|
|
23
|
+
}
|
|
20
24
|
function buildTurnContext({ input, directory, }) {
|
|
21
25
|
const model = input.model
|
|
22
26
|
? `${input.model.providerID}/${input.model.modelID}${input.variant ? `:${input.variant}` : ''}`
|
|
@@ -66,7 +70,7 @@ function writeSystemPromptDiffFile({ dataDir, sessionId, beforePrompt, afterProm
|
|
|
66
70
|
const timestamp = new Date().toISOString().replaceAll(':', '-');
|
|
67
71
|
const sessionDir = path.join(getSystemPromptDiffDir({ dataDir }), sessionId);
|
|
68
72
|
const filePath = path.join(sessionDir, `${timestamp}.diff`);
|
|
69
|
-
const latestPromptPath = path.join(sessionDir, `${
|
|
73
|
+
const latestPromptPath = path.join(sessionDir, `${timestamp}.md`);
|
|
70
74
|
const fileContent = [
|
|
71
75
|
`Session: ${sessionId}`,
|
|
72
76
|
`Created: ${new Date().toISOString()}`,
|
|
@@ -84,6 +88,7 @@ function writeSystemPromptDiffFile({ dataDir, sessionId, beforePrompt, afterProm
|
|
|
84
88
|
additions: diff.additions,
|
|
85
89
|
deletions: diff.deletions,
|
|
86
90
|
filePath,
|
|
91
|
+
latestPromptPath,
|
|
87
92
|
};
|
|
88
93
|
},
|
|
89
94
|
catch: (error) => {
|
|
@@ -154,9 +159,12 @@ async function handleSystemTransform({ input, output, sessions, dataDir, client,
|
|
|
154
159
|
body: {
|
|
155
160
|
variant: 'info',
|
|
156
161
|
title: 'Context cache discarded',
|
|
157
|
-
message:
|
|
158
|
-
|
|
159
|
-
|
|
162
|
+
message: appendToastSessionMarker({
|
|
163
|
+
sessionId,
|
|
164
|
+
message: `system prompt changed since the previous message (+${diffFileResult.additions} / -${diffFileResult.deletions}). ` +
|
|
165
|
+
`Diff: \`${abbreviatePath(diffFileResult.filePath)}\`. ` +
|
|
166
|
+
`Latest prompt: \`${abbreviatePath(diffFileResult.latestPromptPath)}\``,
|
|
167
|
+
}),
|
|
160
168
|
},
|
|
161
169
|
});
|
|
162
170
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "kimaki",
|
|
3
3
|
"module": "index.ts",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.4.
|
|
5
|
+
"version": "0.4.96",
|
|
6
6
|
"repository": "https://github.com/remorses/kimaki",
|
|
7
7
|
"bin": "bin.js",
|
|
8
8
|
"files": [
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"prisma": "7.4.2",
|
|
26
26
|
"tsx": "^4.20.5",
|
|
27
27
|
"undici": "^8.0.2",
|
|
28
|
-
"discord-digital-twin": "^0.1.0",
|
|
29
28
|
"opencode-cached-provider": "^0.0.1",
|
|
29
|
+
"discord-digital-twin": "^0.1.0",
|
|
30
30
|
"db": "^0.0.0",
|
|
31
31
|
"opencode-deterministic-provider": "^0.0.1"
|
|
32
32
|
},
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"zod": "^4.3.6",
|
|
66
66
|
"zustand": "^5.0.11",
|
|
67
67
|
"errore": "^0.14.1",
|
|
68
|
+
"traforo": "^0.2.4",
|
|
68
69
|
"opencode-injection-guard": "^0.2.1",
|
|
69
|
-
"libsqlproxy": "^0.1.0"
|
|
70
|
-
"traforo": "^0.2.4"
|
|
70
|
+
"libsqlproxy": "^0.1.0"
|
|
71
71
|
},
|
|
72
72
|
"optionalDependencies": {
|
|
73
73
|
"@snazzah/davey": "^0.1.10",
|
|
@@ -842,6 +842,7 @@ const AnthropicAuthPlugin: Plugin = async ({ client }) => {
|
|
|
842
842
|
message: `Switching from account ${rotated.fromLabel} to account ${rotated.toLabel}`,
|
|
843
843
|
variant: 'info',
|
|
844
844
|
},
|
|
845
|
+
|
|
845
846
|
}).catch(() => {})
|
|
846
847
|
const retryAuth = await getFreshOAuth(getAuth, client)
|
|
847
848
|
if (retryAuth) {
|
|
@@ -137,6 +137,17 @@ import { extractLeadingOpencodeCommand } from '../opencode-command-detection.js'
|
|
|
137
137
|
const logger = createLogger(LogPrefix.SESSION)
|
|
138
138
|
const discordLogger = createLogger(LogPrefix.DISCORD)
|
|
139
139
|
const DETERMINISTIC_CONTEXT_LIMIT = 100_000
|
|
140
|
+
const TOAST_SESSION_ID_REGEX = /\b(ses_[A-Za-z0-9]+)\b\s*$/u
|
|
141
|
+
|
|
142
|
+
function extractToastSessionId({ message }: { message: string }): string | undefined {
|
|
143
|
+
const match = message.match(TOAST_SESSION_ID_REGEX)
|
|
144
|
+
return match?.[1]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function stripToastSessionId({ message }: { message: string }): string {
|
|
148
|
+
return message.replace(TOAST_SESSION_ID_REGEX, '').trimEnd()
|
|
149
|
+
}
|
|
150
|
+
|
|
140
151
|
const shouldLogSessionEvents =
|
|
141
152
|
process.env['KIMAKI_LOG_SESSION_EVENTS'] === '1' ||
|
|
142
153
|
process.env['KIMAKI_VITEST'] === '1'
|
|
@@ -1381,6 +1392,9 @@ export class ThreadSessionRuntime {
|
|
|
1381
1392
|
const sessionId = this.state?.sessionId
|
|
1382
1393
|
|
|
1383
1394
|
const eventSessionId = getOpencodeEventSessionId(event)
|
|
1395
|
+
const toastSessionId = event.type === 'tui.toast.show'
|
|
1396
|
+
? extractToastSessionId({ message: event.properties.message })
|
|
1397
|
+
: undefined
|
|
1384
1398
|
|
|
1385
1399
|
if (shouldLogSessionEvents) {
|
|
1386
1400
|
const eventDetails = (() => {
|
|
@@ -1412,6 +1426,7 @@ export class ThreadSessionRuntime {
|
|
|
1412
1426
|
}
|
|
1413
1427
|
|
|
1414
1428
|
const isGlobalEvent = event.type === 'tui.toast.show'
|
|
1429
|
+
const isScopedToastEvent = Boolean(toastSessionId)
|
|
1415
1430
|
|
|
1416
1431
|
// Drop events that don't match current session (stale events from
|
|
1417
1432
|
// previous sessions), unless it's a global event or a subtask session.
|
|
@@ -1420,6 +1435,11 @@ export class ThreadSessionRuntime {
|
|
|
1420
1435
|
return // stale event from previous session
|
|
1421
1436
|
}
|
|
1422
1437
|
}
|
|
1438
|
+
if (isScopedToastEvent && toastSessionId !== sessionId) {
|
|
1439
|
+
if (!this.getSubtaskInfoForSession(toastSessionId!)) {
|
|
1440
|
+
return
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1423
1443
|
|
|
1424
1444
|
if (isOpencodeSessionEventLogEnabled()) {
|
|
1425
1445
|
const eventLogResult = await appendOpencodeSessionEventLog({
|
|
@@ -2763,7 +2783,7 @@ export class ThreadSessionRuntime {
|
|
|
2763
2783
|
if (properties.variant === 'warning') {
|
|
2764
2784
|
return
|
|
2765
2785
|
}
|
|
2766
|
-
const toastMessage = properties.message.trim()
|
|
2786
|
+
const toastMessage = stripToastSessionId({ message: properties.message }).trim()
|
|
2767
2787
|
if (!toastMessage) {
|
|
2768
2788
|
return
|
|
2769
2789
|
}
|
|
@@ -14,6 +14,7 @@ import { initSentry, notifyError } from './sentry.js'
|
|
|
14
14
|
import { abbreviatePath } from './utils.js'
|
|
15
15
|
|
|
16
16
|
const logger = createPluginLogger('OPENCODE')
|
|
17
|
+
const TOAST_SESSION_MARKER_SEPARATOR = ' '
|
|
17
18
|
|
|
18
19
|
type PluginHooks = Awaited<ReturnType<Plugin>>
|
|
19
20
|
type SystemTransformHook = NonNullable<PluginHooks['experimental.chat.system.transform']>
|
|
@@ -54,6 +55,16 @@ function normalizeSystemPrompt({ system }: { system: string[] }): string {
|
|
|
54
55
|
return system.join('\n')
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
function appendToastSessionMarker({
|
|
59
|
+
message,
|
|
60
|
+
sessionId,
|
|
61
|
+
}: {
|
|
62
|
+
message: string
|
|
63
|
+
sessionId: string
|
|
64
|
+
}): string {
|
|
65
|
+
return `${message}${TOAST_SESSION_MARKER_SEPARATOR}${sessionId}`
|
|
66
|
+
}
|
|
67
|
+
|
|
57
68
|
function buildTurnContext({
|
|
58
69
|
input,
|
|
59
70
|
directory,
|
|
@@ -131,7 +142,12 @@ function writeSystemPromptDiffFile({
|
|
|
131
142
|
sessionId: string
|
|
132
143
|
beforePrompt: string
|
|
133
144
|
afterPrompt: string
|
|
134
|
-
}): Error | {
|
|
145
|
+
}): Error | {
|
|
146
|
+
additions: number
|
|
147
|
+
deletions: number
|
|
148
|
+
filePath: string
|
|
149
|
+
latestPromptPath: string
|
|
150
|
+
} {
|
|
135
151
|
const diff = buildPatch({
|
|
136
152
|
beforeText: beforePrompt,
|
|
137
153
|
afterText: afterPrompt,
|
|
@@ -141,7 +157,7 @@ function writeSystemPromptDiffFile({
|
|
|
141
157
|
const timestamp = new Date().toISOString().replaceAll(':', '-')
|
|
142
158
|
const sessionDir = path.join(getSystemPromptDiffDir({ dataDir }), sessionId)
|
|
143
159
|
const filePath = path.join(sessionDir, `${timestamp}.diff`)
|
|
144
|
-
const latestPromptPath = path.join(sessionDir, `${
|
|
160
|
+
const latestPromptPath = path.join(sessionDir, `${timestamp}.md`)
|
|
145
161
|
const fileContent = [
|
|
146
162
|
`Session: ${sessionId}`,
|
|
147
163
|
`Created: ${new Date().toISOString()}`,
|
|
@@ -156,11 +172,12 @@ function writeSystemPromptDiffFile({
|
|
|
156
172
|
fs.mkdirSync(sessionDir, { recursive: true })
|
|
157
173
|
fs.writeFileSync(filePath, fileContent)
|
|
158
174
|
// fs.writeFileSync(latestPromptPath, afterPrompt)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
175
|
+
return {
|
|
176
|
+
additions: diff.additions,
|
|
177
|
+
deletions: diff.deletions,
|
|
178
|
+
filePath,
|
|
179
|
+
latestPromptPath,
|
|
180
|
+
}
|
|
164
181
|
},
|
|
165
182
|
catch: (error) => {
|
|
166
183
|
return new Error('Failed to write system prompt diff file', { cause: error })
|
|
@@ -257,10 +274,13 @@ async function handleSystemTransform({
|
|
|
257
274
|
body: {
|
|
258
275
|
variant: 'info',
|
|
259
276
|
title: 'Context cache discarded',
|
|
260
|
-
message:
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
`
|
|
277
|
+
message: appendToastSessionMarker({
|
|
278
|
+
sessionId,
|
|
279
|
+
message:
|
|
280
|
+
`system prompt changed since the previous message (+${diffFileResult.additions} / -${diffFileResult.deletions}). ` +
|
|
281
|
+
`Diff: \`${abbreviatePath(diffFileResult.filePath)}\`. ` +
|
|
282
|
+
`Latest prompt: \`${abbreviatePath(diffFileResult.latestPromptPath)}\``,
|
|
283
|
+
}),
|
|
264
284
|
},
|
|
265
285
|
})
|
|
266
286
|
}
|