@kevin5251984/guild 0.2.20 → 0.2.22
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/cordis.yml +2 -0
- package/package.json +1 -1
- package/src/cron-schedule.ts +388 -0
- package/src/cron.ts +403 -0
- package/src/db.ts +135 -0
- package/src/generate.ts +9 -0
- package/src/handlers.ts +102 -5
- package/src/harness.ts +25 -5
- package/src/llm.ts +1 -1
- package/src/plugins/cron.ts +26 -0
- package/src/public/chat.css +118 -9
- package/src/public/chat.html +475 -43
- package/src/public/i18n.js +23 -0
- package/src/public/md.js +109 -15
- package/src/public/mobile.css +116 -2
- package/src/public/mobile.html +185 -15
- package/src/router.ts +161 -2
- package/src/store.ts +116 -33
- package/src/subagent.ts +3 -0
- package/src/tools.ts +57 -1
- package/vendor/protocol/src/index.ts +7 -2
package/src/handlers.ts
CHANGED
|
@@ -75,6 +75,8 @@ export type HandlerExtras = {
|
|
|
75
75
|
turn?: (input: Parameters<typeof chatReply>[0]) => Promise<ChatReply>;
|
|
76
76
|
/** Bot ids the client already resolved from @mentions. */
|
|
77
77
|
mentions?: string[];
|
|
78
|
+
/** Hermes: cron child sessions cannot manage cron. */
|
|
79
|
+
cronRun?: boolean;
|
|
78
80
|
};
|
|
79
81
|
|
|
80
82
|
export function healthPayload(): HealthResponse {
|
|
@@ -522,6 +524,7 @@ export function workspace(store: GuildStore) {
|
|
|
522
524
|
startedAt: turn.startedAt || "",
|
|
523
525
|
thinking: turn.thinking,
|
|
524
526
|
steps: turn.steps,
|
|
527
|
+
...(turn.paused ? { paused: true } : {}),
|
|
525
528
|
},
|
|
526
529
|
];
|
|
527
530
|
});
|
|
@@ -913,6 +916,8 @@ export function chatTurnForBot(
|
|
|
913
916
|
history,
|
|
914
917
|
userMessage,
|
|
915
918
|
dataDir: store.dataDir,
|
|
919
|
+
roomId,
|
|
920
|
+
botId,
|
|
916
921
|
model: detail.model ?? null,
|
|
917
922
|
skills: mergeSkillRefs(
|
|
918
923
|
staffedSkills(store, botId),
|
|
@@ -996,6 +1001,7 @@ function publicLiveTurn(live: LiveTurn): LiveTurn {
|
|
|
996
1001
|
thinking: live.thinking,
|
|
997
1002
|
steps: live.steps,
|
|
998
1003
|
startedAt: live.startedAt,
|
|
1004
|
+
...(live.paused ? { paused: true } : {}),
|
|
999
1005
|
};
|
|
1000
1006
|
}
|
|
1001
1007
|
|
|
@@ -1085,6 +1091,84 @@ export function abortLiveTurn(
|
|
|
1085
1091
|
return { ok: true };
|
|
1086
1092
|
}
|
|
1087
1093
|
|
|
1094
|
+
export function pauseLiveTurn(
|
|
1095
|
+
store: GuildStore,
|
|
1096
|
+
roomId: string,
|
|
1097
|
+
botId?: string,
|
|
1098
|
+
) {
|
|
1099
|
+
if (!store.getRoom(roomId)) throw new StoreError(404, "room not found");
|
|
1100
|
+
const live = botId
|
|
1101
|
+
? store.getLiveBotTurn(roomId, botId)
|
|
1102
|
+
: store.getLiveTurn(roomId);
|
|
1103
|
+
if (live?.paused) return { ok: true, paused: true as const };
|
|
1104
|
+
const had = store.pauseTurn(roomId, botId);
|
|
1105
|
+
if (!live && !had) throw new StoreError(409, "no live turn");
|
|
1106
|
+
return { ok: true, paused: true as const };
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
function resumeSteer(live: LiveTurn): string {
|
|
1110
|
+
const lines = [
|
|
1111
|
+
"Paused mid-turn so the user could switch models. Continue from here. Do not redo finished tools unless you need a different result.",
|
|
1112
|
+
];
|
|
1113
|
+
const thinking = (live.thinking || "").trim();
|
|
1114
|
+
if (thinking) lines.push(`Thinking so far:\n${thinking.slice(0, 6000)}`);
|
|
1115
|
+
const tools = (live.traces || []).filter((tr) => tr.name && tr.name !== "think");
|
|
1116
|
+
if (tools.length) {
|
|
1117
|
+
lines.push(
|
|
1118
|
+
"Tools already run:\n" +
|
|
1119
|
+
tools
|
|
1120
|
+
.slice(-20)
|
|
1121
|
+
.map((tr) => {
|
|
1122
|
+
const bit = String(tr.text || "")
|
|
1123
|
+
.replace(/\s+/g, " ")
|
|
1124
|
+
.trim()
|
|
1125
|
+
.slice(0, 400);
|
|
1126
|
+
return `- ${tr.name}${bit ? `: ${bit}` : ""}`;
|
|
1127
|
+
})
|
|
1128
|
+
.join("\n"),
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
return lines.join("\n\n");
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
export async function continueLiveTurn(
|
|
1135
|
+
store: GuildStore,
|
|
1136
|
+
roomId: string,
|
|
1137
|
+
botId: string,
|
|
1138
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
1139
|
+
extras: HandlerExtras = {},
|
|
1140
|
+
) {
|
|
1141
|
+
if (!store.getRoom(roomId)) throw new StoreError(404, "room not found");
|
|
1142
|
+
const id = botId.trim();
|
|
1143
|
+
if (!id) throw new StoreError(400, "botId is required");
|
|
1144
|
+
const live = store.getLiveBotTurn(roomId, id);
|
|
1145
|
+
if (!live?.paused) throw new StoreError(409, "no paused turn");
|
|
1146
|
+
const messages = store.listMessages(roomId);
|
|
1147
|
+
let userIndex = messages.length - 1;
|
|
1148
|
+
while (userIndex >= 0 && messages[userIndex].author !== "you") userIndex -= 1;
|
|
1149
|
+
if (userIndex < 0) throw new StoreError(400, "no user message to continue");
|
|
1150
|
+
const userMessage = messages[userIndex];
|
|
1151
|
+
const asked = live.asked?.trim() || userMessage.body.trim();
|
|
1152
|
+
if (!asked) throw new StoreError(409, "nothing to continue");
|
|
1153
|
+
const history = messages.slice(0, userIndex).map(toHistoryItem);
|
|
1154
|
+
const parent = parentMessage(messages.slice(0, userIndex), userMessage.replyTo);
|
|
1155
|
+
const room = store.getRoom(roomId);
|
|
1156
|
+
store.setLiveTurn(roomId, { ...live, paused: false });
|
|
1157
|
+
store.pushSteer(roomId, resumeSteer(live), id);
|
|
1158
|
+
const replies = await generateReplies(
|
|
1159
|
+
store,
|
|
1160
|
+
roomId,
|
|
1161
|
+
room?.memberIds ?? [id],
|
|
1162
|
+
{ ...userMessage, body: asked },
|
|
1163
|
+
history,
|
|
1164
|
+
id,
|
|
1165
|
+
env,
|
|
1166
|
+
parent,
|
|
1167
|
+
extras,
|
|
1168
|
+
);
|
|
1169
|
+
return { replies };
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1088
1172
|
function isAbortError(err: unknown): boolean {
|
|
1089
1173
|
return Boolean(
|
|
1090
1174
|
err &&
|
|
@@ -1169,14 +1253,19 @@ async function generateReplies(
|
|
|
1169
1253
|
if (!memberIds.includes(botId)) return;
|
|
1170
1254
|
if (signal.aborted) return;
|
|
1171
1255
|
const prev = store.getLiveBotTurn(roomId, botId);
|
|
1256
|
+
if (!prev || prev.paused) return;
|
|
1172
1257
|
const startedAt = prev?.startedAt || new Date().toISOString();
|
|
1173
1258
|
store.dropLastFailedReply(roomId, botId);
|
|
1174
1259
|
store.setLiveTurn(roomId, {
|
|
1175
1260
|
botId,
|
|
1176
1261
|
thinking: prev?.thinking || "",
|
|
1177
1262
|
steps: prev?.steps || [],
|
|
1263
|
+
traces: prev?.traces,
|
|
1264
|
+
asked: turnAsked,
|
|
1178
1265
|
startedAt,
|
|
1266
|
+
paused: false,
|
|
1179
1267
|
});
|
|
1268
|
+
const botSignal = store.armBotTurn(roomId, botId, signal);
|
|
1180
1269
|
let generated;
|
|
1181
1270
|
try {
|
|
1182
1271
|
generated = await (extras.turn ?? chatReply)({
|
|
@@ -1189,10 +1278,12 @@ async function generateReplies(
|
|
|
1189
1278
|
userMessage.body,
|
|
1190
1279
|
),
|
|
1191
1280
|
env,
|
|
1192
|
-
signal,
|
|
1281
|
+
signal: botSignal,
|
|
1193
1282
|
mcpTools,
|
|
1283
|
+
...(extras.cronRun ? { cronRun: true } : {}),
|
|
1194
1284
|
onProgress: (update) => {
|
|
1195
1285
|
const prev = store.getLiveBotTurn(roomId, botId);
|
|
1286
|
+
if (prev?.paused) return;
|
|
1196
1287
|
const next = toLiveTurn(botId, update);
|
|
1197
1288
|
const handoff = (prev?.steps || []).find((step) => step.name === "handoff");
|
|
1198
1289
|
const pendingSteers = store.peekSteers(roomId, botId).map((text) => ({
|
|
@@ -1209,14 +1300,16 @@ async function generateReplies(
|
|
|
1209
1300
|
);
|
|
1210
1301
|
store.setLiveTurn(roomId, {
|
|
1211
1302
|
...next,
|
|
1303
|
+
asked: prev?.asked || turnAsked,
|
|
1212
1304
|
startedAt: prev?.startedAt || startedAt,
|
|
1305
|
+
paused: false,
|
|
1213
1306
|
steps: [...(handoff ? [handoff] : []), ...keptSteer, ...rest].slice(0, 5),
|
|
1214
1307
|
});
|
|
1215
1308
|
},
|
|
1216
1309
|
pullSteers: () => store.drainSteers(roomId, botId),
|
|
1217
1310
|
});
|
|
1218
1311
|
} catch (err) {
|
|
1219
|
-
if (isAbortError(err) || signal.aborted) return;
|
|
1312
|
+
if (isAbortError(err) || botSignal.aborted || signal.aborted) return;
|
|
1220
1313
|
throw err;
|
|
1221
1314
|
}
|
|
1222
1315
|
const usage = { ...(generated.usage || {}), startedAt };
|
|
@@ -1406,11 +1499,15 @@ function plantLiveTurns(
|
|
|
1406
1499
|
if (!botId) continue;
|
|
1407
1500
|
if (!memberIds.includes(botId)) continue;
|
|
1408
1501
|
store.dropLastFailedReply(roomId, botId);
|
|
1502
|
+
const prev = store.getLiveBotTurn(roomId, botId);
|
|
1409
1503
|
store.setLiveTurn(roomId, {
|
|
1410
1504
|
botId,
|
|
1411
|
-
thinking: "",
|
|
1412
|
-
steps: [],
|
|
1413
|
-
|
|
1505
|
+
thinking: prev?.thinking || "",
|
|
1506
|
+
steps: prev?.steps || [],
|
|
1507
|
+
traces: prev?.traces,
|
|
1508
|
+
asked: prev?.asked,
|
|
1509
|
+
startedAt: prev?.startedAt || startedAt,
|
|
1510
|
+
paused: false,
|
|
1414
1511
|
});
|
|
1415
1512
|
}
|
|
1416
1513
|
return startedAt;
|
package/src/harness.ts
CHANGED
|
@@ -133,6 +133,23 @@ export function pathInsideWorkspace(target: string, workspace: string): boolean
|
|
|
133
133
|
return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/** Scratch dirs `workspace_write` may also read / write / run-cwd. Not `$HOME`. */
|
|
137
|
+
export function extraWriteRoots(dataDir?: string): string[] {
|
|
138
|
+
const roots = [resolve("/tmp")];
|
|
139
|
+
const home = dataDir?.trim();
|
|
140
|
+
if (home) roots.push(resolve(home, "cache"));
|
|
141
|
+
return roots;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function allowedWorkspaceWritePath(
|
|
145
|
+
target: string,
|
|
146
|
+
workspace: string,
|
|
147
|
+
dataDir?: string,
|
|
148
|
+
): boolean {
|
|
149
|
+
if (pathInsideWorkspace(target, workspace)) return true;
|
|
150
|
+
return extraWriteRoots(dataDir).some((root) => pathInsideWorkspace(target, root));
|
|
151
|
+
}
|
|
152
|
+
|
|
136
153
|
export function mutatingTool(name: string): boolean {
|
|
137
154
|
return (
|
|
138
155
|
name === "run" ||
|
|
@@ -150,6 +167,7 @@ export function gateTool(
|
|
|
150
167
|
input: {
|
|
151
168
|
sandbox?: Sandbox;
|
|
152
169
|
workspace?: string;
|
|
170
|
+
dataDir?: string;
|
|
153
171
|
} = {},
|
|
154
172
|
): SandboxRefusal | null {
|
|
155
173
|
const sandbox = parseSandbox(input.sandbox);
|
|
@@ -161,7 +179,8 @@ export function gateTool(
|
|
|
161
179
|
name === "list" ||
|
|
162
180
|
name === "skill" ||
|
|
163
181
|
name === "spawn" ||
|
|
164
|
-
name === "read_spawn"
|
|
182
|
+
name === "read_spawn" ||
|
|
183
|
+
name === "cronjob"
|
|
165
184
|
) {
|
|
166
185
|
return null;
|
|
167
186
|
}
|
|
@@ -187,7 +206,7 @@ export function gateTool(
|
|
|
187
206
|
const raw = typeof args.path === "string" ? args.path : "";
|
|
188
207
|
if (name === "list" && !raw.trim()) return null;
|
|
189
208
|
const target = resolveToolPath(raw, workspace);
|
|
190
|
-
if (!
|
|
209
|
+
if (!allowedWorkspaceWritePath(target, workspace, input.dataDir)) {
|
|
191
210
|
return {
|
|
192
211
|
text: `sandbox=workspace_write refused ${name} outside workspace: ${target}`,
|
|
193
212
|
isError: true,
|
|
@@ -199,7 +218,8 @@ export function gateTool(
|
|
|
199
218
|
if (
|
|
200
219
|
name === "skill" ||
|
|
201
220
|
name === "spawn" ||
|
|
202
|
-
name === "read_spawn"
|
|
221
|
+
name === "read_spawn" ||
|
|
222
|
+
name === "cronjob"
|
|
203
223
|
) {
|
|
204
224
|
return null;
|
|
205
225
|
}
|
|
@@ -208,7 +228,7 @@ export function gateTool(
|
|
|
208
228
|
const raw = typeof args.path === "string" ? args.path : "";
|
|
209
229
|
if (!raw.trim()) return null;
|
|
210
230
|
const target = resolveToolPath(raw, workspace);
|
|
211
|
-
if (!
|
|
231
|
+
if (!allowedWorkspaceWritePath(target, workspace, input.dataDir)) {
|
|
212
232
|
return {
|
|
213
233
|
text: `sandbox=workspace_write refused write outside workspace: ${target}`,
|
|
214
234
|
isError: true,
|
|
@@ -220,7 +240,7 @@ export function gateTool(
|
|
|
220
240
|
if (name === "run") {
|
|
221
241
|
const workdir = typeof args.workdir === "string" ? args.workdir.trim() : "";
|
|
222
242
|
const cwd = workdir ? resolveToolPath(workdir, workspace) : workspace;
|
|
223
|
-
if (!
|
|
243
|
+
if (!allowedWorkspaceWritePath(cwd, workspace, input.dataDir)) {
|
|
224
244
|
return {
|
|
225
245
|
text: `sandbox=workspace_write refused run cwd outside workspace: ${cwd}`,
|
|
226
246
|
isError: true,
|
package/src/llm.ts
CHANGED
|
@@ -496,7 +496,7 @@ export async function llmComplete(input: {
|
|
|
496
496
|
};
|
|
497
497
|
const file = readModelsFile(input.dataDir);
|
|
498
498
|
const effort = clampEffort(
|
|
499
|
-
file.fast ? "low" : file.reasoning,
|
|
499
|
+
file.fast ? "low" : input.prefer ? input.prefer.reasoning : file.reasoning,
|
|
500
500
|
reasoningFor(target.providerId, target.model),
|
|
501
501
|
Boolean(file.fast),
|
|
502
502
|
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Service, type Context } from "cordis";
|
|
2
|
+
import { CRON_TICK_MS } from "../cron-schedule.ts";
|
|
3
|
+
import { executeCronjob, tickCronJobs } from "../cron.ts";
|
|
4
|
+
import { guildEnvOf } from "../start.ts";
|
|
5
|
+
|
|
6
|
+
export class CronService extends Service {
|
|
7
|
+
static inject = ["store", "tools"];
|
|
8
|
+
|
|
9
|
+
constructor(ctx: Context) {
|
|
10
|
+
super(ctx, "cron");
|
|
11
|
+
ctx.tools.register("cronjob", (args, toolCtx) =>
|
|
12
|
+
executeCronjob(this.ctx.store.guild, args, toolCtx),
|
|
13
|
+
);
|
|
14
|
+
const tick = () => {
|
|
15
|
+
void tickCronJobs(this.ctx.store.guild, guildEnvOf(this.ctx));
|
|
16
|
+
};
|
|
17
|
+
const interval = setInterval(tick, CRON_TICK_MS);
|
|
18
|
+
interval.unref();
|
|
19
|
+
ctx.effect(() => () => clearInterval(interval));
|
|
20
|
+
const boot = setTimeout(tick, 1500);
|
|
21
|
+
boot.unref();
|
|
22
|
+
ctx.effect(() => () => clearTimeout(boot));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default CronService;
|
package/src/public/chat.css
CHANGED
|
@@ -257,6 +257,57 @@ body.resizing-sidebar iframe { pointer-events: none; }
|
|
|
257
257
|
font-size: 0.72rem;
|
|
258
258
|
letter-spacing: 0.12em;
|
|
259
259
|
}
|
|
260
|
+
.nav-sec {
|
|
261
|
+
display: flex;
|
|
262
|
+
flex-direction: column;
|
|
263
|
+
min-height: 0;
|
|
264
|
+
}
|
|
265
|
+
.nav-sec[data-nav-sec="dms"],
|
|
266
|
+
.nav-sec[data-nav-sec="cron"] {
|
|
267
|
+
flex: 1 1 0;
|
|
268
|
+
}
|
|
269
|
+
.nav-sec.folded {
|
|
270
|
+
flex: 0 0 auto;
|
|
271
|
+
}
|
|
272
|
+
.nav-sec.folded .navlist,
|
|
273
|
+
.nav-sec.folded .create-row {
|
|
274
|
+
display: none !important;
|
|
275
|
+
}
|
|
276
|
+
.sec-fold {
|
|
277
|
+
display: flex;
|
|
278
|
+
align-items: center;
|
|
279
|
+
gap: 6px;
|
|
280
|
+
flex: 1;
|
|
281
|
+
min-width: 0;
|
|
282
|
+
margin: 0;
|
|
283
|
+
padding: 0;
|
|
284
|
+
border: 0;
|
|
285
|
+
background: transparent;
|
|
286
|
+
color: inherit;
|
|
287
|
+
font: inherit;
|
|
288
|
+
letter-spacing: inherit;
|
|
289
|
+
font-weight: inherit;
|
|
290
|
+
text-shadow: inherit;
|
|
291
|
+
text-align: left;
|
|
292
|
+
cursor: pointer;
|
|
293
|
+
}
|
|
294
|
+
.sec-fold:hover { color: var(--text); }
|
|
295
|
+
.sec-chev {
|
|
296
|
+
display: inline-block;
|
|
297
|
+
width: 0.9em;
|
|
298
|
+
flex: none;
|
|
299
|
+
font-size: 0.8em;
|
|
300
|
+
line-height: 1;
|
|
301
|
+
transition: transform var(--press-ms) var(--ease-out);
|
|
302
|
+
}
|
|
303
|
+
.nav-sec.folded .sec-chev { transform: rotate(-90deg); }
|
|
304
|
+
.nav-empty {
|
|
305
|
+
list-style: none;
|
|
306
|
+
margin: 0;
|
|
307
|
+
padding: 0.35rem 0.9rem 0.7rem;
|
|
308
|
+
color: var(--muted);
|
|
309
|
+
font-size: 0.78rem;
|
|
310
|
+
}
|
|
260
311
|
.add-ch {
|
|
261
312
|
margin: 0;
|
|
262
313
|
width: 22px;
|
|
@@ -367,6 +418,7 @@ body.resizing-sidebar iframe { pointer-events: none; }
|
|
|
367
418
|
}
|
|
368
419
|
@media (prefers-reduced-motion: reduce) {
|
|
369
420
|
.busy-flash { animation: none; }
|
|
421
|
+
.sec-chev { transition: none; }
|
|
370
422
|
}
|
|
371
423
|
.navlist a.nav-row:hover { background: var(--lift); }
|
|
372
424
|
.navlist a.nav-row.on { background: var(--bubble); }
|
|
@@ -648,6 +700,8 @@ body.grok .sidebar .section-h {
|
|
|
648
700
|
font-weight: 700;
|
|
649
701
|
text-shadow: 0 1px 0 #0008;
|
|
650
702
|
}
|
|
703
|
+
body.grok .sidebar .sec-fold:hover { color: #F4E3A4; }
|
|
704
|
+
body.grok .sidebar .nav-empty { color: #B08960; }
|
|
651
705
|
body.grok .sidebar .add-ch {
|
|
652
706
|
color: var(--pin);
|
|
653
707
|
border-radius: 2px;
|
|
@@ -890,7 +944,8 @@ body.grok #channels li.nav-quest:has(a.on) .notice-party .members-stack .avatar,
|
|
|
890
944
|
body.grok #channels li.nav-quest:hover .notice-party .members-stack .avatar {
|
|
891
945
|
border-color: var(--plaque-on);
|
|
892
946
|
}
|
|
893
|
-
body.grok #dms.navlist
|
|
947
|
+
body.grok #dms.navlist,
|
|
948
|
+
body.grok #cron-nav.navlist {
|
|
894
949
|
flex-grow: 1;
|
|
895
950
|
flex-shrink: 1;
|
|
896
951
|
flex-basis: 0;
|
|
@@ -901,25 +956,30 @@ body.grok #dms.navlist {
|
|
|
901
956
|
scrollbar-width: thin;
|
|
902
957
|
scrollbar-color: #8A623C transparent;
|
|
903
958
|
}
|
|
904
|
-
body.grok #dms a.nav-row
|
|
959
|
+
body.grok #dms a.nav-row,
|
|
960
|
+
body.grok #cron-nav a.nav-row {
|
|
905
961
|
color: #F0E2C8;
|
|
906
962
|
border-radius: 2px;
|
|
907
963
|
border: 1px solid transparent;
|
|
908
964
|
background: color-mix(in srgb, #16100B 35%, transparent);
|
|
909
965
|
}
|
|
910
|
-
body.grok #dms a.nav-row:hover
|
|
966
|
+
body.grok #dms a.nav-row:hover,
|
|
967
|
+
body.grok #cron-nav a.nav-row:hover {
|
|
911
968
|
background: color-mix(in srgb, #EDE6D6 12%, transparent);
|
|
912
969
|
border-color: color-mix(in srgb, var(--board-edge) 45%, transparent);
|
|
913
970
|
}
|
|
914
|
-
body.grok #dms a.nav-row.on
|
|
971
|
+
body.grok #dms a.nav-row.on,
|
|
972
|
+
body.grok #cron-nav a.nav-row.on {
|
|
915
973
|
background: color-mix(in srgb, #EDE6D6 18%, transparent);
|
|
916
974
|
border-color: color-mix(in srgb, var(--pin) 40%, transparent);
|
|
917
975
|
}
|
|
918
|
-
body.grok #dms .nav-chip
|
|
976
|
+
body.grok #dms .nav-chip,
|
|
977
|
+
body.grok #cron-nav .nav-chip {
|
|
919
978
|
color: #E8C48A;
|
|
920
979
|
background: #16100B;
|
|
921
980
|
}
|
|
922
|
-
body.grok #dms .busy-flash
|
|
981
|
+
body.grok #dms .busy-flash,
|
|
982
|
+
body.grok #cron-nav .busy-flash { box-shadow: 0 0 0 2px #16100B; }
|
|
923
983
|
body.grok .sidebar .side-foot {
|
|
924
984
|
position: absolute;
|
|
925
985
|
left: 0;
|
|
@@ -1578,7 +1638,8 @@ a.invite-btn:hover { text-decoration: none; }
|
|
|
1578
1638
|
min-height: 240px;
|
|
1579
1639
|
height: 360px;
|
|
1580
1640
|
border: 0;
|
|
1581
|
-
background:
|
|
1641
|
+
background: transparent;
|
|
1642
|
+
color-scheme: dark;
|
|
1582
1643
|
border-radius: 0 0 12px 12px;
|
|
1583
1644
|
}
|
|
1584
1645
|
.assistant-text .md-html-preview[data-view="code"] .md-html-frame { display: none; }
|
|
@@ -1894,6 +1955,10 @@ a.invite-btn:hover { text-decoration: none; }
|
|
|
1894
1955
|
.turn-actions .turn-stop {
|
|
1895
1956
|
color: var(--danger);
|
|
1896
1957
|
}
|
|
1958
|
+
.turn-actions .turn-pause,
|
|
1959
|
+
.turn-actions .turn-continue {
|
|
1960
|
+
color: var(--steel);
|
|
1961
|
+
}
|
|
1897
1962
|
.turn-actions .turn-steer {
|
|
1898
1963
|
color: #b4c8e8;
|
|
1899
1964
|
}
|
|
@@ -1907,6 +1972,8 @@ body.grok .turn-actions button:hover {
|
|
|
1907
1972
|
color: #f2eadf;
|
|
1908
1973
|
}
|
|
1909
1974
|
body.grok .turn-actions .turn-stop { color: #d4a090; }
|
|
1975
|
+
body.grok .turn-actions .turn-pause,
|
|
1976
|
+
body.grok .turn-actions .turn-continue { color: #c4b090; }
|
|
1910
1977
|
body.grok .turn-actions .turn-steer { color: #c4b090; }
|
|
1911
1978
|
.turn-status .clock {
|
|
1912
1979
|
margin-left: 8px;
|
|
@@ -2931,6 +2998,38 @@ dialog p { margin: 0 0 0.8rem; color: var(--muted); font-size: 0.9rem; }
|
|
|
2931
2998
|
color: var(--muted);
|
|
2932
2999
|
font-size: 0.8rem;
|
|
2933
3000
|
}
|
|
3001
|
+
.cron-list {
|
|
3002
|
+
display: flex;
|
|
3003
|
+
flex-direction: column;
|
|
3004
|
+
gap: 8px;
|
|
3005
|
+
max-height: 40vh;
|
|
3006
|
+
overflow: auto;
|
|
3007
|
+
margin: 0 0 1rem;
|
|
3008
|
+
}
|
|
3009
|
+
.cron-row {
|
|
3010
|
+
display: grid;
|
|
3011
|
+
grid-template-columns: 1fr auto;
|
|
3012
|
+
gap: 8px;
|
|
3013
|
+
padding: 8px 10px;
|
|
3014
|
+
border: 1px solid var(--line);
|
|
3015
|
+
border-radius: 12px;
|
|
3016
|
+
background: var(--fill);
|
|
3017
|
+
}
|
|
3018
|
+
.cron-row b { display: block; font-size: 0.92rem; }
|
|
3019
|
+
.cron-row span { color: var(--muted); font-size: 0.78rem; }
|
|
3020
|
+
.cron-row .cron-acts { display: flex; gap: 4px; align-items: center; }
|
|
3021
|
+
.cron-row .cron-acts button {
|
|
3022
|
+
margin: 0;
|
|
3023
|
+
padding: 0.15rem 0.45rem;
|
|
3024
|
+
font-size: 0.72rem;
|
|
3025
|
+
}
|
|
3026
|
+
#cron-dialog label { display: block; margin: 0.6rem 0 0.2rem; font-size: 0.8rem; color: var(--muted); }
|
|
3027
|
+
#cron-dialog input,
|
|
3028
|
+
#cron-dialog select,
|
|
3029
|
+
#cron-dialog textarea {
|
|
3030
|
+
width: 100%;
|
|
3031
|
+
box-sizing: border-box;
|
|
3032
|
+
}
|
|
2934
3033
|
.dialog-actions { display: flex; justify-content: flex-end; gap: 0.4rem; }
|
|
2935
3034
|
.dialog-actions button {
|
|
2936
3035
|
margin: 0;
|
|
@@ -3107,13 +3206,23 @@ dialog p { margin: 0 0 0.8rem; color: var(--muted); font-size: 0.9rem; }
|
|
|
3107
3206
|
line-height: 1.45;
|
|
3108
3207
|
}
|
|
3109
3208
|
.traj-empty { color: #8b8b8b; padding: 1.2rem; }
|
|
3209
|
+
.md-img-link {
|
|
3210
|
+
display: inline-block;
|
|
3211
|
+
max-width: 22rem;
|
|
3212
|
+
margin: 0.45rem 0;
|
|
3213
|
+
line-height: 0;
|
|
3214
|
+
border-radius: 12px;
|
|
3215
|
+
}
|
|
3110
3216
|
.md-img,
|
|
3111
3217
|
.row-card.image .md-img {
|
|
3112
3218
|
display: block;
|
|
3113
|
-
max-width:
|
|
3219
|
+
max-width: 22rem;
|
|
3220
|
+
max-height: min(16rem, 40vh);
|
|
3221
|
+
width: auto;
|
|
3114
3222
|
height: auto;
|
|
3223
|
+
object-fit: contain;
|
|
3115
3224
|
border-radius: 12px;
|
|
3116
|
-
margin: 0
|
|
3225
|
+
margin: 0;
|
|
3117
3226
|
background: #111;
|
|
3118
3227
|
}
|
|
3119
3228
|
.row-card.image .term-out {
|