vibe-coding-master 0.2.1 → 0.2.3

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.
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { TRANSLATION_PROMPT_KEYS } from "../../shared/types/translation.js";
2
+ import { TRANSLATION_ENTRY_RETENTION_LIMIT, TRANSLATION_PROMPT_KEYS } from "../../shared/types/translation.js";
3
3
  import { TranslationProviderError } from "../adapters/translation-provider.js";
4
4
  import { VcmError } from "../errors.js";
5
5
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
@@ -17,11 +17,11 @@ const DEFAULT_SETTINGS = {
17
17
  inputMode: "review-before-send",
18
18
  translateOutput: true,
19
19
  translateUserInput: true,
20
- contextEnabled: true,
20
+ contextEnabled: false,
21
21
  preserveTechnicalTokens: true,
22
22
  skipCjkText: true,
23
23
  redactSecrets: true,
24
- requestTimeoutMs: 15000,
24
+ requestTimeoutMs: 120000,
25
25
  temperature: 0.1
26
26
  };
27
27
  const TRANSCRIPT_REPLAY_GRACE_MS = 5000;
@@ -62,6 +62,7 @@ export function createTranslationService(deps) {
62
62
  listeners: new Set(),
63
63
  seenTranscriptIds: new Set(),
64
64
  entries: [],
65
+ failures: new Map(),
65
66
  status: "ready",
66
67
  events: [],
67
68
  nextSeq: 1
@@ -92,6 +93,11 @@ export function createTranslationService(deps) {
92
93
  appendEvent(sessionId, { type: "error", id, message });
93
94
  emit(sessionId, { type: "translation-error", id, message });
94
95
  }
96
+ function publishFailures(sessionId) {
97
+ const failures = getFailureItems(getState(sessionId));
98
+ appendEvent(sessionId, { type: "failures", failures });
99
+ emit(sessionId, { type: "translation-failures", failures });
100
+ }
95
101
  function appendEvent(sessionId, input) {
96
102
  const state = getState(sessionId);
97
103
  const event = {
@@ -117,6 +123,7 @@ export function createTranslationService(deps) {
117
123
  if (!state.cacheLoaded) {
118
124
  await loadCachedEvents(state);
119
125
  state.cacheLoaded = true;
126
+ pruneTranslationEntries(input.sessionId);
120
127
  }
121
128
  await deps.fs.ensureDir(path.dirname(cachePath));
122
129
  return state;
@@ -150,6 +157,9 @@ export function createTranslationService(deps) {
150
157
  else if (event.type === "error") {
151
158
  state.status = "failed";
152
159
  }
160
+ else if (event.type === "failures") {
161
+ state.failures = new Map(event.failures.map((failure) => [failure.translationId, failure]));
162
+ }
153
163
  }
154
164
  }
155
165
  async function persistEvents(state) {
@@ -216,14 +226,20 @@ export function createTranslationService(deps) {
216
226
  }
217
227
  }
218
228
  function processClaudeOutputText(sessionId, rawText, config, entryId) {
229
+ return startClaudeOutputTranslation(sessionId, rawText, config, {
230
+ entryId,
231
+ replaceExisting: false
232
+ }) !== undefined;
233
+ }
234
+ function startClaudeOutputTranslation(sessionId, rawText, config, options) {
219
235
  const session = deps.runtime.getSession(sessionId);
220
236
  const roleSession = deps.sessionRegistry.get(sessionId);
221
237
  if (!session && !roleSession) {
222
- return false;
238
+ return undefined;
223
239
  }
224
240
  const { settings, secrets } = config;
225
241
  if (!rawText.trim()) {
226
- return false;
242
+ return undefined;
227
243
  }
228
244
  const text = rawText;
229
245
  const baseEntry = {
@@ -236,11 +252,16 @@ export function createTranslationService(deps) {
236
252
  settings,
237
253
  status: "translating",
238
254
  contextUsed: false,
239
- id: entryId
255
+ id: options.entryId
240
256
  }),
241
257
  translationStartedAt: now()
242
258
  };
243
- pushEntry(sessionId, baseEntry);
259
+ if (options.replaceExisting) {
260
+ replaceEntry(sessionId, baseEntry);
261
+ }
262
+ else {
263
+ pushEntry(sessionId, baseEntry);
264
+ }
244
265
  const queue = queues.getQueue(sessionId);
245
266
  void queue.enqueue(async () => {
246
267
  publishStatus(sessionId, "translating");
@@ -265,6 +286,7 @@ export function createTranslationService(deps) {
265
286
  tokenUsage: result.tokenUsage
266
287
  };
267
288
  replaceEntry(sessionId, completed);
289
+ clearFailure(sessionId, completed.id);
268
290
  getState(sessionId).lastAssistantText = text;
269
291
  publishStatus(sessionId, "ready");
270
292
  }
@@ -276,15 +298,23 @@ export function createTranslationService(deps) {
276
298
  completedAt: now()
277
299
  };
278
300
  replaceEntry(sessionId, failed);
301
+ recordFailure(sessionId, failed);
279
302
  publishStatus(sessionId, "failed");
280
303
  }
281
304
  }).catch((error) => {
282
305
  publishError(sessionId, error instanceof Error ? error.message : "Translation failed.");
283
306
  });
284
- return true;
307
+ return baseEntry;
285
308
  }
286
309
  function pushEntry(sessionId, entry) {
287
310
  getState(sessionId).entries.push(entry);
311
+ pruneTranslationEntries(sessionId, new Set([entry.id]));
312
+ publishEntry(sessionId, entry);
313
+ }
314
+ function upsertAndPublishEntry(sessionId, entry) {
315
+ const state = getState(sessionId);
316
+ state.entries = upsertEntry(state.entries, entry);
317
+ pruneTranslationEntries(sessionId, new Set([entry.id]));
288
318
  publishEntry(sessionId, entry);
289
319
  }
290
320
  function pushPreservedTranscriptEntry(sessionId, entryId, sourceText, settings) {
@@ -311,9 +341,100 @@ export function createTranslationService(deps) {
311
341
  }
312
342
  function replaceEntry(sessionId, entry) {
313
343
  const state = getState(sessionId);
314
- state.entries = state.entries.map((current) => current.id === entry.id ? entry : current);
344
+ state.entries = upsertEntry(state.entries, entry);
345
+ pruneTranslationEntries(sessionId, new Set([entry.id]));
315
346
  publishEntry(sessionId, entry);
316
347
  }
348
+ function recordFailure(sessionId, entry) {
349
+ if (!isRetryableFailedEntry(entry)) {
350
+ return;
351
+ }
352
+ const state = getState(sessionId);
353
+ const existing = state.failures.get(entry.id);
354
+ state.failures.set(entry.id, {
355
+ translationId: entry.id,
356
+ sessionId,
357
+ taskSlug: entry.taskSlug,
358
+ role: entry.role,
359
+ sourceText: entry.sourceText,
360
+ error: entry.error ?? "Translation failed.",
361
+ failedAt: entry.completedAt ?? now(),
362
+ retryCount: existing?.retryCount ?? 0,
363
+ lastRetryAt: existing?.lastRetryAt
364
+ });
365
+ publishFailures(sessionId);
366
+ }
367
+ function clearFailure(sessionId, translationId) {
368
+ const state = getState(sessionId);
369
+ if (state.failures.delete(translationId)) {
370
+ publishFailures(sessionId);
371
+ }
372
+ }
373
+ function pruneTranslationEntries(sessionId, protectedIds = new Set()) {
374
+ const state = getState(sessionId);
375
+ const overflow = state.entries.length - TRANSLATION_ENTRY_RETENTION_LIMIT;
376
+ if (overflow <= 0) {
377
+ return;
378
+ }
379
+ const removedIds = new Set();
380
+ for (const entry of state.entries) {
381
+ if (removedIds.size >= overflow) {
382
+ break;
383
+ }
384
+ if (protectedIds.has(entry.id) || isActiveTranslationEntry(entry)) {
385
+ continue;
386
+ }
387
+ removedIds.add(entry.id);
388
+ }
389
+ if (removedIds.size === 0) {
390
+ return;
391
+ }
392
+ state.entries = state.entries.filter((entry) => !removedIds.has(entry.id));
393
+ state.events = pruneTranslationEntryEvents(state.events, removedIds);
394
+ void persistEvents(state);
395
+ let failuresChanged = false;
396
+ for (const entryId of removedIds) {
397
+ failuresChanged = state.failures.delete(entryId) || failuresChanged;
398
+ }
399
+ if (failuresChanged) {
400
+ publishFailures(sessionId);
401
+ }
402
+ }
403
+ function markFailureRetrying(sessionId, failure) {
404
+ const retrying = {
405
+ ...failure,
406
+ retryCount: failure.retryCount + 1,
407
+ lastRetryAt: now()
408
+ };
409
+ getState(sessionId).failures.set(failure.translationId, retrying);
410
+ return retrying;
411
+ }
412
+ function retryOneTranslation(sessionId, original, config) {
413
+ const state = getState(sessionId);
414
+ const existingFailure = state.failures.get(original.id) ?? {
415
+ translationId: original.id,
416
+ sessionId,
417
+ taskSlug: original.taskSlug,
418
+ role: original.role,
419
+ sourceText: original.sourceText,
420
+ error: original.error ?? "Translation failed.",
421
+ failedAt: original.completedAt ?? now(),
422
+ retryCount: 0
423
+ };
424
+ markFailureRetrying(sessionId, existingFailure);
425
+ const retrying = startClaudeOutputTranslation(sessionId, original.sourceText, config, {
426
+ entryId: original.id,
427
+ replaceExisting: true
428
+ });
429
+ if (!retrying) {
430
+ throw new VcmError({
431
+ code: "TRANSLATION_RETRY_UNSUPPORTED",
432
+ message: "Translation entry cannot be retried.",
433
+ statusCode: 400
434
+ });
435
+ }
436
+ return retrying;
437
+ }
317
438
  function createEntry(input) {
318
439
  return {
319
440
  id: input.id ?? id(),
@@ -327,6 +448,9 @@ export function createTranslationService(deps) {
327
448
  translatedText: input.translatedText ?? "",
328
449
  status: input.status,
329
450
  contextUsed: input.contextUsed,
451
+ boundaryKind: input.boundaryKind,
452
+ conversationTurn: input.conversationTurn,
453
+ occurredAt: input.occurredAt,
330
454
  createdAt: now(),
331
455
  completedAt: input.completedAt,
332
456
  provider: input.settings.providerType,
@@ -413,6 +537,44 @@ export function createTranslationService(deps) {
413
537
  events
414
538
  };
415
539
  },
540
+ async recordConversationBoundary(input) {
541
+ const { settings } = await loadConfig();
542
+ const state = await prepareCache({
543
+ repoRoot: input.taskRepoRoot ?? input.repoRoot,
544
+ taskSlug: input.taskSlug,
545
+ role: input.role,
546
+ sessionId: input.sessionId
547
+ });
548
+ const conversationTurn = resolveConversationBoundaryTurn(state, input.boundaryKind);
549
+ if (conversationTurn === undefined) {
550
+ return undefined;
551
+ }
552
+ const entryId = `boundary:${input.sessionId}:${conversationTurn}:${input.boundaryKind}`;
553
+ const existing = state.entries.find((entry) => entry.id === entryId);
554
+ if (existing) {
555
+ return existing;
556
+ }
557
+ const occurredAt = input.occurredAt ?? now();
558
+ const sourceText = formatConversationBoundaryText(input.boundaryKind, conversationTurn, occurredAt);
559
+ const entry = createEntry({
560
+ taskSlug: input.taskSlug,
561
+ role: input.role,
562
+ direction: "cc-output-to-user",
563
+ sourceKind: "conversation-boundary",
564
+ sourceText,
565
+ settings,
566
+ status: "preserved",
567
+ contextUsed: false,
568
+ id: entryId,
569
+ translatedText: sourceText,
570
+ completedAt: occurredAt,
571
+ boundaryKind: input.boundaryKind,
572
+ conversationTurn,
573
+ occurredAt
574
+ });
575
+ upsertAndPublishEntry(input.sessionId, entry);
576
+ return entry;
577
+ },
416
578
  async translateUserInput(input) {
417
579
  const { settings, secrets } = await loadConfig();
418
580
  if (!input.text.trim()) {
@@ -524,6 +686,7 @@ export function createTranslationService(deps) {
524
686
  for (const entry of state.entries) {
525
687
  listener({ type: "translation-entry", entry });
526
688
  }
689
+ listener({ type: "translation-failures", failures: getFailureItems(state) });
527
690
  if (!state.unsubscribeTranscript) {
528
691
  if (!roleSession) {
529
692
  listener({
@@ -545,6 +708,7 @@ export function createTranslationService(deps) {
545
708
  async clearSession(sessionId) {
546
709
  const state = getState(sessionId);
547
710
  state.entries = [];
711
+ state.failures.clear();
548
712
  state.events = [];
549
713
  state.nextSeq = 1;
550
714
  queues.clearQueue(sessionId);
@@ -577,16 +741,45 @@ export function createTranslationService(deps) {
577
741
  statusCode: 404
578
742
  });
579
743
  }
580
- if (original.direction !== "cc-output-to-user") {
744
+ if (!isRetryableFailedEntry(original)) {
581
745
  throw new VcmError({
582
746
  code: "TRANSLATION_RETRY_UNSUPPORTED",
583
- message: "Only Claude Code output translation entries can be retried.",
747
+ message: "Only failed Claude Code output prose translation entries can be retried.",
584
748
  statusCode: 400
585
749
  });
586
750
  }
587
751
  const config = await loadConfig();
588
- processClaudeOutputText(sessionId, original.sourceText, config);
589
- return state.entries[state.entries.length - 1] ?? original;
752
+ const retrying = retryOneTranslation(sessionId, original, config);
753
+ publishFailures(sessionId);
754
+ return retrying;
755
+ },
756
+ async retryFailedTranslations(sessionId) {
757
+ const state = getState(sessionId);
758
+ const failures = getFailureItems(state);
759
+ const config = await loadConfig();
760
+ let changed = false;
761
+ for (const failure of failures) {
762
+ const original = state.entries.find((entry) => entry.id === failure.translationId);
763
+ if (!original || !isRetryableFailedEntry(original)) {
764
+ state.failures.delete(failure.translationId);
765
+ changed = true;
766
+ continue;
767
+ }
768
+ retryOneTranslation(sessionId, original, config);
769
+ changed = true;
770
+ }
771
+ if (changed) {
772
+ publishFailures(sessionId);
773
+ }
774
+ return { failures: getFailureItems(state) };
775
+ },
776
+ async ignoreTranslationFailures(sessionId) {
777
+ const state = getState(sessionId);
778
+ if (state.failures.size > 0) {
779
+ state.failures.clear();
780
+ publishFailures(sessionId);
781
+ }
782
+ return { failures: [] };
590
783
  }
591
784
  };
592
785
  async function writeToCurrentRole(repoRoot, taskSlug, role, text) {
@@ -658,6 +851,80 @@ function formatUnknown(value) {
658
851
  return String(value);
659
852
  }
660
853
  }
854
+ function resolveConversationBoundaryTurn(state, boundaryKind) {
855
+ const openTurn = getOpenConversationTurn(state);
856
+ if (boundaryKind === "start") {
857
+ return openTurn ?? getMaxConversationTurn(state) + 1;
858
+ }
859
+ return openTurn;
860
+ }
861
+ function getOpenConversationTurn(state) {
862
+ const startTurns = getBoundaryEntries(state)
863
+ .filter((entry) => entry.boundaryKind === "start")
864
+ .map((entry) => entry.conversationTurn)
865
+ .filter((turn) => typeof turn === "number" && Number.isFinite(turn))
866
+ .sort((left, right) => right - left);
867
+ return startTurns.find((turn) => !hasBoundaryEntry(state, turn, "end"));
868
+ }
869
+ function getMaxConversationTurn(state) {
870
+ return Math.max(0, ...getBoundaryEntries(state)
871
+ .map((entry) => entry.conversationTurn)
872
+ .filter((turn) => typeof turn === "number" && Number.isFinite(turn)));
873
+ }
874
+ function hasBoundaryEntry(state, conversationTurn, boundaryKind) {
875
+ return getBoundaryEntries(state).some((entry) => entry.boundaryKind === boundaryKind &&
876
+ entry.conversationTurn === conversationTurn);
877
+ }
878
+ function getBoundaryEntries(state) {
879
+ return state.entries.filter((entry) => entry.sourceKind === "conversation-boundary");
880
+ }
881
+ function getFailureItems(state) {
882
+ return Array.from(state.failures.values());
883
+ }
884
+ function isActiveTranslationEntry(entry) {
885
+ return entry.status === "queued" || entry.status === "translating";
886
+ }
887
+ function isRetryableFailedEntry(entry) {
888
+ return entry.status === "failed"
889
+ && entry.direction === "cc-output-to-user"
890
+ && entry.sourceKind === "prose";
891
+ }
892
+ function pruneTranslationEntryEvents(events, removedIds) {
893
+ const pruned = [];
894
+ for (const event of events) {
895
+ if (event.type === "entry") {
896
+ if (!removedIds.has(event.entry.id)) {
897
+ pruned.push(event);
898
+ }
899
+ continue;
900
+ }
901
+ if (event.type === "failures") {
902
+ pruned.push({
903
+ ...event,
904
+ failures: event.failures.filter((failure) => !removedIds.has(failure.translationId))
905
+ });
906
+ continue;
907
+ }
908
+ pruned.push(event);
909
+ }
910
+ return pruned;
911
+ }
912
+ function formatConversationBoundaryText(boundaryKind, conversationTurn, occurredAt) {
913
+ const label = boundaryKind === "start" ? "开始" : "结束";
914
+ return `-------${label}---第 ${conversationTurn} 轮----${formatBoundaryTime(occurredAt)}---------------`;
915
+ }
916
+ function formatBoundaryTime(value) {
917
+ const date = new Date(value);
918
+ if (Number.isNaN(date.getTime())) {
919
+ return value;
920
+ }
921
+ return new Intl.DateTimeFormat(undefined, {
922
+ hour: "2-digit",
923
+ minute: "2-digit",
924
+ second: "2-digit",
925
+ hour12: false
926
+ }).format(date);
927
+ }
661
928
  function upsertEntry(entries, entry) {
662
929
  const index = entries.findIndex((current) => current.id === entry.id);
663
930
  if (index === -1) {
@@ -23,6 +23,8 @@ export function renderReviewerHarnessRules() {
23
23
 
24
24
  - Independently design the test coverage needed to prove the implemented behavior, including missing cases beyond coder's baseline tests.
25
25
  - Decide whether stronger L1/L2/L3 validation is needed for final confidence.
26
+ - Before final validation, perform a full cache cleanup, then rerun validation from a clean state.
27
+ - Do not use validation results produced before full cache cleanup as final acceptance evidence.
26
28
  - Add or modify tests needed for test adequacy.
27
29
  - Do not skip smoke, integration, or E2E checks only because coder did not run them.
28
30
 
@@ -3,3 +3,4 @@ export const TRANSLATION_PROMPT_KEYS = [
3
3
  "zh-to-en-with-context",
4
4
  "en-to-zh"
5
5
  ];
6
+ export const TRANSLATION_ENTRY_RETENTION_LIMIT = 500;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
+ * https://github.com/chjj/term.js
5
+ * @license MIT
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ *
25
+ * Originally forked from (with the author's permission):
26
+ * Fabrice Bellard's javascript vt100 for jslinux:
27
+ * http://bellard.org/jslinux/
28
+ * Copyright (c) 2011 Fabrice Bellard
29
+ * The original design remains. The terminal itself
30
+ * has been extended to include xterm CSI codes, among
31
+ * other features.
32
+ */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}:root{color-scheme:light;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;background:#f5f2ea;color:#1c2024;line-height:1.5}html,body,#root{height:100%}*{box-sizing:border-box}body{margin:0;min-width:320px;min-height:100vh;background:#f5f2ea}button,input,select,textarea{font:inherit}button{border:1px solid #9ba6ad;background:#f8f7f2;color:#1d252b;border-radius:6px;min-height:34px;padding:6px 10px;cursor:pointer}button:hover:not(:disabled){background:#eef4f2;border-color:#607d74}button:disabled{cursor:not-allowed;opacity:.55}.danger-button{border-color:#b84a45;background:#fff1ee;color:#8d211d;font-weight:750}.danger-button:hover:not(:disabled){border-color:#8d211d;background:#ffe2dc}input,select,textarea:not(.xterm-helper-textarea){width:100%;border:1px solid #b9b0a1;border-radius:6px;background:#fffdf8;color:#202326;padding:8px 10px}textarea:not(.xterm-helper-textarea){min-height:240px;resize:vertical;font-family:Menlo,Monaco,Consolas,monospace;font-size:12px}h1,h2,p{margin-top:0}h1{margin-bottom:4px;font-size:26px;line-height:1.15}h2{margin-bottom:10px;font-size:14px;letter-spacing:0}.app-shell{display:grid;grid-template-columns:minmax(280px,320px) minmax(0,1fr);height:100vh;min-height:0;overflow:hidden}.app-shell.is-sidebar-collapsed{grid-template-columns:46px minmax(0,1fr)}.app-sidebar{position:relative;display:flex;flex-direction:column;min-width:0;border-right:1px solid #d3c9b8;background:#fbfaf6;padding:14px;overflow:auto}.app-shell.is-sidebar-collapsed .app-sidebar{overflow:hidden;padding:8px}.sidebar-toggle{position:absolute;top:10px;right:10px;z-index:2;display:grid;place-items:center;width:28px;min-height:28px;padding:0;background:#fffdf8}.sidebar-toggle:before{width:8px;height:8px;border-color:currentColor;border-style:solid;border-width:0 2px 2px 0;content:"";transform:translate(2px) rotate(135deg)}.app-shell.is-sidebar-collapsed .sidebar-toggle{left:9px;right:auto}.app-shell.is-sidebar-collapsed .sidebar-toggle:before{transform:translate(-2px) rotate(-45deg)}.sidebar-content{display:flex;flex:1;min-width:0;min-height:0}.project-dashboard{display:flex;flex:1;flex-direction:column;width:100%;min-height:100%}.app-shell.is-sidebar-collapsed .sidebar-content{width:0;opacity:0;pointer-events:none}.app-main{min-width:0;height:100%;padding:14px 16px;overflow:auto}.brand-header{display:flex;gap:12px;align-items:baseline;margin-bottom:10px;padding-right:34px}.brand-header strong{font-size:18px}.brand-header span,.muted{color:#667071;font-size:13px}.sidebar-section{margin-bottom:8px;border:1px solid #e0d6c7;border-radius:8px;background:#fffdfa;overflow:hidden}.sidebar-section-toggle{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center;width:100%;min-height:34px;border:0;border-radius:0;background:transparent;color:#1c2024;font-size:13px;font-weight:750;text-align:left}.sidebar-section-toggle:hover{background:#f5f1e8}.sidebar-section-toggle[aria-expanded=true]{border-bottom:1px solid #ece5d9}.sidebar-section-chevron{width:8px;height:8px;border-color:currentColor;border-style:solid;border-width:0 2px 2px 0;transform:rotate(45deg)}.sidebar-section-toggle[aria-expanded=true] .sidebar-section-chevron{transform:rotate(-135deg)}.sidebar-section-content{padding:8px}.repo-connect,.project-summary,.harness-panel,.task-create{margin:0}.inline-form{display:grid;grid-template-columns:minmax(0,1fr);gap:8px}.inline-form.has-recent-paths{grid-template-columns:minmax(0,1fr) auto}.inline-form>input{grid-column:1 / -1}.inline-form>button{justify-self:end}.inline-form.has-recent-paths>button{justify-self:auto}.repo-recent-select{min-width:0;max-width:none}.project-summary dl{display:grid;gap:8px;margin:0}.project-summary div{min-width:0}.project-summary dt{color:#6c6255;font-size:12px}.project-summary dd{margin:0;overflow-wrap:anywhere;font-size:13px}.warnings,.error-banner{border:1px solid #c87b54;background:#fff4ed;color:#6f3218;border-radius:6px;padding:10px 12px}.flow-pause-alert-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;display:grid;place-items:center;background:#1f242b85;padding:20px}.flow-pause-alert{display:grid;gap:12px;width:min(460px,100%);border:1px solid #2f6f73;border-radius:8px;background:#e8f4f2;color:#123f43;box-shadow:0 28px 72px #1f242b47;padding:22px}.flow-pause-alert-kicker{margin:0;color:#2f6f73;font-size:12px;font-weight:700;text-transform:uppercase}.flow-pause-alert h2{margin:0;font-size:24px;line-height:1.15}.flow-pause-alert p{margin:0;font-size:14px;line-height:1.5}.flow-pause-alert-hint{color:#49666a}.flow-pause-alert button{justify-self:end;min-width:110px;border:1px solid #2f6f73;background:#2f6f73;color:#fffdf8}.flow-pause-alert button:hover{background:#245c60}.warnings{margin:12px 0 0;padding-left:26px;font-size:13px}.harness-panel{display:grid;gap:10px}.harness-stage{display:grid;gap:8px}.harness-panel-header{display:flex;justify-content:space-between;gap:8px;align-items:center}.harness-panel-header h2,.harness-panel-header p,.harness-result p{margin-bottom:0}.harness-actions{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.harness-file-list{display:grid;gap:6px;margin:0;padding:0;list-style:none}.harness-file-list li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center;border:1px solid #ece5d9;border-radius:6px;padding:6px 8px;background:#fffdfa}.harness-file-list span{overflow:hidden;font-size:12px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.harness-changes,.harness-result{border:1px solid #e0d6c7;border-radius:6px;padding:8px;background:#f8f7f2;font-size:12px}.harness-changes h3{margin:0 0 4px;font-size:12px}.harness-changes ul,.harness-result ul{margin:0;padding-left:18px}.harness-bootstrap-session{display:grid;gap:8px;border:1px solid #e0d6c7;border-radius:6px;padding:8px;background:#f8f7f2;font-size:12px}.harness-bootstrap-session>div:first-child{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center}.harness-bootstrap-session span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.harness-bootstrap-modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:80;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr);background:#1f242b94;padding:14px}.harness-bootstrap-modal-surface{min-width:0;min-height:0;display:grid;grid-template-rows:auto minmax(0,1fr);gap:10px;border:1px solid #b9b0a1;border-radius:8px;background:#fffdf8;box-shadow:0 22px 64px #1f242b47;padding:10px}.harness-bootstrap-modal-header{display:flex;gap:12px;align-items:center;justify-content:space-between;min-width:0}.harness-bootstrap-modal-header p{max-width:min(720px,70vw);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.harness-bootstrap-modal-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:flex-end}.harness-bootstrap-terminal{min-width:0;min-height:0;display:grid}.task-create form{display:grid;gap:8px}.task-create-preview{display:grid;gap:2px;border:1px solid #e0d6c7;border-radius:6px;background:#f8f7f2;padding:6px 8px}.task-create-option{display:flex;align-items:center;gap:8px;color:#1f242b;font-size:13px;font-weight:700}.task-create-option input{width:16px;height:16px;margin:0}.task-create-preview span{color:#255f3d;font-size:12px;font-weight:700}.task-create-preview small{overflow:hidden;color:#687273;font-size:11px;text-overflow:ellipsis;white-space:nowrap}.task-nav{display:grid;gap:8px}.task-nav-item{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center;text-align:left}.task-nav-item.is-active,.role-tab.is-active{border-color:#2f6f73;background:#e8f1ef}.sidebar-settings{display:grid;gap:8px}.sidebar-settings button{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center;width:100%;text-align:left}.sidebar-settings .settings-toggle.is-active{border-color:#2f6f73;background:#e8f1ef}.sidebar-settings .theme-mode-toggle span:last-child{font-weight:750}.task-status-dock{display:grid;gap:10px;margin-top:auto;border-top:1px solid #d3c9b8;padding-top:10px}.task-status-dock-title,.current-round-title{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center}.task-status-dock-title strong{overflow:hidden;font-size:13px;line-height:1.25;text-overflow:ellipsis;white-space:nowrap}.task-status-stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;margin:0}.task-status-stats div,.current-round-status{min-width:0;border:1px solid #e0d6c7;border-radius:6px;background:#fffdfa}.task-status-stats div{padding:6px 7px}.task-status-stats dt{color:#6c6255;font-size:10px;font-weight:700;text-transform:uppercase}.task-status-stats dd{margin:0;overflow:hidden;color:#1f242b;font-size:12px;font-weight:750;text-overflow:ellipsis;white-space:nowrap}.current-round-status{display:grid;gap:8px;padding:8px}.current-round-title span:first-child{color:#3a4647;font-size:12px;font-weight:750}.workspace-header{display:grid;grid-template-columns:minmax(120px,max-content) minmax(420px,1fr) auto;gap:10px;align-items:center;margin-bottom:6px}.workspace-title-line{display:flex;min-width:0}.workspace-title-line h1{margin-bottom:0;font-size:18px;line-height:1.15}.workspace-header-actions{display:flex;gap:8px;align-items:center;justify-content:flex-end;min-width:max-content}.role-tabs{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin-bottom:8px;min-width:0}.workspace-header .role-tabs{margin-bottom:0}.role-tab{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px;align-items:center;min-height:30px;padding:4px 8px;text-align:left}.workspace-grid{display:grid;grid-template-columns:minmax(0,1fr);flex:1;gap:10px;align-items:stretch;min-height:0}.workspace-main{min-width:0;min-height:0;display:flex;flex-direction:column;gap:8px}.role-console-stack{min-width:0;min-height:0;flex:1;display:flex;flex-direction:column}.role-console-panel{min-width:0;min-height:0;flex:1;display:none}.role-console-panel.is-active{display:flex;flex-direction:column}.session-console,.message-panel,.event-log,.empty-workspace{border:1px solid #d6d0c6;border-radius:8px;background:#fffdf8;padding:10px}.task-workspace{display:flex;flex-direction:column;gap:8px;height:100%;min-height:0}.session-console{display:grid;grid-template-rows:auto minmax(0,1fr);gap:8px;flex:1;height:100%;min-height:0}.session-console-top{display:flex;gap:10px;align-items:center;justify-content:space-between}.session-console-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:flex-end}.session-controls{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:space-between;margin:0 0 8px}.permission-mode-field{display:grid;grid-template-columns:auto minmax(180px,260px);gap:8px;align-items:center;width:fit-content;max-width:100%}.permission-mode-field span{color:#5f6a6c;font-size:13px;font-weight:650}.permission-mode-field small{display:block;color:#7b8587;font-size:11px;font-weight:500;line-height:1.2}.permission-mode-field select{width:100%;min-height:30px;border:1px solid #b9b0a1;border-radius:6px;background:#fffdf8;color:#202326;padding:4px 8px}.session-toolbar{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.session-toolbar button{min-height:30px;padding:4px 9px}.translation-toggle{display:inline-flex;align-items:center;justify-content:center;min-height:28px;border:1px solid #b5bec4;border-radius:6px;background:#fffefa;color:#4f5558;font-size:12px;font-weight:650;padding:3px 10px;white-space:nowrap}.translation-toggle.is-active{border-color:#2f7e84;background:#e8f4f2;color:#145e64}.translation-settings-grid input[type=checkbox]{width:auto}.session-console-body{display:grid;grid-template-columns:minmax(0,1fr);min-width:0;min-height:0;height:100%}.session-console-body.has-translation{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:10px;align-items:stretch}.terminal-pane,.translation-pane{display:grid;min-width:0;min-height:0;height:100%}.terminal-frame,.terminal-empty{width:100%;height:100%;min-height:0;border-radius:6px;overflow:hidden;background:#111316}.terminal-empty{display:grid;place-items:center;align-content:center;gap:8px;color:#d6d0c6;border:1px solid #292d31}.translation-panel{display:grid;grid-template-rows:auto minmax(0,1fr) auto;gap:8px;height:100%;min-height:0;border:1px solid #292d31;border-radius:6px;background:#0d1117;color:#d6deeb;padding:8px;min-width:0;width:100%;overflow:hidden;font-family:Menlo,Monaco,Consolas,monospace}.translation-panel-header{display:grid;gap:3px}.translation-panel-titlebar,.translation-panel-actions,.translation-status-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center;justify-content:space-between}.translation-panel-header h2,.translation-panel-header p{margin-bottom:0}.translation-panel-titlebar h2{font-size:16px}.translation-panel-header p,.translation-composer span{color:#8b949e;font-size:12px}.translation-panel-actions button{border-color:#3a4149;background:#161b22;color:#d6deeb;min-height:26px;padding:2px 8px;font-size:12px}.translation-panel-actions button:hover:not(:disabled),.translation-composer-actions button:hover:not(:disabled){border-color:#58a6ff;background:#1f2937}.translation-panel-actions .auto-send-toggle.is-active{border-color:#56d364;background:#12261a;color:#d6deeb}.translation-panel-actions{justify-content:flex-end}.translation-status-row{flex-wrap:nowrap}.translation-status-row p:last-child{flex:0 0 auto;text-align:right}.translation-entry-list{display:grid;align-content:start;gap:8px;min-height:0;min-width:0;overflow-x:hidden;overflow-y:auto;scrollbar-color:#4b5563 #0d1117}.translation-entry{border:0;border-radius:0;background:transparent;min-width:0;max-width:100%;padding:0}.translation-conversation-boundary{display:grid;grid-template-columns:minmax(18px,1fr) auto auto auto minmax(18px,1fr);gap:8px;align-items:center;color:#8b949e;font-family:Menlo,Monaco,Consolas,monospace;font-size:12px;line-height:1.4;white-space:nowrap}.translation-boundary-dash{min-width:0;border-top:1px dashed #3a4149}.translation-entry.is-user-input{border-top:4px solid #3a4149;margin-top:14px;padding-top:14px}.translation-entry.is-user-input:first-child{margin-top:0}.translation-entry pre{box-sizing:border-box;margin:0;max-height:none;max-width:100%;min-width:0;overflow:visible;white-space:pre-wrap;overflow-wrap:anywhere;font-family:Menlo,Monaco,Consolas,monospace;font-size:12px;line-height:1.45;color:#d6deeb}.translation-markdown{max-width:100%;min-width:0;color:#d6deeb;font-size:12px;line-height:1.55;overflow-wrap:anywhere}.translation-markdown>:first-child{margin-top:0}.translation-markdown>:last-child{margin-bottom:0}.translation-markdown p,.translation-markdown ul,.translation-markdown ol,.translation-markdown blockquote,.translation-markdown pre,.translation-markdown table{margin:0 0 8px}.translation-markdown h1,.translation-markdown h2,.translation-markdown h3,.translation-markdown h4,.translation-markdown h5,.translation-markdown h6{margin:10px 0 6px;color:#f0f6fc;font-weight:700;line-height:1.25}.translation-markdown h1{font-size:17px}.translation-markdown h2{font-size:15px}.translation-markdown h3,.translation-markdown h4,.translation-markdown h5,.translation-markdown h6{font-size:13px}.translation-markdown ul,.translation-markdown ol{padding-left:22px}.translation-markdown li{margin:2px 0}.translation-markdown .task-list-item{list-style:none}.translation-markdown input[type=checkbox]{width:13px;height:13px;margin:0 6px 0 0;accent-color:#56d364}.translation-markdown blockquote{border-left:3px solid #3a4149;color:#b7c0ca;padding-left:10px}.translation-markdown a{color:#79c0ff}.translation-markdown code{border-radius:4px;background:#161b22;color:#f0f6fc;padding:1px 4px;font-family:Menlo,Monaco,Consolas,monospace;font-size:.95em}.translation-markdown pre{overflow-x:auto;border:1px solid #292d31;border-radius:6px;background:#111316;padding:8px;white-space:pre}.translation-markdown pre code{background:transparent;padding:0}.translation-markdown table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse}.translation-markdown img{max-width:100%;border-radius:4px}.translation-markdown th,.translation-markdown td{border:1px solid #30363d;padding:4px 6px;text-align:left;vertical-align:top}.translation-markdown hr{border:0;border-top:1px solid #292d31;margin:10px 0}.translation-entry.is-tool-output pre{display:block;overflow:hidden;color:#7d8590;text-overflow:ellipsis;white-space:nowrap;width:100%}.translation-entry-note{margin:2px 0 0;color:#6e7681;font-size:10px;line-height:1.35}.translation-entry-note.is-error{color:#a3715f}.translation-composer{display:grid;gap:6px;border-top:1px solid #292d31;padding-top:8px}.translation-composer-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:stretch}.translation-composer textarea{width:100%;min-height:38px;max-height:88px;border-color:#3a4149;background:#0d1117;color:#d6deeb;font-family:inherit;font-size:12px;line-height:1.35;resize:vertical}.translation-composer textarea::placeholder{color:#7d8590}.translation-composer textarea::selection{background:#8b949e59;color:#fff}.translation-composer textarea:focus,.translation-composer textarea:focus-visible{border-color:#4b5563;outline:none;box-shadow:none}.translation-composer-actions{display:grid;align-content:start;gap:6px;min-width:104px}.translation-composer-actions button{border-color:#3a4149;background:#161b22;color:#d6deeb;width:100%;min-height:38px;padding:4px 9px;font-size:12px}.translation-panel .muted{color:#8b949e}.translation-panel .error-banner{border-color:#da7b72;background:#2d1518;color:#ffdcd7}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:20;display:grid;place-items:center;background:#181c1f61;padding:18px}.translation-settings-modal{display:grid;gap:12px;width:min(980px,100%);max-height:min(760px,92vh);overflow:auto;border:1px solid #d6d0c6;border-radius:8px;background:#fffdf8;padding:14px}.message-modal,.event-modal{display:grid;grid-template-rows:auto minmax(0,1fr);gap:12px;width:min(980px,100%);max-height:min(760px,92vh);overflow:hidden;border:1px solid #d6d0c6;border-radius:8px;background:#fffdf8;padding:14px}.translation-settings-modal header,.message-modal header,.event-modal header,.translation-settings-modal footer{display:flex;gap:8px;align-items:center;justify-content:space-between}.translation-prompt-settings{display:grid;gap:10px;border-top:1px solid #ece5d9;padding-top:12px}.translation-prompt-settings header{display:flex;gap:10px;align-items:end;justify-content:space-between}.translation-prompt-settings h3,.translation-prompt-settings p{margin-bottom:0}.translation-prompt-settings h3{font-size:13px}.translation-prompt-settings textarea{min-height:120px;max-height:260px;font-family:Menlo,Monaco,Consolas,monospace;font-size:12px}.translation-prompt-stack{display:grid;gap:10px}.translation-prompt-stack label{display:grid;gap:4px}.translation-prompt-stack span{color:#4f5558;font-size:12px;font-weight:650}.translation-settings-modal h2,.message-modal h2,.message-modal p,.event-modal h2,.event-modal p,.translation-settings-modal p{margin-bottom:0}.translation-settings-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.translation-settings-grid label{display:grid;gap:4px}.translation-settings-grid span{color:#4f5558;font-size:12px;font-weight:650}.translation-settings-grid select,.translation-prompt-settings select{min-height:34px;border:1px solid #b9b0a1;border-radius:6px;background:#fffdf8;color:#202326;padding:6px 10px}.translation-test-result{border-radius:6px;padding:8px;font-size:13px}.translation-test-result.is-ok{border:1px solid #6ea77e;background:#e6f3e9;color:#245334}.translation-test-result.is-error{border:1px solid #c46e5f;background:#fae9e6;color:#6f2b21}.message-panel{display:grid;gap:8px}.message-modal .message-panel,.event-modal .event-log{min-height:0;overflow:auto;border:0;background:transparent;padding:0}.message-panel-header{display:flex;justify-content:space-between;gap:12px;align-items:center}.message-panel-header h2,.message-panel-header p{margin-bottom:0}.message-controls,.message-mode-toggle,.modal-actions,.message-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.message-mode-toggle{color:#4f5558;font-size:13px;font-weight:650}.message-mode-toggle input{width:auto}.message-list{display:grid;gap:8px;margin:0;padding:0;list-style:none}.message-item{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;align-items:start;border:1px solid #ece5d9;border-radius:6px;padding:8px;background:#fffdfa}.message-meta{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:4px}.message-meta time,.message-meta span:not(.status-badge),.message-reason,.message-path{color:#667071;font-size:12px}.message-sequence{min-width:32px;color:#1f242b;font-weight:800}.message-actions button{min-height:30px;padding:4px 10px}.message-item p{display:-webkit-box;margin-bottom:4px;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:2}.message-reason,.message-path{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.event-log ol{margin:0;padding-left:22px;font-size:13px}.event-log{max-height:92px;overflow:auto}.event-log h2{margin-bottom:4px;font-size:13px}.event-log p{margin-bottom:0}.event-log li{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status-badge{display:inline-flex;align-items:center;justify-content:center;min-width:66px;min-height:20px;border-radius:999px;padding:2px 8px;border:1px solid #c7c1b8;background:#f2eee7;color:#4f5558;font-size:11px;white-space:nowrap}.status-active,.status-running,.status-ok{border-color:#6ea77e;background:#e6f3e9;color:#245334}.status-created,.status-idle{border-color:#9aa3a8;background:#edf0f1;color:#465056}.status-blocked,.status-crashed,.status-missing,.status-empty{border-color:#c46e5f;background:#fae9e6;color:#6f2b21}.status-planning,.status-settling,.status-waiting,.status-starting,.status-incomplete{border-color:#c4a34e;background:#f7efcf;color:#604e16}.status-exited,.status-done,.status-stopped,.status-resumable{border-color:#7b98b8;background:#e9f0f8;color:#2e4e70}.status-queued,.status-translating,.status-ready,.status-create,.status-insert,.status-update{border-color:#c4a34e;background:#f7efcf;color:#604e16}.status-rejected,.status-failed,.status-delivery_failed,.status-response_ready_missing_result,.status-cancelled,.status-blocked{border-color:#c46e5f;background:#fae9e6;color:#6f2b21}.status-pending{border-color:#c7c1b8;background:#f2eee7;color:#4f5558}.status-translated,.status-preserved{border-color:#7b98b8;background:#e9f0f8;color:#2e4e70}.empty-workspace{max-width:680px}@media(max-width:980px){.app-shell,.workspace-grid{grid-template-columns:1fr}.app-sidebar{border-right:0;border-bottom:1px solid #d3c9b8}.role-tabs{grid-template-columns:repeat(2,minmax(0,1fr))}.session-console-body.has-translation,.translation-settings-grid{grid-template-columns:1fr}}@media(max-width:560px){.app-main,.app-sidebar{padding:12px}.workspace-header,.inline-form,.inline-form.has-recent-paths{grid-template-columns:1fr;display:grid}.role-tabs{grid-template-columns:1fr}.terminal-frame,.terminal-empty{min-height:300px;height:54vh}.permission-mode-field{grid-template-columns:1fr;width:100%}}.harness-bootstrap-terminal .terminal-frame{height:100%;min-height:0}:root[data-theme=dark]{color-scheme:dark;background:#0d1117;color:#e6edf3}:root[data-theme=dark] body,:root[data-theme=dark] .app-main{background:#0d1117;color:#e6edf3}:root[data-theme=dark] button{border-color:#3d4652;background:#161b22;color:#e6edf3}:root[data-theme=dark] button:hover:not(:disabled){border-color:#58a6ff;background:#1f2937}:root[data-theme=dark] input,:root[data-theme=dark] select,:root[data-theme=dark] textarea:not(.xterm-helper-textarea){border-color:#3d4652;background:#0d1117;color:#e6edf3}:root[data-theme=dark] input::placeholder,:root[data-theme=dark] textarea::placeholder{color:#7d8590}:root[data-theme=dark] .danger-button{border-color:#da7b72;background:#2d1518;color:#ffdcd7}:root[data-theme=dark] .danger-button:hover:not(:disabled){border-color:#ffa198;background:#3d1f23}:root[data-theme=dark] .app-sidebar{border-color:#30363d;background:#0f141b}:root[data-theme=dark] .sidebar-toggle{background:#161b22}:root[data-theme=dark] .brand-header span,:root[data-theme=dark] .muted,:root[data-theme=dark] .message-meta time,:root[data-theme=dark] .message-meta span:not(.status-badge),:root[data-theme=dark] .message-reason,:root[data-theme=dark] .message-path{color:#8b949e}:root[data-theme=dark] .sidebar-section,:root[data-theme=dark] .session-console,:root[data-theme=dark] .message-panel,:root[data-theme=dark] .event-log,:root[data-theme=dark] .empty-workspace,:root[data-theme=dark] .translation-settings-modal,:root[data-theme=dark] .message-modal,:root[data-theme=dark] .event-modal{border-color:#30363d;background:#11161d}:root[data-theme=dark] .sidebar-section-toggle{color:#e6edf3}:root[data-theme=dark] .sidebar-section-toggle:hover{background:#161b22}:root[data-theme=dark] .sidebar-section-toggle[aria-expanded=true],:root[data-theme=dark] .translation-prompt-settings{border-color:#30363d}:root[data-theme=dark] .project-summary dt,:root[data-theme=dark] .permission-mode-field span,:root[data-theme=dark] .translation-settings-grid span,:root[data-theme=dark] .translation-prompt-stack span,:root[data-theme=dark] .message-mode-toggle{color:#b7c0ca}:root[data-theme=dark] .permission-mode-field small,:root[data-theme=dark] .task-create-preview small{color:#8b949e}:root[data-theme=dark] .warnings,:root[data-theme=dark] .error-banner{border-color:#da7b72;background:#2d1518;color:#ffdcd7}:root[data-theme=dark] .flow-pause-alert-backdrop{background:#010409b8}:root[data-theme=dark] .flow-pause-alert{border-color:#56d4dd;background:#10262b;color:#d6fbff;box-shadow:0 28px 72px #0104099e}:root[data-theme=dark] .flow-pause-alert-kicker,:root[data-theme=dark] .flow-pause-alert-hint{color:#9debf2}:root[data-theme=dark] .flow-pause-alert button{border-color:#56d4dd;background:#56d4dd;color:#061114}:root[data-theme=dark] .flow-pause-alert button:hover{background:#84f3fa}:root[data-theme=dark] .harness-file-list li,:root[data-theme=dark] .harness-changes,:root[data-theme=dark] .harness-result,:root[data-theme=dark] .harness-bootstrap-session,:root[data-theme=dark] .task-create-preview,:root[data-theme=dark] .task-status-stats div,:root[data-theme=dark] .current-round-status,:root[data-theme=dark] .message-item{border-color:#30363d;background:#0d1117}:root[data-theme=dark] .harness-bootstrap-modal{background:#010409b8}:root[data-theme=dark] .harness-bootstrap-modal-surface{border-color:#30363d;background:#0d1117;box-shadow:0 22px 64px #0104099e}:root[data-theme=dark] .task-create-option{color:#e6edf3}:root[data-theme=dark] .task-status-dock{border-color:#30363d}:root[data-theme=dark] .task-create-preview span{color:#7ee787}:root[data-theme=dark] .task-status-stats dt{color:#8b949e}:root[data-theme=dark] .task-status-stats dd,:root[data-theme=dark] .current-round-title span:first-child{color:#e6edf3}:root[data-theme=dark] .message-sequence{color:#f0f6fc}:root[data-theme=dark] .task-nav-item.is-active,:root[data-theme=dark] .role-tab.is-active,:root[data-theme=dark] .sidebar-settings .settings-toggle.is-active,:root[data-theme=dark] .translation-toggle.is-active{border-color:#56d4dd;background:#10262b;color:#d6fbff}:root[data-theme=dark] .translation-toggle{border-color:#3d4652;background:#161b22;color:#b7c0ca}:root[data-theme=dark] .permission-mode-field select,:root[data-theme=dark] .translation-settings-grid select,:root[data-theme=dark] .translation-prompt-settings select{border-color:#3d4652;background:#0d1117;color:#e6edf3}:root[data-theme=dark] .terminal-empty{border-color:#30363d}:root[data-theme=dark] .modal-backdrop{background:#010409b8}:root[data-theme=dark] .message-modal .message-panel,:root[data-theme=dark] .event-modal .event-log{background:transparent}:root[data-theme=dark] .status-badge{border-color:#3d4652;background:#161b22;color:#d6deeb}:root[data-theme=dark] .translation-test-result.is-ok,:root[data-theme=dark] .status-active,:root[data-theme=dark] .status-running,:root[data-theme=dark] .status-ok{border-color:#3fb950;background:#12261a;color:#aff5b4}:root[data-theme=dark] .status-created,:root[data-theme=dark] .status-idle{border-color:#69717d;background:#20262f;color:#d6deeb}:root[data-theme=dark] .translation-test-result.is-error,:root[data-theme=dark] .status-blocked,:root[data-theme=dark] .status-crashed,:root[data-theme=dark] .status-missing,:root[data-theme=dark] .status-empty,:root[data-theme=dark] .status-rejected,:root[data-theme=dark] .status-failed,:root[data-theme=dark] .status-delivery_failed,:root[data-theme=dark] .status-response_ready_missing_result,:root[data-theme=dark] .status-cancelled{border-color:#f85149;background:#2d1518;color:#ffdcd7}:root[data-theme=dark] .status-planning,:root[data-theme=dark] .status-settling,:root[data-theme=dark] .status-waiting,:root[data-theme=dark] .status-starting,:root[data-theme=dark] .status-incomplete,:root[data-theme=dark] .status-queued,:root[data-theme=dark] .status-translating,:root[data-theme=dark] .status-ready,:root[data-theme=dark] .status-create,:root[data-theme=dark] .status-insert,:root[data-theme=dark] .status-update{border-color:#d29922;background:#2d2208;color:#f8e3a1}:root[data-theme=dark] .status-exited,:root[data-theme=dark] .status-done,:root[data-theme=dark] .status-stopped,:root[data-theme=dark] .status-resumable,:root[data-theme=dark] .status-translated,:root[data-theme=dark] .status-preserved{border-color:#388bfd;background:#10223a;color:#c9e2ff}:root[data-theme=dark] .status-pending{border-color:#3d4652;background:#161b22;color:#d6deeb}