kimaki 0.26.0 → 0.28.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/dist/agent-model.e2e.test.js +250 -164
- package/dist/anthropic-account-identity.js +9 -1
- package/dist/anthropic-auth-plugin.js +10 -13
- package/dist/anthropic-auth-plugin.test.js +17 -0
- package/dist/anthropic-auth-state.js +1 -0
- package/dist/bash-tool-schema-plugin.js +32 -0
- package/dist/bash-tool-schema-plugin.test.js +49 -0
- package/dist/bin.js +73 -19
- package/dist/cli-commands/maintenance.js +46 -7
- package/dist/cli-commands/project.js +11 -27
- package/dist/cli-commands/send.js +30 -1
- package/dist/cli-commands/session.js +322 -90
- package/dist/cli-commands/task.js +34 -15
- package/dist/cli-parsing.test.js +83 -4
- package/dist/cli-runner.js +44 -8
- package/dist/cli-runner.test.js +27 -0
- package/dist/cli-send-thread.e2e.test.js +9 -9
- package/dist/cli.js +32 -0
- package/dist/commands/abort.js +5 -1
- package/dist/commands/action-buttons.js +2 -1
- package/dist/commands/add-project.js +4 -7
- package/dist/commands/agent.js +164 -32
- package/dist/commands/ask-question.js +23 -33
- package/dist/commands/ask-question.test.js +43 -1
- package/dist/commands/btw.js +28 -6
- package/dist/commands/file-upload.js +2 -1
- package/dist/commands/fork-subagent.js +2 -4
- package/dist/commands/fork.js +4 -4
- package/dist/commands/last-sessions.js +17 -10
- package/dist/commands/login.js +6 -0
- package/dist/commands/merge-worktree.js +47 -14
- package/dist/commands/model-variant.js +8 -1
- package/dist/commands/model.js +16 -9
- package/dist/commands/multioauth.js +1 -0
- package/dist/commands/new-worktree.js +170 -102
- package/dist/commands/queue.js +3 -2
- package/dist/commands/resume.js +4 -4
- package/dist/commands/session.js +78 -63
- package/dist/commands/user-command.js +6 -8
- package/dist/commands/worktrees.js +5 -13
- package/dist/config.js +16 -1
- package/dist/cpu-profiler.js +211 -0
- package/dist/cpu-profiler.test.js +160 -0
- package/dist/database.js +152 -3
- package/dist/db.js +33 -0
- package/dist/db.test.js +267 -1
- package/dist/default-channel-provisioning.test.js +148 -0
- package/dist/discord-bot.js +517 -488
- package/dist/discord-command-registration.js +25 -10
- package/dist/discord-command-registration.test.js +17 -0
- package/dist/discord-utils.js +97 -5
- package/dist/discord-utils.test.js +151 -1
- package/dist/errors.js +10 -0
- package/dist/exec-async.js +17 -5
- package/dist/external-directory-permissions.e2e.test.js +11 -7
- package/dist/external-directory-project-deny.e2e.test.js +5 -3
- package/dist/external-opencode-sync.js +94 -17
- package/dist/external-opencode-sync.test.js +206 -0
- package/dist/extract-sdk-error-message.test.js +13 -0
- package/dist/file-edit-log.js +197 -0
- package/dist/file-edit-log.test.js +258 -0
- package/dist/format-tables.js +52 -0
- package/dist/format-tables.test.js +93 -0
- package/dist/gateway-proxy-reconnect.e2e.test.js +28 -8
- package/dist/gateway-proxy.e2e.test.js +18 -18
- package/dist/git-worktree-core.js +182 -17
- package/dist/interaction-handler.js +416 -398
- package/dist/ipc-tools-plugin.js +68 -3
- package/dist/kimaki-opencode-plugin-loading.e2e.test.js +122 -58
- package/dist/kimaki-opencode-plugin.js +9 -3
- package/dist/kimaki-workspace-adaptor.js +33 -10
- package/dist/logger.js +11 -1
- package/dist/markdown.js +3 -1
- package/dist/markdown.test.js +11 -3
- package/dist/message-formatting.js +181 -51
- package/dist/message-formatting.test.js +529 -22
- package/dist/message-preprocessing.js +105 -1
- package/dist/oauth-rotation-shared.js +10 -0
- package/dist/openai-auth-plugin.js +1 -0
- package/dist/openai-auth-state.js +1 -0
- package/dist/opencode-command.js +28 -0
- package/dist/opencode-command.test.js +109 -1
- package/dist/opencode-interrupt-plugin.js +4 -0
- package/dist/opencode-interrupt-plugin.test.js +24 -0
- package/dist/opencode.js +82 -15
- package/dist/project-add.test.js +62 -0
- package/dist/question-abort-clears-dropdown.e2e.test.js +89 -0
- package/dist/question-abort-retry-text-before-dropdown.e2e.test.js +150 -0
- package/dist/question-text-before-dropdown.e2e.test.js +52 -0
- package/dist/queue-advanced-abort.e2e.test.js +8 -10
- package/dist/queue-advanced-action-buttons.e2e.test.js +8 -8
- package/dist/queue-advanced-e2e-setup.js +78 -5
- package/dist/queue-advanced-footer.e2e.test.js +60 -269
- package/dist/queue-advanced-model-switch.e2e.test.js +9 -11
- package/dist/queue-advanced-permissions-typing.e2e.test.js +15 -15
- package/dist/queue-advanced-question.e2e.test.js +32 -27
- package/dist/queue-advanced-typing-interrupt.e2e.test.js +10 -12
- package/dist/queue-advanced-typing.e2e.test.js +7 -9
- package/dist/queue-delete-message.e2e.test.js +3 -3
- package/dist/queue-drain-after-interactive-ui.e2e.test.js +5 -5
- package/dist/queue-interrupt-drain.e2e.test.js +122 -12
- package/dist/queue-question-select-drain.e2e.test.js +34 -132
- package/dist/runtime-lifecycle.e2e.test.js +21 -51
- package/dist/schema.js +35 -1
- package/dist/session-handler/event-stream-state.js +391 -50
- package/dist/session-handler/event-stream-state.test.js +736 -8
- package/dist/session-handler/model-utils.js +172 -5
- package/dist/session-handler/model-utils.test.js +445 -0
- package/dist/session-handler/opencode-session-event-log.js +30 -1
- package/dist/session-handler/thread-ingress.test.js +41 -0
- package/dist/session-handler/thread-runtime-state.js +2 -0
- package/dist/session-handler/thread-session-runtime-summary.test.js +121 -0
- package/dist/session-handler/thread-session-runtime.js +574 -299
- package/dist/session-search.js +123 -4
- package/dist/session-search.test.js +248 -1
- package/dist/session-sleep.test.js +224 -0
- package/dist/session-title-rename.test.js +37 -39
- package/dist/sleep.e2e.test.js +494 -0
- package/dist/startup-time.e2e.test.js +2 -1
- package/dist/store.js +3 -0
- package/dist/system-message.js +102 -16
- package/dist/system-message.test.js +192 -15
- package/dist/task-runner.js +96 -5
- package/dist/task-schedule.js +87 -1
- package/dist/task-schedule.test.js +149 -1
- package/dist/test-utils.js +17 -9
- package/dist/thinking-utils.js +10 -0
- package/dist/thinking-utils.test.js +35 -0
- package/dist/thread-message-queue.e2e.test.js +65 -213
- package/dist/undo-redo.e2e.test.js +17 -9
- package/dist/voice-attachment.js +23 -1
- package/dist/voice-handler.js +21 -4
- package/dist/voice-message.e2e.test.js +144 -30
- package/dist/voice-routing-input.e2e.test.js +195 -0
- package/dist/voice-routing-question.e2e.test.js +125 -0
- package/dist/voice.js +28 -8
- package/dist/voice.test.js +96 -30
- package/dist/worktree-clone-isolation.e2e.test.js +188 -0
- package/dist/worktree-lifecycle.e2e.test.js +55 -36
- package/dist/worktrees.js +226 -443
- package/dist/worktrees.test.js +435 -58
- package/dist/xai-auth-plugin.js +1 -0
- package/dist/xai-auth-state.js +1 -0
- package/package.json +8 -6
- package/skills/bump-holocron-consumers/SKILL.md +137 -0
- package/skills/critique/SKILL.md +4 -4
- package/skills/errore/SKILL.md +60 -218
- package/skills/goke/SKILL.md +150 -10
- package/skills/holocron/SKILL.md +154 -17
- package/skills/npm-package/SKILL.md +55 -35
- package/skills/profano/SKILL.md +17 -2
- package/skills/spiceflow/SKILL.md +39 -3
- package/skills/strada/SKILL.md +39 -0
- package/skills/zele/SKILL.md +19 -0
- package/src/agent-model.e2e.test.ts +287 -186
- package/src/anthropic-account-identity.ts +20 -1
- package/src/anthropic-auth-plugin.test.ts +19 -0
- package/src/anthropic-auth-plugin.ts +12 -16
- package/src/anthropic-auth-state.ts +1 -0
- package/src/bash-tool-schema-plugin.test.ts +55 -0
- package/src/bash-tool-schema-plugin.ts +46 -0
- package/src/bin.ts +78 -26
- package/src/cli-commands/maintenance.ts +59 -13
- package/src/cli-commands/project.ts +14 -35
- package/src/cli-commands/send.ts +31 -1
- package/src/cli-commands/session.ts +374 -114
- package/src/cli-commands/task.ts +34 -15
- package/src/cli-parsing.test.ts +103 -4
- package/src/cli-runner.test.ts +49 -0
- package/src/cli-runner.ts +44 -7
- package/src/cli-send-thread.e2e.test.ts +9 -11
- package/src/cli.ts +53 -0
- package/src/commands/abort.ts +6 -1
- package/src/commands/action-buttons.ts +2 -1
- package/src/commands/add-project.ts +4 -8
- package/src/commands/agent.ts +205 -29
- package/src/commands/ask-question.test.ts +48 -1
- package/src/commands/ask-question.ts +23 -33
- package/src/commands/btw.ts +40 -5
- package/src/commands/file-upload.ts +2 -1
- package/src/commands/fork-subagent.ts +2 -3
- package/src/commands/fork.ts +4 -3
- package/src/commands/last-sessions.ts +10 -4
- package/src/commands/login.ts +6 -0
- package/src/commands/merge-worktree.ts +54 -16
- package/src/commands/model-variant.ts +9 -1
- package/src/commands/model.ts +20 -9
- package/src/commands/multioauth.ts +1 -0
- package/src/commands/new-worktree.ts +138 -55
- package/src/commands/queue.ts +3 -2
- package/src/commands/resume.ts +4 -3
- package/src/commands/session.ts +94 -70
- package/src/commands/user-command.ts +6 -6
- package/src/commands/worktrees.ts +5 -12
- package/src/config.ts +18 -1
- package/src/cpu-profiler.test.ts +183 -0
- package/src/cpu-profiler.ts +293 -0
- package/src/database.ts +235 -3
- package/src/db.test.ts +293 -0
- package/src/db.ts +38 -0
- package/src/default-channel-provisioning.test.ts +155 -0
- package/src/discord-bot.ts +78 -36
- package/src/discord-command-registration.test.ts +20 -0
- package/src/discord-command-registration.ts +42 -12
- package/src/discord-utils.test.ts +176 -0
- package/src/discord-utils.ts +152 -7
- package/src/errors.ts +12 -0
- package/src/exec-async.ts +20 -11
- package/src/external-directory-permissions.e2e.test.ts +11 -7
- package/src/external-directory-project-deny.e2e.test.ts +5 -3
- package/src/external-opencode-sync.test.ts +248 -0
- package/src/external-opencode-sync.ts +122 -19
- package/src/extract-sdk-error-message.test.ts +18 -0
- package/src/file-edit-log.test.ts +287 -0
- package/src/file-edit-log.ts +283 -0
- package/src/format-tables.test.ts +101 -0
- package/src/format-tables.ts +75 -0
- package/src/gateway-proxy-reconnect.e2e.test.ts +38 -8
- package/src/gateway-proxy.e2e.test.ts +18 -17
- package/src/git-worktree-core.ts +247 -20
- package/src/interaction-handler.ts +36 -13
- package/src/ipc-tools-plugin.ts +82 -5
- package/src/kimaki-opencode-plugin-loading.e2e.test.ts +188 -97
- package/src/kimaki-opencode-plugin.ts +9 -3
- package/src/kimaki-workspace-adaptor.ts +40 -11
- package/src/logger.ts +14 -4
- package/src/markdown.test.ts +13 -3
- package/src/markdown.ts +4 -1
- package/src/message-formatting.test.ts +672 -25
- package/src/message-formatting.ts +242 -53
- package/src/message-preprocessing.ts +116 -1
- package/src/oauth-rotation-shared.ts +10 -0
- package/src/openai-auth-plugin.ts +1 -0
- package/src/openai-auth-state.ts +1 -0
- package/src/opencode-command.test.ts +140 -0
- package/src/opencode-command.ts +38 -0
- package/src/opencode-interrupt-plugin.test.ts +32 -0
- package/src/opencode-interrupt-plugin.ts +3 -0
- package/src/opencode.ts +115 -15
- package/src/project-add.test.ts +80 -0
- package/src/question-abort-clears-dropdown.e2e.test.ts +109 -0
- package/src/question-abort-retry-text-before-dropdown.e2e.test.ts +172 -0
- package/src/question-text-before-dropdown.e2e.test.ts +67 -0
- package/src/queue-advanced-abort.e2e.test.ts +8 -9
- package/src/queue-advanced-action-buttons.e2e.test.ts +8 -8
- package/src/queue-advanced-e2e-setup.ts +83 -4
- package/src/queue-advanced-footer.e2e.test.ts +67 -315
- package/src/queue-advanced-model-switch.e2e.test.ts +9 -10
- package/src/queue-advanced-permissions-typing.e2e.test.ts +15 -15
- package/src/queue-advanced-question.e2e.test.ts +36 -30
- package/src/queue-advanced-typing-interrupt.e2e.test.ts +11 -11
- package/src/queue-advanced-typing.e2e.test.ts +8 -8
- package/src/queue-delete-message.e2e.test.ts +3 -3
- package/src/queue-drain-after-interactive-ui.e2e.test.ts +5 -5
- package/src/queue-interrupt-drain.e2e.test.ts +133 -12
- package/src/queue-question-select-drain.e2e.test.ts +36 -161
- package/src/runtime-lifecycle.e2e.test.ts +21 -54
- package/src/schema.sql +15 -2
- package/src/schema.ts +37 -1
- package/src/session-handler/event-stream-state.test.ts +790 -7
- package/src/session-handler/event-stream-state.ts +568 -53
- package/src/session-handler/model-utils.test.ts +537 -0
- package/src/session-handler/model-utils.ts +281 -5
- package/src/session-handler/opencode-session-event-log.ts +31 -1
- package/src/session-handler/thread-ingress.test.ts +54 -0
- package/src/session-handler/thread-runtime-state.ts +3 -0
- package/src/session-handler/thread-session-runtime-summary.test.ts +169 -0
- package/src/session-handler/thread-session-runtime.ts +712 -358
- package/src/session-search.test.ts +281 -0
- package/src/session-search.ts +241 -7
- package/src/session-sleep.test.ts +294 -0
- package/src/session-title-rename.test.ts +54 -48
- package/src/sleep.e2e.test.ts +565 -0
- package/src/startup-time.e2e.test.ts +5 -1
- package/src/store.ts +22 -0
- package/src/system-message.test.ts +226 -15
- package/src/system-message.ts +130 -16
- package/src/task-runner.ts +138 -3
- package/src/task-schedule.test.ts +170 -0
- package/src/task-schedule.ts +141 -1
- package/src/test-utils.ts +21 -10
- package/src/thinking-utils.test.ts +46 -0
- package/src/thinking-utils.ts +21 -0
- package/src/thread-message-queue.e2e.test.ts +65 -245
- package/src/undo-redo.e2e.test.ts +17 -9
- package/src/voice-attachment.ts +29 -1
- package/src/voice-handler.ts +23 -4
- package/src/voice-message.e2e.test.ts +149 -29
- package/src/voice-routing-input.e2e.test.ts +203 -0
- package/src/voice-routing-question.e2e.test.ts +131 -0
- package/src/voice.test.ts +107 -29
- package/src/voice.ts +34 -6
- package/src/worktree-clone-isolation.e2e.test.ts +213 -0
- package/src/worktree-lifecycle.e2e.test.ts +55 -35
- package/src/worktrees.test.ts +505 -64
- package/src/worktrees.ts +257 -603
- package/src/xai-auth-plugin.ts +1 -0
- package/src/xai-auth-state.ts +1 -0
- package/dist/message-finish-field.e2e.test.js +0 -169
- package/dist/subagent-rate-limit-plugin.js +0 -184
- package/src/message-finish-field.e2e.test.ts +0 -196
- package/src/subagent-rate-limit-plugin.ts +0 -228
|
@@ -35,6 +35,76 @@ export function getEventBufferSessionId(event: EventBufferEvent): string | undef
|
|
|
35
35
|
type AssistantMessage = Extract<OpenCodeMessage, { role: 'assistant' }>
|
|
36
36
|
type UserMessage = Extract<OpenCodeMessage, { role: 'user' }>
|
|
37
37
|
|
|
38
|
+
function isCompactionContinuePart(part: Part): boolean {
|
|
39
|
+
if (part.type !== 'text') {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
return part.synthetic === true && part.metadata?.compaction_continue === true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isInternalOpenCodeUserMessageId({
|
|
46
|
+
events,
|
|
47
|
+
sessionId,
|
|
48
|
+
messageId,
|
|
49
|
+
upToIndex,
|
|
50
|
+
}: {
|
|
51
|
+
events: EventBufferEntry[]
|
|
52
|
+
sessionId: string
|
|
53
|
+
messageId: string
|
|
54
|
+
upToIndex?: number
|
|
55
|
+
}): boolean {
|
|
56
|
+
const end = upToIndex ?? events.length - 1
|
|
57
|
+
for (let i = 0; i <= end; i++) {
|
|
58
|
+
const event = events[i]?.event
|
|
59
|
+
if (event?.type !== 'message.part.updated') {
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
const part = event.properties.part
|
|
63
|
+
if (part.sessionID !== sessionId || part.messageID !== messageId) {
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
if (part.type === 'compaction') {
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
69
|
+
if (isCompactionContinuePart(part)) {
|
|
70
|
+
return true
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isUserFacingAssistantMessage(message: AssistantMessage): boolean {
|
|
77
|
+
return message.summary !== true
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type AssistantMessageKind = 'user-facing' | 'summary' | 'unknown'
|
|
81
|
+
|
|
82
|
+
export function getAssistantMessageKind({
|
|
83
|
+
events,
|
|
84
|
+
sessionId,
|
|
85
|
+
messageId,
|
|
86
|
+
upToIndex,
|
|
87
|
+
}: {
|
|
88
|
+
events: EventBufferEntry[]
|
|
89
|
+
sessionId: string
|
|
90
|
+
messageId: string
|
|
91
|
+
upToIndex?: number
|
|
92
|
+
}): AssistantMessageKind {
|
|
93
|
+
const end = upToIndex ?? events.length - 1
|
|
94
|
+
for (let i = end; i >= 0; i--) {
|
|
95
|
+
const event = events[i]?.event
|
|
96
|
+
if (event?.type !== 'message.updated') {
|
|
97
|
+
continue
|
|
98
|
+
}
|
|
99
|
+
const info = event.properties.info
|
|
100
|
+
if (info.sessionID !== sessionId || info.role !== 'assistant' || info.id !== messageId) {
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
return info.summary === true ? 'summary' : 'user-facing'
|
|
104
|
+
}
|
|
105
|
+
return 'unknown'
|
|
106
|
+
}
|
|
107
|
+
|
|
38
108
|
function getTaskChildSessionId({
|
|
39
109
|
part,
|
|
40
110
|
}: {
|
|
@@ -165,6 +235,119 @@ export function didQuestionQueueHandoffSinceLatestQuestionAsked({
|
|
|
165
235
|
return false
|
|
166
236
|
}
|
|
167
237
|
|
|
238
|
+
export type DerivedUnansweredQuestion = {
|
|
239
|
+
id: string
|
|
240
|
+
questions: Array<{
|
|
241
|
+
question: string
|
|
242
|
+
header: string
|
|
243
|
+
options: Array<{
|
|
244
|
+
label: string
|
|
245
|
+
description: string
|
|
246
|
+
}>
|
|
247
|
+
multiple?: boolean
|
|
248
|
+
}>
|
|
249
|
+
tool?: {
|
|
250
|
+
messageID: string
|
|
251
|
+
callID: string
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// OpenCode emits question.asked when the tool starts, often before the
|
|
256
|
+
// preceding text part gets time.end. Discord must wait for that end event
|
|
257
|
+
// or the question UI posts first and the text dumps later.
|
|
258
|
+
export function isAssistantTextReadyForQuestion({
|
|
259
|
+
events,
|
|
260
|
+
sessionId,
|
|
261
|
+
messageId,
|
|
262
|
+
upToIndex,
|
|
263
|
+
}: {
|
|
264
|
+
events: EventBufferEntry[]
|
|
265
|
+
sessionId: string
|
|
266
|
+
messageId: string
|
|
267
|
+
upToIndex?: number
|
|
268
|
+
}): boolean {
|
|
269
|
+
const end = upToIndex ?? events.length - 1
|
|
270
|
+
for (let i = end; i >= 0; i--) {
|
|
271
|
+
const event = events[i]?.event
|
|
272
|
+
if (!event || event.type !== 'message.part.updated') {
|
|
273
|
+
continue
|
|
274
|
+
}
|
|
275
|
+
const part = event.properties.part
|
|
276
|
+
if (part.sessionID !== sessionId) {
|
|
277
|
+
continue
|
|
278
|
+
}
|
|
279
|
+
if (part.messageID !== messageId) {
|
|
280
|
+
continue
|
|
281
|
+
}
|
|
282
|
+
if (part.type !== 'text') {
|
|
283
|
+
continue
|
|
284
|
+
}
|
|
285
|
+
return Boolean(part.time?.end)
|
|
286
|
+
}
|
|
287
|
+
return true
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function deriveLatestUnansweredQuestion({
|
|
291
|
+
events,
|
|
292
|
+
sessionId,
|
|
293
|
+
upToIndex,
|
|
294
|
+
}: {
|
|
295
|
+
events: EventBufferEntry[]
|
|
296
|
+
sessionId: string
|
|
297
|
+
upToIndex?: number
|
|
298
|
+
}): DerivedUnansweredQuestion | undefined {
|
|
299
|
+
const end = upToIndex ?? events.length - 1
|
|
300
|
+
for (let i = end; i >= 0; i--) {
|
|
301
|
+
const entry = events[i]
|
|
302
|
+
if (!entry) {
|
|
303
|
+
continue
|
|
304
|
+
}
|
|
305
|
+
const event = entry.event
|
|
306
|
+
if (getEventBufferSessionId(event) !== sessionId) {
|
|
307
|
+
continue
|
|
308
|
+
}
|
|
309
|
+
if (event.type === 'question.replied' || event.type === 'question.rejected') {
|
|
310
|
+
return undefined
|
|
311
|
+
}
|
|
312
|
+
if (event.type === 'message.part.updated') {
|
|
313
|
+
const part = event.properties.part
|
|
314
|
+
if (
|
|
315
|
+
part.type === 'tool'
|
|
316
|
+
&& part.tool === 'question'
|
|
317
|
+
&& (part.state.status === 'error' || part.state.status === 'completed')
|
|
318
|
+
) {
|
|
319
|
+
return undefined
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (event.type === 'question.asked') {
|
|
323
|
+
const messageId = event.properties.tool?.messageID
|
|
324
|
+
const latestUserMessage = getLatestUserMessage({
|
|
325
|
+
events,
|
|
326
|
+
sessionId,
|
|
327
|
+
upToIndex: end,
|
|
328
|
+
})
|
|
329
|
+
if (
|
|
330
|
+
messageId
|
|
331
|
+
&& latestUserMessage
|
|
332
|
+
&& !isAssistantMessageInLatestUserTurn({
|
|
333
|
+
events,
|
|
334
|
+
sessionId,
|
|
335
|
+
messageId,
|
|
336
|
+
upToIndex: end,
|
|
337
|
+
})
|
|
338
|
+
) {
|
|
339
|
+
return undefined
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
id: event.properties.id,
|
|
343
|
+
questions: event.properties.questions,
|
|
344
|
+
tool: event.properties.tool,
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return undefined
|
|
349
|
+
}
|
|
350
|
+
|
|
168
351
|
export function derivePendingPermissionRequests({
|
|
169
352
|
events,
|
|
170
353
|
sessionId,
|
|
@@ -199,6 +382,9 @@ export function isAssistantMessageNaturalCompletion({
|
|
|
199
382
|
}: {
|
|
200
383
|
message: AssistantMessage
|
|
201
384
|
}): boolean {
|
|
385
|
+
if (!isUserFacingAssistantMessage(message)) {
|
|
386
|
+
return false
|
|
387
|
+
}
|
|
202
388
|
if (typeof message.time.completed !== 'number') {
|
|
203
389
|
return false
|
|
204
390
|
}
|
|
@@ -269,6 +455,14 @@ export function getLatestUserMessage({
|
|
|
269
455
|
if (info.sessionID !== sessionId || info.role !== 'user') {
|
|
270
456
|
continue
|
|
271
457
|
}
|
|
458
|
+
if (isInternalOpenCodeUserMessageId({
|
|
459
|
+
events,
|
|
460
|
+
sessionId,
|
|
461
|
+
messageId: info.id,
|
|
462
|
+
upToIndex: end,
|
|
463
|
+
})) {
|
|
464
|
+
continue
|
|
465
|
+
}
|
|
272
466
|
if (!latestUserMessage) {
|
|
273
467
|
latestUserMessage = info
|
|
274
468
|
continue
|
|
@@ -337,73 +531,162 @@ function emptyTurnTokenUsage(): TurnTokenUsage {
|
|
|
337
531
|
}
|
|
338
532
|
}
|
|
339
533
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
534
|
+
function addAssistantTokens({
|
|
535
|
+
usage,
|
|
536
|
+
message,
|
|
537
|
+
}: {
|
|
538
|
+
usage: TurnTokenUsage
|
|
539
|
+
message: AssistantMessage
|
|
540
|
+
}): void {
|
|
541
|
+
if (message.tokens) {
|
|
542
|
+
usage.input += message.tokens.input
|
|
543
|
+
usage.output += message.tokens.output
|
|
544
|
+
usage.reasoning += message.tokens.reasoning
|
|
545
|
+
usage.cacheRead += message.tokens.cache.read
|
|
546
|
+
usage.cacheWrite += message.tokens.cache.write
|
|
547
|
+
usage.total += message.tokens.total ?? getTokenTotal(message.tokens)
|
|
548
|
+
}
|
|
549
|
+
usage.cost += message.cost
|
|
550
|
+
usage.model = message.modelID
|
|
551
|
+
usage.providerID = message.providerID
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function sumAssistantMessages({
|
|
555
|
+
messages,
|
|
556
|
+
userMessageId,
|
|
557
|
+
}: {
|
|
558
|
+
messages: Map<string, AssistantMessage>
|
|
559
|
+
userMessageId?: string
|
|
560
|
+
}): TurnTokenUsage {
|
|
561
|
+
if (messages.size === 0) {
|
|
562
|
+
return {
|
|
563
|
+
...emptyTurnTokenUsage(),
|
|
564
|
+
userMessageId,
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const usage = emptyTurnTokenUsage()
|
|
568
|
+
usage.userMessageId = userMessageId
|
|
569
|
+
usage.assistantMessageCount = messages.size
|
|
570
|
+
for (const message of messages.values()) {
|
|
571
|
+
addAssistantTokens({ usage, message })
|
|
572
|
+
}
|
|
573
|
+
return usage
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function collectAssistantMessages({
|
|
345
577
|
events,
|
|
346
578
|
sessionId,
|
|
347
579
|
upToIndex,
|
|
580
|
+
parentID,
|
|
348
581
|
}: {
|
|
349
582
|
events: EventBufferEntry[]
|
|
350
583
|
sessionId: string
|
|
351
|
-
upToIndex
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
events,
|
|
355
|
-
sessionId,
|
|
356
|
-
upToIndex,
|
|
357
|
-
})
|
|
358
|
-
if (!latestUserMessage) {
|
|
359
|
-
return emptyTurnTokenUsage()
|
|
360
|
-
}
|
|
361
|
-
const end = upToIndex ?? events.length - 1
|
|
584
|
+
upToIndex: number
|
|
585
|
+
parentID?: string
|
|
586
|
+
}): Map<string, AssistantMessage> {
|
|
362
587
|
const latestByMessageId = new Map<string, AssistantMessage>()
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (!entry) {
|
|
367
|
-
continue
|
|
368
|
-
}
|
|
369
|
-
const event = entry.event
|
|
370
|
-
if (event.type !== 'message.updated') {
|
|
588
|
+
for (let i = 0; i <= upToIndex; i++) {
|
|
589
|
+
const event = events[i]?.event
|
|
590
|
+
if (event?.type !== 'message.updated') {
|
|
371
591
|
continue
|
|
372
592
|
}
|
|
373
593
|
const info = event.properties.info
|
|
374
594
|
if (info.sessionID !== sessionId || info.role !== 'assistant') {
|
|
375
595
|
continue
|
|
376
596
|
}
|
|
377
|
-
if (info.parentID !==
|
|
597
|
+
if (parentID && info.parentID !== parentID) {
|
|
378
598
|
continue
|
|
379
599
|
}
|
|
380
600
|
latestByMessageId.set(info.id, info)
|
|
381
601
|
}
|
|
602
|
+
return latestByMessageId
|
|
603
|
+
}
|
|
382
604
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
605
|
+
function getSessionInfoTokenUsage({
|
|
606
|
+
events,
|
|
607
|
+
sessionId,
|
|
608
|
+
upToIndex,
|
|
609
|
+
}: {
|
|
610
|
+
events: EventBufferEntry[]
|
|
611
|
+
sessionId: string
|
|
612
|
+
upToIndex: number
|
|
613
|
+
}): TurnTokenUsage | undefined {
|
|
614
|
+
for (let i = upToIndex; i >= 0; i--) {
|
|
615
|
+
const event = events[i]?.event
|
|
616
|
+
if (event?.type !== 'session.updated' && event?.type !== 'session.created') {
|
|
617
|
+
continue
|
|
387
618
|
}
|
|
619
|
+
const info = event.properties.info
|
|
620
|
+
if (info.id !== sessionId) {
|
|
621
|
+
continue
|
|
622
|
+
}
|
|
623
|
+
if (!info.tokens) {
|
|
624
|
+
continue
|
|
625
|
+
}
|
|
626
|
+
const usage = emptyTurnTokenUsage()
|
|
627
|
+
usage.input = info.tokens.input
|
|
628
|
+
usage.output = info.tokens.output
|
|
629
|
+
usage.reasoning = info.tokens.reasoning
|
|
630
|
+
usage.cacheRead = info.tokens.cache.read
|
|
631
|
+
usage.cacheWrite = info.tokens.cache.write
|
|
632
|
+
usage.total = getTokenTotal(info.tokens)
|
|
633
|
+
usage.cost = info.cost ?? 0
|
|
634
|
+
usage.model = info.model?.id
|
|
635
|
+
usage.providerID = info.model?.providerID
|
|
636
|
+
return usage.total > 0 || usage.cost > 0 ? usage : undefined
|
|
388
637
|
}
|
|
638
|
+
return undefined
|
|
639
|
+
}
|
|
389
640
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
641
|
+
// Latest billed token snapshot for the current user turn.
|
|
642
|
+
// Sums the last message.updated tokens per assistant message id so streaming
|
|
643
|
+
// updates are not double-counted. Scoped to sessionId so subagent idles
|
|
644
|
+
// report their own usage. Child task sessions often have no user message in
|
|
645
|
+
// the buffer; fall back to all assistant messages, then Session.tokens on
|
|
646
|
+
// session.updated (OpenCode projects per-session usage there).
|
|
647
|
+
export function getLatestTurnTokenUsage({
|
|
648
|
+
events,
|
|
649
|
+
sessionId,
|
|
650
|
+
upToIndex,
|
|
651
|
+
}: {
|
|
652
|
+
events: EventBufferEntry[]
|
|
653
|
+
sessionId: string
|
|
654
|
+
upToIndex?: number
|
|
655
|
+
}): TurnTokenUsage {
|
|
656
|
+
const end = upToIndex ?? events.length - 1
|
|
657
|
+
const latestUserMessage = getLatestUserMessage({
|
|
658
|
+
events,
|
|
659
|
+
sessionId,
|
|
660
|
+
upToIndex,
|
|
661
|
+
})
|
|
662
|
+
if (latestUserMessage) {
|
|
663
|
+
return sumAssistantMessages({
|
|
664
|
+
messages: collectAssistantMessages({
|
|
665
|
+
events,
|
|
666
|
+
sessionId,
|
|
667
|
+
upToIndex: end,
|
|
668
|
+
parentID: latestUserMessage.id,
|
|
669
|
+
}),
|
|
670
|
+
userMessageId: latestUserMessage.id,
|
|
671
|
+
})
|
|
405
672
|
}
|
|
406
|
-
|
|
673
|
+
|
|
674
|
+
const sessionAssistants = sumAssistantMessages({
|
|
675
|
+
messages: collectAssistantMessages({
|
|
676
|
+
events,
|
|
677
|
+
sessionId,
|
|
678
|
+
upToIndex: end,
|
|
679
|
+
}),
|
|
680
|
+
})
|
|
681
|
+
if (sessionAssistants.total > 0 || sessionAssistants.assistantMessageCount > 0) {
|
|
682
|
+
return sessionAssistants
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return getSessionInfoTokenUsage({
|
|
686
|
+
events,
|
|
687
|
+
sessionId,
|
|
688
|
+
upToIndex: end,
|
|
689
|
+
}) ?? emptyTurnTokenUsage()
|
|
407
690
|
}
|
|
408
691
|
|
|
409
692
|
function findFirstUserMessageIndex({
|
|
@@ -469,8 +752,31 @@ function subtractTokenUsage({
|
|
|
469
752
|
}
|
|
470
753
|
}
|
|
471
754
|
|
|
755
|
+
function findFirstSessionEventIndex({
|
|
756
|
+
events,
|
|
757
|
+
sessionId,
|
|
758
|
+
upToIndex,
|
|
759
|
+
}: {
|
|
760
|
+
events: EventBufferEntry[]
|
|
761
|
+
sessionId: string
|
|
762
|
+
upToIndex: number
|
|
763
|
+
}): number {
|
|
764
|
+
for (let i = 0; i <= upToIndex; i++) {
|
|
765
|
+
const event = events[i]?.event
|
|
766
|
+
if (!event) {
|
|
767
|
+
continue
|
|
768
|
+
}
|
|
769
|
+
if (getEventBufferSessionId(event) === sessionId) {
|
|
770
|
+
return i
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return 0
|
|
774
|
+
}
|
|
775
|
+
|
|
472
776
|
// Tokens billed since the previous session.idle in this user turn.
|
|
473
777
|
// Survives process restart because both idles stay in the event buffer.
|
|
778
|
+
// Child task sessions may have no user message.updated; scope from the first
|
|
779
|
+
// event for that sessionId instead so their tokens still emit.
|
|
474
780
|
export function getIdleTokenUsageDelta({
|
|
475
781
|
events,
|
|
476
782
|
sessionId,
|
|
@@ -485,16 +791,22 @@ export function getIdleTokenUsageDelta({
|
|
|
485
791
|
sessionId,
|
|
486
792
|
upToIndex: idleEventIndex,
|
|
487
793
|
})
|
|
488
|
-
if (
|
|
794
|
+
if (current.total <= 0) {
|
|
489
795
|
return undefined
|
|
490
796
|
}
|
|
491
|
-
const firstUserMessageIndex =
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
797
|
+
const firstUserMessageIndex = current.userMessageId
|
|
798
|
+
? findFirstUserMessageIndex({
|
|
799
|
+
events,
|
|
800
|
+
userMessageId: current.userMessageId,
|
|
801
|
+
upToIndex: idleEventIndex,
|
|
802
|
+
})
|
|
803
|
+
: findFirstSessionEventIndex({
|
|
804
|
+
events,
|
|
805
|
+
sessionId,
|
|
806
|
+
upToIndex: idleEventIndex,
|
|
807
|
+
})
|
|
496
808
|
if (firstUserMessageIndex === undefined) {
|
|
497
|
-
return
|
|
809
|
+
return current
|
|
498
810
|
}
|
|
499
811
|
const previousIdleIndex = findPreviousIdleIndexInTurn({
|
|
500
812
|
events,
|
|
@@ -503,7 +815,7 @@ export function getIdleTokenUsageDelta({
|
|
|
503
815
|
beforeIndex: idleEventIndex,
|
|
504
816
|
})
|
|
505
817
|
if (previousIdleIndex === undefined) {
|
|
506
|
-
return current
|
|
818
|
+
return current
|
|
507
819
|
}
|
|
508
820
|
const previous = getLatestTurnTokenUsage({
|
|
509
821
|
events,
|
|
@@ -553,6 +865,9 @@ export function getLatestRunInfo({
|
|
|
553
865
|
if (msg.sessionID !== sessionId || msg.role !== 'assistant') {
|
|
554
866
|
continue
|
|
555
867
|
}
|
|
868
|
+
if (!isUserFacingAssistantMessage(msg)) {
|
|
869
|
+
continue
|
|
870
|
+
}
|
|
556
871
|
return {
|
|
557
872
|
model: msg.modelID,
|
|
558
873
|
providerID: msg.providerID,
|
|
@@ -597,6 +912,9 @@ export function getAssistantMessageIdsForLatestUserTurn({
|
|
|
597
912
|
if (msg.sessionID !== sessionId || msg.role !== 'assistant') {
|
|
598
913
|
continue
|
|
599
914
|
}
|
|
915
|
+
if (!isUserFacingAssistantMessage(msg)) {
|
|
916
|
+
continue
|
|
917
|
+
}
|
|
600
918
|
if (msg.parentID === latestUserMessage.id) {
|
|
601
919
|
assistantMessageIds.add(msg.id)
|
|
602
920
|
}
|
|
@@ -604,6 +922,33 @@ export function getAssistantMessageIdsForLatestUserTurn({
|
|
|
604
922
|
return assistantMessageIds
|
|
605
923
|
}
|
|
606
924
|
|
|
925
|
+
export function didLatestUserTurnUseSleepTool({
|
|
926
|
+
events,
|
|
927
|
+
sessionId,
|
|
928
|
+
upToIndex,
|
|
929
|
+
}: {
|
|
930
|
+
events: EventBufferEntry[]
|
|
931
|
+
sessionId: string
|
|
932
|
+
upToIndex?: number
|
|
933
|
+
}): boolean {
|
|
934
|
+
const assistantMessageIds = getAssistantMessageIdsForLatestUserTurn({
|
|
935
|
+
events,
|
|
936
|
+
sessionId,
|
|
937
|
+
upToIndex,
|
|
938
|
+
})
|
|
939
|
+
const end = upToIndex ?? events.length - 1
|
|
940
|
+
for (let i = end; i >= 0; i--) {
|
|
941
|
+
const event = events[i]?.event
|
|
942
|
+
if (event?.type !== 'message.part.updated') continue
|
|
943
|
+
const part = event.properties.part
|
|
944
|
+
if (part.sessionID !== sessionId || part.type !== 'tool') continue
|
|
945
|
+
if (part.tool === 'kimaki_sleep' && assistantMessageIds.has(part.messageID)) {
|
|
946
|
+
return true
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return false
|
|
950
|
+
}
|
|
951
|
+
|
|
607
952
|
export function getLatestAssistantMessageIdForLatestUserTurn({
|
|
608
953
|
events,
|
|
609
954
|
sessionId,
|
|
@@ -638,6 +983,9 @@ export function getLatestAssistantMessageIdForLatestUserTurn({
|
|
|
638
983
|
if (info.sessionID !== sessionId || info.role !== 'assistant') {
|
|
639
984
|
continue
|
|
640
985
|
}
|
|
986
|
+
if (!isUserFacingAssistantMessage(info)) {
|
|
987
|
+
continue
|
|
988
|
+
}
|
|
641
989
|
if (info.parentID !== latestUserMessage.id) {
|
|
642
990
|
continue
|
|
643
991
|
}
|
|
@@ -816,6 +1164,25 @@ export function isAssistantMessageInLatestUserTurn({
|
|
|
816
1164
|
return assistantMessageIds.has(messageId)
|
|
817
1165
|
}
|
|
818
1166
|
|
|
1167
|
+
export function isSummaryAssistantMessage({
|
|
1168
|
+
events,
|
|
1169
|
+
sessionId,
|
|
1170
|
+
messageId,
|
|
1171
|
+
upToIndex,
|
|
1172
|
+
}: {
|
|
1173
|
+
events: EventBufferEntry[]
|
|
1174
|
+
sessionId: string
|
|
1175
|
+
messageId: string
|
|
1176
|
+
upToIndex?: number
|
|
1177
|
+
}): boolean {
|
|
1178
|
+
return getAssistantMessageKind({
|
|
1179
|
+
events,
|
|
1180
|
+
sessionId,
|
|
1181
|
+
messageId,
|
|
1182
|
+
upToIndex,
|
|
1183
|
+
}) === 'summary'
|
|
1184
|
+
}
|
|
1185
|
+
|
|
819
1186
|
// Returns a stable 1-based subtask index for candidateSessionId.
|
|
820
1187
|
// Indexing scope is the parent assistant message that spawned the task tool calls,
|
|
821
1188
|
// so numbering restarts at 1 for each assistant message.
|
|
@@ -945,3 +1312,151 @@ export function getDerivedSubagentSessions({
|
|
|
945
1312
|
|
|
946
1313
|
return sessions
|
|
947
1314
|
}
|
|
1315
|
+
|
|
1316
|
+
function getParentIdFromSessionEvent(event: EventBufferEvent): {
|
|
1317
|
+
sessionId: string
|
|
1318
|
+
parentID: string
|
|
1319
|
+
} | undefined {
|
|
1320
|
+
if (event.type !== 'session.created' && event.type !== 'session.updated') {
|
|
1321
|
+
return undefined
|
|
1322
|
+
}
|
|
1323
|
+
const parentID = event.properties.info.parentID
|
|
1324
|
+
if (typeof parentID !== 'string' || parentID.length === 0) {
|
|
1325
|
+
return undefined
|
|
1326
|
+
}
|
|
1327
|
+
return {
|
|
1328
|
+
sessionId: event.properties.info.id,
|
|
1329
|
+
parentID,
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// Global SSE is broadcast to every thread. Buffer only this thread's session
|
|
1334
|
+
// plus its task/subagent children. /btw fork clones history onto a new
|
|
1335
|
+
// sessionId; those events must not evict the parent's latest-turn buffer.
|
|
1336
|
+
export function shouldBufferSessionEvent({
|
|
1337
|
+
event,
|
|
1338
|
+
mainSessionId,
|
|
1339
|
+
isKnownChildSession,
|
|
1340
|
+
}: {
|
|
1341
|
+
event: EventBufferEvent
|
|
1342
|
+
mainSessionId?: string
|
|
1343
|
+
isKnownChildSession: (sessionId: string) => boolean
|
|
1344
|
+
}): boolean {
|
|
1345
|
+
if (event.type === 'session.diff' || event.type.endsWith('.delta')) {
|
|
1346
|
+
return false
|
|
1347
|
+
}
|
|
1348
|
+
if (event.type === 'tui.toast.show') {
|
|
1349
|
+
return true
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
const eventSessionId = getEventBufferSessionId(event)
|
|
1353
|
+
if (!eventSessionId) {
|
|
1354
|
+
return true
|
|
1355
|
+
}
|
|
1356
|
+
// A new /btw thread registers the SSE listener before ensureSession.
|
|
1357
|
+
// Until the child session id is bound, drop every scoped event so the
|
|
1358
|
+
// parent clone flood cannot fill this buffer first.
|
|
1359
|
+
if (!mainSessionId) {
|
|
1360
|
+
return false
|
|
1361
|
+
}
|
|
1362
|
+
if (eventSessionId === mainSessionId) {
|
|
1363
|
+
return true
|
|
1364
|
+
}
|
|
1365
|
+
if (isKnownChildSession(eventSessionId)) {
|
|
1366
|
+
return true
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
const parented = getParentIdFromSessionEvent(event)
|
|
1370
|
+
if (!parented) {
|
|
1371
|
+
return false
|
|
1372
|
+
}
|
|
1373
|
+
return parented.parentID === mainSessionId || isKnownChildSession(parented.parentID)
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// Child sessions of the main thread: task tool metadata.sessionId, plus
|
|
1377
|
+
// session.created/updated parentID (available before task metadata lands).
|
|
1378
|
+
export function getDerivedChildSessionIds({
|
|
1379
|
+
events,
|
|
1380
|
+
mainSessionId,
|
|
1381
|
+
upToIndex,
|
|
1382
|
+
}: {
|
|
1383
|
+
events: EventBufferEntry[]
|
|
1384
|
+
mainSessionId: string
|
|
1385
|
+
upToIndex?: number
|
|
1386
|
+
}): Set<string> {
|
|
1387
|
+
const end = upToIndex ?? events.length - 1
|
|
1388
|
+
const ids = new Set<string>()
|
|
1389
|
+
for (const session of getDerivedSubagentSessions({
|
|
1390
|
+
events,
|
|
1391
|
+
mainSessionId,
|
|
1392
|
+
upToIndex,
|
|
1393
|
+
})) {
|
|
1394
|
+
ids.add(session.childSessionId)
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
let grew = true
|
|
1398
|
+
while (grew) {
|
|
1399
|
+
grew = false
|
|
1400
|
+
for (let i = 0; i <= end; i++) {
|
|
1401
|
+
const event = events[i]?.event
|
|
1402
|
+
if (!event) {
|
|
1403
|
+
continue
|
|
1404
|
+
}
|
|
1405
|
+
const parented = getParentIdFromSessionEvent(event)
|
|
1406
|
+
if (!parented || ids.has(parented.sessionId)) {
|
|
1407
|
+
continue
|
|
1408
|
+
}
|
|
1409
|
+
if (parented.parentID !== mainSessionId && !ids.has(parented.parentID)) {
|
|
1410
|
+
continue
|
|
1411
|
+
}
|
|
1412
|
+
ids.add(parented.sessionId)
|
|
1413
|
+
grew = true
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
return ids
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
export function isDerivedChildSession({
|
|
1420
|
+
events,
|
|
1421
|
+
mainSessionId,
|
|
1422
|
+
candidateSessionId,
|
|
1423
|
+
upToIndex,
|
|
1424
|
+
}: {
|
|
1425
|
+
events: EventBufferEntry[]
|
|
1426
|
+
mainSessionId: string
|
|
1427
|
+
candidateSessionId: string
|
|
1428
|
+
upToIndex?: number
|
|
1429
|
+
}): boolean {
|
|
1430
|
+
if (candidateSessionId === mainSessionId) {
|
|
1431
|
+
return false
|
|
1432
|
+
}
|
|
1433
|
+
return getDerivedChildSessionIds({
|
|
1434
|
+
events,
|
|
1435
|
+
mainSessionId,
|
|
1436
|
+
upToIndex,
|
|
1437
|
+
}).has(candidateSessionId)
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
export function getTokenUsageSessionIdsForIdle({
|
|
1441
|
+
events,
|
|
1442
|
+
mainSessionId,
|
|
1443
|
+
idleSessionId,
|
|
1444
|
+
upToIndex,
|
|
1445
|
+
}: {
|
|
1446
|
+
events: EventBufferEntry[]
|
|
1447
|
+
mainSessionId: string
|
|
1448
|
+
idleSessionId: string
|
|
1449
|
+
upToIndex?: number
|
|
1450
|
+
}): string[] {
|
|
1451
|
+
if (idleSessionId !== mainSessionId) {
|
|
1452
|
+
return [idleSessionId]
|
|
1453
|
+
}
|
|
1454
|
+
return [
|
|
1455
|
+
mainSessionId,
|
|
1456
|
+
...getDerivedChildSessionIds({
|
|
1457
|
+
events,
|
|
1458
|
+
mainSessionId,
|
|
1459
|
+
upToIndex,
|
|
1460
|
+
}),
|
|
1461
|
+
]
|
|
1462
|
+
}
|