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, `${sessionId}.md`);
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: `System prompt changed since the previous message (+${diffFileResult.additions} / -${diffFileResult.deletions}). ` +
158
- `This usually means a plugin mutated the prompt and increased rate-limit usage. ` +
159
- `Diff: ${abbreviatePath(diffFileResult.filePath)}`,
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.95",
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 | { additions: number; deletions: number; filePath: string } {
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, `${sessionId}.md`)
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
- return {
160
- additions: diff.additions,
161
- deletions: diff.deletions,
162
- filePath,
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
- `System prompt changed since the previous message (+${diffFileResult.additions} / -${diffFileResult.deletions}). ` +
262
- `This usually means a plugin mutated the prompt and increased rate-limit usage. ` +
263
- `Diff: ${abbreviatePath(diffFileResult.filePath)}`,
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
  }