@deepmaid/maid-cron 0.1.0-alpha.0 → 0.1.0-alpha.2
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/lib/index.d.ts +28 -1
- package/lib/index.js +150 -46
- package/package.json +4 -4
package/lib/index.d.ts
CHANGED
|
@@ -24,6 +24,12 @@ interface JobDeclaration {
|
|
|
24
24
|
maidId: string;
|
|
25
25
|
type?: string;
|
|
26
26
|
name?: string;
|
|
27
|
+
/**
|
|
28
|
+
* M3f:该 job 的基底模式壳(默认 = maid 的 basePreset ?? preset)。声明为非 maid 自带
|
|
29
|
+
* preset(如 cordis/standard)时,cron 会话 = 基底壳 + maidComposer 差异层(persona/
|
|
30
|
+
* anchor/gestures/veil);maidComposer 未挂 → fail-loud 拒绝生成(半人格防线)。
|
|
31
|
+
*/
|
|
32
|
+
basePreset?: string;
|
|
27
33
|
enabled?: boolean;
|
|
28
34
|
schedule: JobSchedule;
|
|
29
35
|
promptTemplate?: string;
|
|
@@ -45,6 +51,7 @@ interface CompiledJob {
|
|
|
45
51
|
jobId: string;
|
|
46
52
|
type: string;
|
|
47
53
|
maidId: string;
|
|
54
|
+
basePreset: string | undefined;
|
|
48
55
|
enabled: boolean;
|
|
49
56
|
kind: 'script' | 'llm';
|
|
50
57
|
promptTemplate: string;
|
|
@@ -74,12 +81,24 @@ declare function renderBriefingPrompt(template: string, vars: {
|
|
|
74
81
|
//#endregion
|
|
75
82
|
//#region src/state.d.ts
|
|
76
83
|
type RunStatus = 'ok' | 'ok-silent' | 'script-failed' | 'gen-failed' | 'deliver-failed' | 'misfire-skipped' | 'overlap-skipped';
|
|
84
|
+
/** M3e §4.1:跨版本稳定受控读面(ok=成功/error=执行失败/skipped=未执行)。 */
|
|
85
|
+
type RunOutcome = 'ok' | 'error' | 'skipped';
|
|
86
|
+
/** M3e §4.1:失败分类受控枚举(调用点赋值,不从 lastError 反解析)。 */
|
|
87
|
+
type RunFailureKind = 'script' | 'generation' | 'delivery' | 'timeout' | 'cancelled' | 'unknown';
|
|
88
|
+
type RunSkipReason = 'overlap' | 'misfire';
|
|
77
89
|
interface JobRunState {
|
|
78
90
|
lastRunAt: string | undefined;
|
|
79
91
|
lastStatus: RunStatus | undefined;
|
|
80
92
|
lastError: string | undefined;
|
|
81
93
|
misfireCount: number;
|
|
82
94
|
nextDueAt: number | undefined;
|
|
95
|
+
/** M3e 可观测:受控三态读面(旧档经 loadState 归一化补水)。 */
|
|
96
|
+
lastOutcome?: RunOutcome | undefined;
|
|
97
|
+
failureKind?: RunFailureKind | undefined;
|
|
98
|
+
skipReason?: RunSkipReason | undefined;
|
|
99
|
+
lastSuccessAt?: string | undefined;
|
|
100
|
+
lastFailureAt?: string | undefined;
|
|
101
|
+
consecutiveFailures?: number | undefined;
|
|
83
102
|
}
|
|
84
103
|
type RunStateMap = Record<string, JobRunState>;
|
|
85
104
|
declare function loadState(file: string): RunStateMap;
|
|
@@ -96,7 +115,9 @@ interface RecentMemory {
|
|
|
96
115
|
//#region src/engine.d.ts
|
|
97
116
|
interface EnginePorts {
|
|
98
117
|
identity: {
|
|
99
|
-
|
|
118
|
+
/** M3f:basePreset 合成面——job.basePreset ?? record.basePreset ?? record.preset(job 级覆写进 engine)。 */
|
|
119
|
+
basePresetOf(maidId: string, jobBasePreset?: string): string | undefined;
|
|
120
|
+
maidOwnPresetOf(maidId: string): string | undefined;
|
|
100
121
|
addressOf(maidId: string): {
|
|
101
122
|
botId: string;
|
|
102
123
|
targetId: string;
|
|
@@ -144,6 +165,9 @@ interface ResolvedCronConfig {
|
|
|
144
165
|
} | undefined;
|
|
145
166
|
agentSetup: ((agentCtx: unknown) => Promise<void>) | undefined;
|
|
146
167
|
stateFile: string;
|
|
168
|
+
/** M3e §4.2:连续失败指数退避(base/max,毫秒;缺省不退避 = 行为零变化)。 */
|
|
169
|
+
failureBackoffBaseMs?: number;
|
|
170
|
+
failureBackoffMaxMs?: number;
|
|
147
171
|
/** 终局观测回调:每次终局迁移(finish 各终局 + overlap-skipped/misfire-skipped)在 persist 之后触发,携带同一 status/error。 */
|
|
148
172
|
onTerminal?: (jobId: string, status: RunStatus, error: string | undefined) => void;
|
|
149
173
|
}
|
|
@@ -158,6 +182,9 @@ declare function createCronEngine(config: ResolvedCronConfig, jobs: readonly Com
|
|
|
158
182
|
interface MaidCronConfig {
|
|
159
183
|
tickIntervalMs?: number;
|
|
160
184
|
misfireWindowMs?: number;
|
|
185
|
+
/** M3e §4.2:连续失败指数退避基数/封顶(毫秒);缺省不退避。 */
|
|
186
|
+
failureBackoffBaseMs?: number;
|
|
187
|
+
failureBackoffMaxMs?: number;
|
|
161
188
|
recentLimit?: number;
|
|
162
189
|
recentMaxChars?: number;
|
|
163
190
|
timezone?: string;
|
package/lib/index.js
CHANGED
|
@@ -794,6 +794,7 @@ function compileJobs(jobs, warn) {
|
|
|
794
794
|
jobId,
|
|
795
795
|
type: decl.type ?? "briefing",
|
|
796
796
|
maidId: decl.maidId,
|
|
797
|
+
basePreset: decl.basePreset,
|
|
797
798
|
enabled: decl.enabled !== false,
|
|
798
799
|
kind,
|
|
799
800
|
promptTemplate: decl.promptTemplate ?? "",
|
|
@@ -851,13 +852,25 @@ function freshRunState() {
|
|
|
851
852
|
lastStatus: void 0,
|
|
852
853
|
lastError: void 0,
|
|
853
854
|
misfireCount: 0,
|
|
854
|
-
nextDueAt: void 0
|
|
855
|
+
nextDueAt: void 0,
|
|
856
|
+
lastOutcome: void 0,
|
|
857
|
+
failureKind: void 0,
|
|
858
|
+
skipReason: void 0,
|
|
859
|
+
lastSuccessAt: void 0,
|
|
860
|
+
lastFailureAt: void 0,
|
|
861
|
+
consecutiveFailures: 0
|
|
855
862
|
};
|
|
856
863
|
}
|
|
857
864
|
function loadState(file) {
|
|
858
865
|
if (!existsSync(file)) return {};
|
|
859
866
|
try {
|
|
860
|
-
|
|
867
|
+
const raw = JSON.parse(readFileSync(file, "utf8"));
|
|
868
|
+
const normalized = {};
|
|
869
|
+
for (const [jobId, state] of Object.entries(raw)) normalized[jobId] = {
|
|
870
|
+
...freshRunState(),
|
|
871
|
+
...state
|
|
872
|
+
};
|
|
873
|
+
return normalized;
|
|
861
874
|
} catch {
|
|
862
875
|
return {};
|
|
863
876
|
}
|
|
@@ -869,6 +882,47 @@ function saveState(file, map) {
|
|
|
869
882
|
renameSync(tmp, file);
|
|
870
883
|
}
|
|
871
884
|
//#endregion
|
|
885
|
+
//#region src/observability.ts
|
|
886
|
+
const iso = (ms) => new Date(ms).toISOString();
|
|
887
|
+
function applyOutcome(state, input) {
|
|
888
|
+
const { status, now } = input;
|
|
889
|
+
const next = {
|
|
890
|
+
...state,
|
|
891
|
+
lastStatus: status,
|
|
892
|
+
lastRunAt: iso(now)
|
|
893
|
+
};
|
|
894
|
+
if (status === "ok" || status === "ok-silent") {
|
|
895
|
+
next.lastOutcome = "ok";
|
|
896
|
+
next.lastSuccessAt = iso(now);
|
|
897
|
+
next.consecutiveFailures = 0;
|
|
898
|
+
next.failureKind = void 0;
|
|
899
|
+
next.lastError = void 0;
|
|
900
|
+
next.lastFailureAt = void 0;
|
|
901
|
+
next.skipReason = void 0;
|
|
902
|
+
return next;
|
|
903
|
+
}
|
|
904
|
+
if (status === "overlap-skipped" || status === "misfire-skipped") {
|
|
905
|
+
next.lastOutcome = "skipped";
|
|
906
|
+
next.skipReason = status === "overlap-skipped" ? "overlap" : "misfire";
|
|
907
|
+
next.lastError = void 0;
|
|
908
|
+
return next;
|
|
909
|
+
}
|
|
910
|
+
next.lastOutcome = "error";
|
|
911
|
+
next.failureKind = input.kind ?? "unknown";
|
|
912
|
+
next.lastError = input.error;
|
|
913
|
+
next.lastFailureAt = iso(now);
|
|
914
|
+
next.consecutiveFailures = (state.consecutiveFailures ?? 0) + 1;
|
|
915
|
+
next.skipReason = void 0;
|
|
916
|
+
return next;
|
|
917
|
+
}
|
|
918
|
+
function nextDueWithBackoff(input) {
|
|
919
|
+
const chained = nextDueAfter(input.schedule, input.due);
|
|
920
|
+
if (chained === null) return null;
|
|
921
|
+
if (input.consecutiveFailures <= 0) return Math.max(input.previousNextDueAt ?? 0, chained);
|
|
922
|
+
const backoff = input.now + Math.min(input.maxMs, input.baseMs * 2 ** (input.consecutiveFailures - 1));
|
|
923
|
+
return Math.max(input.previousNextDueAt ?? 0, chained, backoff);
|
|
924
|
+
}
|
|
925
|
+
//#endregion
|
|
872
926
|
//#region src/script-runner.ts
|
|
873
927
|
const KILL_GRACE_MS = 5e3;
|
|
874
928
|
/**
|
|
@@ -997,12 +1051,25 @@ function createCronEngine(config, jobs, ports) {
|
|
|
997
1051
|
}
|
|
998
1052
|
async function runJob(job, due) {
|
|
999
1053
|
const state = stateOf(job.jobId);
|
|
1000
|
-
|
|
1054
|
+
let deliveryPhase = false;
|
|
1055
|
+
const finish = (status, error, runAt, kind) => {
|
|
1056
|
+
const nowMs = ports.now();
|
|
1057
|
+
Object.assign(state, applyOutcome(state, {
|
|
1058
|
+
status,
|
|
1059
|
+
now: nowMs,
|
|
1060
|
+
...kind !== void 0 ? { kind } : {},
|
|
1061
|
+
...error !== void 0 ? { error } : {}
|
|
1062
|
+
}));
|
|
1001
1063
|
state.lastRunAt = new Date(runAt).toISOString();
|
|
1002
|
-
state.
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1064
|
+
state.nextDueAt = nextDueWithBackoff({
|
|
1065
|
+
schedule: job.schedule,
|
|
1066
|
+
due,
|
|
1067
|
+
now: nowMs,
|
|
1068
|
+
previousNextDueAt: state.nextDueAt,
|
|
1069
|
+
consecutiveFailures: state.consecutiveFailures ?? 0,
|
|
1070
|
+
baseMs: config.failureBackoffBaseMs ?? 0,
|
|
1071
|
+
maxMs: config.failureBackoffMaxMs ?? 0
|
|
1072
|
+
}) ?? void 0;
|
|
1006
1073
|
persist();
|
|
1007
1074
|
config.onTerminal?.(job.jobId, status, error);
|
|
1008
1075
|
};
|
|
@@ -1021,13 +1088,13 @@ function createCronEngine(config, jobs, ports) {
|
|
|
1021
1088
|
};
|
|
1022
1089
|
})();
|
|
1023
1090
|
if (dest === void 0) {
|
|
1024
|
-
finish("gen-failed", `no delivery address for maid '${job.maidId}'`, due);
|
|
1091
|
+
finish("gen-failed", `no delivery address for maid '${job.maidId}'`, due, "generation");
|
|
1025
1092
|
return;
|
|
1026
1093
|
}
|
|
1027
1094
|
if (job.kind === "script") {
|
|
1028
1095
|
const res = await runScript(job.script);
|
|
1029
1096
|
if (!res.ok) {
|
|
1030
|
-
finish("script-failed", `${res.reason}: ${res.stderrTail}`.slice(0, 500), due);
|
|
1097
|
+
finish("script-failed", `${res.reason}: ${res.stderrTail}`.slice(0, 500), due, "script");
|
|
1031
1098
|
return;
|
|
1032
1099
|
}
|
|
1033
1100
|
if (res.stdout.trim().length === 0) {
|
|
@@ -1044,17 +1111,18 @@ function createCronEngine(config, jobs, ports) {
|
|
|
1044
1111
|
finish("ok", void 0, due);
|
|
1045
1112
|
return;
|
|
1046
1113
|
}
|
|
1114
|
+
deliveryPhase = true;
|
|
1047
1115
|
if ((await ports.delivery.send(job.maidId, dest.address, res.stdout)).sent) {
|
|
1048
1116
|
finish("ok", void 0, due);
|
|
1049
1117
|
return;
|
|
1050
1118
|
}
|
|
1051
1119
|
if (config.deliverRetryDelayMs > 0) await sleep(config.deliverRetryDelayMs);
|
|
1052
1120
|
const second = await ports.delivery.send(job.maidId, dest.address, res.stdout);
|
|
1053
|
-
finish(second.sent ? "ok" : "deliver-failed", second.sent ? void 0 : `delivery failed: ${second.code}`, due);
|
|
1121
|
+
finish(second.sent ? "ok" : "deliver-failed", second.sent ? void 0 : `delivery failed: ${second.code}`, due, second.sent ? void 0 : "delivery");
|
|
1054
1122
|
return;
|
|
1055
1123
|
}
|
|
1056
1124
|
if (config.agent === void 0) {
|
|
1057
|
-
finish("gen-failed", "config.agent (provider/model) required", due);
|
|
1125
|
+
finish("gen-failed", "config.agent (provider/model) required", due, "generation");
|
|
1058
1126
|
return;
|
|
1059
1127
|
}
|
|
1060
1128
|
const limit = job.recentLimit ?? config.recentLimit;
|
|
@@ -1063,7 +1131,7 @@ function createCronEngine(config, jobs, ports) {
|
|
|
1063
1131
|
if (job.script !== void 0) {
|
|
1064
1132
|
const res = await runScript(job.script);
|
|
1065
1133
|
if (!res.ok) {
|
|
1066
|
-
finish("script-failed", `${res.reason}: ${res.stderrTail}`.slice(0, 500), due);
|
|
1134
|
+
finish("script-failed", `${res.reason}: ${res.stderrTail}`.slice(0, 500), due, "script");
|
|
1067
1135
|
return;
|
|
1068
1136
|
}
|
|
1069
1137
|
if (res.stdout.trim().length === 0 && job.skipOnEmptyScriptOutput) {
|
|
@@ -1084,7 +1152,8 @@ function createCronEngine(config, jobs, ports) {
|
|
|
1084
1152
|
handle = await ports.agents.create({
|
|
1085
1153
|
sessionId,
|
|
1086
1154
|
meta: {
|
|
1087
|
-
agentPreset: ports.identity.
|
|
1155
|
+
agentPreset: ports.identity.basePresetOf(job.maidId, job.basePreset),
|
|
1156
|
+
maidId: job.maidId,
|
|
1088
1157
|
...config.agent.cwd !== void 0 ? { cwd: config.agent.cwd } : {}
|
|
1089
1158
|
},
|
|
1090
1159
|
agentOptions: {
|
|
@@ -1118,12 +1187,12 @@ function createCronEngine(config, jobs, ports) {
|
|
|
1118
1187
|
}, job.genTimeoutMs).unref?.();
|
|
1119
1188
|
await handle.agent.whenIdle();
|
|
1120
1189
|
if (timedOut) {
|
|
1121
|
-
finish("gen-failed", `generation timeout after ${job.genTimeoutMs}ms`, due);
|
|
1190
|
+
finish("gen-failed", `generation timeout after ${job.genTimeoutMs}ms`, due, "timeout");
|
|
1122
1191
|
return;
|
|
1123
1192
|
}
|
|
1124
1193
|
const text = texts.at(-1);
|
|
1125
1194
|
if (text === void 0) {
|
|
1126
|
-
finish("gen-failed", "no assistant output", due);
|
|
1195
|
+
finish("gen-failed", "no assistant output", due, "generation");
|
|
1127
1196
|
return;
|
|
1128
1197
|
}
|
|
1129
1198
|
if (dest.kind === "persist") {
|
|
@@ -1136,20 +1205,21 @@ function createCronEngine(config, jobs, ports) {
|
|
|
1136
1205
|
finish("ok", void 0, due);
|
|
1137
1206
|
return;
|
|
1138
1207
|
}
|
|
1208
|
+
deliveryPhase = true;
|
|
1139
1209
|
if ((await ports.delivery.send(job.maidId, dest.address, text)).sent) {
|
|
1140
1210
|
finish("ok", void 0, due);
|
|
1141
1211
|
return;
|
|
1142
1212
|
}
|
|
1143
1213
|
if (config.deliverRetryDelayMs > 0) await sleep(config.deliverRetryDelayMs);
|
|
1144
1214
|
const second = await ports.delivery.send(job.maidId, dest.address, text);
|
|
1145
|
-
finish(second.sent ? "ok" : "deliver-failed", second.sent ? void 0 : `delivery failed: ${second.code}`, due);
|
|
1215
|
+
finish(second.sent ? "ok" : "deliver-failed", second.sent ? void 0 : `delivery failed: ${second.code}`, due, second.sent ? void 0 : "delivery");
|
|
1146
1216
|
} finally {
|
|
1147
1217
|
cancels.delete(job.jobId);
|
|
1148
1218
|
off();
|
|
1149
1219
|
await handle?.dispose();
|
|
1150
1220
|
}
|
|
1151
1221
|
} catch (error) {
|
|
1152
|
-
finish("gen-failed", error instanceof Error ? error.message : String(error), due);
|
|
1222
|
+
finish("gen-failed", error instanceof Error ? error.message : String(error), due, deliveryPhase ? "delivery" : "generation");
|
|
1153
1223
|
}
|
|
1154
1224
|
}
|
|
1155
1225
|
function tick(nowMs = ports.now()) {
|
|
@@ -1160,17 +1230,21 @@ function createCronEngine(config, jobs, ports) {
|
|
|
1160
1230
|
if (nowMs < state.nextDueAt) continue;
|
|
1161
1231
|
const due = state.nextDueAt;
|
|
1162
1232
|
if (inFlight.has(job.jobId)) {
|
|
1233
|
+
Object.assign(state, applyOutcome(state, {
|
|
1234
|
+
status: "overlap-skipped",
|
|
1235
|
+
now: nowMs
|
|
1236
|
+
}));
|
|
1163
1237
|
state.lastRunAt = new Date(nowMs).toISOString();
|
|
1164
|
-
state.lastStatus = "overlap-skipped";
|
|
1165
|
-
state.lastError = void 0;
|
|
1166
1238
|
state.nextDueAt = nextDueAfter(job.schedule, due) ?? void 0;
|
|
1167
1239
|
persist();
|
|
1168
1240
|
config.onTerminal?.(job.jobId, "overlap-skipped", void 0);
|
|
1169
1241
|
continue;
|
|
1170
1242
|
}
|
|
1171
1243
|
if (nowMs - due > config.misfireWindowMs) {
|
|
1172
|
-
state
|
|
1173
|
-
|
|
1244
|
+
Object.assign(state, applyOutcome(state, {
|
|
1245
|
+
status: "misfire-skipped",
|
|
1246
|
+
now: nowMs
|
|
1247
|
+
}));
|
|
1174
1248
|
state.misfireCount += 1;
|
|
1175
1249
|
state.nextDueAt = nextDueAfter(job.schedule, nowMs) ?? void 0;
|
|
1176
1250
|
persist();
|
|
@@ -1194,32 +1268,34 @@ function createCronEngine(config, jobs, ports) {
|
|
|
1194
1268
|
};
|
|
1195
1269
|
}
|
|
1196
1270
|
//#endregion
|
|
1197
|
-
//#region src/
|
|
1271
|
+
//#region src/maid-mount.ts
|
|
1198
1272
|
/** registry 服务消费入口:reflect 免 inject 直读(照 maid-cron maidMemory 同款探测纪律)。 */
|
|
1199
1273
|
function presetRegistryOf(ctx, serviceName) {
|
|
1200
1274
|
return ctx?.reflect.get(serviceName, false);
|
|
1201
1275
|
}
|
|
1202
1276
|
/**
|
|
1203
|
-
* 包装 agents.create:
|
|
1204
|
-
*
|
|
1205
|
-
*
|
|
1206
|
-
* - 无 preset id → 不注入,fail-open(无 preset 的 job 与现状同);
|
|
1207
|
-
* - registry 缺失 / resolve 抛(未注册) / mount throw → 拒绝 create(fail-loud,
|
|
1208
|
-
* job 以 gen-failed 落 state 可观测)。
|
|
1277
|
+
* 包装 agents.create(M3f):meta.maidId 在场且 mountPresets 通道开启时注入 setup 链——
|
|
1278
|
+
* registry.mount(meta.agentPreset) → (需差异层时)composer.setup(agentCtx, maidId) →
|
|
1279
|
+
* 既有 opts.setup。无 maidId / 无 agentPreset → 不注入(fail-open,现状行为)。
|
|
1209
1280
|
*/
|
|
1210
|
-
function
|
|
1211
|
-
return async (
|
|
1212
|
-
const
|
|
1213
|
-
|
|
1281
|
+
function createMaidMountingAgents(base, registry, opts) {
|
|
1282
|
+
return async (createOpts) => {
|
|
1283
|
+
const meta = createOpts.meta;
|
|
1284
|
+
const maidId = meta?.maidId;
|
|
1285
|
+
const presetId = meta?.agentPreset;
|
|
1286
|
+
if (typeof maidId !== "string" || maidId.length === 0 || typeof presetId !== "string" || presetId.length === 0) return base(createOpts);
|
|
1287
|
+
const needsComposer = presetId !== opts.maidOwnPresetOf(maidId);
|
|
1214
1288
|
if (registry === void 0) throw new Error("maid-cron: mountPresets 开启但 agentPresets registry 服务不可用——0.2.0 起预设须经插件行声明(profile patch 的 preset-doris 行),且宿主组合须含 @deepseek-ai/dsh-agent-preset-registry");
|
|
1289
|
+
if (needsComposer && opts.composer === void 0) throw new Error(`maid-cron: job 声明 basePreset '${presetId}'(≠ maid 自带 preset)但 maidComposer 未挂载——差异层缺位即半人格会话,拒绝生成;请安装 @deepmaid/maid-composer 或改用 maid 自带 preset`);
|
|
1215
1290
|
const resolved = await registry.resolve(presetId);
|
|
1216
|
-
const chained = typeof
|
|
1291
|
+
const chained = typeof createOpts.setup === "function" ? createOpts.setup : void 0;
|
|
1217
1292
|
const setup = async (agentCtx) => {
|
|
1218
1293
|
await registry.mount(agentCtx, resolved.id);
|
|
1294
|
+
if (needsComposer) await opts.composer.setup(agentCtx, maidId);
|
|
1219
1295
|
await chained?.(agentCtx);
|
|
1220
1296
|
};
|
|
1221
1297
|
return base({
|
|
1222
|
-
...
|
|
1298
|
+
...createOpts,
|
|
1223
1299
|
setup
|
|
1224
1300
|
});
|
|
1225
1301
|
};
|
|
@@ -1233,6 +1309,8 @@ const apply = (ctx, config) => {
|
|
|
1233
1309
|
const resolved = {
|
|
1234
1310
|
tickIntervalMs: config.tickIntervalMs ?? 3e4,
|
|
1235
1311
|
misfireWindowMs: config.misfireWindowMs ?? 36e5,
|
|
1312
|
+
...config.failureBackoffBaseMs !== void 0 ? { failureBackoffBaseMs: config.failureBackoffBaseMs } : {},
|
|
1313
|
+
...config.failureBackoffMaxMs !== void 0 ? { failureBackoffMaxMs: config.failureBackoffMaxMs } : {},
|
|
1236
1314
|
recentLimit: config.recentLimit ?? 10,
|
|
1237
1315
|
recentMaxChars: config.recentMaxChars ?? 1500,
|
|
1238
1316
|
timezone: config.timezone ?? "Asia/Shanghai",
|
|
@@ -1242,29 +1320,55 @@ const apply = (ctx, config) => {
|
|
|
1242
1320
|
stateFile: config.stateFile,
|
|
1243
1321
|
onTerminal: (jobId, status, error) => warn(`maid-cron: ${jobId} → ${status}${error !== void 0 ? ` (${error})` : ""}`)
|
|
1244
1322
|
};
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1247
|
-
|
|
1323
|
+
const lazyReflect = (name, onMiss) => {
|
|
1324
|
+
let cached;
|
|
1325
|
+
let warned = false;
|
|
1326
|
+
return () => {
|
|
1327
|
+
if (cached !== void 0) return cached;
|
|
1328
|
+
const found = ctx.reflect.get(name, false);
|
|
1329
|
+
if (found !== void 0) {
|
|
1330
|
+
cached = found;
|
|
1331
|
+
return found;
|
|
1332
|
+
}
|
|
1333
|
+
if (!warned) {
|
|
1334
|
+
warned = true;
|
|
1335
|
+
warn(onMiss);
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
};
|
|
1339
|
+
const maidMemoryOf = lazyReflect("maidMemory", "maid-cron: maidMemory 未挂载——晨报将不带记忆摘要");
|
|
1340
|
+
const memory = {
|
|
1248
1341
|
recent: (maidId, limit) => {
|
|
1249
|
-
const
|
|
1342
|
+
const maidMemoryService = maidMemoryOf();
|
|
1343
|
+
if (maidMemoryService === void 0) return Promise.resolve([]);
|
|
1344
|
+
const ns = maidMemoryService.namespaceForMaid(maidId);
|
|
1250
1345
|
return ns === void 0 ? Promise.resolve([]) : maidMemoryService.recent(ns, limit);
|
|
1251
1346
|
},
|
|
1252
1347
|
markSkip: (sessionId) => {
|
|
1253
|
-
|
|
1348
|
+
maidMemoryOf()?.markSkipMemory(sessionId);
|
|
1254
1349
|
}
|
|
1255
1350
|
};
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
getMorningSentence: (maidId) => maidVoiceService.getMorningSentence(maidId) ?? ""
|
|
1351
|
+
const maidVoiceOf = lazyReflect("maidVoice", "maid-cron: maidVoice 未挂载——{{nightContext}}/{{overnightSentence}} 将渲染为空");
|
|
1352
|
+
const voice = {
|
|
1353
|
+
buildNightContext: (maidId) => maidVoiceOf()?.buildNightContext(maidId) ?? "",
|
|
1354
|
+
getMorningSentence: (maidId) => maidVoiceOf()?.getMorningSentence(maidId) ?? ""
|
|
1261
1355
|
};
|
|
1262
1356
|
const baseCreate = (opts) => ctx.agents.create(opts);
|
|
1263
1357
|
const presetRegistry = config.mountPresets === true ? presetRegistryOf(ctx, config.presetRegistryService ?? "agentPresets") : void 0;
|
|
1264
|
-
const
|
|
1358
|
+
const composerOf = lazyReflect("maidComposer", "maid-cron: maidComposer 未挂载");
|
|
1359
|
+
const recordOf = (maidId) => ctx.maidIdentity.get(maidId);
|
|
1360
|
+
const agentsCreate = config.mountPresets === true ? createMaidMountingAgents(baseCreate, presetRegistry, {
|
|
1361
|
+
maidOwnPresetOf: (maidId) => recordOf(maidId)?.preset,
|
|
1362
|
+
composer: { setup: (agentCtx, maidId) => {
|
|
1363
|
+
const c = composerOf();
|
|
1364
|
+
if (c === void 0) throw new Error("maid-cron: maidComposer 未挂载(执行时探测)——差异层缺位,拒绝生成");
|
|
1365
|
+
return c.setup(agentCtx, maidId);
|
|
1366
|
+
} }
|
|
1367
|
+
}) : baseCreate;
|
|
1265
1368
|
const engine = createCronEngine(resolved, jobs, {
|
|
1266
1369
|
identity: {
|
|
1267
|
-
|
|
1370
|
+
basePresetOf: (maidId, jobBasePreset) => jobBasePreset ?? recordOf(maidId)?.basePreset ?? recordOf(maidId)?.preset,
|
|
1371
|
+
maidOwnPresetOf: (maidId) => recordOf(maidId)?.preset,
|
|
1268
1372
|
addressOf: (maidId) => ctx.maidIdentity.deliveryTargets(maidId)?.[0]
|
|
1269
1373
|
},
|
|
1270
1374
|
memory,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepmaid/maid-cron",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@deepseek-ai/dsh-llm": "0.2.1-rc.2",
|
|
32
32
|
"croner": "10.0.1",
|
|
33
|
-
"@deepmaid/maid-delivery": "0.1.0-alpha.
|
|
33
|
+
"@deepmaid/maid-delivery": "0.1.0-alpha.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@deepseek-ai/cordis": "4.0
|
|
36
|
+
"@deepseek-ai/cordis": "4.1.0",
|
|
37
37
|
"tsdown": "0.22.14",
|
|
38
38
|
"typescript": "6.0.3",
|
|
39
|
-
"@deepmaid/maid-memory": "0.1.0-alpha.
|
|
39
|
+
"@deepmaid/maid-memory": "0.1.0-alpha.2"
|
|
40
40
|
},
|
|
41
41
|
"publishConfig": {
|
|
42
42
|
"access": "public"
|