@otto-assistant/bridge 0.4.97 → 0.4.100
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 +7 -1
- package/dist/anthropic-auth-plugin.js +2 -0
- package/dist/cli-send-thread.e2e.test.js +4 -7
- package/dist/cli.js +2 -2
- package/dist/commands/screenshare.js +1 -1
- package/dist/commands/screenshare.test.js +2 -2
- package/dist/commands/vscode.js +269 -0
- package/dist/db.js +1 -0
- package/dist/discord-command-registration.js +5 -0
- package/dist/gateway-proxy-reconnect.e2e.test.js +2 -2
- package/dist/interaction-handler.js +4 -0
- package/dist/system-message.js +23 -22
- package/dist/system-message.test.js +23 -22
- package/dist/system-prompt-drift-plugin.js +41 -11
- package/dist/utils.js +1 -1
- package/package.json +1 -1
- package/src/agent-model.e2e.test.ts +8 -1
- package/src/anthropic-auth-plugin.ts +2 -0
- package/src/cli-send-thread.e2e.test.ts +6 -7
- package/src/cli.ts +2 -2
- package/src/commands/screenshare.test.ts +2 -2
- package/src/commands/screenshare.ts +1 -1
- package/src/commands/vscode.ts +342 -0
- package/src/db.ts +1 -0
- package/src/discord-command-registration.ts +7 -0
- package/src/gateway-proxy-reconnect.e2e.test.ts +2 -2
- package/src/interaction-handler.ts +5 -0
- package/src/system-message.test.ts +23 -22
- package/src/system-message.ts +23 -22
- package/src/system-prompt-drift-plugin.ts +48 -12
- package/src/utils.ts +1 -1
package/src/system-message.ts
CHANGED
|
@@ -363,7 +363,7 @@ export function getOpencodeSystemMessage({
|
|
|
363
363
|
.join('\n')}`
|
|
364
364
|
: ''
|
|
365
365
|
return `
|
|
366
|
-
The user is reading your messages from inside Discord, via kimaki.
|
|
366
|
+
The user is reading your messages from inside Discord, via kimaki.dev
|
|
367
367
|
|
|
368
368
|
## bash tool
|
|
369
369
|
|
|
@@ -431,39 +431,40 @@ ${
|
|
|
431
431
|
|
|
432
432
|
To start a new thread/session in this channel pro-grammatically, run:
|
|
433
433
|
|
|
434
|
-
kimaki send --channel ${channelId} --prompt "your prompt here"
|
|
434
|
+
kimaki send --channel ${channelId} --prompt "your prompt here" --agent <current_agent>${userArg}
|
|
435
435
|
|
|
436
436
|
You can use this to "spawn" parallel helper sessions like teammates: start new threads with focused prompts, then come back and collect the results.
|
|
437
|
+
Prefer passing the current agent with \`--agent <current_agent>\` so spawned or scheduled sessions keep the same agent unless you are intentionally switching. Replace \`<current_agent>\` with the value from the per-turn \`Current agent\` reminder.
|
|
437
438
|
|
|
438
439
|
IMPORTANT: NEVER use \`--worktree\` unless the user explicitly asks for a worktree. Default to creating normal threads without worktrees.
|
|
439
440
|
|
|
440
441
|
To send a prompt to an existing thread instead of creating a new one:
|
|
441
442
|
|
|
442
|
-
kimaki send --thread <thread_id> --prompt "follow-up prompt"
|
|
443
|
+
kimaki send --thread <thread_id> --prompt "follow-up prompt" --agent <current_agent>
|
|
443
444
|
|
|
444
445
|
Use this when you already have the Discord thread ID.
|
|
445
446
|
|
|
446
447
|
To send to the thread associated with a known session:
|
|
447
448
|
|
|
448
|
-
kimaki send --session <session_id> --prompt "follow-up prompt"
|
|
449
|
+
kimaki send --session <session_id> --prompt "follow-up prompt" --agent <current_agent>
|
|
449
450
|
|
|
450
451
|
Use this when you have the OpenCode session ID.
|
|
451
452
|
|
|
452
453
|
Use --notify-only to create a notification thread without starting an AI session:
|
|
453
454
|
|
|
454
|
-
kimaki send --channel ${channelId} --prompt "User cancelled subscription" --notify-only
|
|
455
|
+
kimaki send --channel ${channelId} --prompt "User cancelled subscription" --notify-only --agent <current_agent>${userArg}
|
|
455
456
|
|
|
456
457
|
Use --user to add a specific Discord user to the new thread:
|
|
457
458
|
|
|
458
|
-
kimaki send --channel ${channelId} --prompt "Review the latest CI failure"
|
|
459
|
+
kimaki send --channel ${channelId} --prompt "Review the latest CI failure" --agent <current_agent>${userArg}
|
|
459
460
|
|
|
460
461
|
Use --worktree to create a git worktree for the session (ONLY when the user explicitly asks for a worktree):
|
|
461
462
|
|
|
462
|
-
kimaki send --channel ${channelId} --prompt "Add dark mode support" --worktree dark-mode
|
|
463
|
+
kimaki send --channel ${channelId} --prompt "Add dark mode support" --worktree dark-mode --agent <current_agent>${userArg}
|
|
463
464
|
|
|
464
465
|
Use --cwd to start a session in an existing git worktree directory (must be a worktree of the project):
|
|
465
466
|
|
|
466
|
-
kimaki send --channel ${channelId} --prompt "Continue work on feature" --cwd /path/to/existing-worktree
|
|
467
|
+
kimaki send --channel ${channelId} --prompt "Continue work on feature" --cwd /path/to/existing-worktree --agent <current_agent>${userArg}
|
|
467
468
|
|
|
468
469
|
Important:
|
|
469
470
|
- NEVER use \`--worktree\` unless the user explicitly requests a worktree. Most tasks should use normal threads without worktrees.
|
|
@@ -481,8 +482,8 @@ ${availableAgentsContext}
|
|
|
481
482
|
|
|
482
483
|
You can trigger registered opencode commands (slash commands, skills, MCP prompts) by starting the \`--prompt\` with \`/commandname\`:
|
|
483
484
|
|
|
484
|
-
kimaki send --thread <thread_id> --prompt "/review fix the auth module"
|
|
485
|
-
kimaki send --channel ${channelId} --prompt "/build-cmd update dependencies"
|
|
485
|
+
kimaki send --thread <thread_id> --prompt "/review fix the auth module" --agent <current_agent>
|
|
486
|
+
kimaki send --channel ${channelId} --prompt "/build-cmd update dependencies" --agent <current_agent>${userArg}
|
|
486
487
|
|
|
487
488
|
The command name must match a registered opencode command. If the command is not recognized, the prompt is sent as plain text to the model. This works for both new threads (\`--channel\`) and existing threads (\`--thread\`/\`--session\`).
|
|
488
489
|
|
|
@@ -492,14 +493,14 @@ The user can switch the active agent mid-session using the Discord slash command
|
|
|
492
493
|
|
|
493
494
|
You can also switch agents via \`kimaki send\`:
|
|
494
495
|
|
|
495
|
-
kimaki send --thread <thread_id> --prompt "/<agentname>-agent"
|
|
496
|
+
kimaki send --thread <thread_id> --prompt "/<agentname>-agent" --agent <current_agent>
|
|
496
497
|
|
|
497
498
|
## scheduled sends and task management
|
|
498
499
|
|
|
499
500
|
Use \`--send-at\` to schedule a one-time or recurring task:
|
|
500
501
|
|
|
501
|
-
kimaki send --channel ${channelId} --prompt "Reminder: review open PRs" --send-at "2026-03-01T09:00:00Z"
|
|
502
|
-
kimaki send --channel ${channelId} --prompt "Run weekly test suite and summarize failures" --send-at "0 9 * * 1"
|
|
502
|
+
kimaki send --channel ${channelId} --prompt "Reminder: review open PRs" --send-at "2026-03-01T09:00:00Z" --agent <current_agent>${userArg}
|
|
503
|
+
kimaki send --channel ${channelId} --prompt "Run weekly test suite and summarize failures" --send-at "0 9 * * 1" --agent <current_agent>${userArg}
|
|
503
504
|
|
|
504
505
|
ALL scheduling is in UTC. Dates must be UTC ISO format ending with \`Z\`. Cron expressions also fire in UTC (e.g. \`0 9 * * 1\` means 9:00 UTC every Monday).
|
|
505
506
|
When the user specifies a time without a timezone, ask them to confirm their timezone or the UTC equivalent. Never guess the user's timezone.
|
|
@@ -533,13 +534,13 @@ kimaki task delete <id>
|
|
|
533
534
|
|
|
534
535
|
Use case patterns:
|
|
535
536
|
- Reminder flows: create deadline reminders in this channel with one-time \`--send-at\`; mention only if action is required.
|
|
536
|
-
- Proactive reminders: when you encounter time-sensitive information during your work (e.g. creating an API key that expires in 90 days, a certificate with an expiration date, a trial period ending, a deadline mentioned in code comments), proactively schedule a \`--notify-only\` reminder before the expiration so the user gets notified in time. For example, if you generate an API key expiring on 2026-06-01, schedule a reminder a few days before: \`kimaki send --channel ${channelId} --prompt "Reminder: <@USER_ID> the API key created on 2026-03-01 expires on 2026-06-01. Renew it before it breaks production." --send-at "2026-05-28T09:00:00Z" --notify-only
|
|
537
|
+
- Proactive reminders: when you encounter time-sensitive information during your work (e.g. creating an API key that expires in 90 days, a certificate with an expiration date, a trial period ending, a deadline mentioned in code comments), proactively schedule a \`--notify-only\` reminder before the expiration so the user gets notified in time. For example, if you generate an API key expiring on 2026-06-01, schedule a reminder a few days before: \`kimaki send --channel ${channelId} --prompt "Reminder: <@USER_ID> the API key created on 2026-03-01 expires on 2026-06-01. Renew it before it breaks production." --send-at "2026-05-28T09:00:00Z" --notify-only --agent <current_agent>\`. Always tell the user you scheduled the reminder so they know.
|
|
537
538
|
- Weekly QA: schedule "run full test suite, inspect failures, post summary, and mention @username only when failures require review".
|
|
538
539
|
- Weekly benchmark automation: schedule a benchmark prompt that runs model evals, writes JSON outputs in the repo, commits results, and mentions only for regressions.
|
|
539
540
|
- Recurring maintenance: use cron \`--send-at\` for repetitive tasks like rotating secrets, checking dependency updates, running security audits, or cleaning up stale branches. Example: \`--send-at "0 9 1 * *"\` to run on the 1st of every month.
|
|
540
541
|
- Thread reminders: when the user says "remind me about this in 2 hours" (or any duration), use \`--send-at\` with \`--thread\` to resurface the current thread. Compute the future UTC time and send a mention so Discord shows a notification:
|
|
541
542
|
|
|
542
|
-
kimaki send --session ${sessionId} --prompt "Reminder: <@USER_ID> you asked to be reminded about this thread." --send-at "<future_UTC_time>" --notify-only
|
|
543
|
+
kimaki send --session ${sessionId} --prompt "Reminder: <@USER_ID> you asked to be reminded about this thread." --send-at "<future_UTC_time>" --notify-only --agent <current_agent>
|
|
543
544
|
|
|
544
545
|
Replace \`<future_UTC_time>\` with the computed UTC ISO timestamp. The \`--notify-only\` flag creates just a notification message without starting a new AI session. The \`<@userId>\` mention ensures the user gets a Discord notification.
|
|
545
546
|
|
|
@@ -554,7 +555,7 @@ ONLY create worktrees when the user explicitly asks for one. Never proactively u
|
|
|
554
555
|
When the user asks to "create a worktree" or "make a worktree", they mean you should use the kimaki CLI to create it. Do NOT use raw \`git worktree add\` commands. Instead use:
|
|
555
556
|
|
|
556
557
|
\`\`\`bash
|
|
557
|
-
kimaki send --channel ${channelId} --prompt "your task description" --worktree worktree-name
|
|
558
|
+
kimaki send --channel ${channelId} --prompt "your task description" --worktree worktree-name --agent <current_agent>${userArg}
|
|
558
559
|
\`\`\`
|
|
559
560
|
|
|
560
561
|
This creates a new Discord thread with an isolated git worktree and starts a session in it. The worktree name should be kebab-case and descriptive of the task.
|
|
@@ -570,7 +571,7 @@ Critical recursion guard:
|
|
|
570
571
|
Use \`--cwd\` to start a session in an existing git worktree directory instead of creating a new one:
|
|
571
572
|
|
|
572
573
|
\`\`\`bash
|
|
573
|
-
kimaki send --channel ${channelId} --prompt "Continue work on feature X" --cwd /path/to/existing-worktree
|
|
574
|
+
kimaki send --channel ${channelId} --prompt "Continue work on feature X" --cwd /path/to/existing-worktree --agent <current_agent>${userArg}
|
|
574
575
|
\`\`\`
|
|
575
576
|
|
|
576
577
|
The path must be a git worktree of the project (validated via \`git worktree list\`). The session resolves to the correct project channel but uses the worktree as its working directory. Use \`--worktree\` to create a new worktree, \`--cwd\` to reuse an existing one.
|
|
@@ -584,7 +585,7 @@ This is useful for automation (cron jobs, GitHub webhooks, n8n, etc.)
|
|
|
584
585
|
When you are approaching the **context window limit** or the user explicitly asks to **handoff to a new thread**, use the \`kimaki send\` command to start a fresh session with context:
|
|
585
586
|
|
|
586
587
|
\`\`\`bash
|
|
587
|
-
kimaki send --channel ${channelId} --prompt "Continuing from previous session: <summary of current task and state>"
|
|
588
|
+
kimaki send --channel ${channelId} --prompt "Continuing from previous session: <summary of current task and state>" --agent <current_agent>${userArg}
|
|
588
589
|
\`\`\`
|
|
589
590
|
|
|
590
591
|
The command automatically handles long prompts (over 2000 chars) by sending them as file attachments.
|
|
@@ -642,10 +643,10 @@ To send a task to another project:
|
|
|
642
643
|
|
|
643
644
|
\`\`\`bash
|
|
644
645
|
# Send to a specific channel
|
|
645
|
-
kimaki send --channel <channel_id> --prompt "Plan how to update the API client to v2"
|
|
646
|
+
kimaki send --channel <channel_id> --prompt "Plan how to update the API client to v2" --agent <current_agent>
|
|
646
647
|
|
|
647
648
|
# Or use --project to resolve from directory
|
|
648
|
-
kimaki send --project /path/to/other-repo --prompt "Plan how to bump version to 1.2.0"
|
|
649
|
+
kimaki send --project /path/to/other-repo --prompt "Plan how to bump version to 1.2.0" --agent <current_agent>
|
|
649
650
|
\`\`\`
|
|
650
651
|
|
|
651
652
|
When sending prompts to other projects, always ask the agent to plan first, never build upfront. The prompt should start with "Plan how to ..." so the user can review before greenlighting implementation.
|
|
@@ -668,10 +669,10 @@ If your Bash tool timeout triggers anyway, fall back to reading the session outp
|
|
|
668
669
|
|
|
669
670
|
\`\`\`bash
|
|
670
671
|
# Start a session and wait for it to finish
|
|
671
|
-
kimaki send --channel <channel_id> --prompt "Fix the auth bug" --wait
|
|
672
|
+
kimaki send --channel <channel_id> --prompt "Fix the auth bug" --wait --agent <current_agent>
|
|
672
673
|
|
|
673
674
|
# Send to an existing thread and wait
|
|
674
|
-
kimaki send --thread <thread_id> --prompt "Run the tests" --wait
|
|
675
|
+
kimaki send --thread <thread_id> --prompt "Run the tests" --wait --agent <current_agent>
|
|
675
676
|
\`\`\`
|
|
676
677
|
|
|
677
678
|
The command exits with the session markdown on stdout once the model finishes responding.
|
|
@@ -33,6 +33,7 @@ type SessionState = {
|
|
|
33
33
|
comparedTurn: number
|
|
34
34
|
previousTurnContext: TurnContext | undefined
|
|
35
35
|
currentTurnContext: TurnContext | undefined
|
|
36
|
+
pendingCompareTimeout: ReturnType<typeof setTimeout> | undefined
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
type SystemPromptDiff = {
|
|
@@ -146,7 +147,6 @@ function writeSystemPromptDiffFile({
|
|
|
146
147
|
additions: number
|
|
147
148
|
deletions: number
|
|
148
149
|
filePath: string
|
|
149
|
-
latestPromptPath: string
|
|
150
150
|
} {
|
|
151
151
|
const diff = buildPatch({
|
|
152
152
|
beforeText: beforePrompt,
|
|
@@ -157,7 +157,7 @@ function writeSystemPromptDiffFile({
|
|
|
157
157
|
const timestamp = new Date().toISOString().replaceAll(':', '-')
|
|
158
158
|
const sessionDir = path.join(getSystemPromptDiffDir({ dataDir }), sessionId)
|
|
159
159
|
const filePath = path.join(sessionDir, `${timestamp}.diff`)
|
|
160
|
-
|
|
160
|
+
|
|
161
161
|
const fileContent = [
|
|
162
162
|
`Session: ${sessionId}`,
|
|
163
163
|
`Created: ${new Date().toISOString()}`,
|
|
@@ -176,7 +176,6 @@ function writeSystemPromptDiffFile({
|
|
|
176
176
|
additions: diff.additions,
|
|
177
177
|
deletions: diff.deletions,
|
|
178
178
|
filePath,
|
|
179
|
-
latestPromptPath,
|
|
180
179
|
}
|
|
181
180
|
},
|
|
182
181
|
catch: (error) => {
|
|
@@ -204,6 +203,7 @@ function getOrCreateSessionState({
|
|
|
204
203
|
comparedTurn: 0,
|
|
205
204
|
previousTurnContext: undefined,
|
|
206
205
|
currentTurnContext: undefined,
|
|
206
|
+
pendingCompareTimeout: undefined,
|
|
207
207
|
}
|
|
208
208
|
sessions.set(sessionId, state)
|
|
209
209
|
return state
|
|
@@ -278,8 +278,7 @@ async function handleSystemTransform({
|
|
|
278
278
|
sessionId,
|
|
279
279
|
message:
|
|
280
280
|
`system prompt changed since the previous message (+${diffFileResult.additions} / -${diffFileResult.deletions}). ` +
|
|
281
|
-
`Diff: \`${abbreviatePath(diffFileResult.filePath)}\`. `
|
|
282
|
-
`Latest prompt: \`${abbreviatePath(diffFileResult.latestPromptPath)}\``,
|
|
281
|
+
`Diff: \`${abbreviatePath(diffFileResult.filePath)}\`. `
|
|
283
282
|
}),
|
|
284
283
|
},
|
|
285
284
|
})
|
|
@@ -315,13 +314,46 @@ const systemPromptDriftPlugin: Plugin = async ({ client, directory }) => {
|
|
|
315
314
|
'experimental.chat.system.transform': async (input, output) => {
|
|
316
315
|
const result = await errore.tryAsync({
|
|
317
316
|
try: async () => {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
317
|
+
const sessionId = input.sessionID
|
|
318
|
+
if (!sessionId) {
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
const state = getOrCreateSessionState({ sessions, sessionId })
|
|
322
|
+
if (state.pendingCompareTimeout) {
|
|
323
|
+
clearTimeout(state.pendingCompareTimeout)
|
|
324
|
+
}
|
|
325
|
+
// Delay one tick so other system-transform hooks can finish mutating
|
|
326
|
+
// output.system before we snapshot it for drift detection.
|
|
327
|
+
state.pendingCompareTimeout = setTimeout(() => {
|
|
328
|
+
state.pendingCompareTimeout = undefined
|
|
329
|
+
void errore.tryAsync({
|
|
330
|
+
try: async () => {
|
|
331
|
+
await handleSystemTransform({
|
|
332
|
+
input,
|
|
333
|
+
output,
|
|
334
|
+
sessions,
|
|
335
|
+
dataDir,
|
|
336
|
+
client,
|
|
337
|
+
})
|
|
338
|
+
},
|
|
339
|
+
catch: (error) => {
|
|
340
|
+
return new Error('system prompt drift transform hook failed', {
|
|
341
|
+
cause: error,
|
|
342
|
+
})
|
|
343
|
+
},
|
|
344
|
+
}).then((delayedResult) => {
|
|
345
|
+
if (!(delayedResult instanceof Error)) {
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
logger.warn(
|
|
349
|
+
`[system-prompt-drift-plugin] ${formatPluginErrorWithStack(delayedResult)}`,
|
|
350
|
+
)
|
|
351
|
+
void notifyError(
|
|
352
|
+
delayedResult,
|
|
353
|
+
'system prompt drift plugin transform hook failed',
|
|
354
|
+
)
|
|
355
|
+
})
|
|
356
|
+
}, 0)
|
|
325
357
|
},
|
|
326
358
|
catch: (error) => {
|
|
327
359
|
return new Error('system prompt drift transform hook failed', {
|
|
@@ -346,6 +378,10 @@ const systemPromptDriftPlugin: Plugin = async ({ client, directory }) => {
|
|
|
346
378
|
if (!deletedSessionId) {
|
|
347
379
|
return
|
|
348
380
|
}
|
|
381
|
+
const state = sessions.get(deletedSessionId)
|
|
382
|
+
if (state?.pendingCompareTimeout) {
|
|
383
|
+
clearTimeout(state.pendingCompareTimeout)
|
|
384
|
+
}
|
|
349
385
|
sessions.delete(deletedSessionId)
|
|
350
386
|
},
|
|
351
387
|
catch: (error) => {
|
package/src/utils.ts
CHANGED
|
@@ -82,7 +82,7 @@ export function generateBotInstallUrl({
|
|
|
82
82
|
|
|
83
83
|
export const KIMAKI_GATEWAY_APP_ID =
|
|
84
84
|
process.env.KIMAKI_GATEWAY_APP_ID || '1477605701202481173'
|
|
85
|
-
export const KIMAKI_WEBSITE_URL = process.env.KIMAKI_WEBSITE_URL || 'https://kimaki.
|
|
85
|
+
export const KIMAKI_WEBSITE_URL = process.env.KIMAKI_WEBSITE_URL || 'https://kimaki.dev'
|
|
86
86
|
|
|
87
87
|
export function generateDiscordInstallUrlForBot({
|
|
88
88
|
appId,
|