@oyasmi/pipiclaw 0.1.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/CHANGELOG.md +31 -0
- package/README.md +247 -0
- package/dist/agent.d.ts +18 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +938 -0
- package/dist/agent.js.map +1 -0
- package/dist/commands.d.ts +9 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +45 -0
- package/dist/commands.js.map +1 -0
- package/dist/context.d.ts +139 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +432 -0
- package/dist/context.js.map +1 -0
- package/dist/delivery.d.ts +4 -0
- package/dist/delivery.d.ts.map +1 -0
- package/dist/delivery.js +221 -0
- package/dist/delivery.js.map +1 -0
- package/dist/dingtalk.d.ts +109 -0
- package/dist/dingtalk.d.ts.map +1 -0
- package/dist/dingtalk.js +655 -0
- package/dist/dingtalk.js.map +1 -0
- package/dist/events.d.ts +51 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +287 -0
- package/dist/events.js.map +1 -0
- package/dist/log.d.ts +33 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +188 -0
- package/dist/log.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +298 -0
- package/dist/main.js.map +1 -0
- package/dist/paths.d.ts +8 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +10 -0
- package/dist/paths.js.map +1 -0
- package/dist/sandbox.d.ts +34 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +180 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/shell-escape.d.ts +6 -0
- package/dist/shell-escape.d.ts.map +1 -0
- package/dist/shell-escape.js +8 -0
- package/dist/shell-escape.js.map +1 -0
- package/dist/store.d.ts +41 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +110 -0
- package/dist/store.js.map +1 -0
- package/dist/tools/attach.d.ts +14 -0
- package/dist/tools/attach.d.ts.map +1 -0
- package/dist/tools/attach.js +35 -0
- package/dist/tools/attach.js.map +1 -0
- package/dist/tools/bash.d.ts +10 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +78 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit.d.ts +11 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +129 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +15 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/read.d.ts +11 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +132 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/truncate.d.ts +57 -0
- package/dist/tools/truncate.d.ts.map +1 -0
- package/dist/tools/truncate.js +184 -0
- package/dist/tools/truncate.js.map +1 -0
- package/dist/tools/write.d.ts +10 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +31 -0
- package/dist/tools/write.js.map +1 -0
- package/package.json +54 -0
package/dist/context.js
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context management for pipiclaw.
|
|
3
|
+
*
|
|
4
|
+
* Uses two files per channel:
|
|
5
|
+
* - context.jsonl: Structured API messages for LLM context (same format as coding-agent sessions)
|
|
6
|
+
* - log.jsonl: Human-readable channel history for grep (no tool results)
|
|
7
|
+
*
|
|
8
|
+
* This module provides:
|
|
9
|
+
* - syncLogToSessionManager: Syncs messages from log.jsonl to SessionManager
|
|
10
|
+
* - PipiclawSettingsManager: Simple settings for pipiclaw (compaction, retry, model preferences)
|
|
11
|
+
*/
|
|
12
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, writeFileSync } from "fs";
|
|
13
|
+
import { dirname, join } from "path";
|
|
14
|
+
/**
|
|
15
|
+
* Sync user messages from log.jsonl to SessionManager.
|
|
16
|
+
*
|
|
17
|
+
* Uses byte-offset tracking for incremental reads (only processes new content)
|
|
18
|
+
* and timestamp-based dedup for crash safety.
|
|
19
|
+
*
|
|
20
|
+
* This ensures that messages logged while mom wasn't running (channel chatter,
|
|
21
|
+
* messages while busy) are added to the LLM context.
|
|
22
|
+
*
|
|
23
|
+
* @param sessionManager - The SessionManager to sync to
|
|
24
|
+
* @param channelDir - Path to channel directory containing log.jsonl
|
|
25
|
+
* @param excludeTs - Timestamp of current message (will be added via prompt(), not sync)
|
|
26
|
+
* @returns Number of messages synced
|
|
27
|
+
*/
|
|
28
|
+
export function syncLogToSessionManager(sessionManager, channelDir, excludeTs) {
|
|
29
|
+
const logFile = join(channelDir, "log.jsonl");
|
|
30
|
+
const syncOffsetFile = join(channelDir, ".sync-offset");
|
|
31
|
+
if (!existsSync(logFile))
|
|
32
|
+
return 0;
|
|
33
|
+
// Read sync offset (byte position of last processed content)
|
|
34
|
+
let offset = 0;
|
|
35
|
+
if (existsSync(syncOffsetFile)) {
|
|
36
|
+
try {
|
|
37
|
+
offset = Number(readFileSync(syncOffsetFile, "utf-8").trim()) || 0;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
offset = 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Check file size
|
|
44
|
+
const fileStats = statSync(logFile);
|
|
45
|
+
if (fileStats.size < offset) {
|
|
46
|
+
// File was truncated or rotated — reset to beginning
|
|
47
|
+
offset = 0;
|
|
48
|
+
}
|
|
49
|
+
if (fileStats.size === offset) {
|
|
50
|
+
return 0; // No new content
|
|
51
|
+
}
|
|
52
|
+
// Read only new bytes from log.jsonl
|
|
53
|
+
const bytesToRead = fileStats.size - offset;
|
|
54
|
+
const fd = openSync(logFile, "r");
|
|
55
|
+
let newContent;
|
|
56
|
+
try {
|
|
57
|
+
const buffer = Buffer.alloc(bytesToRead);
|
|
58
|
+
readSync(fd, buffer, 0, bytesToRead, offset);
|
|
59
|
+
newContent = buffer.toString("utf-8");
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
closeSync(fd);
|
|
63
|
+
}
|
|
64
|
+
const newLines = newContent.trim().split("\n").filter(Boolean);
|
|
65
|
+
if (newLines.length === 0) {
|
|
66
|
+
writeFileSync(syncOffsetFile, String(fileStats.size));
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
// Build set of existing user message timestamps for crash-safe dedup.
|
|
70
|
+
// This prevents duplicates if the process crashed after appendMessage()
|
|
71
|
+
// but before writing the sync offset.
|
|
72
|
+
const existingTimestamps = new Set();
|
|
73
|
+
for (const entry of sessionManager.getEntries()) {
|
|
74
|
+
if (entry.type === "message") {
|
|
75
|
+
const msgEntry = entry;
|
|
76
|
+
const msg = msgEntry.message;
|
|
77
|
+
if (msg.role === "user" && typeof msg.timestamp === "number") {
|
|
78
|
+
existingTimestamps.add(msg.timestamp);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Process new log lines
|
|
83
|
+
const newMessages = [];
|
|
84
|
+
for (const line of newLines) {
|
|
85
|
+
try {
|
|
86
|
+
const logMsg = JSON.parse(line);
|
|
87
|
+
const ts = logMsg.ts;
|
|
88
|
+
const date = logMsg.date;
|
|
89
|
+
if (!ts || !date)
|
|
90
|
+
continue;
|
|
91
|
+
// Skip the current message being processed (will be added via prompt())
|
|
92
|
+
if (excludeTs && ts === excludeTs)
|
|
93
|
+
continue;
|
|
94
|
+
// Skip bot messages — they're managed by SessionManager
|
|
95
|
+
if (logMsg.isBot)
|
|
96
|
+
continue;
|
|
97
|
+
const msgTime = new Date(date).getTime() || Date.now();
|
|
98
|
+
// Skip if already in context (crash recovery dedup)
|
|
99
|
+
if (existingTimestamps.has(msgTime))
|
|
100
|
+
continue;
|
|
101
|
+
existingTimestamps.add(msgTime); // Track within this batch
|
|
102
|
+
const messageText = `[${logMsg.userName || logMsg.user || "unknown"}]: ${logMsg.text || ""}`;
|
|
103
|
+
newMessages.push({
|
|
104
|
+
timestamp: msgTime,
|
|
105
|
+
message: {
|
|
106
|
+
role: "user",
|
|
107
|
+
content: [{ type: "text", text: messageText }],
|
|
108
|
+
timestamp: msgTime,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Skip malformed lines
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (newMessages.length > 0) {
|
|
117
|
+
newMessages.sort((a, b) => a.timestamp - b.timestamp);
|
|
118
|
+
for (const { message } of newMessages) {
|
|
119
|
+
sessionManager.appendMessage(message);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Update sync offset to current file end
|
|
123
|
+
writeFileSync(syncOffsetFile, String(fileStats.size));
|
|
124
|
+
return newMessages.length;
|
|
125
|
+
}
|
|
126
|
+
const DEFAULT_COMPACTION = {
|
|
127
|
+
enabled: true,
|
|
128
|
+
reserveTokens: 16384,
|
|
129
|
+
keepRecentTokens: 20000,
|
|
130
|
+
};
|
|
131
|
+
const DEFAULT_RETRY = {
|
|
132
|
+
enabled: true,
|
|
133
|
+
maxRetries: 3,
|
|
134
|
+
baseDelayMs: 2000,
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Settings manager for pipiclaw.
|
|
138
|
+
* Stores global settings in the pipiclaw root directory.
|
|
139
|
+
*/
|
|
140
|
+
export class PipiclawSettingsManager {
|
|
141
|
+
settingsPath;
|
|
142
|
+
settings;
|
|
143
|
+
constructor(baseDir) {
|
|
144
|
+
this.settingsPath = join(baseDir, "settings.json");
|
|
145
|
+
this.settings = this.load();
|
|
146
|
+
}
|
|
147
|
+
load() {
|
|
148
|
+
if (!existsSync(this.settingsPath)) {
|
|
149
|
+
return {};
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const content = readFileSync(this.settingsPath, "utf-8");
|
|
153
|
+
return JSON.parse(content);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return {};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
save() {
|
|
160
|
+
try {
|
|
161
|
+
const dir = dirname(this.settingsPath);
|
|
162
|
+
if (!existsSync(dir)) {
|
|
163
|
+
mkdirSync(dir, { recursive: true });
|
|
164
|
+
}
|
|
165
|
+
writeFileSync(this.settingsPath, JSON.stringify(this.settings, null, 2), "utf-8");
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.error(`Warning: Could not save settings file: ${error}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
getCompactionSettings() {
|
|
172
|
+
return {
|
|
173
|
+
...DEFAULT_COMPACTION,
|
|
174
|
+
...this.settings.compaction,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
getCompactionEnabled() {
|
|
178
|
+
return this.settings.compaction?.enabled ?? DEFAULT_COMPACTION.enabled;
|
|
179
|
+
}
|
|
180
|
+
setCompactionEnabled(enabled) {
|
|
181
|
+
this.settings.compaction = { ...this.settings.compaction, enabled };
|
|
182
|
+
this.save();
|
|
183
|
+
}
|
|
184
|
+
getRetrySettings() {
|
|
185
|
+
return {
|
|
186
|
+
...DEFAULT_RETRY,
|
|
187
|
+
...this.settings.retry,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
getRetryEnabled() {
|
|
191
|
+
return this.settings.retry?.enabled ?? DEFAULT_RETRY.enabled;
|
|
192
|
+
}
|
|
193
|
+
setRetryEnabled(enabled) {
|
|
194
|
+
this.settings.retry = { ...this.settings.retry, enabled };
|
|
195
|
+
this.save();
|
|
196
|
+
}
|
|
197
|
+
getDefaultModel() {
|
|
198
|
+
return this.settings.defaultModel;
|
|
199
|
+
}
|
|
200
|
+
getDefaultProvider() {
|
|
201
|
+
return this.settings.defaultProvider;
|
|
202
|
+
}
|
|
203
|
+
setDefaultModelAndProvider(provider, modelId) {
|
|
204
|
+
this.settings.defaultProvider = provider;
|
|
205
|
+
this.settings.defaultModel = modelId;
|
|
206
|
+
this.save();
|
|
207
|
+
}
|
|
208
|
+
getDefaultThinkingLevel() {
|
|
209
|
+
return this.settings.defaultThinkingLevel || "off";
|
|
210
|
+
}
|
|
211
|
+
setDefaultThinkingLevel(level) {
|
|
212
|
+
this.settings.defaultThinkingLevel = level;
|
|
213
|
+
this.save();
|
|
214
|
+
}
|
|
215
|
+
// Compatibility methods for AgentSession
|
|
216
|
+
getSteeringMode() {
|
|
217
|
+
return "one-at-a-time";
|
|
218
|
+
}
|
|
219
|
+
setSteeringMode(_mode) {
|
|
220
|
+
// No-op
|
|
221
|
+
}
|
|
222
|
+
getFollowUpMode() {
|
|
223
|
+
return "one-at-a-time";
|
|
224
|
+
}
|
|
225
|
+
setFollowUpMode(_mode) {
|
|
226
|
+
// No-op
|
|
227
|
+
}
|
|
228
|
+
getHookPaths() {
|
|
229
|
+
return [];
|
|
230
|
+
}
|
|
231
|
+
getHookTimeout() {
|
|
232
|
+
return 30000;
|
|
233
|
+
}
|
|
234
|
+
// Image settings
|
|
235
|
+
getImageAutoResize() {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
setImageAutoResize(_enabled) {
|
|
239
|
+
// No-op
|
|
240
|
+
}
|
|
241
|
+
getBlockImages() {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
setBlockImages(_blocked) {
|
|
245
|
+
// No-op
|
|
246
|
+
}
|
|
247
|
+
getShowImages() {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
setShowImages(_show) {
|
|
251
|
+
// No-op
|
|
252
|
+
}
|
|
253
|
+
// Compaction details
|
|
254
|
+
getCompactionReserveTokens() {
|
|
255
|
+
return DEFAULT_COMPACTION.reserveTokens;
|
|
256
|
+
}
|
|
257
|
+
getCompactionKeepRecentTokens() {
|
|
258
|
+
return DEFAULT_COMPACTION.keepRecentTokens;
|
|
259
|
+
}
|
|
260
|
+
getBranchSummarySettings() {
|
|
261
|
+
return { reserveTokens: 16384 };
|
|
262
|
+
}
|
|
263
|
+
// Thinking
|
|
264
|
+
getHideThinkingBlock() {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
setHideThinkingBlock(_hide) {
|
|
268
|
+
// No-op
|
|
269
|
+
}
|
|
270
|
+
getThinkingBudgets() {
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
// Shell
|
|
274
|
+
getShellPath() {
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
setShellPath(_path) {
|
|
278
|
+
// No-op
|
|
279
|
+
}
|
|
280
|
+
getShellCommandPrefix() {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
setShellCommandPrefix(_prefix) {
|
|
284
|
+
// No-op
|
|
285
|
+
}
|
|
286
|
+
// Misc settings stubs
|
|
287
|
+
getQuietStartup() {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
setQuietStartup(_quiet) {
|
|
291
|
+
// No-op
|
|
292
|
+
}
|
|
293
|
+
getCollapseChangelog() {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
setCollapseChangelog(_collapse) {
|
|
297
|
+
// No-op
|
|
298
|
+
}
|
|
299
|
+
getTransport() {
|
|
300
|
+
return "stdio";
|
|
301
|
+
}
|
|
302
|
+
setTransport(_transport) {
|
|
303
|
+
// No-op
|
|
304
|
+
}
|
|
305
|
+
getTheme() {
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
setTheme(_theme) {
|
|
309
|
+
// No-op
|
|
310
|
+
}
|
|
311
|
+
getPackages() {
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
setPackages(_packages) {
|
|
315
|
+
// No-op
|
|
316
|
+
}
|
|
317
|
+
setProjectPackages(_packages) {
|
|
318
|
+
// No-op
|
|
319
|
+
}
|
|
320
|
+
getExtensionPaths() {
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
323
|
+
setExtensionPaths(_paths) {
|
|
324
|
+
// No-op
|
|
325
|
+
}
|
|
326
|
+
setProjectExtensionPaths(_paths) {
|
|
327
|
+
// No-op
|
|
328
|
+
}
|
|
329
|
+
getSkillPaths() {
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
setSkillPaths(_paths) {
|
|
333
|
+
// No-op
|
|
334
|
+
}
|
|
335
|
+
setProjectSkillPaths(_paths) {
|
|
336
|
+
// No-op
|
|
337
|
+
}
|
|
338
|
+
getPromptTemplatePaths() {
|
|
339
|
+
return [];
|
|
340
|
+
}
|
|
341
|
+
setPromptTemplatePaths(_paths) {
|
|
342
|
+
// No-op
|
|
343
|
+
}
|
|
344
|
+
setProjectPromptTemplatePaths(_paths) {
|
|
345
|
+
// No-op
|
|
346
|
+
}
|
|
347
|
+
getThemePaths() {
|
|
348
|
+
return [];
|
|
349
|
+
}
|
|
350
|
+
setThemePaths(_paths) {
|
|
351
|
+
// No-op
|
|
352
|
+
}
|
|
353
|
+
setProjectThemePaths(_paths) {
|
|
354
|
+
// No-op
|
|
355
|
+
}
|
|
356
|
+
getEnableSkillCommands() {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
setEnableSkillCommands(_enabled) {
|
|
360
|
+
// No-op
|
|
361
|
+
}
|
|
362
|
+
getEnabledModels() {
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
setEnabledModels(_patterns) {
|
|
366
|
+
// No-op
|
|
367
|
+
}
|
|
368
|
+
getDoubleEscapeAction() {
|
|
369
|
+
return "none";
|
|
370
|
+
}
|
|
371
|
+
setDoubleEscapeAction(_action) {
|
|
372
|
+
// No-op
|
|
373
|
+
}
|
|
374
|
+
getShowHardwareCursor() {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
setShowHardwareCursor(_enabled) {
|
|
378
|
+
// No-op
|
|
379
|
+
}
|
|
380
|
+
getClearOnShrink() {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
setClearOnShrink(_enabled) {
|
|
384
|
+
// No-op
|
|
385
|
+
}
|
|
386
|
+
getEditorPaddingX() {
|
|
387
|
+
return 0;
|
|
388
|
+
}
|
|
389
|
+
setEditorPaddingX(_padding) {
|
|
390
|
+
// No-op
|
|
391
|
+
}
|
|
392
|
+
getAutocompleteMaxVisible() {
|
|
393
|
+
return 10;
|
|
394
|
+
}
|
|
395
|
+
setAutocompleteMaxVisible(_maxVisible) {
|
|
396
|
+
// No-op
|
|
397
|
+
}
|
|
398
|
+
getCodeBlockIndent() {
|
|
399
|
+
return " ";
|
|
400
|
+
}
|
|
401
|
+
getLastChangelogVersion() {
|
|
402
|
+
return undefined;
|
|
403
|
+
}
|
|
404
|
+
setLastChangelogVersion(_version) {
|
|
405
|
+
// No-op
|
|
406
|
+
}
|
|
407
|
+
setDefaultProvider(_provider) {
|
|
408
|
+
// No-op
|
|
409
|
+
}
|
|
410
|
+
setDefaultModel(_modelId) {
|
|
411
|
+
// No-op
|
|
412
|
+
}
|
|
413
|
+
getGlobalSettings() {
|
|
414
|
+
return {};
|
|
415
|
+
}
|
|
416
|
+
getProjectSettings() {
|
|
417
|
+
return {};
|
|
418
|
+
}
|
|
419
|
+
reload() {
|
|
420
|
+
this.settings = this.load();
|
|
421
|
+
}
|
|
422
|
+
applyOverrides(_overrides) {
|
|
423
|
+
// No-op
|
|
424
|
+
}
|
|
425
|
+
flush() {
|
|
426
|
+
return Promise.resolve();
|
|
427
|
+
}
|
|
428
|
+
drainErrors() {
|
|
429
|
+
return [];
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Context management for pipiclaw.\n *\n * Uses two files per channel:\n * - context.jsonl: Structured API messages for LLM context (same format as coding-agent sessions)\n * - log.jsonl: Human-readable channel history for grep (no tool results)\n *\n * This module provides:\n * - syncLogToSessionManager: Syncs messages from log.jsonl to SessionManager\n * - PipiclawSettingsManager: Simple settings for pipiclaw (compaction, retry, model preferences)\n */\n\nimport type { UserMessage } from \"@mariozechner/pi-ai\";\nimport type { SessionManager, SessionMessageEntry } from \"@mariozechner/pi-coding-agent\";\nimport { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\n\n// ============================================================================\n// Sync log.jsonl to SessionManager\n// ============================================================================\n\ninterface LogMessage {\n\tdate?: string;\n\tts?: string;\n\tuser?: string;\n\tuserName?: string;\n\ttext?: string;\n\tisBot?: boolean;\n}\n\n/**\n * Sync user messages from log.jsonl to SessionManager.\n *\n * Uses byte-offset tracking for incremental reads (only processes new content)\n * and timestamp-based dedup for crash safety.\n *\n * This ensures that messages logged while mom wasn't running (channel chatter,\n * messages while busy) are added to the LLM context.\n *\n * @param sessionManager - The SessionManager to sync to\n * @param channelDir - Path to channel directory containing log.jsonl\n * @param excludeTs - Timestamp of current message (will be added via prompt(), not sync)\n * @returns Number of messages synced\n */\nexport function syncLogToSessionManager(\n\tsessionManager: SessionManager,\n\tchannelDir: string,\n\texcludeTs?: string,\n): number {\n\tconst logFile = join(channelDir, \"log.jsonl\");\n\tconst syncOffsetFile = join(channelDir, \".sync-offset\");\n\n\tif (!existsSync(logFile)) return 0;\n\n\t// Read sync offset (byte position of last processed content)\n\tlet offset = 0;\n\tif (existsSync(syncOffsetFile)) {\n\t\ttry {\n\t\t\toffset = Number(readFileSync(syncOffsetFile, \"utf-8\").trim()) || 0;\n\t\t} catch {\n\t\t\toffset = 0;\n\t\t}\n\t}\n\n\t// Check file size\n\tconst fileStats = statSync(logFile);\n\tif (fileStats.size < offset) {\n\t\t// File was truncated or rotated — reset to beginning\n\t\toffset = 0;\n\t}\n\tif (fileStats.size === offset) {\n\t\treturn 0; // No new content\n\t}\n\n\t// Read only new bytes from log.jsonl\n\tconst bytesToRead = fileStats.size - offset;\n\tconst fd = openSync(logFile, \"r\");\n\tlet newContent: string;\n\ttry {\n\t\tconst buffer = Buffer.alloc(bytesToRead);\n\t\treadSync(fd, buffer, 0, bytesToRead, offset);\n\t\tnewContent = buffer.toString(\"utf-8\");\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\n\tconst newLines = newContent.trim().split(\"\\n\").filter(Boolean);\n\tif (newLines.length === 0) {\n\t\twriteFileSync(syncOffsetFile, String(fileStats.size));\n\t\treturn 0;\n\t}\n\n\t// Build set of existing user message timestamps for crash-safe dedup.\n\t// This prevents duplicates if the process crashed after appendMessage()\n\t// but before writing the sync offset.\n\tconst existingTimestamps = new Set<number>();\n\tfor (const entry of sessionManager.getEntries()) {\n\t\tif (entry.type === \"message\") {\n\t\t\tconst msgEntry = entry as SessionMessageEntry;\n\t\t\tconst msg = msgEntry.message as { role?: string; timestamp?: number };\n\t\t\tif (msg.role === \"user\" && typeof msg.timestamp === \"number\") {\n\t\t\t\texistingTimestamps.add(msg.timestamp);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Process new log lines\n\tconst newMessages: Array<{ timestamp: number; message: UserMessage }> = [];\n\n\tfor (const line of newLines) {\n\t\ttry {\n\t\t\tconst logMsg: LogMessage = JSON.parse(line);\n\n\t\t\tconst ts = logMsg.ts;\n\t\t\tconst date = logMsg.date;\n\t\t\tif (!ts || !date) continue;\n\n\t\t\t// Skip the current message being processed (will be added via prompt())\n\t\t\tif (excludeTs && ts === excludeTs) continue;\n\n\t\t\t// Skip bot messages — they're managed by SessionManager\n\t\t\tif (logMsg.isBot) continue;\n\n\t\t\tconst msgTime = new Date(date).getTime() || Date.now();\n\n\t\t\t// Skip if already in context (crash recovery dedup)\n\t\t\tif (existingTimestamps.has(msgTime)) continue;\n\t\t\texistingTimestamps.add(msgTime); // Track within this batch\n\n\t\t\tconst messageText = `[${logMsg.userName || logMsg.user || \"unknown\"}]: ${logMsg.text || \"\"}`;\n\t\t\tnewMessages.push({\n\t\t\t\ttimestamp: msgTime,\n\t\t\t\tmessage: {\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent: [{ type: \"text\", text: messageText }],\n\t\t\t\t\ttimestamp: msgTime,\n\t\t\t\t},\n\t\t\t});\n\t\t} catch {\n\t\t\t// Skip malformed lines\n\t\t}\n\t}\n\n\tif (newMessages.length > 0) {\n\t\tnewMessages.sort((a, b) => a.timestamp - b.timestamp);\n\t\tfor (const { message } of newMessages) {\n\t\t\tsessionManager.appendMessage(message);\n\t\t}\n\t}\n\n\t// Update sync offset to current file end\n\twriteFileSync(syncOffsetFile, String(fileStats.size));\n\n\treturn newMessages.length;\n}\n\n// ============================================================================\n// PipiclawSettingsManager - Simple settings for pipiclaw\n// ============================================================================\n\nexport interface PipiclawCompactionSettings {\n\tenabled: boolean;\n\treserveTokens: number;\n\tkeepRecentTokens: number;\n}\n\nexport interface PipiclawRetrySettings {\n\tenabled: boolean;\n\tmaxRetries: number;\n\tbaseDelayMs: number;\n}\n\nexport interface PipiclawSettings {\n\tdefaultProvider?: string;\n\tdefaultModel?: string;\n\tdefaultThinkingLevel?: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\";\n\tcompaction?: Partial<PipiclawCompactionSettings>;\n\tretry?: Partial<PipiclawRetrySettings>;\n}\n\nconst DEFAULT_COMPACTION: PipiclawCompactionSettings = {\n\tenabled: true,\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n};\n\nconst DEFAULT_RETRY: PipiclawRetrySettings = {\n\tenabled: true,\n\tmaxRetries: 3,\n\tbaseDelayMs: 2000,\n};\n\n/**\n * Settings manager for pipiclaw.\n * Stores global settings in the pipiclaw root directory.\n */\nexport class PipiclawSettingsManager {\n\tprivate settingsPath: string;\n\tprivate settings: PipiclawSettings;\n\n\tconstructor(baseDir: string) {\n\t\tthis.settingsPath = join(baseDir, \"settings.json\");\n\t\tthis.settings = this.load();\n\t}\n\n\tprivate load(): PipiclawSettings {\n\t\tif (!existsSync(this.settingsPath)) {\n\t\t\treturn {};\n\t\t}\n\n\t\ttry {\n\t\t\tconst content = readFileSync(this.settingsPath, \"utf-8\");\n\t\t\treturn JSON.parse(content);\n\t\t} catch {\n\t\t\treturn {};\n\t\t}\n\t}\n\n\tprivate save(): void {\n\t\ttry {\n\t\t\tconst dir = dirname(this.settingsPath);\n\t\t\tif (!existsSync(dir)) {\n\t\t\t\tmkdirSync(dir, { recursive: true });\n\t\t\t}\n\t\t\twriteFileSync(this.settingsPath, JSON.stringify(this.settings, null, 2), \"utf-8\");\n\t\t} catch (error) {\n\t\t\tconsole.error(`Warning: Could not save settings file: ${error}`);\n\t\t}\n\t}\n\n\tgetCompactionSettings(): PipiclawCompactionSettings {\n\t\treturn {\n\t\t\t...DEFAULT_COMPACTION,\n\t\t\t...this.settings.compaction,\n\t\t};\n\t}\n\n\tgetCompactionEnabled(): boolean {\n\t\treturn this.settings.compaction?.enabled ?? DEFAULT_COMPACTION.enabled;\n\t}\n\n\tsetCompactionEnabled(enabled: boolean): void {\n\t\tthis.settings.compaction = { ...this.settings.compaction, enabled };\n\t\tthis.save();\n\t}\n\n\tgetRetrySettings(): PipiclawRetrySettings {\n\t\treturn {\n\t\t\t...DEFAULT_RETRY,\n\t\t\t...this.settings.retry,\n\t\t};\n\t}\n\n\tgetRetryEnabled(): boolean {\n\t\treturn this.settings.retry?.enabled ?? DEFAULT_RETRY.enabled;\n\t}\n\n\tsetRetryEnabled(enabled: boolean): void {\n\t\tthis.settings.retry = { ...this.settings.retry, enabled };\n\t\tthis.save();\n\t}\n\n\tgetDefaultModel(): string | undefined {\n\t\treturn this.settings.defaultModel;\n\t}\n\n\tgetDefaultProvider(): string | undefined {\n\t\treturn this.settings.defaultProvider;\n\t}\n\n\tsetDefaultModelAndProvider(provider: string, modelId: string): void {\n\t\tthis.settings.defaultProvider = provider;\n\t\tthis.settings.defaultModel = modelId;\n\t\tthis.save();\n\t}\n\n\tgetDefaultThinkingLevel(): string {\n\t\treturn this.settings.defaultThinkingLevel || \"off\";\n\t}\n\n\tsetDefaultThinkingLevel(level: string): void {\n\t\tthis.settings.defaultThinkingLevel = level as PipiclawSettings[\"defaultThinkingLevel\"];\n\t\tthis.save();\n\t}\n\n\t// Compatibility methods for AgentSession\n\tgetSteeringMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn \"one-at-a-time\";\n\t}\n\n\tsetSteeringMode(_mode: \"all\" | \"one-at-a-time\"): void {\n\t\t// No-op\n\t}\n\n\tgetFollowUpMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn \"one-at-a-time\";\n\t}\n\n\tsetFollowUpMode(_mode: \"all\" | \"one-at-a-time\"): void {\n\t\t// No-op\n\t}\n\n\tgetHookPaths(): string[] {\n\t\treturn [];\n\t}\n\n\tgetHookTimeout(): number {\n\t\treturn 30000;\n\t}\n\n\t// Image settings\n\tgetImageAutoResize(): boolean {\n\t\treturn false;\n\t}\n\n\tsetImageAutoResize(_enabled: boolean): void {\n\t\t// No-op\n\t}\n\n\tgetBlockImages(): boolean {\n\t\treturn false;\n\t}\n\n\tsetBlockImages(_blocked: boolean): void {\n\t\t// No-op\n\t}\n\n\tgetShowImages(): boolean {\n\t\treturn false;\n\t}\n\n\tsetShowImages(_show: boolean): void {\n\t\t// No-op\n\t}\n\n\t// Compaction details\n\tgetCompactionReserveTokens(): number {\n\t\treturn DEFAULT_COMPACTION.reserveTokens;\n\t}\n\n\tgetCompactionKeepRecentTokens(): number {\n\t\treturn DEFAULT_COMPACTION.keepRecentTokens;\n\t}\n\n\tgetBranchSummarySettings(): { reserveTokens: number } {\n\t\treturn { reserveTokens: 16384 };\n\t}\n\n\t// Thinking\n\tgetHideThinkingBlock(): boolean {\n\t\treturn false;\n\t}\n\n\tsetHideThinkingBlock(_hide: boolean): void {\n\t\t// No-op\n\t}\n\n\tgetThinkingBudgets(): undefined {\n\t\treturn undefined;\n\t}\n\n\t// Shell\n\tgetShellPath(): string | undefined {\n\t\treturn undefined;\n\t}\n\n\tsetShellPath(_path: string | undefined): void {\n\t\t// No-op\n\t}\n\n\tgetShellCommandPrefix(): string | undefined {\n\t\treturn undefined;\n\t}\n\n\tsetShellCommandPrefix(_prefix: string | undefined): void {\n\t\t// No-op\n\t}\n\n\t// Misc settings stubs\n\tgetQuietStartup(): boolean {\n\t\treturn true;\n\t}\n\n\tsetQuietStartup(_quiet: boolean): void {\n\t\t// No-op\n\t}\n\n\tgetCollapseChangelog(): boolean {\n\t\treturn true;\n\t}\n\n\tsetCollapseChangelog(_collapse: boolean): void {\n\t\t// No-op\n\t}\n\n\tgetTransport(): string {\n\t\treturn \"stdio\";\n\t}\n\n\tsetTransport(_transport: string): void {\n\t\t// No-op\n\t}\n\n\tgetTheme(): string | undefined {\n\t\treturn undefined;\n\t}\n\n\tsetTheme(_theme: string): void {\n\t\t// No-op\n\t}\n\n\tgetPackages(): unknown[] {\n\t\treturn [];\n\t}\n\n\tsetPackages(_packages: unknown[]): void {\n\t\t// No-op\n\t}\n\n\tsetProjectPackages(_packages: unknown[]): void {\n\t\t// No-op\n\t}\n\n\tgetExtensionPaths(): string[] {\n\t\treturn [];\n\t}\n\n\tsetExtensionPaths(_paths: string[]): void {\n\t\t// No-op\n\t}\n\n\tsetProjectExtensionPaths(_paths: string[]): void {\n\t\t// No-op\n\t}\n\n\tgetSkillPaths(): string[] {\n\t\treturn [];\n\t}\n\n\tsetSkillPaths(_paths: string[]): void {\n\t\t// No-op\n\t}\n\n\tsetProjectSkillPaths(_paths: string[]): void {\n\t\t// No-op\n\t}\n\n\tgetPromptTemplatePaths(): string[] {\n\t\treturn [];\n\t}\n\n\tsetPromptTemplatePaths(_paths: string[]): void {\n\t\t// No-op\n\t}\n\n\tsetProjectPromptTemplatePaths(_paths: string[]): void {\n\t\t// No-op\n\t}\n\n\tgetThemePaths(): string[] {\n\t\treturn [];\n\t}\n\n\tsetThemePaths(_paths: string[]): void {\n\t\t// No-op\n\t}\n\n\tsetProjectThemePaths(_paths: string[]): void {\n\t\t// No-op\n\t}\n\n\tgetEnableSkillCommands(): boolean {\n\t\treturn false;\n\t}\n\n\tsetEnableSkillCommands(_enabled: boolean): void {\n\t\t// No-op\n\t}\n\n\tgetEnabledModels(): string[] | undefined {\n\t\treturn undefined;\n\t}\n\n\tsetEnabledModels(_patterns: string[] | undefined): void {\n\t\t// No-op\n\t}\n\n\tgetDoubleEscapeAction(): \"none\" {\n\t\treturn \"none\";\n\t}\n\n\tsetDoubleEscapeAction(_action: string): void {\n\t\t// No-op\n\t}\n\n\tgetShowHardwareCursor(): boolean {\n\t\treturn false;\n\t}\n\n\tsetShowHardwareCursor(_enabled: boolean): void {\n\t\t// No-op\n\t}\n\n\tgetClearOnShrink(): boolean {\n\t\treturn false;\n\t}\n\n\tsetClearOnShrink(_enabled: boolean): void {\n\t\t// No-op\n\t}\n\n\tgetEditorPaddingX(): number {\n\t\treturn 0;\n\t}\n\n\tsetEditorPaddingX(_padding: number): void {\n\t\t// No-op\n\t}\n\n\tgetAutocompleteMaxVisible(): number {\n\t\treturn 10;\n\t}\n\n\tsetAutocompleteMaxVisible(_maxVisible: number): void {\n\t\t// No-op\n\t}\n\n\tgetCodeBlockIndent(): string {\n\t\treturn \" \";\n\t}\n\n\tgetLastChangelogVersion(): string | undefined {\n\t\treturn undefined;\n\t}\n\n\tsetLastChangelogVersion(_version: string): void {\n\t\t// No-op\n\t}\n\n\tsetDefaultProvider(_provider: string): void {\n\t\t// No-op\n\t}\n\n\tsetDefaultModel(_modelId: string): void {\n\t\t// No-op\n\t}\n\n\tgetGlobalSettings(): object {\n\t\treturn {};\n\t}\n\n\tgetProjectSettings(): object {\n\t\treturn {};\n\t}\n\n\treload(): void {\n\t\tthis.settings = this.load();\n\t}\n\n\tapplyOverrides(_overrides: Partial<PipiclawSettings>): void {\n\t\t// No-op\n\t}\n\n\tflush(): Promise<void> {\n\t\treturn Promise.resolve();\n\t}\n\n\tdrainErrors(): unknown[] {\n\t\treturn [];\n\t}\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DingTalkBot, DingTalkContext, DingTalkEvent } from "./dingtalk.js";
|
|
2
|
+
import type { ChannelStore } from "./store.js";
|
|
3
|
+
export declare function createDingTalkContext(event: DingTalkEvent, bot: DingTalkBot, store: ChannelStore): DingTalkContext;
|
|
4
|
+
//# sourceMappingURL=delivery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAiP/C,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,GAAG,eAAe,CAElH","sourcesContent":["import type { DingTalkBot, DingTalkContext, DingTalkEvent } from \"./dingtalk.js\";\nimport * as log from \"./log.js\";\nimport type { ChannelStore } from \"./store.js\";\n\nconst MIN_UPDATE_INTERVAL_MS = 800;\n\ntype DeliveryMode = \"progress\" | \"finalize-existing\" | \"finalize-with-fallback\" | \"silent\";\n\nclass ChannelDeliveryController {\n\tprivate progressText = \"\";\n\tprivate mode: DeliveryMode = \"progress\";\n\tprivate desiredRevision = 0;\n\tprivate appliedRevision = 0;\n\tprivate running = false;\n\tprivate closed = false;\n\tprivate finalResponseDelivered = false;\n\tprivate progressWindowStartedAt = 0;\n\tprivate lastDeliveredAt = 0;\n\tprivate timer: NodeJS.Timeout | null = null;\n\tprivate flushWaiters: Array<() => void> = [];\n\n\tconstructor(\n\t\tprivate event: DingTalkEvent,\n\t\tprivate bot: DingTalkBot,\n\t\tprivate store: ChannelStore,\n\t) {}\n\n\tbuildContext(): DingTalkContext {\n\t\treturn {\n\t\t\tmessage: {\n\t\t\t\ttext: this.event.text,\n\t\t\t\trawText: this.event.text,\n\t\t\t\tuser: this.event.user,\n\t\t\t\tuserName: this.event.userName,\n\t\t\t\tchannel: this.event.channelId,\n\t\t\t\tts: this.event.ts,\n\t\t\t},\n\t\t\tchannelName: this.event.channelId,\n\t\t\trespond: async (text: string, shouldLog = true) => this.appendProgress(text, shouldLog),\n\t\t\trespondPlain: async (text: string, shouldLog = true) => this.sendFinal(text, shouldLog),\n\t\t\treplaceMessage: async (text: string) => this.replaceWithFinal(text),\n\t\t\trespondInThread: async (text: string) => {\n\t\t\t\tlog.logInfo(`[thread] ${text.substring(0, 200)}`);\n\t\t\t},\n\t\t\tsetTyping: async (_isTyping: boolean) => {},\n\t\t\tsetWorking: async (_working: boolean) => {},\n\t\t\tdeleteMessage: async () => this.silence(),\n\t\t\tflush: async () => this.flush(),\n\t\t\tclose: async () => this.close(),\n\t\t};\n\t}\n\n\tprivate async appendProgress(text: string, shouldLog: boolean): Promise<void> {\n\t\tif (this.closed || this.finalResponseDelivered || !text.trim()) return;\n\n\t\tthis.progressText = this.progressText ? `${this.progressText}\\n\\n${text}` : text;\n\t\tif (this.progressWindowStartedAt === 0) {\n\t\t\tthis.progressWindowStartedAt = Date.now();\n\t\t}\n\t\tif (shouldLog) {\n\t\t\tawait this.store.logBotResponse(this.event.channelId, text, Date.now().toString());\n\t\t}\n\n\t\tthis.mode = \"progress\";\n\t\tthis.bumpRevision(false);\n\t}\n\n\tprivate async sendFinal(text: string, shouldLog: boolean): Promise<boolean> {\n\t\tif (this.closed || this.finalResponseDelivered) return this.finalResponseDelivered;\n\n\t\tif (shouldLog) {\n\t\t\tawait this.store.logBotResponse(this.event.channelId, text, Date.now().toString());\n\t\t}\n\n\t\tconst delivered = await this.bot.sendPlain(this.event.channelId, text);\n\t\tif (!delivered) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.finalResponseDelivered = true;\n\t\tthis.mode = \"finalize-existing\";\n\t\tthis.bumpRevision(true);\n\t\treturn true;\n\t}\n\n\tprivate async replaceWithFinal(text: string): Promise<void> {\n\t\tif (this.closed || this.finalResponseDelivered) return;\n\n\t\tthis.progressText = text;\n\t\tthis.mode = \"finalize-with-fallback\";\n\t\tthis.bumpRevision(true);\n\t}\n\n\tprivate async silence(): Promise<void> {\n\t\tif (this.closed) return;\n\n\t\tthis.finalResponseDelivered = true;\n\t\tthis.mode = \"silent\";\n\t\tthis.bumpRevision(true);\n\t}\n\n\tprivate bumpRevision(forceImmediate: boolean): void {\n\t\tthis.desiredRevision++;\n\t\tthis.schedule(forceImmediate);\n\t}\n\n\tprivate schedule(forceImmediate: boolean): void {\n\t\tif (this.running) return;\n\n\t\tif (this.timer) {\n\t\t\tclearTimeout(this.timer);\n\t\t\tthis.timer = null;\n\t\t}\n\n\t\tconst delay =\n\t\t\tforceImmediate || this.mode !== \"progress\"\n\t\t\t\t? 0\n\t\t\t\t: Math.max(\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tMIN_UPDATE_INTERVAL_MS -\n\t\t\t\t\t\t\t(Date.now() - (this.lastDeliveredAt > 0 ? this.lastDeliveredAt : this.progressWindowStartedAt)),\n\t\t\t\t\t);\n\n\t\tif (delay === 0) {\n\t\t\tvoid this.runSyncLoop();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.timer = setTimeout(() => {\n\t\t\tthis.timer = null;\n\t\t\tvoid this.runSyncLoop();\n\t\t}, delay);\n\t}\n\n\tprivate async runSyncLoop(): Promise<void> {\n\t\tif (this.running) return;\n\t\tthis.running = true;\n\n\t\ttry {\n\t\t\twhile (this.appliedRevision < this.desiredRevision) {\n\t\t\t\tconst mode = this.mode;\n\t\t\t\tconst throttleBaseAt = this.lastDeliveredAt > 0 ? this.lastDeliveredAt : this.progressWindowStartedAt;\n\t\t\t\tif (mode === \"progress\" && throttleBaseAt > 0) {\n\t\t\t\t\tconst remaining = MIN_UPDATE_INTERVAL_MS - (Date.now() - throttleBaseAt);\n\t\t\t\t\tif (remaining > 0) {\n\t\t\t\t\t\tthis.timer = setTimeout(() => {\n\t\t\t\t\t\t\tthis.timer = null;\n\t\t\t\t\t\t\tvoid this.runSyncLoop();\n\t\t\t\t\t\t}, remaining);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst revision = this.desiredRevision;\n\t\t\t\tconst content = this.progressText.trim();\n\t\t\t\tlet touchedRemote = false;\n\n\t\t\t\ttry {\n\t\t\t\t\tif (mode === \"progress\") {\n\t\t\t\t\t\tif (content) {\n\t\t\t\t\t\t\ttouchedRemote = await this.bot.streamToCard(this.event.channelId, this.progressText);\n\t\t\t\t\t\t\tif (!touchedRemote) {\n\t\t\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (mode === \"finalize-existing\") {\n\t\t\t\t\t\tif (content) {\n\t\t\t\t\t\t\ttouchedRemote = await this.bot.finalizeExistingCard(this.event.channelId, this.progressText);\n\t\t\t\t\t\t\tif (!touchedRemote) {\n\t\t\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (mode === \"finalize-with-fallback\") {\n\t\t\t\t\t\tif (content) {\n\t\t\t\t\t\t\ttouchedRemote = await this.bot.finalizeCard(this.event.channelId, this.progressText);\n\t\t\t\t\t\t\tif (!touchedRemote) {\n\t\t\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (mode === \"silent\") {\n\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlog.logWarning(\n\t\t\t\t\t\t`[${this.event.channelId}] Delivery sync failed`,\n\t\t\t\t\t\terr instanceof Error ? err.message : String(err),\n\t\t\t\t\t);\n\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t}\n\n\t\t\t\tif (touchedRemote) {\n\t\t\t\t\tthis.lastDeliveredAt = Date.now();\n\t\t\t\t}\n\t\t\t\tif (mode !== \"progress\" || touchedRemote) {\n\t\t\t\t\tthis.progressWindowStartedAt = 0;\n\t\t\t\t}\n\t\t\t\tthis.appliedRevision = revision;\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.running = false;\n\t\t\tthis.resolveFlushWaiters();\n\n\t\t\tif (this.appliedRevision < this.desiredRevision && !this.timer) {\n\t\t\t\tthis.schedule(false);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate isSettled(): boolean {\n\t\treturn !this.running && !this.timer && this.appliedRevision >= this.desiredRevision;\n\t}\n\n\tprivate resolveFlushWaiters(): void {\n\t\tif (!this.isSettled()) return;\n\t\tconst waiters = this.flushWaiters;\n\t\tthis.flushWaiters = [];\n\t\tfor (const resolve of waiters) {\n\t\t\tresolve();\n\t\t}\n\t}\n\n\tprivate async flush(): Promise<void> {\n\t\tif (this.isSettled()) return;\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tthis.flushWaiters.push(resolve);\n\t\t});\n\t}\n\n\tprivate async close(): Promise<void> {\n\t\tif (this.closed) {\n\t\t\tawait this.flush();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.closed = true;\n\t\tawait this.flush();\n\t}\n}\n\nexport function createDingTalkContext(event: DingTalkEvent, bot: DingTalkBot, store: ChannelStore): DingTalkContext {\n\treturn new ChannelDeliveryController(event, bot, store).buildContext();\n}\n"]}
|