@downcity/plugins 1.0.60 → 1.0.61
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/bin/BuiltinPlugins.d.ts +15 -0
- package/bin/BuiltinPlugins.d.ts.map +1 -1
- package/bin/BuiltinPlugins.js +7 -1
- package/bin/BuiltinPlugins.js.map +1 -1
- package/bin/index.d.ts +6 -0
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +3 -0
- package/bin/index.js.map +1 -1
- package/bin/memory/Action.d.ts +15 -10
- package/bin/memory/Action.d.ts.map +1 -1
- package/bin/memory/Action.js +233 -16
- package/bin/memory/Action.js.map +1 -1
- package/bin/memory/MemoryPlugin.d.ts +10 -4
- package/bin/memory/MemoryPlugin.d.ts.map +1 -1
- package/bin/memory/MemoryPlugin.js +79 -37
- package/bin/memory/MemoryPlugin.js.map +1 -1
- package/bin/memory/runtime/Search.d.ts +1 -1
- package/bin/memory/runtime/Search.d.ts.map +1 -1
- package/bin/memory/runtime/Search.js +11 -7
- package/bin/memory/runtime/Search.js.map +1 -1
- package/bin/memory/runtime/Store.d.ts +8 -23
- package/bin/memory/runtime/Store.d.ts.map +1 -1
- package/bin/memory/runtime/Store.js +28 -43
- package/bin/memory/runtime/Store.js.map +1 -1
- package/bin/memory/runtime/SystemProvider.d.ts +4 -8
- package/bin/memory/runtime/SystemProvider.d.ts.map +1 -1
- package/bin/memory/runtime/SystemProvider.js +55 -62
- package/bin/memory/runtime/SystemProvider.js.map +1 -1
- package/bin/memory/runtime/Writer.d.ts +48 -10
- package/bin/memory/runtime/Writer.d.ts.map +1 -1
- package/bin/memory/runtime/Writer.js +197 -60
- package/bin/memory/runtime/Writer.js.map +1 -1
- package/bin/memory/types/Memory.d.ts +222 -33
- package/bin/memory/types/Memory.d.ts.map +1 -1
- package/bin/memory/types/Memory.js +4 -3
- package/bin/memory/types/Memory.js.map +1 -1
- package/bin/shell/ShellPlugin.d.ts +2 -1
- package/bin/shell/ShellPlugin.d.ts.map +1 -1
- package/bin/shell/ShellPlugin.js +3 -3
- package/bin/shell/ShellPlugin.js.map +1 -1
- package/bin/shell/ShellRuntimeTypes.d.ts +7 -2
- package/bin/shell/ShellRuntimeTypes.d.ts.map +1 -1
- package/bin/shell/runtime/ShellActionRuntime.d.ts.map +1 -1
- package/bin/shell/runtime/ShellActionRuntime.js +6 -6
- package/bin/shell/runtime/ShellActionRuntime.js.map +1 -1
- package/bin/shell/runtime/ShellActionRuntimeSupport.d.ts +14 -5
- package/bin/shell/runtime/ShellActionRuntimeSupport.d.ts.map +1 -1
- package/bin/shell/runtime/ShellActionRuntimeSupport.js +58 -22
- package/bin/shell/runtime/ShellActionRuntimeSupport.js.map +1 -1
- package/bin/shell/runtime/ShellProcessEvents.js +3 -3
- package/bin/shell/runtime/ShellProcessEvents.js.map +1 -1
- package/bin/shell/types/ShellPluginOptions.d.ts +95 -0
- package/bin/shell/types/ShellPluginOptions.d.ts.map +1 -0
- package/bin/shell/types/ShellPluginOptions.js +10 -0
- package/bin/shell/types/ShellPluginOptions.js.map +1 -0
- package/bin/task/Scheduler.d.ts +8 -0
- package/bin/task/Scheduler.d.ts.map +1 -1
- package/bin/task/Scheduler.js +7 -9
- package/bin/task/Scheduler.js.map +1 -1
- package/bin/task/TaskPlugin.d.ts +18 -1
- package/bin/task/TaskPlugin.d.ts.map +1 -1
- package/bin/task/TaskPlugin.js +23 -1
- package/bin/task/TaskPlugin.js.map +1 -1
- package/bin/task/types/TaskPluginOptions.d.ts +22 -0
- package/bin/task/types/TaskPluginOptions.d.ts.map +1 -0
- package/bin/task/types/TaskPluginOptions.js +9 -0
- package/bin/task/types/TaskPluginOptions.js.map +1 -0
- package/package.json +2 -2
- package/src/BuiltinPlugins.ts +27 -1
- package/src/index.ts +35 -0
- package/src/memory/Action.ts +292 -25
- package/src/memory/MemoryPlugin.ts +82 -40
- package/src/memory/runtime/Search.ts +16 -9
- package/src/memory/runtime/Store.ts +52 -49
- package/src/memory/runtime/SystemProvider.ts +55 -69
- package/src/memory/runtime/Writer.ts +262 -81
- package/src/memory/types/Memory.ts +296 -35
- package/src/shell/ShellPlugin.ts +4 -3
- package/src/shell/ShellRuntimeTypes.ts +7 -2
- package/src/shell/runtime/ShellActionRuntime.ts +18 -9
- package/src/shell/runtime/ShellActionRuntimeSupport.ts +106 -21
- package/src/shell/runtime/ShellProcessEvents.ts +3 -3
- package/src/shell/types/ShellPluginOptions.ts +112 -0
- package/src/task/Scheduler.ts +15 -9
- package/src/task/TaskPlugin.ts +27 -1
- package/src/task/types/TaskPluginOptions.ts +22 -0
- package/bin/memory/runtime/Flush.d.ts +0 -15
- package/bin/memory/runtime/Flush.d.ts.map +0 -1
- package/bin/memory/runtime/Flush.js +0 -63
- package/bin/memory/runtime/Flush.js.map +0 -1
- package/src/memory/runtime/Flush.ts +0 -83
|
@@ -13,6 +13,10 @@ import type {
|
|
|
13
13
|
ShellPluginState,
|
|
14
14
|
ShellSessionRuntimeState,
|
|
15
15
|
} from "@/shell/ShellRuntimeTypes.js";
|
|
16
|
+
import type {
|
|
17
|
+
ResolvedShellPluginOptions,
|
|
18
|
+
ShellPluginOptions,
|
|
19
|
+
} from "@/shell/types/ShellPluginOptions.js";
|
|
16
20
|
import type {
|
|
17
21
|
ShellQueryRequest,
|
|
18
22
|
ShellSessionSnapshot,
|
|
@@ -32,35 +36,104 @@ export {
|
|
|
32
36
|
createOutputChunk,
|
|
33
37
|
} from "./ShellActionResponse.js";
|
|
34
38
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const DEFAULT_SHELL_PLUGIN_OPTIONS: ResolvedShellPluginOptions = {
|
|
40
|
+
maxActiveShells: 64,
|
|
41
|
+
cleanupDelayMs: 10 * 60 * 1000,
|
|
42
|
+
maxInMemoryOutputChars: 1_000_000,
|
|
43
|
+
outputPreviewChars: 280,
|
|
44
|
+
minWaitMs: 50,
|
|
45
|
+
maxWaitMs: 30_000,
|
|
46
|
+
defaultInlineWaitMs: 1_200,
|
|
47
|
+
defaultWaitTimeoutMs: 10_000,
|
|
48
|
+
defaultExecTimeoutMs: 60_000,
|
|
49
|
+
};
|
|
41
50
|
|
|
42
51
|
/**
|
|
43
52
|
* shell.start 默认内联等待时间。
|
|
44
53
|
*/
|
|
45
|
-
export const DEFAULT_INLINE_WAIT_MS =
|
|
54
|
+
export const DEFAULT_INLINE_WAIT_MS = DEFAULT_SHELL_PLUGIN_OPTIONS.defaultInlineWaitMs;
|
|
46
55
|
|
|
47
56
|
/**
|
|
48
57
|
* shell.wait 默认等待超时。
|
|
49
58
|
*/
|
|
50
|
-
export const DEFAULT_WAIT_TIMEOUT_MS =
|
|
59
|
+
export const DEFAULT_WAIT_TIMEOUT_MS = DEFAULT_SHELL_PLUGIN_OPTIONS.defaultWaitTimeoutMs;
|
|
51
60
|
|
|
52
61
|
/**
|
|
53
62
|
* shell.exec 默认总超时。
|
|
54
63
|
*/
|
|
55
|
-
export const DEFAULT_EXEC_TIMEOUT_MS =
|
|
64
|
+
export const DEFAULT_EXEC_TIMEOUT_MS = DEFAULT_SHELL_PLUGIN_OPTIONS.defaultExecTimeoutMs;
|
|
65
|
+
|
|
66
|
+
function readPositiveInteger(
|
|
67
|
+
value: number | undefined,
|
|
68
|
+
fallback: number,
|
|
69
|
+
): number {
|
|
70
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
71
|
+
return fallback;
|
|
72
|
+
}
|
|
73
|
+
return Math.max(1, Math.floor(value));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 归一化 ShellPlugin 可选运行参数。
|
|
78
|
+
*/
|
|
79
|
+
export function resolveShellPluginOptions(
|
|
80
|
+
options: ShellPluginOptions = {},
|
|
81
|
+
): ResolvedShellPluginOptions {
|
|
82
|
+
const minWaitMs = readPositiveInteger(
|
|
83
|
+
options.minWaitMs,
|
|
84
|
+
DEFAULT_SHELL_PLUGIN_OPTIONS.minWaitMs,
|
|
85
|
+
);
|
|
86
|
+
const maxWaitMs = Math.max(
|
|
87
|
+
minWaitMs,
|
|
88
|
+
readPositiveInteger(
|
|
89
|
+
options.maxWaitMs,
|
|
90
|
+
DEFAULT_SHELL_PLUGIN_OPTIONS.maxWaitMs,
|
|
91
|
+
),
|
|
92
|
+
);
|
|
93
|
+
return {
|
|
94
|
+
maxActiveShells: readPositiveInteger(
|
|
95
|
+
options.maxActiveShells,
|
|
96
|
+
DEFAULT_SHELL_PLUGIN_OPTIONS.maxActiveShells,
|
|
97
|
+
),
|
|
98
|
+
cleanupDelayMs: readPositiveInteger(
|
|
99
|
+
options.cleanupDelayMs,
|
|
100
|
+
DEFAULT_SHELL_PLUGIN_OPTIONS.cleanupDelayMs,
|
|
101
|
+
),
|
|
102
|
+
maxInMemoryOutputChars: readPositiveInteger(
|
|
103
|
+
options.maxInMemoryOutputChars,
|
|
104
|
+
DEFAULT_SHELL_PLUGIN_OPTIONS.maxInMemoryOutputChars,
|
|
105
|
+
),
|
|
106
|
+
outputPreviewChars: readPositiveInteger(
|
|
107
|
+
options.outputPreviewChars,
|
|
108
|
+
DEFAULT_SHELL_PLUGIN_OPTIONS.outputPreviewChars,
|
|
109
|
+
),
|
|
110
|
+
minWaitMs,
|
|
111
|
+
maxWaitMs,
|
|
112
|
+
defaultInlineWaitMs: readPositiveInteger(
|
|
113
|
+
options.defaultInlineWaitMs,
|
|
114
|
+
DEFAULT_SHELL_PLUGIN_OPTIONS.defaultInlineWaitMs,
|
|
115
|
+
),
|
|
116
|
+
defaultWaitTimeoutMs: readPositiveInteger(
|
|
117
|
+
options.defaultWaitTimeoutMs,
|
|
118
|
+
DEFAULT_SHELL_PLUGIN_OPTIONS.defaultWaitTimeoutMs,
|
|
119
|
+
),
|
|
120
|
+
defaultExecTimeoutMs: readPositiveInteger(
|
|
121
|
+
options.defaultExecTimeoutMs,
|
|
122
|
+
DEFAULT_SHELL_PLUGIN_OPTIONS.defaultExecTimeoutMs,
|
|
123
|
+
),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
56
126
|
|
|
57
127
|
/**
|
|
58
128
|
* 创建 shell plugin runtime 初始状态。
|
|
59
129
|
*/
|
|
60
|
-
export function createShellPluginState(
|
|
130
|
+
export function createShellPluginState(
|
|
131
|
+
options: ShellPluginOptions = {},
|
|
132
|
+
): ShellPluginState {
|
|
61
133
|
return {
|
|
134
|
+
options: resolveShellPluginOptions(options),
|
|
62
135
|
sessions: new Map<string, ShellSessionRuntimeState>(),
|
|
63
|
-
|
|
136
|
+
context: null,
|
|
64
137
|
};
|
|
65
138
|
}
|
|
66
139
|
|
|
@@ -75,11 +148,22 @@ export function nowMs(): number {
|
|
|
75
148
|
* 归一化 wait/timeout 参数。
|
|
76
149
|
*/
|
|
77
150
|
export function clampWaitMs(value: number | undefined, fallback: number): number {
|
|
151
|
+
return clampWaitMsWithOptions(DEFAULT_SHELL_PLUGIN_OPTIONS, value, fallback);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 结合 ShellPlugin options 归一化 wait/timeout 参数。
|
|
156
|
+
*/
|
|
157
|
+
export function clampWaitMsWithOptions(
|
|
158
|
+
options: ResolvedShellPluginOptions,
|
|
159
|
+
value: number | undefined,
|
|
160
|
+
fallback: number,
|
|
161
|
+
): number {
|
|
78
162
|
const raw =
|
|
79
163
|
typeof value === "number" && Number.isFinite(value)
|
|
80
164
|
? Math.floor(value)
|
|
81
165
|
: fallback;
|
|
82
|
-
return Math.min(
|
|
166
|
+
return Math.min(options.maxWaitMs, Math.max(options.minWaitMs, raw));
|
|
83
167
|
}
|
|
84
168
|
|
|
85
169
|
function normalizeOutputChunk(raw: string): string {
|
|
@@ -223,6 +307,7 @@ export async function updateSessionSnapshot(
|
|
|
223
307
|
* 追加 shell 输出并同步更新快照。
|
|
224
308
|
*/
|
|
225
309
|
export async function appendSessionOutput(
|
|
310
|
+
state: ShellPluginState,
|
|
226
311
|
session: ShellSessionRuntimeState,
|
|
227
312
|
raw: string,
|
|
228
313
|
): Promise<void> {
|
|
@@ -230,8 +315,8 @@ export async function appendSessionOutput(
|
|
|
230
315
|
if (!text) return;
|
|
231
316
|
|
|
232
317
|
session.outputText += text;
|
|
233
|
-
if (session.outputText.length >
|
|
234
|
-
const overflow = session.outputText.length -
|
|
318
|
+
if (session.outputText.length > state.options.maxInMemoryOutputChars) {
|
|
319
|
+
const overflow = session.outputText.length - state.options.maxInMemoryOutputChars;
|
|
235
320
|
session.outputText = session.outputText.slice(overflow);
|
|
236
321
|
session.snapshot.droppedChars += overflow;
|
|
237
322
|
}
|
|
@@ -239,7 +324,7 @@ export async function appendSessionOutput(
|
|
|
239
324
|
session.snapshot.outputChars += text.length;
|
|
240
325
|
session.snapshot.lastOutputAt = nowMs();
|
|
241
326
|
session.snapshot.lastOutputPreview = session.outputText
|
|
242
|
-
.slice(-
|
|
327
|
+
.slice(-state.options.outputPreviewChars)
|
|
243
328
|
.trim();
|
|
244
329
|
session.snapshot.externalRefs = extractExternalRefsFromText(
|
|
245
330
|
text,
|
|
@@ -260,7 +345,7 @@ export function scheduleCleanup(state: ShellPluginState, shellId: string): void
|
|
|
260
345
|
const current = state.sessions.get(shellId);
|
|
261
346
|
if (!current) return;
|
|
262
347
|
state.sessions.delete(shellId);
|
|
263
|
-
},
|
|
348
|
+
}, state.options.cleanupDelayMs);
|
|
264
349
|
if (typeof session.cleanupTimer.unref === "function") {
|
|
265
350
|
session.cleanupTimer.unref();
|
|
266
351
|
}
|
|
@@ -270,15 +355,15 @@ export function scheduleCleanup(state: ShellPluginState, shellId: string): void
|
|
|
270
355
|
* 控制 in-memory shell session 容量。
|
|
271
356
|
*/
|
|
272
357
|
export function ensureCapacity(state: ShellPluginState): void {
|
|
273
|
-
if (state.sessions.size <
|
|
358
|
+
if (state.sessions.size < state.options.maxActiveShells) return;
|
|
274
359
|
const removable = Array.from(state.sessions.values())
|
|
275
360
|
.filter((item) => item.snapshot.status !== "running" && item.snapshot.status !== "starting")
|
|
276
361
|
.sort((a, b) => a.snapshot.updatedAt - b.snapshot.updatedAt);
|
|
277
362
|
for (const item of removable) {
|
|
278
|
-
if (state.sessions.size <
|
|
363
|
+
if (state.sessions.size < state.options.maxActiveShells) break;
|
|
279
364
|
state.sessions.delete(item.snapshot.shellId);
|
|
280
365
|
}
|
|
281
|
-
if (state.sessions.size >=
|
|
366
|
+
if (state.sessions.size >= state.options.maxActiveShells) {
|
|
282
367
|
throw new Error(
|
|
283
368
|
`Too many active shell sessions (${state.sessions.size}). Please close or wait older sessions first.`,
|
|
284
369
|
);
|
|
@@ -377,9 +462,9 @@ export async function finalizeExit(
|
|
|
377
462
|
if (
|
|
378
463
|
session.snapshot.autoNotifyOnExit &&
|
|
379
464
|
session.snapshot.notificationSent === false &&
|
|
380
|
-
state.
|
|
465
|
+
state.context
|
|
381
466
|
) {
|
|
382
|
-
await emitChatCompletionEvent(state.
|
|
467
|
+
await emitChatCompletionEvent(state.context, session.snapshot);
|
|
383
468
|
session.snapshot.notificationSent = true;
|
|
384
469
|
await persistSnapshot(session);
|
|
385
470
|
}
|
|
@@ -44,13 +44,13 @@ export function attachShellProcessEventHandlers(params: {
|
|
|
44
44
|
const { state, session } = params;
|
|
45
45
|
const { child } = session;
|
|
46
46
|
child.stdout.on("data", (chunk: string | Buffer) => {
|
|
47
|
-
void appendSessionOutput(session, String(chunk ?? "")).catch(() => undefined);
|
|
47
|
+
void appendSessionOutput(state, session, String(chunk ?? "")).catch(() => undefined);
|
|
48
48
|
});
|
|
49
49
|
child.stderr.on("data", (chunk: string | Buffer) => {
|
|
50
|
-
void appendSessionOutput(session, String(chunk ?? "")).catch(() => undefined);
|
|
50
|
+
void appendSessionOutput(state, session, String(chunk ?? "")).catch(() => undefined);
|
|
51
51
|
});
|
|
52
52
|
child.on("error", (error: Error) => {
|
|
53
|
-
void appendSessionOutput(session, `\n[process error] ${String(error)}\n`).catch(
|
|
53
|
+
void appendSessionOutput(state, session, `\n[process error] ${String(error)}\n`).catch(
|
|
54
54
|
() => undefined,
|
|
55
55
|
);
|
|
56
56
|
void finalizeExitAfterOutputDrain(state, session, -1).catch(() => undefined);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShellPlugin constructor 参数类型。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - 所有字段都是可选 runtime 调参。
|
|
6
|
+
* - 不传参数时保持 ShellPlugin 既有硬编码默认行为。
|
|
7
|
+
* - 这些参数只影响 shell plugin runtime,不改变 sandbox 权限模型。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ShellPlugin 可选运行参数。
|
|
12
|
+
*/
|
|
13
|
+
export interface ShellPluginOptions {
|
|
14
|
+
/**
|
|
15
|
+
* 最大 in-memory shell session 数量。
|
|
16
|
+
*
|
|
17
|
+
* 超过该数量时,runtime 会优先清理已经结束的旧 session;如果仍然超限则拒绝启动新 shell。
|
|
18
|
+
*/
|
|
19
|
+
maxActiveShells?: number;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 终态 shell session 在内存中保留多久后自动清理,单位毫秒。
|
|
23
|
+
*/
|
|
24
|
+
cleanupDelayMs?: number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 单个 shell session 在内存中保留的最大输出字符数。
|
|
28
|
+
*
|
|
29
|
+
* 超出的历史输出仍会写入持久化输出文件,但内存快照只保留尾部内容。
|
|
30
|
+
*/
|
|
31
|
+
maxInMemoryOutputChars?: number;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* shell 输出预览保留的最大字符数。
|
|
35
|
+
*/
|
|
36
|
+
outputPreviewChars?: number;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* wait/timeout 参数允许的最小毫秒数。
|
|
40
|
+
*/
|
|
41
|
+
minWaitMs?: number;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* wait/timeout 参数允许的最大毫秒数。
|
|
45
|
+
*/
|
|
46
|
+
maxWaitMs?: number;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* `shell.start` 默认内联等待时间,单位毫秒。
|
|
50
|
+
*/
|
|
51
|
+
defaultInlineWaitMs?: number;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* `shell.wait` 默认等待超时,单位毫秒。
|
|
55
|
+
*/
|
|
56
|
+
defaultWaitTimeoutMs?: number;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* `shell.exec` 默认总超时,单位毫秒。
|
|
60
|
+
*/
|
|
61
|
+
defaultExecTimeoutMs?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* ShellPlugin 归一化后的运行参数。
|
|
66
|
+
*/
|
|
67
|
+
export interface ResolvedShellPluginOptions {
|
|
68
|
+
/**
|
|
69
|
+
* 最大 in-memory shell session 数量。
|
|
70
|
+
*/
|
|
71
|
+
maxActiveShells: number;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 终态 shell session 在内存中保留多久后自动清理,单位毫秒。
|
|
75
|
+
*/
|
|
76
|
+
cleanupDelayMs: number;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 单个 shell session 在内存中保留的最大输出字符数。
|
|
80
|
+
*/
|
|
81
|
+
maxInMemoryOutputChars: number;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* shell 输出预览保留的最大字符数。
|
|
85
|
+
*/
|
|
86
|
+
outputPreviewChars: number;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* wait/timeout 参数允许的最小毫秒数。
|
|
90
|
+
*/
|
|
91
|
+
minWaitMs: number;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* wait/timeout 参数允许的最大毫秒数。
|
|
95
|
+
*/
|
|
96
|
+
maxWaitMs: number;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* `shell.start` 默认内联等待时间,单位毫秒。
|
|
100
|
+
*/
|
|
101
|
+
defaultInlineWaitMs: number;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* `shell.wait` 默认等待超时,单位毫秒。
|
|
105
|
+
*/
|
|
106
|
+
defaultWaitTimeoutMs: number;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* `shell.exec` 默认总超时,单位毫秒。
|
|
110
|
+
*/
|
|
111
|
+
defaultExecTimeoutMs: number;
|
|
112
|
+
}
|
package/src/task/Scheduler.ts
CHANGED
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
import { listTasks, readTask, writeTask } from "./runtime/Store.js";
|
|
16
16
|
import { runTaskNow } from "./runtime/Runner.js";
|
|
17
17
|
import { TaskCronEngine } from "./types/Cron.js";
|
|
18
|
-
import { resolveRuntimeTimezone } from "@downcity/agent/internal/utils/Time.js";
|
|
19
18
|
|
|
20
19
|
const TASK_LOG_PREFIX = "[TASK]";
|
|
21
20
|
|
|
@@ -26,13 +25,20 @@ function formatTaskLogMessage(message: string): string {
|
|
|
26
25
|
export async function registerTaskCronJobs(params: {
|
|
27
26
|
context: AgentContext;
|
|
28
27
|
engine: TaskCronEngine;
|
|
28
|
+
/**
|
|
29
|
+
* cron 表达式使用的 IANA 时区。
|
|
30
|
+
*/
|
|
31
|
+
timezone: string;
|
|
32
|
+
/**
|
|
33
|
+
* 当前 TaskPlugin 实例持有的运行中 task 锁。
|
|
34
|
+
*/
|
|
35
|
+
runningTaskIds: Set<string>;
|
|
29
36
|
}): Promise<{ tasksFound: number; jobsScheduled: number }> {
|
|
30
37
|
const context = params.context;
|
|
31
38
|
const logger = context.logger;
|
|
32
39
|
const tasks = await listTasks(context.rootPath);
|
|
33
|
-
const runtimeTimezone =
|
|
40
|
+
const runtimeTimezone = params.timezone;
|
|
34
41
|
|
|
35
|
-
const runningByTaskId = new Set<string>();
|
|
36
42
|
let jobsScheduled = 0;
|
|
37
43
|
|
|
38
44
|
for (const item of tasks) {
|
|
@@ -59,7 +65,7 @@ export async function registerTaskCronJobs(params: {
|
|
|
59
65
|
if (!taskId) return;
|
|
60
66
|
|
|
61
67
|
// 关键点(中文):同一 taskId 串行;重叠触发时跳过,避免并发执行污染 run 目录。
|
|
62
|
-
if (
|
|
68
|
+
if (params.runningTaskIds.has(taskId)) {
|
|
63
69
|
void logger.log("warn", formatTaskLogMessage("Task skipped (already running)"), {
|
|
64
70
|
taskId,
|
|
65
71
|
via: "cron",
|
|
@@ -67,7 +73,7 @@ export async function registerTaskCronJobs(params: {
|
|
|
67
73
|
return;
|
|
68
74
|
}
|
|
69
75
|
|
|
70
|
-
|
|
76
|
+
params.runningTaskIds.add(taskId);
|
|
71
77
|
try {
|
|
72
78
|
// 关键点(中文):触发瞬间复查最新 task.md,避免 status/when 变更后仍沿用旧注册状态。
|
|
73
79
|
const latest = await readTask({
|
|
@@ -110,7 +116,7 @@ export async function registerTaskCronJobs(params: {
|
|
|
110
116
|
error: String(error),
|
|
111
117
|
});
|
|
112
118
|
} finally {
|
|
113
|
-
|
|
119
|
+
params.runningTaskIds.delete(taskId);
|
|
114
120
|
}
|
|
115
121
|
},
|
|
116
122
|
});
|
|
@@ -147,7 +153,7 @@ export async function registerTaskCronJobs(params: {
|
|
|
147
153
|
if (Date.now() < plannedTimeMs) return;
|
|
148
154
|
|
|
149
155
|
// 关键点(中文):同一 taskId 串行;重叠触发时跳过,避免并发执行污染 run 目录。
|
|
150
|
-
if (
|
|
156
|
+
if (params.runningTaskIds.has(taskId)) {
|
|
151
157
|
void logger.log("warn", formatTaskLogMessage("Task skipped (already running)"), {
|
|
152
158
|
taskId,
|
|
153
159
|
via: "time",
|
|
@@ -156,7 +162,7 @@ export async function registerTaskCronJobs(params: {
|
|
|
156
162
|
}
|
|
157
163
|
|
|
158
164
|
let shouldDeactivateOneShot = false;
|
|
159
|
-
|
|
165
|
+
params.runningTaskIds.add(taskId);
|
|
160
166
|
try {
|
|
161
167
|
const latest = await readTask({
|
|
162
168
|
taskId,
|
|
@@ -224,7 +230,7 @@ export async function registerTaskCronJobs(params: {
|
|
|
224
230
|
});
|
|
225
231
|
}
|
|
226
232
|
}
|
|
227
|
-
|
|
233
|
+
params.runningTaskIds.delete(taskId);
|
|
228
234
|
}
|
|
229
235
|
},
|
|
230
236
|
});
|
package/src/task/TaskPlugin.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
TaskCronRegisterResult,
|
|
15
15
|
TaskSchedulerReloadResult,
|
|
16
16
|
} from "@/task/types/TaskPluginTypes.js";
|
|
17
|
+
import type { TaskPluginOptions } from "@/task/types/TaskPluginOptions.js";
|
|
17
18
|
import { TaskCronTriggerEngine } from "@/task/runtime/CronTrigger.js";
|
|
18
19
|
import { registerTaskCronJobs } from "@/task/Scheduler.js";
|
|
19
20
|
import {
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
reloadTaskSchedulerAfterMutation,
|
|
24
25
|
} from "@/task/runtime/TaskActionExecution.js";
|
|
25
26
|
import { TASK_PLUGIN_PROMPT } from "@/task/runtime/TaskPluginSystem.js";
|
|
27
|
+
import { resolveRuntimeTimezone } from "@downcity/agent/internal/utils/Time.js";
|
|
26
28
|
|
|
27
29
|
const TASK_LOG_PREFIX = "[TASK]";
|
|
28
30
|
|
|
@@ -49,6 +51,11 @@ export class TaskPlugin extends BasePlugin {
|
|
|
49
51
|
*/
|
|
50
52
|
readonly actions: PluginActions;
|
|
51
53
|
|
|
54
|
+
/**
|
|
55
|
+
* 当前实例持有的显式配置。
|
|
56
|
+
*/
|
|
57
|
+
public readonly options: TaskPluginOptions;
|
|
58
|
+
|
|
52
59
|
/**
|
|
53
60
|
* 当前实例持有的 cron engine。
|
|
54
61
|
*
|
|
@@ -58,8 +65,18 @@ export class TaskPlugin extends BasePlugin {
|
|
|
58
65
|
*/
|
|
59
66
|
public cronEngine: TaskCronTriggerEngine | null = null;
|
|
60
67
|
|
|
61
|
-
|
|
68
|
+
/**
|
|
69
|
+
* 当前实例持有的运行中 task 锁。
|
|
70
|
+
*
|
|
71
|
+
* 关键点(中文)
|
|
72
|
+
* - scheduler reload 会替换 cron engine。
|
|
73
|
+
* - 锁必须挂在 plugin 实例上,避免 reload 后同一 task 被新旧调度器并发触发。
|
|
74
|
+
*/
|
|
75
|
+
private readonly runningTaskIds = new Set<string>();
|
|
76
|
+
|
|
77
|
+
constructor(options?: TaskPluginOptions) {
|
|
62
78
|
super();
|
|
79
|
+
this.options = options || {};
|
|
63
80
|
|
|
64
81
|
this.actions = createTaskPluginActions({
|
|
65
82
|
reloadSchedulerAfterMutation: async (params) =>
|
|
@@ -115,6 +132,8 @@ export class TaskPlugin extends BasePlugin {
|
|
|
115
132
|
const registerResult = await registerTaskCronJobs({
|
|
116
133
|
context,
|
|
117
134
|
engine,
|
|
135
|
+
timezone: this.resolveTimezone(),
|
|
136
|
+
runningTaskIds: this.runningTaskIds,
|
|
118
137
|
});
|
|
119
138
|
await engine.start();
|
|
120
139
|
this.cronEngine = engine;
|
|
@@ -163,4 +182,11 @@ export class TaskPlugin extends BasePlugin {
|
|
|
163
182
|
reloadScheduler: async (context) => this.restartCronRuntime(context),
|
|
164
183
|
});
|
|
165
184
|
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 解析当前 task cron 使用的时区。
|
|
188
|
+
*/
|
|
189
|
+
private resolveTimezone(): string {
|
|
190
|
+
return String(this.options.timezone || "").trim() || resolveRuntimeTimezone();
|
|
191
|
+
}
|
|
166
192
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TaskPluginOptions:TaskPlugin 构造参数。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - TaskPlugin 本身就是定时任务 runtime,不提供 enabled 开关。
|
|
6
|
+
* - 只暴露用户能直接理解的时区配置,内部调度实现细节不进入 constructor。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* TaskPlugin 构造参数。
|
|
11
|
+
*/
|
|
12
|
+
export interface TaskPluginOptions {
|
|
13
|
+
/**
|
|
14
|
+
* cron task 使用的 IANA 时区。
|
|
15
|
+
*
|
|
16
|
+
* 说明(中文)
|
|
17
|
+
* - 例如 `Asia/Shanghai`、`America/Los_Angeles`。
|
|
18
|
+
* - 省略时使用当前运行机器的本机时区。
|
|
19
|
+
* - `time:<ISO8601-with-timezone>` 一次性任务以 ISO 字符串自身的 offset 为准,这里的时区主要影响 cron 表达式。
|
|
20
|
+
*/
|
|
21
|
+
timezone?: string;
|
|
22
|
+
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memory Flush(手动刷写)。
|
|
3
|
-
*
|
|
4
|
-
* 关键点(中文)
|
|
5
|
-
* - 首版使用确定性规则,不依赖额外模型调用。
|
|
6
|
-
* - 仅提取最近消息的可读文本并落盘到 daily 记忆。
|
|
7
|
-
*/
|
|
8
|
-
import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/AgentContext.js";
|
|
9
|
-
import type { MemoryFlushPayload, MemoryFlushResponse } from "../../memory/types/Memory.js";
|
|
10
|
-
import type { MemoryRuntimeState } from "./Store.js";
|
|
11
|
-
/**
|
|
12
|
-
* 把当前会话最近消息刷写到 daily 记忆。
|
|
13
|
-
*/
|
|
14
|
-
export declare function flushMemory(context: AgentContext, state: MemoryRuntimeState, payload: MemoryFlushPayload): Promise<MemoryFlushResponse>;
|
|
15
|
-
//# sourceMappingURL=Flush.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Flush.d.ts","sourceRoot":"","sources":["../../../src/memory/runtime/Flush.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAC;AACjG,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA0BrD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAkC9B"}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memory Flush(手动刷写)。
|
|
3
|
-
*
|
|
4
|
-
* 关键点(中文)
|
|
5
|
-
* - 首版使用确定性规则,不依赖额外模型调用。
|
|
6
|
-
* - 仅提取最近消息的可读文本并落盘到 daily 记忆。
|
|
7
|
-
*/
|
|
8
|
-
import { isTextUIPart } from "ai";
|
|
9
|
-
import { storeMemory } from "./Writer.js";
|
|
10
|
-
function toUiParts(message) {
|
|
11
|
-
return Array.isArray(message?.parts) ? message.parts : [];
|
|
12
|
-
}
|
|
13
|
-
function extractReadableLine(message) {
|
|
14
|
-
const role = String(message.role || "").toLowerCase() === "user" ? "User" : "Assistant";
|
|
15
|
-
const text = toUiParts(message)
|
|
16
|
-
.filter(isTextUIPart)
|
|
17
|
-
.map((part) => String(part.text || "").trim())
|
|
18
|
-
.filter(Boolean)
|
|
19
|
-
.join("\n")
|
|
20
|
-
.trim();
|
|
21
|
-
if (!text) {
|
|
22
|
-
return "";
|
|
23
|
-
}
|
|
24
|
-
return `${role}: ${text}`;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* 把当前会话最近消息刷写到 daily 记忆。
|
|
28
|
-
*/
|
|
29
|
-
export async function flushMemory(context, state, payload) {
|
|
30
|
-
const sessionId = String(payload.sessionId || "").trim();
|
|
31
|
-
if (!sessionId) {
|
|
32
|
-
throw new Error("sessionId is required");
|
|
33
|
-
}
|
|
34
|
-
const maxMessages = Number.isFinite(payload.maxMessages)
|
|
35
|
-
? Math.max(1, Math.floor(payload.maxMessages))
|
|
36
|
-
: 30;
|
|
37
|
-
const historyStore = context.session.get(sessionId).getHistoryStore();
|
|
38
|
-
const total = await historyStore.size();
|
|
39
|
-
const start = Math.max(0, total - maxMessages);
|
|
40
|
-
const messages = await historyStore.slice(start, total);
|
|
41
|
-
const lines = messages
|
|
42
|
-
.map((msg) => extractReadableLine(msg))
|
|
43
|
-
.filter((line) => line.length > 0);
|
|
44
|
-
const summary = lines.length > 0
|
|
45
|
-
? lines.join("\n\n")
|
|
46
|
-
: "本次 flush 未找到可写入的用户/助手文本内容。";
|
|
47
|
-
const content = [
|
|
48
|
-
`Flush Session: ${sessionId}`,
|
|
49
|
-
`Window: ${start}-${Math.max(start, total - 1)}`,
|
|
50
|
-
"",
|
|
51
|
-
summary,
|
|
52
|
-
].join("\n");
|
|
53
|
-
const saved = await storeMemory(context, state, {
|
|
54
|
-
content,
|
|
55
|
-
target: "daily",
|
|
56
|
-
});
|
|
57
|
-
return {
|
|
58
|
-
path: saved.path,
|
|
59
|
-
messageCount: lines.length,
|
|
60
|
-
writtenChars: saved.writtenChars,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
//# sourceMappingURL=Flush.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Flush.js","sourceRoot":"","sources":["../../../src/memory/runtime/Flush.ts"],"names":[],"mappings":"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"}
|