@polderlabs/bizar 5.0.0 → 5.0.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/bizar-dash/dist/assets/{icons-CFqu2M-c.js → icons-Bo0iH9EC.js} +166 -121
- package/bizar-dash/dist/assets/icons-Bo0iH9EC.js.map +1 -0
- package/bizar-dash/dist/assets/main-C1cpttnv.js +19 -0
- package/bizar-dash/dist/assets/main-C1cpttnv.js.map +1 -0
- package/bizar-dash/dist/assets/main-DTkNlLrw.css +1 -0
- package/bizar-dash/dist/assets/mobile-BtxQJftK.js +2 -0
- package/bizar-dash/dist/assets/mobile-BtxQJftK.js.map +1 -0
- package/bizar-dash/dist/assets/{mobile-DHXXbn1A.js → mobile-DD-FZrTC.js} +1 -1
- package/bizar-dash/dist/assets/{mobile-DHXXbn1A.js.map → mobile-DD-FZrTC.js.map} +1 -1
- package/bizar-dash/dist/index.html +4 -4
- package/bizar-dash/dist/mobile.html +3 -3
- package/bizar-dash/node_modules/.package-lock.json +6 -0
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/package-lock.json +6 -0
- package/bizar-dash/src/server/api.mjs +7 -0
- package/bizar-dash/src/server/diagnostics-store.mjs +452 -2
- package/bizar-dash/src/server/memory-store.mjs +46 -0
- package/bizar-dash/src/server/providers-store.mjs +11 -4
- package/bizar-dash/src/server/routes/_shared.mjs +2 -2
- package/bizar-dash/src/server/routes/doctor.mjs +71 -0
- package/bizar-dash/src/server/routes/env-vars.mjs +67 -1
- package/bizar-dash/src/server/routes/memory.mjs +3 -0
- package/bizar-dash/src/server/routes/opencode-session-detail.mjs +61 -42
- package/bizar-dash/src/server/routes/schedules.mjs +55 -0
- package/bizar-dash/src/server/serve-info.mjs +172 -0
- package/bizar-dash/src/web/App.tsx +48 -11
- package/bizar-dash/src/web/components/AutosaveField.tsx +50 -0
- package/bizar-dash/src/web/components/DoctorPanel.tsx +160 -0
- package/bizar-dash/src/web/components/EnvVarManager.tsx +245 -62
- package/bizar-dash/src/web/components/ScheduleTemplateCard.tsx +71 -0
- package/bizar-dash/src/web/components/SettingsNav.tsx +101 -0
- package/bizar-dash/src/web/components/Sidebar.tsx +25 -1
- package/bizar-dash/src/web/components/StatusBadge.tsx +33 -2
- package/bizar-dash/src/web/components/Topbar.tsx +11 -0
- package/bizar-dash/src/web/components/chat/ChatInfoPanel.tsx +64 -0
- package/bizar-dash/src/web/components/chat/Composer.tsx +1 -1
- package/bizar-dash/src/web/components/chat/useChat.ts +118 -1
- package/bizar-dash/src/web/hooks/useAutosave.ts +107 -0
- package/bizar-dash/src/web/lib/types.ts +134 -0
- package/bizar-dash/src/web/styles/chat.css +8 -5
- package/bizar-dash/src/web/styles/main.css +77 -2
- package/bizar-dash/src/web/styles/settings.css +265 -0
- package/bizar-dash/src/web/views/Chat.tsx +15 -1
- package/bizar-dash/src/web/views/Doctor.tsx +317 -0
- package/bizar-dash/src/web/views/MiniMaxUsage.tsx +1 -0
- package/bizar-dash/src/web/views/Schedules.tsx +94 -12
- package/bizar-dash/src/web/views/Settings.tsx +76 -46
- package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +114 -133
- package/bizar-dash/src/web/views/settings/AgentSection.tsx +23 -12
- package/bizar-dash/src/web/views/settings/EnvVarsSection.tsx +5 -6
- package/bizar-dash/src/web/views/settings/GeneralSection.tsx +38 -15
- package/bizar-dash/src/web/views/settings/MemorySection.tsx +92 -8
- package/bizar-dash/tests/autosave.test.tsx +276 -0
- package/bizar-dash/tests/chat-composer.test.tsx +140 -0
- package/bizar-dash/tests/components/doctor-panel.test.tsx +105 -0
- package/bizar-dash/tests/diagnostics-store.test.mjs +206 -0
- package/bizar-dash/tests/env-vars-extended.test.mjs +190 -0
- package/bizar-dash/tests/layout-spacing.test.mjs +101 -0
- package/bizar-dash/tests/memory-default-vault.test.mjs +98 -0
- package/bizar-dash/tests/minimax-bar.test.tsx +113 -0
- package/bizar-dash/tests/minimax-models.test.mjs +123 -0
- package/bizar-dash/tests/opencode-sessions-detail.test.mjs +158 -0
- package/bizar-dash/tests/routes-doctor.test.mjs +159 -0
- package/bizar-dash/tests/schedules-templates.test.mjs +144 -0
- package/bizar-dash/tests/settings-layout.test.tsx +129 -0
- package/bizar-dash/tests/settings-mode-wiring.test.tsx +151 -0
- package/bizar-dash/tests/settings-nav.test.tsx +126 -0
- package/cli/commands/dash.mjs +5 -1
- package/cli/commands/minimax.mjs +5 -2
- package/cli/tests/minimax-cli.test.mjs +79 -0
- package/config/agents/frigg.md +1 -1
- package/config/agents/heimdall.md +1 -1
- package/config/agents/mimir.md +1 -1
- package/config/agents/quick.md +1 -1
- package/config/agents/semble-search.md +1 -1
- package/config/agents/vor.md +1 -1
- package/config/opencode.json +28 -21
- package/config/opencode.json.template +8 -1
- package/package.json +1 -1
- package/plugins/bizar/index.ts +77 -0
- package/plugins/bizar/src/compaction.d.mts +48 -0
- package/plugins/bizar/src/compaction.mjs +192 -0
- package/plugins/bizar/tests/compaction.test.ts +264 -0
- package/templates/schedules/daily-backup.json +12 -0
- package/templates/schedules/daily-cleanup.json +12 -0
- package/templates/schedules/hourly-health-check.json +12 -0
- package/templates/schedules/webhook-on-push.json +13 -0
- package/templates/schedules/weekly-digest.json +13 -0
- package/bizar-dash/dist/assets/icons-CFqu2M-c.js.map +0 -1
- package/bizar-dash/dist/assets/main-Dl8yY5_H.js +0 -16
- package/bizar-dash/dist/assets/main-Dl8yY5_H.js.map +0 -1
- package/bizar-dash/dist/assets/main-ZAfGKENE.css +0 -1
- package/bizar-dash/dist/assets/mobile-C2gysFOZ.js +0 -2
- package/bizar-dash/dist/assets/mobile-C2gysFOZ.js.map +0 -1
- package/bizar-dash/src/web/views/settings/BackupSection.tsx +0 -16
- package/bizar-dash/src/web/views/settings/ProvidersSection.tsx +0 -16
- package/bizar-dash/src/web/views/settings/SkillsSection.tsx +0 -16
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* compaction.test.ts
|
|
3
|
+
*
|
|
4
|
+
* Spec contract for the compaction gate (see src/compaction.mjs):
|
|
5
|
+
* - `shouldCompact` returns false below 50%, true at and above 50%.
|
|
6
|
+
* - `shouldCompact` is defensive: null usage, undefined usage.total, and
|
|
7
|
+
* non-positive maxContext all return false (no false positives).
|
|
8
|
+
* - `maybeCompactSession` returns `{ compacted: false, reason: "below_threshold" }`
|
|
9
|
+
* when below the gate.
|
|
10
|
+
* - `maybeCompactSession` returns `{ compacted: true, sessionId, threshold, … }`
|
|
11
|
+
* when at/over the gate.
|
|
12
|
+
* - The default threshold is 0.5.
|
|
13
|
+
* - `setCompactionThreshold` rejects out-of-range values with a throw;
|
|
14
|
+
* accepts in-range values silently.
|
|
15
|
+
* - State leaks between tests are not allowed — every test that mutates
|
|
16
|
+
* the threshold calls `resetCompactionDefaults()` in its teardown
|
|
17
|
+
* (bun:test does not have a global afterEach, so we use try/finally).
|
|
18
|
+
*
|
|
19
|
+
* Tests use `bun:test` to match the rest of the plugin suite (see
|
|
20
|
+
* `tests/loop.test.ts`, `tests/options.test.ts`, etc.).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
describe,
|
|
25
|
+
test,
|
|
26
|
+
expect,
|
|
27
|
+
beforeEach,
|
|
28
|
+
afterEach,
|
|
29
|
+
} from "bun:test";
|
|
30
|
+
|
|
31
|
+
import {
|
|
32
|
+
shouldCompact,
|
|
33
|
+
maybeCompactSession,
|
|
34
|
+
getCompactionThreshold,
|
|
35
|
+
setCompactionThreshold,
|
|
36
|
+
resetCompactionDefaults,
|
|
37
|
+
} from "../src/compaction.mjs";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Bun:test does not expose a global `afterEach`/`beforeEach` reset hook
|
|
41
|
+
* that runs across describe blocks, so we wire per-block setup/teardown
|
|
42
|
+
* via the standard `beforeEach`/`afterEach` imports. We also reset on
|
|
43
|
+
* entry to each test as a belt-and-braces guarantee that no leak from a
|
|
44
|
+
* prior test (run in the same process by `bun test`) can poison state.
|
|
45
|
+
*/
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
resetCompactionDefaults();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
resetCompactionDefaults();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("shouldCompact", () => {
|
|
55
|
+
test("does not compact below 50%", () => {
|
|
56
|
+
expect(shouldCompact({ total: 40000 }, 100000)).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("does not compact at 49.99%", () => {
|
|
60
|
+
expect(shouldCompact({ total: 49999 }, 100000)).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("compacts at exactly 50%", () => {
|
|
64
|
+
expect(shouldCompact({ total: 50000 }, 100000)).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("compacts above 50%", () => {
|
|
68
|
+
expect(shouldCompact({ total: 80000 }, 100000)).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("compacts at 100% (full context)", () => {
|
|
72
|
+
expect(shouldCompact({ total: 100000 }, 100000)).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("does not compact with null usage", () => {
|
|
76
|
+
expect(shouldCompact(null, 100000)).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("does not compact with undefined usage", () => {
|
|
80
|
+
expect(shouldCompact(undefined, 100000)).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("does not compact when usage.total is missing", () => {
|
|
84
|
+
expect(shouldCompact({}, 100000)).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("does not compact with non-positive maxContext", () => {
|
|
88
|
+
expect(shouldCompact({ total: 50000 }, 0)).toBe(false);
|
|
89
|
+
expect(shouldCompact({ total: 50000 }, -1)).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("respects a non-default threshold (0.7)", () => {
|
|
93
|
+
setCompactionThreshold(0.7);
|
|
94
|
+
expect(shouldCompact({ total: 60000 }, 100000)).toBe(false);
|
|
95
|
+
expect(shouldCompact({ total: 70000 }, 100000)).toBe(true);
|
|
96
|
+
expect(shouldCompact({ total: 80000 }, 100000)).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("getCompactionThreshold / setCompactionThreshold", () => {
|
|
101
|
+
test("default threshold is 0.5", () => {
|
|
102
|
+
expect(getCompactionThreshold()).toBe(0.5);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("setCompactionThreshold rejects 0 (below minimum)", () => {
|
|
106
|
+
expect(() => setCompactionThreshold(0)).toThrow(/Invalid compaction threshold/);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("setCompactionThreshold rejects 0.05 (below minimum 0.1)", () => {
|
|
110
|
+
expect(() => setCompactionThreshold(0.05)).toThrow(/Invalid compaction threshold/);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("setCompactionThreshold rejects 1.5 (above maximum)", () => {
|
|
114
|
+
expect(() => setCompactionThreshold(1.5)).toThrow(/Invalid compaction threshold/);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("setCompactionThreshold rejects NaN", () => {
|
|
118
|
+
expect(() => setCompactionThreshold(Number.NaN)).toThrow(/Invalid compaction threshold/);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("setCompactionThreshold rejects Infinity", () => {
|
|
122
|
+
expect(() => setCompactionThreshold(Number.POSITIVE_INFINITY)).toThrow(
|
|
123
|
+
/Invalid compaction threshold/,
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("setCompactionThreshold rejects non-number values", () => {
|
|
128
|
+
expect(() => setCompactionThreshold("0.5" as unknown as number)).toThrow(
|
|
129
|
+
/Invalid compaction threshold/,
|
|
130
|
+
);
|
|
131
|
+
expect(() => setCompactionThreshold(null as unknown as number)).toThrow(
|
|
132
|
+
/Invalid compaction threshold/,
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("setCompactionThreshold accepts 0.1 (lower bound)", () => {
|
|
137
|
+
expect(() => setCompactionThreshold(0.1)).not.toThrow();
|
|
138
|
+
expect(getCompactionThreshold()).toBe(0.1);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("setCompactionThreshold accepts 1.0 (upper bound)", () => {
|
|
142
|
+
expect(() => setCompactionThreshold(1.0)).not.toThrow();
|
|
143
|
+
expect(getCompactionThreshold()).toBe(1.0);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("setCompactionThreshold accepts 0.7 (in-range)", () => {
|
|
147
|
+
expect(() => setCompactionThreshold(0.7)).not.toThrow();
|
|
148
|
+
expect(getCompactionThreshold()).toBe(0.7);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe("maybeCompactSession", () => {
|
|
153
|
+
test("returns below_threshold when under limit", async () => {
|
|
154
|
+
const r = await maybeCompactSession({
|
|
155
|
+
sessionId: "s1",
|
|
156
|
+
messageCount: 5,
|
|
157
|
+
currentUsage: { total: 30000 },
|
|
158
|
+
maxContext: 100000,
|
|
159
|
+
summarizer: async (texts) => texts.join(" "),
|
|
160
|
+
});
|
|
161
|
+
expect(r.compacted).toBe(false);
|
|
162
|
+
expect(r.reason).toBe("below_threshold");
|
|
163
|
+
expect(r.ratio).toBeCloseTo(0.3);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("triggers when over limit", async () => {
|
|
167
|
+
const r = await maybeCompactSession({
|
|
168
|
+
sessionId: "s1",
|
|
169
|
+
messageCount: 100,
|
|
170
|
+
currentUsage: { total: 60000 },
|
|
171
|
+
maxContext: 100000,
|
|
172
|
+
summarizer: async () => "summary",
|
|
173
|
+
});
|
|
174
|
+
expect(r.compacted).toBe(true);
|
|
175
|
+
expect(r.sessionId).toBe("s1");
|
|
176
|
+
expect(r.atMessages).toBe(100);
|
|
177
|
+
expect(r.threshold).toBe(0.5);
|
|
178
|
+
expect(r.preservedRecent).toBe(10);
|
|
179
|
+
expect(r.ratio).toBeCloseTo(0.6);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("triggers at exactly the threshold (50000/100000 = 0.5)", async () => {
|
|
183
|
+
const r = await maybeCompactSession({
|
|
184
|
+
sessionId: "edge",
|
|
185
|
+
messageCount: 42,
|
|
186
|
+
currentUsage: { total: 50000 },
|
|
187
|
+
maxContext: 100000,
|
|
188
|
+
summarizer: async () => "",
|
|
189
|
+
});
|
|
190
|
+
expect(r.compacted).toBe(true);
|
|
191
|
+
expect(r.atMessages).toBe(42);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("respects a custom preserveRecent override", async () => {
|
|
195
|
+
const r = await maybeCompactSession({
|
|
196
|
+
sessionId: "s2",
|
|
197
|
+
messageCount: 200,
|
|
198
|
+
currentUsage: { total: 90000 },
|
|
199
|
+
maxContext: 100000,
|
|
200
|
+
summarizer: async () => "",
|
|
201
|
+
preserveRecent: 25,
|
|
202
|
+
});
|
|
203
|
+
expect(r.compacted).toBe(true);
|
|
204
|
+
expect(r.preservedRecent).toBe(25);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("respects a non-default threshold", async () => {
|
|
208
|
+
setCompactionThreshold(0.8);
|
|
209
|
+
const r1 = await maybeCompactSession({
|
|
210
|
+
sessionId: "s3",
|
|
211
|
+
messageCount: 50,
|
|
212
|
+
currentUsage: { total: 70000 },
|
|
213
|
+
maxContext: 100000,
|
|
214
|
+
summarizer: async () => "",
|
|
215
|
+
});
|
|
216
|
+
expect(r1.compacted).toBe(false);
|
|
217
|
+
|
|
218
|
+
const r2 = await maybeCompactSession({
|
|
219
|
+
sessionId: "s3",
|
|
220
|
+
messageCount: 50,
|
|
221
|
+
currentUsage: { total: 85000 },
|
|
222
|
+
maxContext: 100000,
|
|
223
|
+
summarizer: async () => "",
|
|
224
|
+
});
|
|
225
|
+
expect(r2.compacted).toBe(true);
|
|
226
|
+
expect(r2.threshold).toBe(0.8);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("does not invoke the summarizer when below threshold", async () => {
|
|
230
|
+
let called = false;
|
|
231
|
+
await maybeCompactSession({
|
|
232
|
+
sessionId: "s4",
|
|
233
|
+
messageCount: 10,
|
|
234
|
+
currentUsage: { total: 1000 },
|
|
235
|
+
maxContext: 100000,
|
|
236
|
+
summarizer: async () => {
|
|
237
|
+
called = true;
|
|
238
|
+
return "should-not-run";
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
expect(called).toBe(false);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("throws when sessionId is missing", async () => {
|
|
245
|
+
await expect(
|
|
246
|
+
maybeCompactSession({
|
|
247
|
+
sessionId: "",
|
|
248
|
+
messageCount: 1,
|
|
249
|
+
currentUsage: { total: 999999 },
|
|
250
|
+
maxContext: 100000,
|
|
251
|
+
summarizer: async () => "",
|
|
252
|
+
}),
|
|
253
|
+
).rejects.toThrow(/sessionId is required/);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe("resetCompactionDefaults", () => {
|
|
258
|
+
test("restores the threshold to 0.5 after a custom set", () => {
|
|
259
|
+
setCompactionThreshold(0.9);
|
|
260
|
+
expect(getCompactionThreshold()).toBe(0.9);
|
|
261
|
+
resetCompactionDefaults();
|
|
262
|
+
expect(getCompactionThreshold()).toBe(0.5);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "template-daily-backup",
|
|
3
|
+
"name": "Daily backup",
|
|
4
|
+
"description": "Create a backup every day at 03:00",
|
|
5
|
+
"type": "cron",
|
|
6
|
+
"schedule": "0 3 * * *",
|
|
7
|
+
"action": {
|
|
8
|
+
"type": "command",
|
|
9
|
+
"target": "bizar backup --label daily"
|
|
10
|
+
},
|
|
11
|
+
"tags": ["backup", "maintenance"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "template-daily-cleanup",
|
|
3
|
+
"name": "Daily cleanup",
|
|
4
|
+
"description": "Clean up old backups and logs every day at 04:00",
|
|
5
|
+
"type": "cron",
|
|
6
|
+
"schedule": "0 4 * * *",
|
|
7
|
+
"action": {
|
|
8
|
+
"type": "command",
|
|
9
|
+
"target": "bizar backup delete --older-than 30d"
|
|
10
|
+
},
|
|
11
|
+
"tags": ["backup", "maintenance", "cleanup"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "template-hourly-health",
|
|
3
|
+
"name": "Hourly health check",
|
|
4
|
+
"description": "Run bizar doctor every hour",
|
|
5
|
+
"type": "interval",
|
|
6
|
+
"schedule": "1h",
|
|
7
|
+
"action": {
|
|
8
|
+
"type": "command",
|
|
9
|
+
"target": "bizar doctor"
|
|
10
|
+
},
|
|
11
|
+
"tags": ["health", "monitoring"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "template-webhook-push",
|
|
3
|
+
"name": "Webhook on push (example)",
|
|
4
|
+
"description": "Trigger a webhook on git push events",
|
|
5
|
+
"type": "cron",
|
|
6
|
+
"schedule": "0 5 * * *",
|
|
7
|
+
"action": {
|
|
8
|
+
"type": "webhook",
|
|
9
|
+
"target": "https://your-server.example.com/hook",
|
|
10
|
+
"method": "POST"
|
|
11
|
+
},
|
|
12
|
+
"tags": ["webhook", "integration"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "template-weekly-digest",
|
|
3
|
+
"name": "Weekly digest",
|
|
4
|
+
"description": "Generate the weekly digest every Sunday at 00:00",
|
|
5
|
+
"type": "cron",
|
|
6
|
+
"schedule": "0 0 * * 0",
|
|
7
|
+
"action": {
|
|
8
|
+
"type": "agent",
|
|
9
|
+
"target": "",
|
|
10
|
+
"prompt": "Generate the weekly digest and save to vault"
|
|
11
|
+
},
|
|
12
|
+
"tags": ["memory", "reporting"]
|
|
13
|
+
}
|