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
|
@@ -6,7 +6,7 @@ import { afterAll, afterEach, describe, expect, test } from 'vitest';
|
|
|
6
6
|
import { completeScheduledTaskRunsForSession, createScheduledTask, createScheduledTaskRun, getActiveScheduledTaskRuns, getScheduledTask, startScheduledTaskRunSession, } from './database.js';
|
|
7
7
|
import { closeDb } from './db.js';
|
|
8
8
|
import { runTaskCommand } from './task-runner.js';
|
|
9
|
-
import { appendTaskCommandOutput, parseScheduledTaskPayload, parseSendAtValue, } from './task-schedule.js';
|
|
9
|
+
import { appendTaskCommandOutput, applyScheduledTaskUserEdit, parseScheduledTaskPayload, formatSessionSleepToolOutput, formatSessionSleepWakePrompt, parseSendAtValue, parseSleepWakeAt, } from './task-schedule.js';
|
|
10
10
|
describe('parseSendAtValue', () => {
|
|
11
11
|
test('accepts UTC ISO date ending with Z', () => {
|
|
12
12
|
const now = new Date('2026-02-22T13:00:00Z');
|
|
@@ -75,6 +75,101 @@ describe('parseSendAtValue', () => {
|
|
|
75
75
|
expect(result.nextRunAt.toISOString()).toBe('2026-02-23T09:00:00.000Z');
|
|
76
76
|
});
|
|
77
77
|
});
|
|
78
|
+
describe('parseSleepWakeAt', () => {
|
|
79
|
+
const now = new Date('2026-08-19T12:00:00Z');
|
|
80
|
+
test('adds 2h to now', () => {
|
|
81
|
+
const result = parseSleepWakeAt({ duration: '2h', now });
|
|
82
|
+
expect(result).not.toBeInstanceOf(Error);
|
|
83
|
+
if (result instanceof Error)
|
|
84
|
+
throw result;
|
|
85
|
+
expect(result.toISOString()).toBe('2026-08-19T14:00:00.000Z');
|
|
86
|
+
});
|
|
87
|
+
test('adds 30s and 1d', () => {
|
|
88
|
+
const thirtySeconds = parseSleepWakeAt({ duration: '30s', now });
|
|
89
|
+
expect(thirtySeconds).not.toBeInstanceOf(Error);
|
|
90
|
+
if (thirtySeconds instanceof Error)
|
|
91
|
+
throw thirtySeconds;
|
|
92
|
+
expect(thirtySeconds.toISOString()).toBe('2026-08-19T12:00:30.000Z');
|
|
93
|
+
const oneDay = parseSleepWakeAt({ duration: '1d', now });
|
|
94
|
+
expect(oneDay).not.toBeInstanceOf(Error);
|
|
95
|
+
if (oneDay instanceof Error)
|
|
96
|
+
throw oneDay;
|
|
97
|
+
expect(oneDay.toISOString()).toBe('2026-08-20T12:00:00.000Z');
|
|
98
|
+
});
|
|
99
|
+
test('accepts until UTC ISO date ending with Z', () => {
|
|
100
|
+
const result = parseSleepWakeAt({
|
|
101
|
+
until: '2026-08-20T09:00:00Z',
|
|
102
|
+
now,
|
|
103
|
+
});
|
|
104
|
+
expect(result).not.toBeInstanceOf(Error);
|
|
105
|
+
if (result instanceof Error)
|
|
106
|
+
throw result;
|
|
107
|
+
expect(result.toISOString()).toBe('2026-08-20T09:00:00.000Z');
|
|
108
|
+
});
|
|
109
|
+
test('rejects past until', () => {
|
|
110
|
+
const result = parseSleepWakeAt({
|
|
111
|
+
until: '2026-08-19T11:59:59Z',
|
|
112
|
+
now,
|
|
113
|
+
});
|
|
114
|
+
expect(result).toBeInstanceOf(Error);
|
|
115
|
+
if (result instanceof Error) {
|
|
116
|
+
expect(result.message).toContain('until must be in the future (UTC)');
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
test('rejects both duration and until', () => {
|
|
120
|
+
const result = parseSleepWakeAt({
|
|
121
|
+
duration: '1h',
|
|
122
|
+
until: '2026-08-20T09:00:00Z',
|
|
123
|
+
now,
|
|
124
|
+
});
|
|
125
|
+
expect(result).toBeInstanceOf(Error);
|
|
126
|
+
if (result instanceof Error) {
|
|
127
|
+
expect(result.message).toContain('either duration or until');
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
test('rejects neither duration nor until', () => {
|
|
131
|
+
const result = parseSleepWakeAt({ now });
|
|
132
|
+
expect(result).toBeInstanceOf(Error);
|
|
133
|
+
if (result instanceof Error) {
|
|
134
|
+
expect(result.message).toContain('duration or until');
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
test('rejects until with a non-UTC offset', () => {
|
|
138
|
+
const result = parseSleepWakeAt({
|
|
139
|
+
until: '2026-08-20T09:00:00+01:00',
|
|
140
|
+
now,
|
|
141
|
+
});
|
|
142
|
+
expect(result).toBeInstanceOf(Error);
|
|
143
|
+
if (result instanceof Error) {
|
|
144
|
+
expect(result.message).toContain('UTC ISO format ending with Z');
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
describe('formatSessionSleepToolOutput', () => {
|
|
149
|
+
const wakeAt = new Date('2026-09-07T13:21:00Z');
|
|
150
|
+
test('starts with Sleeping until and tells the model to wait', () => {
|
|
151
|
+
const output = formatSessionSleepToolOutput({
|
|
152
|
+
wakeAt,
|
|
153
|
+
reason: 'Wait 1 day, then check mail',
|
|
154
|
+
});
|
|
155
|
+
expect(output).toBe('Sleeping until 2026-09-07 13:21 UTC. Reason: Wait 1 day, then check mail. This tool result is not a wake. Do not continue the waited work. Do not call more tools. Reply with one short line that you are waiting until that time, then stop. The real wake is a later Discord message that starts with "Woke after sleeping until". Only then continue the wait reason. A new user message in this thread cancels the sleep. If you still need that later wake after answering, call kimaki_sleep again with until set to the same UTC time.');
|
|
156
|
+
expect(output.startsWith('Sleeping until ')).toBe(true);
|
|
157
|
+
expect(output).toContain('This tool result is not a wake');
|
|
158
|
+
expect(output).not.toContain('You will be woken');
|
|
159
|
+
expect(output).not.toContain('Stop now');
|
|
160
|
+
});
|
|
161
|
+
test('omits reason when none is passed', () => {
|
|
162
|
+
const output = formatSessionSleepToolOutput({ wakeAt });
|
|
163
|
+
expect(output.startsWith('Sleeping until 2026-09-07 13:21 UTC.')).toBe(true);
|
|
164
|
+
expect(output).not.toContain('Reason:');
|
|
165
|
+
});
|
|
166
|
+
test('wake prompt stays a separate later message', () => {
|
|
167
|
+
expect(formatSessionSleepWakePrompt({
|
|
168
|
+
wakeAt,
|
|
169
|
+
reason: 'Wait 1 day, then check mail',
|
|
170
|
+
})).toBe('⬦ Woke after sleeping until 2026-09-07 13:21 UTC\nReason: Wait 1 day, then check mail\nContinue the work you were waiting for.');
|
|
171
|
+
});
|
|
172
|
+
});
|
|
78
173
|
describe('scheduled task execution options', () => {
|
|
79
174
|
const tempDirectories = [];
|
|
80
175
|
afterEach(() => {
|
|
@@ -105,6 +200,59 @@ describe('scheduled task execution options', () => {
|
|
|
105
200
|
}
|
|
106
201
|
`);
|
|
107
202
|
});
|
|
203
|
+
test('clears the stored user when task edit passes an empty --user', () => {
|
|
204
|
+
const payload = parseScheduledTaskPayload(JSON.stringify({
|
|
205
|
+
kind: 'thread',
|
|
206
|
+
threadId: 'thread-1',
|
|
207
|
+
prompt: 'Handle support requests',
|
|
208
|
+
userId: '535922349652836367',
|
|
209
|
+
username: 'Tommy',
|
|
210
|
+
}));
|
|
211
|
+
expect(payload).not.toBeInstanceOf(Error);
|
|
212
|
+
if (payload instanceof Error)
|
|
213
|
+
throw payload;
|
|
214
|
+
expect(applyScheduledTaskUserEdit({
|
|
215
|
+
payload,
|
|
216
|
+
userOption: '',
|
|
217
|
+
})).toMatchInlineSnapshot(`
|
|
218
|
+
{
|
|
219
|
+
"agent": null,
|
|
220
|
+
"allowConcurrency": false,
|
|
221
|
+
"injectionGuardPatterns": null,
|
|
222
|
+
"kind": "thread",
|
|
223
|
+
"model": null,
|
|
224
|
+
"parentSessionId": null,
|
|
225
|
+
"permissions": null,
|
|
226
|
+
"preRunCommand": null,
|
|
227
|
+
"prompt": "Handle support requests",
|
|
228
|
+
"threadId": "thread-1",
|
|
229
|
+
"userId": null,
|
|
230
|
+
"username": null,
|
|
231
|
+
}
|
|
232
|
+
`);
|
|
233
|
+
expect(applyScheduledTaskUserEdit({
|
|
234
|
+
payload,
|
|
235
|
+
userOption: ' ',
|
|
236
|
+
}).userId).toBeNull();
|
|
237
|
+
});
|
|
238
|
+
test('sets the stored user when task edit passes a resolved --user', () => {
|
|
239
|
+
const payload = parseScheduledTaskPayload(JSON.stringify({
|
|
240
|
+
kind: 'channel',
|
|
241
|
+
channelId: 'chan-1',
|
|
242
|
+
prompt: 'Daily digest',
|
|
243
|
+
}));
|
|
244
|
+
expect(payload).not.toBeInstanceOf(Error);
|
|
245
|
+
if (payload instanceof Error)
|
|
246
|
+
throw payload;
|
|
247
|
+
expect(applyScheduledTaskUserEdit({
|
|
248
|
+
payload,
|
|
249
|
+
userOption: '535922349652836367',
|
|
250
|
+
resolvedUser: { id: '535922349652836367', username: 'Tommy' },
|
|
251
|
+
})).toMatchObject({
|
|
252
|
+
userId: '535922349652836367',
|
|
253
|
+
username: 'Tommy',
|
|
254
|
+
});
|
|
255
|
+
});
|
|
108
256
|
test('adds command stdout to the prompt', () => {
|
|
109
257
|
expect(appendTaskCommandOutput({
|
|
110
258
|
prompt: 'Handle support requests',
|
package/dist/test-utils.js
CHANGED
|
@@ -67,6 +67,8 @@ export function initTestGitRepo(directory) {
|
|
|
67
67
|
execSync('git config user.name "Test"', { cwd: directory, stdio: 'pipe' });
|
|
68
68
|
execSync('git commit --allow-empty -m "init"', { cwd: directory, stdio: 'pipe' });
|
|
69
69
|
}
|
|
70
|
+
import { getMessageVisibleText, } from 'discord-digital-twin/src';
|
|
71
|
+
export { getMessageVisibleText };
|
|
70
72
|
import { getOpencodeClient, initializeOpencodeForDirectory, } from './opencode.js';
|
|
71
73
|
import { getThreadState, } from './session-handler/thread-runtime-state.js';
|
|
72
74
|
const MAX_VITEST_WAIT_TIMEOUT_MS = 10_000;
|
|
@@ -155,7 +157,7 @@ export async function waitForBotReplyAfterUserMessage({ discord, threadId, userI
|
|
|
155
157
|
const messages = await discord.thread(threadId).getMessages();
|
|
156
158
|
const userMessageIndex = messages.findIndex((message) => {
|
|
157
159
|
return (message.author.id === userId &&
|
|
158
|
-
message.
|
|
160
|
+
getMessageVisibleText(message).includes(userMessageIncludes));
|
|
159
161
|
});
|
|
160
162
|
const botReplyIndex = messages.findIndex((message, index) => {
|
|
161
163
|
return index > userMessageIndex && message.author.id === discord.botUserId;
|
|
@@ -189,7 +191,7 @@ export async function waitForBotMessageContaining({ discord, threadId, userId, t
|
|
|
189
191
|
if (afterUserMessageIncludes && userId) {
|
|
190
192
|
return messages.findLastIndex((message) => {
|
|
191
193
|
return (message.author.id === userId &&
|
|
192
|
-
message.
|
|
194
|
+
getMessageVisibleText(message).includes(afterUserMessageIncludes));
|
|
193
195
|
});
|
|
194
196
|
}
|
|
195
197
|
return -1;
|
|
@@ -207,7 +209,7 @@ export async function waitForBotMessageContaining({ discord, threadId, userId, t
|
|
|
207
209
|
return false;
|
|
208
210
|
}
|
|
209
211
|
return (message.author.id === discord.botUserId &&
|
|
210
|
-
message.
|
|
212
|
+
getMessageVisibleText(message).includes(text));
|
|
211
213
|
});
|
|
212
214
|
if (match) {
|
|
213
215
|
return messages;
|
|
@@ -220,7 +222,7 @@ export async function waitForBotMessageContaining({ discord, threadId, userId, t
|
|
|
220
222
|
.slice(-12)
|
|
221
223
|
.map((message) => {
|
|
222
224
|
const role = message.author.id === discord.botUserId ? 'bot' : 'user';
|
|
223
|
-
return `${role}: ${message.
|
|
225
|
+
return `${role}: ${getMessageVisibleText(message).slice(0, 120)}`;
|
|
224
226
|
})
|
|
225
227
|
.join('\n');
|
|
226
228
|
throw new Error(`Timed out waiting for bot message containing "${text}" in thread ${threadId}. Recent messages:\n${recent}`);
|
|
@@ -243,14 +245,20 @@ export async function waitForMessageById({ discord, threadId, messageId, timeout
|
|
|
243
245
|
}
|
|
244
246
|
throw new Error(`Timed out waiting for message ${messageId} in thread ${threadId}`);
|
|
245
247
|
}
|
|
246
|
-
function isFooterMessage({ message, botUserId, }) {
|
|
248
|
+
export function isFooterMessage({ message, botUserId, }) {
|
|
247
249
|
if (message.author.id !== botUserId) {
|
|
248
250
|
return false;
|
|
249
251
|
}
|
|
250
|
-
|
|
252
|
+
const content = message.content.startsWith('> ')
|
|
253
|
+
? message.content.slice(2)
|
|
254
|
+
: message.content;
|
|
255
|
+
if (!content.startsWith('*')) {
|
|
251
256
|
return false;
|
|
252
257
|
}
|
|
253
|
-
|
|
258
|
+
if (content.startsWith('*using ')) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
return content.includes('⋅');
|
|
254
262
|
}
|
|
255
263
|
/**
|
|
256
264
|
* Poll until a footer message appears, optionally after an anchor message.
|
|
@@ -265,7 +273,7 @@ export async function waitForFooterMessage({ discord, threadId, timeout, afterMe
|
|
|
265
273
|
lastMessages = messages;
|
|
266
274
|
const afterIndex = afterMessageIncludes
|
|
267
275
|
? messages.findLastIndex((message) => {
|
|
268
|
-
if (!message.
|
|
276
|
+
if (!getMessageVisibleText(message).includes(afterMessageIncludes)) {
|
|
269
277
|
return false;
|
|
270
278
|
}
|
|
271
279
|
if (!afterAuthorId) {
|
|
@@ -297,7 +305,7 @@ export async function waitForFooterMessage({ discord, threadId, timeout, afterMe
|
|
|
297
305
|
.slice(-12)
|
|
298
306
|
.map((message) => {
|
|
299
307
|
const role = message.author.id === discord.botUserId ? 'bot' : 'user';
|
|
300
|
-
return `${role}: ${message.
|
|
308
|
+
return `${role}: ${getMessageVisibleText(message).slice(0, 120)}`;
|
|
301
309
|
})
|
|
302
310
|
.join('\n');
|
|
303
311
|
const anchorText = afterMessageIncludes || 'start';
|
package/dist/thinking-utils.js
CHANGED
|
@@ -33,3 +33,13 @@ export function matchThinkingValue({ requestedValue, availableValues, }) {
|
|
|
33
33
|
return availableValue.toLowerCase() === normalizedRequestedValue;
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
+
export function resolveRequestedThinkingVariant({ requestedValue, providers, providerId, modelId, }) {
|
|
37
|
+
return matchThinkingValue({
|
|
38
|
+
requestedValue,
|
|
39
|
+
availableValues: getThinkingValuesForModel({
|
|
40
|
+
providers,
|
|
41
|
+
providerId,
|
|
42
|
+
modelId,
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Tests thinking-level variant matching used by /xxx-agent variant.
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
import { getThinkingValuesForModel, matchThinkingValue, } from './thinking-utils.js';
|
|
4
|
+
describe('matchThinkingValue', () => {
|
|
5
|
+
const providers = [
|
|
6
|
+
{
|
|
7
|
+
id: 'anthropic',
|
|
8
|
+
models: {
|
|
9
|
+
'claude-opus-4-6': {
|
|
10
|
+
variants: {
|
|
11
|
+
high: {},
|
|
12
|
+
max: {},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
const availableValues = getThinkingValuesForModel({
|
|
19
|
+
providers,
|
|
20
|
+
providerId: 'anthropic',
|
|
21
|
+
modelId: 'claude-opus-4-6',
|
|
22
|
+
});
|
|
23
|
+
test('matches a supported thinking level case-insensitively', () => {
|
|
24
|
+
expect(matchThinkingValue({
|
|
25
|
+
requestedValue: 'HIGH',
|
|
26
|
+
availableValues,
|
|
27
|
+
})).toBe('high');
|
|
28
|
+
});
|
|
29
|
+
test('returns undefined for an unsupported thinking level', () => {
|
|
30
|
+
expect(matchThinkingValue({
|
|
31
|
+
requestedValue: 'not-a-real-level',
|
|
32
|
+
availableValues,
|
|
33
|
+
})).toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
});
|