kimaki 0.4.84 → 0.4.85
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/discord-bot.js +5 -1
- package/dist/external-opencode-sync.js +11 -11
- package/dist/session-handler/thread-session-runtime.js +7 -1
- package/package.json +2 -2
- package/src/discord-bot.ts +7 -1
- package/src/external-opencode-sync.ts +11 -13
- package/src/session-handler/thread-session-runtime.ts +8 -1
package/dist/discord-bot.js
CHANGED
|
@@ -185,7 +185,11 @@ export async function startDiscordBot({ token, appId, discordClient, useWorktree
|
|
|
185
185
|
await setupHandlers(discordClient);
|
|
186
186
|
}
|
|
187
187
|
else {
|
|
188
|
-
discordClient.once(Events.ClientReady,
|
|
188
|
+
discordClient.once(Events.ClientReady, (readyClient) => {
|
|
189
|
+
void setupHandlers(readyClient).catch((error) => {
|
|
190
|
+
discordLogger.error(`[GATEWAY] ClientReady handler failed: ${formatErrorWithStack(error)}`);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
189
193
|
}
|
|
190
194
|
discordClient.on(Events.Error, (error) => {
|
|
191
195
|
discordLogger.error('[GATEWAY] Client error:', formatErrorWithStack(error));
|
|
@@ -19,7 +19,7 @@ function isSyntheticTextPart(part) {
|
|
|
19
19
|
return candidate.synthetic === true;
|
|
20
20
|
}
|
|
21
21
|
function parseDiscordOriginMetadata(text) {
|
|
22
|
-
const match = text.match(
|
|
22
|
+
const match = text.match(/<discord-user\s+([^>]+)\s*\/>/);
|
|
23
23
|
if (!match?.[1]) {
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
@@ -42,17 +42,17 @@ function parseDiscordOriginMetadata(text) {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
function getDiscordOriginMetadataFromMessage({ message, }) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
return [];
|
|
48
|
-
}
|
|
49
|
-
if (!isSyntheticTextPart(part)) {
|
|
50
|
-
return [];
|
|
51
|
-
}
|
|
52
|
-
return [part.text || ''];
|
|
45
|
+
const textParts = message.parts.filter((p) => {
|
|
46
|
+
return p.type === 'text';
|
|
53
47
|
});
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
// Synthetic parts first (normal promptAsync path), then non-synthetic
|
|
49
|
+
// (session.command() path where the tag is embedded in arguments text).
|
|
50
|
+
const sorted = [
|
|
51
|
+
...textParts.filter((p) => { return isSyntheticTextPart(p); }),
|
|
52
|
+
...textParts.filter((p) => { return !isSyntheticTextPart(p); }),
|
|
53
|
+
];
|
|
54
|
+
for (const part of sorted) {
|
|
55
|
+
const metadata = parseDiscordOriginMetadata(part.text || '');
|
|
56
56
|
if (metadata) {
|
|
57
57
|
return metadata;
|
|
58
58
|
}
|
|
@@ -2667,12 +2667,18 @@ export class ThreadSessionRuntime {
|
|
|
2667
2667
|
if (input.command) {
|
|
2668
2668
|
const queuedCommand = input.command;
|
|
2669
2669
|
const commandSignal = AbortSignal.timeout(30_000);
|
|
2670
|
+
// session.command() only accepts FilePart in parts, not text parts.
|
|
2671
|
+
// Append <discord-user /> tag to arguments so external sync can
|
|
2672
|
+
// detect this message came from Discord (same tag as promptAsync).
|
|
2673
|
+
const discordTag = input.username
|
|
2674
|
+
? `\n<discord-user name="${input.username}" />`
|
|
2675
|
+
: '';
|
|
2670
2676
|
const commandResponse = await errore.tryAsync(() => {
|
|
2671
2677
|
return getClient().session.command({
|
|
2672
2678
|
sessionID: session.id,
|
|
2673
2679
|
directory: this.sdkDirectory,
|
|
2674
2680
|
command: queuedCommand.name,
|
|
2675
|
-
arguments: queuedCommand.arguments,
|
|
2681
|
+
arguments: queuedCommand.arguments + discordTag,
|
|
2676
2682
|
agent: earlyAgentPreference,
|
|
2677
2683
|
...variantField,
|
|
2678
2684
|
}, { signal: commandSignal });
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "kimaki",
|
|
3
3
|
"module": "index.ts",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.4.
|
|
5
|
+
"version": "0.4.85",
|
|
6
6
|
"repository": "https://github.com/remorses/kimaki",
|
|
7
7
|
"bin": "bin.js",
|
|
8
8
|
"files": [
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"prisma": "7.4.2",
|
|
26
26
|
"tsx": "^4.20.5",
|
|
27
27
|
"discord-digital-twin": "^0.1.0",
|
|
28
|
-
"opencode-cached-provider": "^0.0.1",
|
|
29
28
|
"opencode-deterministic-provider": "^0.0.1",
|
|
29
|
+
"opencode-cached-provider": "^0.0.1",
|
|
30
30
|
"db": "^0.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
package/src/discord-bot.ts
CHANGED
|
@@ -316,7 +316,13 @@ export async function startDiscordBot({
|
|
|
316
316
|
if (discordClient.isReady()) {
|
|
317
317
|
await setupHandlers(discordClient)
|
|
318
318
|
} else {
|
|
319
|
-
discordClient.once(Events.ClientReady,
|
|
319
|
+
discordClient.once(Events.ClientReady, (readyClient) => {
|
|
320
|
+
void setupHandlers(readyClient).catch((error) => {
|
|
321
|
+
discordLogger.error(
|
|
322
|
+
`[GATEWAY] ClientReady handler failed: ${formatErrorWithStack(error)}`,
|
|
323
|
+
)
|
|
324
|
+
})
|
|
325
|
+
})
|
|
320
326
|
}
|
|
321
327
|
|
|
322
328
|
discordClient.on(Events.Error, (error) => {
|
|
@@ -86,7 +86,7 @@ function isSyntheticTextPart(part: Extract<Part, { type: 'text' }>): boolean {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
function parseDiscordOriginMetadata(text: string): DiscordOriginMetadata | null {
|
|
89
|
-
const match = text.match(
|
|
89
|
+
const match = text.match(/<discord-user\s+([^>]+)\s*\/>/)
|
|
90
90
|
if (!match?.[1]) {
|
|
91
91
|
return null
|
|
92
92
|
}
|
|
@@ -117,23 +117,21 @@ function getDiscordOriginMetadataFromMessage({
|
|
|
117
117
|
}: {
|
|
118
118
|
message: SessionMessageLike
|
|
119
119
|
}): DiscordOriginMetadata | null {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
return [] as string[]
|
|
123
|
-
}
|
|
124
|
-
if (!isSyntheticTextPart(part)) {
|
|
125
|
-
return [] as string[]
|
|
126
|
-
}
|
|
127
|
-
return [part.text || '']
|
|
120
|
+
const textParts = message.parts.filter((p): p is Extract<typeof p, { type: 'text' }> => {
|
|
121
|
+
return p.type === 'text'
|
|
128
122
|
})
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
123
|
+
// Synthetic parts first (normal promptAsync path), then non-synthetic
|
|
124
|
+
// (session.command() path where the tag is embedded in arguments text).
|
|
125
|
+
const sorted = [
|
|
126
|
+
...textParts.filter((p) => { return isSyntheticTextPart(p) }),
|
|
127
|
+
...textParts.filter((p) => { return !isSyntheticTextPart(p) }),
|
|
128
|
+
]
|
|
129
|
+
for (const part of sorted) {
|
|
130
|
+
const metadata = parseDiscordOriginMetadata(part.text || '')
|
|
132
131
|
if (metadata) {
|
|
133
132
|
return metadata
|
|
134
133
|
}
|
|
135
134
|
}
|
|
136
|
-
|
|
137
135
|
return null
|
|
138
136
|
}
|
|
139
137
|
|
|
@@ -3510,13 +3510,20 @@ export class ThreadSessionRuntime {
|
|
|
3510
3510
|
if (input.command) {
|
|
3511
3511
|
const queuedCommand = input.command
|
|
3512
3512
|
const commandSignal = AbortSignal.timeout(30_000)
|
|
3513
|
+
// session.command() only accepts FilePart in parts, not text parts.
|
|
3514
|
+
// Append <discord-user /> tag to arguments so external sync can
|
|
3515
|
+
// detect this message came from Discord (same tag as promptAsync).
|
|
3516
|
+
const discordTag = input.username
|
|
3517
|
+
? `\n<discord-user name="${input.username}" />`
|
|
3518
|
+
: ''
|
|
3513
3519
|
const commandResponse = await errore.tryAsync(() => {
|
|
3514
3520
|
return getClient().session.command(
|
|
3515
3521
|
{
|
|
3516
3522
|
sessionID: session.id,
|
|
3523
|
+
|
|
3517
3524
|
directory: this.sdkDirectory,
|
|
3518
3525
|
command: queuedCommand.name,
|
|
3519
|
-
arguments: queuedCommand.arguments,
|
|
3526
|
+
arguments: queuedCommand.arguments + discordTag,
|
|
3520
3527
|
agent: earlyAgentPreference,
|
|
3521
3528
|
...variantField,
|
|
3522
3529
|
},
|