kimaki 0.4.82 → 0.4.84
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/LICENSE +21 -0
- package/dist/anthropic-auth-plugin.js +7 -0
- package/dist/cli.js +51 -7
- package/dist/commands/abort.js +5 -16
- package/dist/commands/action-buttons.js +3 -3
- package/dist/commands/add-project.js +1 -1
- package/dist/commands/ask-question.js +3 -3
- package/dist/commands/context-usage.js +1 -1
- package/dist/commands/create-new-project.js +1 -1
- package/dist/commands/fork.js +11 -8
- package/dist/commands/merge-worktree.js +1 -1
- package/dist/commands/new-worktree.js +63 -44
- package/dist/commands/remove-project.js +1 -1
- package/dist/commands/resume.js +11 -8
- package/dist/commands/screenshare.js +14 -6
- package/dist/commands/screenshare.test.js +20 -0
- package/dist/commands/session.js +1 -1
- package/dist/commands/undo-redo.js +91 -7
- package/dist/commands/user-command.js +1 -1
- package/dist/config.js +16 -1
- package/dist/database.js +53 -2
- package/dist/db.js +6 -0
- package/dist/discord-bot.js +48 -85
- package/dist/discord-command-registration.js +1 -1
- package/dist/external-opencode-sync.js +515 -0
- package/dist/external-opencode-sync.test.js +151 -0
- package/dist/gateway-proxy.e2e.test.js +8 -5
- package/dist/genai.js +1 -1
- package/dist/generated/enums.js +4 -0
- package/dist/generated/internal/class.js +4 -4
- package/dist/generated/internal/prismaNamespace.js +1 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +1 -0
- package/dist/generated/models/external_session_pending_prompts.js +1 -0
- package/dist/hrana-server.js +14 -285
- package/dist/hrana-server.test.js +4 -2
- package/dist/kimaki-opencode-plugin-loading.e2e.test.js +7 -0
- package/dist/kimaki-opencode-plugin.js +2 -0
- package/dist/kitty-graphics-parser.js +3 -0
- package/dist/kitty-graphics-parser.test.js +276 -0
- package/dist/kitty-graphics-plugin.js +3 -0
- package/dist/markdown.js +4 -4
- package/dist/markdown.test.js +1 -1
- package/dist/message-formatting.js +54 -15
- package/dist/onboarding-tutorial.js +1 -1
- package/dist/openai-realtime.js +9 -13
- package/dist/opencode.js +28 -5
- package/dist/queue-advanced-e2e-setup.js +89 -0
- package/dist/queue-advanced-permissions-typing.e2e.test.js +5 -5
- package/dist/queue-advanced-typing.e2e.test.js +9 -22
- package/dist/queue-question-select-drain.e2e.test.js +117 -0
- package/dist/session-handler/event-stream-state.js +101 -7
- package/dist/session-handler/event-stream-state.test.js +7 -3
- package/dist/session-handler/thread-session-runtime.js +120 -9
- package/dist/store.js +1 -0
- package/dist/system-message.js +22 -4
- package/dist/system-message.test.js +19 -0
- package/dist/task-runner.js +1 -1
- package/dist/thread-message-queue.e2e.test.js +8 -14
- package/dist/tools.js +1 -1
- package/dist/undo-redo.e2e.test.js +20 -25
- package/package.json +10 -6
- package/schema.prisma +6 -0
- package/skills/errore/SKILL.md +40 -13
- package/skills/goke/SKILL.md +12 -0
- package/skills/lintcn/SKILL.md +868 -0
- package/skills/npm-package/SKILL.md +1 -0
- package/skills/proxyman/SKILL.md +215 -0
- package/skills/spiceflow/SKILL.md +1 -1
- package/skills/usecomputer/SKILL.md +339 -0
- package/src/ai-tool-to-genai.ts +1 -0
- package/src/anthropic-auth-plugin.ts +7 -0
- package/src/cli.ts +59 -6
- package/src/commands/abort.ts +6 -16
- package/src/commands/action-buttons.ts +5 -1
- package/src/commands/add-project.ts +1 -1
- package/src/commands/ask-question.ts +5 -2
- package/src/commands/context-usage.ts +1 -1
- package/src/commands/create-new-project.ts +1 -1
- package/src/commands/fork.ts +12 -11
- package/src/commands/merge-worktree.ts +1 -1
- package/src/commands/new-worktree.ts +74 -55
- package/src/commands/remove-project.ts +1 -1
- package/src/commands/resume.ts +12 -10
- package/src/commands/screenshare.test.ts +30 -0
- package/src/commands/screenshare.ts +18 -6
- package/src/commands/session.ts +1 -1
- package/src/commands/undo-redo.ts +108 -10
- package/src/commands/user-command.ts +1 -1
- package/src/config.ts +19 -1
- package/src/database.ts +72 -3
- package/src/db.ts +8 -0
- package/src/discord-bot.ts +58 -93
- package/src/discord-command-registration.ts +1 -1
- package/src/external-opencode-sync.ts +729 -0
- package/src/gateway-proxy.e2e.test.ts +9 -5
- package/src/genai.ts +3 -3
- package/src/generated/commonInputTypes.ts +34 -0
- package/src/generated/enums.ts +8 -0
- package/src/generated/internal/class.ts +4 -4
- package/src/generated/internal/prismaNamespace.ts +8 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +1 -0
- package/src/generated/models/thread_sessions.ts +53 -1
- package/src/hrana-server.test.ts +8 -2
- package/src/hrana-server.ts +18 -390
- package/src/kimaki-opencode-plugin-loading.e2e.test.ts +7 -0
- package/src/kimaki-opencode-plugin.ts +2 -0
- package/src/markdown.test.ts +1 -1
- package/src/markdown.ts +4 -4
- package/src/message-formatting.ts +66 -17
- package/src/onboarding-tutorial.ts +1 -1
- package/src/openai-realtime.ts +6 -10
- package/src/opencode.ts +31 -7
- package/src/queue-advanced-e2e-setup.ts +92 -0
- package/src/queue-advanced-permissions-typing.e2e.test.ts +5 -5
- package/src/queue-advanced-typing.e2e.test.ts +9 -22
- package/src/queue-question-select-drain.e2e.test.ts +149 -0
- package/src/schema.sql +1 -0
- package/src/session-handler/event-stream-state.test.ts +7 -2
- package/src/session-handler/event-stream-state.ts +128 -7
- package/src/session-handler/thread-runtime-state.ts +5 -0
- package/src/session-handler/thread-session-runtime.ts +153 -11
- package/src/store.ts +8 -0
- package/src/system-message.ts +27 -4
- package/src/task-runner.ts +1 -1
- package/src/thread-message-queue.e2e.test.ts +8 -14
- package/src/tools.ts +1 -1
- package/src/undo-redo.e2e.test.ts +28 -26
- package/skills/jitter/node_modules/.bin/esbuild +0 -21
- package/skills/jitter/node_modules/.bin/tsc +0 -21
- package/skills/jitter/node_modules/.bin/tsserver +0 -21
- package/skills/jitter/node_modules/typescript/LICENSE.txt +0 -55
- package/skills/jitter/node_modules/typescript/README.md +0 -50
- package/skills/jitter/node_modules/typescript/SECURITY.md +0 -41
- package/skills/jitter/node_modules/typescript/ThirdPartyNoticeText.txt +0 -193
- package/skills/jitter/node_modules/typescript/bin/tsc +0 -2
- package/skills/jitter/node_modules/typescript/bin/tsserver +0 -2
- package/skills/jitter/node_modules/typescript/lib/_tsc.js +0 -133792
- package/skills/jitter/node_modules/typescript/lib/_tsserver.js +0 -659
- package/skills/jitter/node_modules/typescript/lib/_typingsInstaller.js +0 -222
- package/skills/jitter/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/de/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/es/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/it/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/lib.d.ts +0 -22
- package/skills/jitter/node_modules/typescript/lib/lib.decorators.d.ts +0 -384
- package/skills/jitter/node_modules/typescript/lib/lib.decorators.legacy.d.ts +0 -22
- package/skills/jitter/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +0 -41
- package/skills/jitter/node_modules/typescript/lib/lib.dom.d.ts +0 -39429
- package/skills/jitter/node_modules/typescript/lib/lib.dom.iterable.d.ts +0 -571
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.collection.d.ts +0 -147
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.core.d.ts +0 -597
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.d.ts +0 -28
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.generator.d.ts +0 -77
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.iterable.d.ts +0 -605
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.promise.d.ts +0 -81
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.proxy.d.ts +0 -128
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.reflect.d.ts +0 -144
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.symbol.d.ts +0 -46
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +0 -326
- package/skills/jitter/node_modules/typescript/lib/lib.es2016.array.include.d.ts +0 -116
- package/skills/jitter/node_modules/typescript/lib/lib.es2016.d.ts +0 -21
- package/skills/jitter/node_modules/typescript/lib/lib.es2016.full.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.es2016.intl.d.ts +0 -31
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +0 -21
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.d.ts +0 -26
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.date.d.ts +0 -31
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.full.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.intl.d.ts +0 -44
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.object.d.ts +0 -49
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +0 -135
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.string.d.ts +0 -45
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +0 -53
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +0 -77
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +0 -53
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.intl.d.ts +0 -83
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.promise.d.ts +0 -30
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.regexp.d.ts +0 -37
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.array.d.ts +0 -79
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.intl.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.object.d.ts +0 -33
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.string.d.ts +0 -37
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.symbol.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.bigint.d.ts +0 -765
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.d.ts +0 -27
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.date.d.ts +0 -42
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.intl.d.ts +0 -474
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.number.d.ts +0 -28
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.promise.d.ts +0 -47
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +0 -99
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.string.d.ts +0 -44
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +0 -41
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.intl.d.ts +0 -166
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.promise.d.ts +0 -48
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.string.d.ts +0 -33
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.weakref.d.ts +0 -78
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.array.d.ts +0 -121
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.d.ts +0 -25
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.error.d.ts +0 -75
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.intl.d.ts +0 -145
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.object.d.ts +0 -26
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.regexp.d.ts +0 -39
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.string.d.ts +0 -25
- package/skills/jitter/node_modules/typescript/lib/lib.es2023.array.d.ts +0 -924
- package/skills/jitter/node_modules/typescript/lib/lib.es2023.collection.d.ts +0 -21
- package/skills/jitter/node_modules/typescript/lib/lib.es2023.d.ts +0 -22
- package/skills/jitter/node_modules/typescript/lib/lib.es2023.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2023.intl.d.ts +0 -56
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +0 -65
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.collection.d.ts +0 -29
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.d.ts +0 -26
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.object.d.ts +0 -29
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.promise.d.ts +0 -35
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.regexp.d.ts +0 -25
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +0 -68
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.string.d.ts +0 -29
- package/skills/jitter/node_modules/typescript/lib/lib.es5.d.ts +0 -4601
- package/skills/jitter/node_modules/typescript/lib/lib.es6.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.array.d.ts +0 -35
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.collection.d.ts +0 -96
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.d.ts +0 -29
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.decorators.d.ts +0 -28
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.disposable.d.ts +0 -193
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.error.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.float16.d.ts +0 -443
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.intl.d.ts +0 -21
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.iterator.d.ts +0 -148
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.promise.d.ts +0 -34
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +0 -25
- package/skills/jitter/node_modules/typescript/lib/lib.scripthost.d.ts +0 -322
- package/skills/jitter/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +0 -41
- package/skills/jitter/node_modules/typescript/lib/lib.webworker.d.ts +0 -13150
- package/skills/jitter/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.webworker.iterable.d.ts +0 -340
- package/skills/jitter/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/tsc.js +0 -8
- package/skills/jitter/node_modules/typescript/lib/tsserver.js +0 -8
- package/skills/jitter/node_modules/typescript/lib/tsserverlibrary.d.ts +0 -17
- package/skills/jitter/node_modules/typescript/lib/tsserverlibrary.js +0 -21
- package/skills/jitter/node_modules/typescript/lib/typesMap.json +0 -497
- package/skills/jitter/node_modules/typescript/lib/typescript.d.ts +0 -11438
- package/skills/jitter/node_modules/typescript/lib/typescript.js +0 -200253
- package/skills/jitter/node_modules/typescript/lib/typingsInstaller.js +0 -8
- package/skills/jitter/node_modules/typescript/lib/watchGuard.js +0 -53
- package/skills/jitter/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/node_modules/.bin/tsc +0 -21
- package/skills/jitter/node_modules/typescript/node_modules/.bin/tsserver +0 -21
- package/skills/jitter/node_modules/typescript/package.json +0 -120
|
@@ -99,6 +99,7 @@ export function hasAssistantMessageCompletedBefore({ events, sessionId, messageI
|
|
|
99
99
|
}
|
|
100
100
|
export function getLatestUserMessage({ events, sessionId, upToIndex, }) {
|
|
101
101
|
const end = upToIndex ?? events.length - 1;
|
|
102
|
+
let latestUserMessage;
|
|
102
103
|
for (let i = end; i >= 0; i--) {
|
|
103
104
|
const entry = events[i];
|
|
104
105
|
if (!entry) {
|
|
@@ -112,9 +113,15 @@ export function getLatestUserMessage({ events, sessionId, upToIndex, }) {
|
|
|
112
113
|
if (info.sessionID !== sessionId || info.role !== 'user') {
|
|
113
114
|
continue;
|
|
114
115
|
}
|
|
115
|
-
|
|
116
|
+
if (!latestUserMessage) {
|
|
117
|
+
latestUserMessage = info;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (info.time.created > latestUserMessage.time.created) {
|
|
121
|
+
latestUserMessage = info;
|
|
122
|
+
}
|
|
116
123
|
}
|
|
117
|
-
return
|
|
124
|
+
return latestUserMessage;
|
|
118
125
|
}
|
|
119
126
|
export function getCurrentTurnStartTime({ events, sessionId, upToIndex, }) {
|
|
120
127
|
const latestUserMessage = getLatestUserMessage({
|
|
@@ -202,6 +209,7 @@ export function getLatestAssistantMessageIdForLatestUserTurn({ events, sessionId
|
|
|
202
209
|
return undefined;
|
|
203
210
|
}
|
|
204
211
|
const end = upToIndex ?? events.length - 1;
|
|
212
|
+
let latestAssistantMessage;
|
|
205
213
|
for (let i = end; i >= 0; i--) {
|
|
206
214
|
const entry = events[i];
|
|
207
215
|
if (!entry) {
|
|
@@ -215,11 +223,74 @@ export function getLatestAssistantMessageIdForLatestUserTurn({ events, sessionId
|
|
|
215
223
|
if (info.sessionID !== sessionId || info.role !== 'assistant') {
|
|
216
224
|
continue;
|
|
217
225
|
}
|
|
218
|
-
if (info.parentID
|
|
219
|
-
|
|
226
|
+
if (info.parentID !== latestUserMessage.id) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (!latestAssistantMessage) {
|
|
230
|
+
latestAssistantMessage = info;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (info.time.created > latestAssistantMessage.time.created) {
|
|
234
|
+
latestAssistantMessage = info;
|
|
220
235
|
}
|
|
221
236
|
}
|
|
222
|
-
return
|
|
237
|
+
return latestAssistantMessage?.id;
|
|
238
|
+
}
|
|
239
|
+
function hasRenderablePartSummary(message) {
|
|
240
|
+
if (!('partsSummary' in message) || !Array.isArray(message.partsSummary)) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
return message.partsSummary.some((part) => {
|
|
244
|
+
return part.type === 'text' || part.type === 'tool';
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function hasAssistantPartEvidence({ events, sessionId, messageId, upToIndex, }) {
|
|
248
|
+
const end = upToIndex ?? events.length - 1;
|
|
249
|
+
for (let i = end; i >= 0; i--) {
|
|
250
|
+
const entry = events[i];
|
|
251
|
+
if (!entry) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const event = entry.event;
|
|
255
|
+
if (event.type === 'message.updated') {
|
|
256
|
+
const info = event.properties.info;
|
|
257
|
+
if (info.sessionID !== sessionId || info.role !== 'assistant' || info.id !== messageId) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (hasRenderablePartSummary(info)) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (event.type !== 'message.part.updated') {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const { part } = event.properties;
|
|
269
|
+
if (part.messageID !== messageId) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
if (part.type === 'text' || part.type === 'tool') {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
function hasAssistantStepFinished({ events, messageId, upToIndex, }) {
|
|
279
|
+
const end = upToIndex ?? events.length - 1;
|
|
280
|
+
for (let i = end; i >= 0; i--) {
|
|
281
|
+
const entry = events[i];
|
|
282
|
+
if (!entry || entry.event.type !== 'message.part.updated') {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const { part } = entry.event.properties;
|
|
286
|
+
if (part.messageID !== messageId) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (part.type === 'step-finish') {
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
223
294
|
}
|
|
224
295
|
export function doesLatestUserTurnHaveNaturalCompletion({ events, sessionId, upToIndex, }) {
|
|
225
296
|
const latestAssistantMessageId = getLatestAssistantMessageIdForLatestUserTurn({
|
|
@@ -231,6 +302,7 @@ export function doesLatestUserTurnHaveNaturalCompletion({ events, sessionId, upT
|
|
|
231
302
|
return false;
|
|
232
303
|
}
|
|
233
304
|
const end = upToIndex ?? events.length - 1;
|
|
305
|
+
let latestAssistantMessage;
|
|
234
306
|
for (let i = end; i >= 0; i--) {
|
|
235
307
|
const entry = events[i];
|
|
236
308
|
if (!entry) {
|
|
@@ -247,9 +319,31 @@ export function doesLatestUserTurnHaveNaturalCompletion({ events, sessionId, upT
|
|
|
247
319
|
if (info.id !== latestAssistantMessageId) {
|
|
248
320
|
continue;
|
|
249
321
|
}
|
|
250
|
-
|
|
322
|
+
latestAssistantMessage = info;
|
|
323
|
+
if (isAssistantMessageNaturalCompletion({ message: info })) {
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
break;
|
|
251
327
|
}
|
|
252
|
-
|
|
328
|
+
if (!latestAssistantMessage) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
if (latestAssistantMessage.error) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
if (latestAssistantMessage.finish === 'tool-calls') {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
return hasAssistantStepFinished({
|
|
338
|
+
events,
|
|
339
|
+
messageId: latestAssistantMessageId,
|
|
340
|
+
upToIndex,
|
|
341
|
+
}) && hasAssistantPartEvidence({
|
|
342
|
+
events,
|
|
343
|
+
sessionId,
|
|
344
|
+
messageId: latestAssistantMessageId,
|
|
345
|
+
upToIndex,
|
|
346
|
+
});
|
|
253
347
|
}
|
|
254
348
|
export function isAssistantMessageInLatestUserTurn({ events, sessionId, messageId, upToIndex, }) {
|
|
255
349
|
const assistantMessageIds = getAssistantMessageIdsForLatestUserTurn({
|
|
@@ -4,7 +4,7 @@ import fs from 'node:fs';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { describe, expect, test } from 'vitest';
|
|
6
6
|
import { getOpencodeEventSessionId, } from './opencode-session-event-log.js';
|
|
7
|
-
import { getAssistantMessageIdsForLatestUserTurn, getCurrentTurnStartTime, getDerivedSubtaskIndex, getLatestAssistantMessageIdForLatestUserTurn, getLatestRunInfo, hasAssistantMessageCompletedBefore, isAssistantMessageInLatestUserTurn, isAssistantMessageNaturalCompletion, isSessionBusy, } from './event-stream-state.js';
|
|
7
|
+
import { getAssistantMessageIdsForLatestUserTurn, getCurrentTurnStartTime, getDerivedSubtaskIndex, getLatestAssistantMessageIdForLatestUserTurn, getLatestRunInfo, hasAssistantMessageCompletedBefore, doesLatestUserTurnHaveNaturalCompletion, isAssistantMessageInLatestUserTurn, isAssistantMessageNaturalCompletion, isSessionBusy, } from './event-stream-state.js';
|
|
8
8
|
const fixturesDir = path.join(import.meta.dirname, 'event-stream-fixtures');
|
|
9
9
|
function loadFixture(filename) {
|
|
10
10
|
const content = fs.readFileSync(path.join(fixturesDir, filename), 'utf8');
|
|
@@ -187,7 +187,11 @@ describe('session-concurrent-messages-serialized', () => {
|
|
|
187
187
|
events,
|
|
188
188
|
sessionId,
|
|
189
189
|
});
|
|
190
|
-
test('fixture
|
|
190
|
+
test('fixture latest turn is still incomplete even though an older turn completed', () => {
|
|
191
|
+
expect(doesLatestUserTurnHaveNaturalCompletion({
|
|
192
|
+
events,
|
|
193
|
+
sessionId,
|
|
194
|
+
})).toBe(false);
|
|
191
195
|
if (!latestAssistantMessageId) {
|
|
192
196
|
throw new Error('Expected latest assistant message');
|
|
193
197
|
}
|
|
@@ -196,7 +200,7 @@ describe('session-concurrent-messages-serialized', () => {
|
|
|
196
200
|
sessionId,
|
|
197
201
|
messageId: latestAssistantMessageId,
|
|
198
202
|
});
|
|
199
|
-
expect(
|
|
203
|
+
expect(message.id).toBe(latestAssistantMessageId);
|
|
200
204
|
});
|
|
201
205
|
});
|
|
202
206
|
describe('session-tool-call-noisy-stream', () => {
|
|
@@ -194,7 +194,7 @@ function getTokenTotal(tokens) {
|
|
|
194
194
|
tokens.cache.write);
|
|
195
195
|
}
|
|
196
196
|
/** Check if a tool part is "essential" (shown in text-and-essential-tools mode). */
|
|
197
|
-
function isEssentialToolName(toolName) {
|
|
197
|
+
export function isEssentialToolName(toolName) {
|
|
198
198
|
const essentialTools = [
|
|
199
199
|
'edit',
|
|
200
200
|
'write',
|
|
@@ -213,7 +213,7 @@ function isEssentialToolName(toolName) {
|
|
|
213
213
|
return toolName === name || toolName.endsWith(`_${name}`);
|
|
214
214
|
});
|
|
215
215
|
}
|
|
216
|
-
function isEssentialToolPart(part) {
|
|
216
|
+
export function isEssentialToolPart(part) {
|
|
217
217
|
if (part.type !== 'tool') {
|
|
218
218
|
return false;
|
|
219
219
|
}
|
|
@@ -630,10 +630,26 @@ export class ThreadSessionRuntime {
|
|
|
630
630
|
// with every tool call and was the primary OOM vector — 1000 buffer entries
|
|
631
631
|
// each carrying the full cumulative parts array reached 4GB+.
|
|
632
632
|
const info = compacted.properties.info;
|
|
633
|
+
const partsSummary = Array.isArray(info.parts)
|
|
634
|
+
? info.parts.flatMap((part) => {
|
|
635
|
+
if (!part || typeof part !== 'object') {
|
|
636
|
+
return [];
|
|
637
|
+
}
|
|
638
|
+
const candidate = part;
|
|
639
|
+
if (typeof candidate.id !== 'string'
|
|
640
|
+
|| typeof candidate.type !== 'string') {
|
|
641
|
+
return [];
|
|
642
|
+
}
|
|
643
|
+
return [{ id: candidate.id, type: candidate.type }];
|
|
644
|
+
})
|
|
645
|
+
: [];
|
|
633
646
|
delete info.system;
|
|
634
647
|
delete info.summary;
|
|
635
648
|
delete info.tools;
|
|
636
649
|
delete info.parts;
|
|
650
|
+
if (partsSummary.length > 0) {
|
|
651
|
+
info.partsSummary = partsSummary;
|
|
652
|
+
}
|
|
637
653
|
return this.finalizeCompactedEventForEventBuffer(compacted);
|
|
638
654
|
}
|
|
639
655
|
if (compacted.type !== 'message.part.updated') {
|
|
@@ -1442,6 +1458,7 @@ export class ThreadSessionRuntime {
|
|
|
1442
1458
|
sessionId: request.sessionId,
|
|
1443
1459
|
directory: request.directory,
|
|
1444
1460
|
buttons: request.buttons,
|
|
1461
|
+
silent: this.getQueueLength() > 0,
|
|
1445
1462
|
});
|
|
1446
1463
|
});
|
|
1447
1464
|
if (showResult instanceof Error) {
|
|
@@ -1775,6 +1792,7 @@ export class ThreadSessionRuntime {
|
|
|
1775
1792
|
directory: this.projectDirectory,
|
|
1776
1793
|
requestId: questionRequest.id,
|
|
1777
1794
|
input: { questions: questionRequest.questions },
|
|
1795
|
+
silent: this.getQueueLength() > 0,
|
|
1778
1796
|
});
|
|
1779
1797
|
},
|
|
1780
1798
|
});
|
|
@@ -1787,6 +1805,49 @@ export class ThreadSessionRuntime {
|
|
|
1787
1805
|
return;
|
|
1788
1806
|
}
|
|
1789
1807
|
this.onInteractiveUiStateChanged();
|
|
1808
|
+
// When a question is answered and the local queue has items, the model may
|
|
1809
|
+
// continue the same run without ever reaching the local-queue idle gate.
|
|
1810
|
+
// Hand the queued items to OpenCode's own prompt queue immediately instead
|
|
1811
|
+
// of waiting for tryDrainQueue() to see an idle session.
|
|
1812
|
+
if (this.getQueueLength() > 0 && !this.questionReplyQueueHandoffPromise) {
|
|
1813
|
+
logger.log(`[QUESTION REPLIED] Queue has ${this.getQueueLength()} items, handing off to opencode queue`);
|
|
1814
|
+
this.questionReplyQueueHandoffPromise = this.handoffQueuedItemsAfterQuestionReply({
|
|
1815
|
+
sessionId,
|
|
1816
|
+
}).catch((error) => {
|
|
1817
|
+
logger.error('[QUESTION REPLIED] Failed to hand off queued messages:', error);
|
|
1818
|
+
if (error instanceof Error) {
|
|
1819
|
+
void notifyError(error, 'Failed to hand off queued messages after question reply');
|
|
1820
|
+
}
|
|
1821
|
+
}).finally(() => {
|
|
1822
|
+
this.questionReplyQueueHandoffPromise = null;
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
// Detached helper promise for the "question answered while local queue has
|
|
1827
|
+
// items" flow. Prevents starting two overlapping local->opencode queue
|
|
1828
|
+
// handoff sequences when multiple question replies land close together.
|
|
1829
|
+
questionReplyQueueHandoffPromise = null;
|
|
1830
|
+
async handoffQueuedItemsAfterQuestionReply({ sessionId, }) {
|
|
1831
|
+
if (this.listenerAborted) {
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1834
|
+
if (this.state?.sessionId !== sessionId) {
|
|
1835
|
+
logger.log(`[QUESTION REPLIED] Session changed before queue handoff for thread ${this.threadId}`);
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
while (this.state?.sessionId === sessionId) {
|
|
1839
|
+
const next = threadState.dequeueItem(this.threadId);
|
|
1840
|
+
if (!next) {
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
const displayText = next.command
|
|
1844
|
+
? `/${next.command.name}`
|
|
1845
|
+
: `${next.prompt.slice(0, 150)}${next.prompt.length > 150 ? '...' : ''}`;
|
|
1846
|
+
if (displayText.trim()) {
|
|
1847
|
+
await sendThreadMessage(this.thread, `» **${next.username}:** ${displayText}`);
|
|
1848
|
+
}
|
|
1849
|
+
await this.submitViaOpencodeQueue(next);
|
|
1850
|
+
}
|
|
1790
1851
|
}
|
|
1791
1852
|
async handleSessionStatus(properties) {
|
|
1792
1853
|
const sessionId = this.state?.sessionId;
|
|
@@ -2004,7 +2065,9 @@ export class ThreadSessionRuntime {
|
|
|
2004
2065
|
})();
|
|
2005
2066
|
let syntheticContext = '';
|
|
2006
2067
|
if (input.username) {
|
|
2007
|
-
|
|
2068
|
+
const msgAttr = input.sourceMessageId ? ` message-id="${input.sourceMessageId}"` : '';
|
|
2069
|
+
const thrAttr = input.sourceThreadId ? ` thread-id="${input.sourceThreadId}"` : '';
|
|
2070
|
+
syntheticContext += `<discord-user name="${input.username}"${msgAttr}${thrAttr} />`;
|
|
2008
2071
|
}
|
|
2009
2072
|
const parts = [
|
|
2010
2073
|
{ type: 'text', text: promptWithImagePaths },
|
|
@@ -2112,6 +2175,8 @@ export class ThreadSessionRuntime {
|
|
|
2112
2175
|
agent: input.agent,
|
|
2113
2176
|
model: input.model,
|
|
2114
2177
|
permissions: input.permissions,
|
|
2178
|
+
sourceMessageId: input.sourceMessageId,
|
|
2179
|
+
sourceThreadId: input.sourceThreadId,
|
|
2115
2180
|
sessionStartScheduleKind: input.sessionStartSource?.scheduleKind,
|
|
2116
2181
|
sessionStartScheduledTaskId: input.sessionStartSource?.scheduledTaskId,
|
|
2117
2182
|
};
|
|
@@ -2281,10 +2346,50 @@ export class ThreadSessionRuntime {
|
|
|
2281
2346
|
return this.tryDrainQueue({ showIndicator: true });
|
|
2282
2347
|
});
|
|
2283
2348
|
}
|
|
2349
|
+
async abortActiveRunAndWait({ reason, timeoutMs = 2_000, }) {
|
|
2350
|
+
const state = this.state;
|
|
2351
|
+
const sessionId = state?.sessionId;
|
|
2352
|
+
if (!sessionId) {
|
|
2353
|
+
return;
|
|
2354
|
+
}
|
|
2355
|
+
let needsIdleWait = false;
|
|
2356
|
+
const waitSinceTimestamp = Date.now();
|
|
2357
|
+
const abortResult = await errore.tryAsync(() => {
|
|
2358
|
+
return this.dispatchAction(async () => {
|
|
2359
|
+
needsIdleWait = this.isMainSessionBusy();
|
|
2360
|
+
const outcome = this.abortActiveRunInternal({ reason });
|
|
2361
|
+
if (outcome.apiAbortPromise) {
|
|
2362
|
+
void outcome.apiAbortPromise;
|
|
2363
|
+
}
|
|
2364
|
+
});
|
|
2365
|
+
});
|
|
2366
|
+
if (abortResult instanceof Error) {
|
|
2367
|
+
logger.error(`[ABORT WAIT] Failed to abort active run: ${abortResult.message}`);
|
|
2368
|
+
return;
|
|
2369
|
+
}
|
|
2370
|
+
if (!needsIdleWait) {
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
await this.waitForEvent({
|
|
2374
|
+
predicate: (event) => {
|
|
2375
|
+
return event.type === 'session.idle'
|
|
2376
|
+
&& event.properties.sessionID === sessionId;
|
|
2377
|
+
},
|
|
2378
|
+
sinceTimestamp: waitSinceTimestamp,
|
|
2379
|
+
timeoutMs,
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2284
2382
|
/** Number of messages waiting in the queue. */
|
|
2285
2383
|
getQueueLength() {
|
|
2286
2384
|
return this.state?.queueItems.length ?? 0;
|
|
2287
2385
|
}
|
|
2386
|
+
/** NOTIFY_MESSAGE_FLAGS unless queue has a next item, then SILENT.
|
|
2387
|
+
* Permissions should NOT use this — they always notify. */
|
|
2388
|
+
getNotifyFlags() {
|
|
2389
|
+
return this.getQueueLength() > 0
|
|
2390
|
+
? SILENT_MESSAGE_FLAGS
|
|
2391
|
+
: NOTIFY_MESSAGE_FLAGS;
|
|
2392
|
+
}
|
|
2288
2393
|
/** Clear all queued messages. */
|
|
2289
2394
|
clearQueue() {
|
|
2290
2395
|
threadState.clearQueueItems(this.threadId);
|
|
@@ -2501,7 +2606,9 @@ export class ThreadSessionRuntime {
|
|
|
2501
2606
|
})();
|
|
2502
2607
|
let syntheticContext = '';
|
|
2503
2608
|
if (input.username) {
|
|
2504
|
-
|
|
2609
|
+
const msgAttr = input.sourceMessageId ? ` message-id="${input.sourceMessageId}"` : '';
|
|
2610
|
+
const thrAttr = input.sourceThreadId ? ` thread-id="${input.sourceThreadId}"` : '';
|
|
2611
|
+
syntheticContext += `<discord-user name="${input.username}"${msgAttr}${thrAttr} />`;
|
|
2505
2612
|
}
|
|
2506
2613
|
const parts = [
|
|
2507
2614
|
{ type: 'text', text: promptWithImagePaths },
|
|
@@ -2706,7 +2813,6 @@ export class ThreadSessionRuntime {
|
|
|
2706
2813
|
}
|
|
2707
2814
|
}
|
|
2708
2815
|
if (!session) {
|
|
2709
|
-
const sessionTitle = prompt.length > 80 ? prompt.slice(0, 77) + '...' : prompt.slice(0, 80);
|
|
2710
2816
|
// Pass per-session external_directory permissions so this session can
|
|
2711
2817
|
// access its own project directory (and worktree origin if applicable)
|
|
2712
2818
|
// without prompts. These override the server-level 'ask' default via
|
|
@@ -2720,12 +2826,18 @@ export class ThreadSessionRuntime {
|
|
|
2720
2826
|
}),
|
|
2721
2827
|
...parsePermissionRules(permissions ?? []),
|
|
2722
2828
|
];
|
|
2829
|
+
// Omit title so OpenCode auto-generates a summary from the conversation
|
|
2723
2830
|
const sessionResponse = await getClient().session.create({
|
|
2724
|
-
title: sessionTitle,
|
|
2725
2831
|
directory: this.sdkDirectory,
|
|
2726
2832
|
permission: sessionPermissions,
|
|
2727
2833
|
});
|
|
2728
2834
|
session = sessionResponse.data;
|
|
2835
|
+
// Insert DB row immediately so the external-sync poller sees
|
|
2836
|
+
// source='kimaki' before the next poll tick and skips this session.
|
|
2837
|
+
// The upsert at the end of ensureSession is kept for the reuse path.
|
|
2838
|
+
if (session) {
|
|
2839
|
+
await setThreadSession(this.thread.id, session.id);
|
|
2840
|
+
}
|
|
2729
2841
|
createdNewSession = true;
|
|
2730
2842
|
}
|
|
2731
2843
|
if (!session) {
|
|
@@ -2813,7 +2925,7 @@ export class ThreadSessionRuntime {
|
|
|
2813
2925
|
if (m.info.role !== 'assistant') {
|
|
2814
2926
|
return false;
|
|
2815
2927
|
}
|
|
2816
|
-
if (!
|
|
2928
|
+
if (!m.info.tokens) {
|
|
2817
2929
|
return false;
|
|
2818
2930
|
}
|
|
2819
2931
|
return getTokenTotal(m.info.tokens) > 0;
|
|
@@ -2852,9 +2964,8 @@ export class ThreadSessionRuntime {
|
|
|
2852
2964
|
this.stopTyping();
|
|
2853
2965
|
// Skip notification if there's a queued message next — the user only
|
|
2854
2966
|
// needs to be notified when the entire queue finishes.
|
|
2855
|
-
const queuedNext = (threadState.getThreadState(this.threadId)?.queueItems.length ?? 0) > 0;
|
|
2856
2967
|
await sendThreadMessage(this.thread, footerText, {
|
|
2857
|
-
flags:
|
|
2968
|
+
flags: this.getNotifyFlags(),
|
|
2858
2969
|
});
|
|
2859
2970
|
logger.log(`DURATION: Session completed in ${sessionDuration}, model ${runInfo.model}, tokens ${runInfo.tokensUsed}`);
|
|
2860
2971
|
}
|
package/dist/store.js
CHANGED
package/dist/system-message.js
CHANGED
|
@@ -54,6 +54,18 @@ bunx critique --web "Short title describing the changes" --filter "src/config.ts
|
|
|
54
54
|
|
|
55
55
|
The string after \`--web\` becomes the diff page title — make it reflect what the changes do (e.g. "Add retry logic to API client", "Fix auth timeout bug").
|
|
56
56
|
|
|
57
|
+
### fetching user comments from critique diffs
|
|
58
|
+
|
|
59
|
+
Users can add line-level comments (annotations) on any critique diff page via the Agentation widget (bottom-right corner of the diff page). To read those comments:
|
|
60
|
+
|
|
61
|
+
\`\`\`bash
|
|
62
|
+
curl https://critique.work/v/<id>/annotations
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
Returns \`text/markdown\` with each annotation showing the file, line, and comment text.
|
|
66
|
+
Use this when the user says they left comments on a critique diff and you need to read them.
|
|
67
|
+
You can also use WebFetch on \`https://critique.work/v/<id>/annotations\` to get the markdown directly.
|
|
68
|
+
|
|
57
69
|
### about critique
|
|
58
70
|
|
|
59
71
|
critique is an open source tool (MIT license) at https://github.com/remorses/critique.
|
|
@@ -131,7 +143,7 @@ Use random tunnel IDs by default. Only pass \`-t\` when exposing a service that
|
|
|
131
143
|
tmux new-session -d -s myapp-dev
|
|
132
144
|
|
|
133
145
|
# Run the dev server with kimaki tunnel inside the session
|
|
134
|
-
tmux send-keys -t myapp-dev "kimaki tunnel -p 3000 -- pnpm dev" Enter
|
|
146
|
+
tmux send-keys -t myapp-dev "kimaki tunnel --kill -p 3000 -- pnpm dev" Enter
|
|
135
147
|
\`\`\`
|
|
136
148
|
|
|
137
149
|
### getting the tunnel URL
|
|
@@ -146,15 +158,15 @@ tmux capture-pane -t myapp-dev -p | grep -i "tunnel"
|
|
|
146
158
|
\`\`\`bash
|
|
147
159
|
# Next.js project
|
|
148
160
|
tmux new-session -d -s projectname-nextjs-dev-3000
|
|
149
|
-
tmux send-keys -t nextjs-dev "kimaki tunnel -p 3000 -- pnpm dev" Enter
|
|
161
|
+
tmux send-keys -t nextjs-dev "kimaki tunnel --kill -p 3000 -- pnpm dev" Enter
|
|
150
162
|
|
|
151
163
|
# Vite project on port 5173
|
|
152
164
|
tmux new-session -d -s vite-dev-5173
|
|
153
|
-
tmux send-keys -t vite-dev "kimaki tunnel -p 5173 -- pnpm dev" Enter
|
|
165
|
+
tmux send-keys -t vite-dev "kimaki tunnel --kill -p 5173 -- pnpm dev" Enter
|
|
154
166
|
|
|
155
167
|
# Custom tunnel ID (only for intentionally public-safe services)
|
|
156
168
|
tmux new-session -d -s holocron-dev
|
|
157
|
-
tmux send-keys -t holocron-dev "kimaki tunnel -p 3000 -t holocron -- pnpm dev" Enter
|
|
169
|
+
tmux send-keys -t holocron-dev "kimaki tunnel --kill -p 3000 -t holocron -- pnpm dev" Enter
|
|
158
170
|
\`\`\`
|
|
159
171
|
|
|
160
172
|
### stopping the dev server
|
|
@@ -173,6 +185,12 @@ tmux kill-session -t myapp-dev
|
|
|
173
185
|
tmux list-sessions
|
|
174
186
|
\`\`\`
|
|
175
187
|
`;
|
|
188
|
+
export function isInjectedPromptMarker({ marker, }) {
|
|
189
|
+
if (!marker) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
return Boolean(marker.cliThreadPrompt || marker.start);
|
|
193
|
+
}
|
|
176
194
|
export function getOpencodeSystemMessage({ sessionId, channelId, guildId, threadId, worktree, channelTopic, username, userId, agents, currentAgent, }) {
|
|
177
195
|
const agentFlag = currentAgent ? ` --agent ${currentAgent}` : '';
|
|
178
196
|
const topicContext = channelTopic?.trim()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { isInjectedPromptMarker } from './system-message.js';
|
|
3
|
+
describe('isInjectedPromptMarker', () => {
|
|
4
|
+
test('treats start markers as injected prompts', () => {
|
|
5
|
+
expect(isInjectedPromptMarker({
|
|
6
|
+
marker: { start: true },
|
|
7
|
+
})).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
test('treats cli thread prompt markers as injected prompts', () => {
|
|
10
|
+
expect(isInjectedPromptMarker({
|
|
11
|
+
marker: { cliThreadPrompt: true },
|
|
12
|
+
})).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
test('ignores messages without an injection marker', () => {
|
|
15
|
+
expect(isInjectedPromptMarker({
|
|
16
|
+
marker: { scheduledKind: 'cron', scheduledTaskId: 1 },
|
|
17
|
+
})).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
});
|
package/dist/task-runner.js
CHANGED
|
@@ -21,7 +21,7 @@ function parseMessageId(value) {
|
|
|
21
21
|
}
|
|
22
22
|
async function executeThreadScheduledTask({ rest, task, payload, }) {
|
|
23
23
|
const marker = {
|
|
24
|
-
|
|
24
|
+
start: true,
|
|
25
25
|
scheduledKind: task.schedule_kind,
|
|
26
26
|
scheduledTaskId: task.id,
|
|
27
27
|
...(payload.agent ? { agent: payload.agent } : {}),
|
|
@@ -394,20 +394,15 @@ e2eTest('thread message queue ordering', () => {
|
|
|
394
394
|
await waitForFooterMessage({
|
|
395
395
|
discord,
|
|
396
396
|
threadId: thread.id,
|
|
397
|
-
timeout:
|
|
397
|
+
timeout: 8_000,
|
|
398
398
|
afterMessageIncludes: 'beta',
|
|
399
399
|
afterAuthorId: TEST_USER_ID,
|
|
400
400
|
});
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
Reply with exactly:
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
--- from: user (queue-tester)
|
|
407
|
-
Reply with exactly: beta
|
|
408
|
-
--- from: assistant (TestBot)
|
|
409
|
-
*project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
410
|
-
`);
|
|
401
|
+
const timeline = await th.text();
|
|
402
|
+
expect(timeline).toContain('Reply with exactly: alpha');
|
|
403
|
+
expect(timeline).toContain('Reply with exactly: beta');
|
|
404
|
+
expect(timeline).toContain('⬥ ok');
|
|
405
|
+
expect(timeline).toContain('*project ⋅ main ⋅');
|
|
411
406
|
// User B's message must appear before the new bot response
|
|
412
407
|
const userBIndex = after.findIndex((m) => {
|
|
413
408
|
return (m.author.id === TEST_USER_ID &&
|
|
@@ -422,7 +417,7 @@ e2eTest('thread message queue ordering', () => {
|
|
|
422
417
|
// New bot response has non-empty content
|
|
423
418
|
const newBotReply = afterBotMessages[afterBotMessages.length - 1];
|
|
424
419
|
expect(newBotReply.content.trim().length).toBeGreaterThan(0);
|
|
425
|
-
},
|
|
420
|
+
}, 12_000);
|
|
426
421
|
test('two rapid text messages in thread — both processed in order', async () => {
|
|
427
422
|
// 1. Send initial message to text channel → thread + session established
|
|
428
423
|
await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
@@ -820,11 +815,10 @@ e2eTest('thread message queue ordering', () => {
|
|
|
820
815
|
Reply with exactly: echo
|
|
821
816
|
--- from: assistant (TestBot)
|
|
822
817
|
*project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
818
|
+
⬥ ok
|
|
823
819
|
--- from: user (queue-tester)
|
|
824
820
|
Reply with exactly: foxtrot
|
|
825
821
|
--- from: assistant (TestBot)
|
|
826
|
-
⬥ ok
|
|
827
|
-
⬥ ok
|
|
828
822
|
*project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
829
823
|
`);
|
|
830
824
|
expect(userEchoIndex).toBeGreaterThan(-1);
|
package/dist/tools.js
CHANGED
|
@@ -106,7 +106,7 @@ export async function getTools({ onMessageCompleted, directory, }) {
|
|
|
106
106
|
}
|
|
107
107
|
try {
|
|
108
108
|
const session = await getClient().session.create({
|
|
109
|
-
title
|
|
109
|
+
...(title ? { title } : {}),
|
|
110
110
|
});
|
|
111
111
|
if (!session.data) {
|
|
112
112
|
throw new Error('Failed to create session');
|