@tea-agent/loop-agent 0.33.5 → 0.33.6
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/CHANGELOG.md +33 -0
- package/dist/application/task-lifecycle/advance.js +254 -4
- package/dist/application/task-lifecycle/gates.js +50 -0
- package/dist/application/task-lifecycle/observe.js +11 -2
- package/dist/commands/init-upgrade.js +32 -1
- package/dist/commands/init.js +94 -3
- package/dist/executors/shell-write-guard.js +26 -8
- package/dist/shared/operator/capabilities.js +72 -42
- package/dist/shared/resilient-git.js +133 -0
- package/dist/task/source-prepare/artifact-meta.js +137 -0
- package/dist/task/source-prepare/index.js +2 -0
- package/dist/task/source-prepare/parse-intent.js +58 -10
- package/dist/task/source-prepare/prepare.js +180 -16
- package/dist/task/source-prepare/reference-integrity.js +18 -2
- package/dist/task/source-prepare/semantic-intake.js +404 -0
- package/dist/worker/console/app-data.js +2 -0
- package/dist/worker/console/chat/chat-event-store.js +190 -25
- package/dist/worker/console/chat/pi-console-config.js +250 -32
- package/dist/worker/console/chat/pi-runtime.js +625 -71
- package/dist/worker/console/chat/resource-loader.js +5 -4
- package/dist/worker/console/chat/routes.js +324 -146
- package/dist/worker/console/chat/runtime-context.js +48 -12
- package/dist/worker/console/chat/runtime-selection.js +59 -0
- package/dist/worker/console/chat/shortcuts.js +1 -0
- package/dist/worker/console/chat/tool-adapter.js +9 -3
- package/dist/worker/console/chat/tools.js +5 -1
- package/dist/worker/console/dag-execution-receipt.js +380 -0
- package/dist/worker/console/operator-actions.js +559 -68
- package/dist/worker/console/server.js +8 -15
- package/dist/worker/console/static/assets/index-BUOLppPr.js +28 -0
- package/dist/worker/console/static/assets/index-C1KzazY5.css +1 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +45 -8
- package/dist/worker/console/static-src/operator-chat/refs.js +9 -0
- package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +257 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +16 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +210 -184
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +49 -5
- package/dist/worker/console/static-src/operator-chat/useComposer.js +17 -0
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +225 -74
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +196 -0
- package/dist/worker/delivery/final-verification.js +13 -5
- package/dist/worker/delivery/package.js +31 -19
- package/dist/worker/delivery/verification-bundle.js +6 -4
- package/dist/worker/observe/static/operator-chrome.css +5 -2
- package/dist/worker/observe/static/operator-chrome.js +6 -1
- package/dist/worker/observe/static/styles.css +39 -9
- package/dist/workflows/dag/frontend-worktree-diff.js +12 -27
- package/dist/workflows/dag/workspace-checkpoint.js +8 -27
- package/harness.json +1 -1
- package/package.json +1 -1
- package/skills/loop-agent/references/command-reference.md +3 -1
- package/skills/loop-agent/references/source-and-plan-practice.md +13 -0
- package/skills/loop-agent/references/task-workflow.md +4 -0
- package/dist/worker/console/chat/instruction-skills.js +0 -217
- package/dist/worker/console/static/assets/index-CnUXAqxG.css +0 -1
- package/dist/worker/console/static/assets/index-CteJFFL2.js +0 -29
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--canvas: #f7f7f4;--canvas-soft: #fafaf7;--surface: #ffffff;--surface-muted: #f0efe9;--ink: #26251e;--body: #5a5852;--muted: #807d72;--muted-soft: #a09c92;--hairline: #e6e5e0;--hairline-strong: #cfcdc4;--orange: #f54e00;--orange-active: #d04200;--orange-soft: color-mix(in srgb, var(--orange) 12%, white);--blue: #3f6ea4;--green: #1f8a65;--amber: #a66a00;--red: #c72d53;--peach: #f1d8cb;--mint: #d8ead5;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 20px;--space-6: 24px;--space-8: 32px;--space-12: 48px;--radius-sm: 6px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 16px;--radius-pill: 999px;--transition-fast: .16s ease;--transition-base: .22s ease;--ease-out: cubic-bezier(.22, 1, .36, 1);--shadow-sm: 0 1px 2px rgb(38 37 30 / 5%);--shadow-panel: 0 1px 2px rgb(38 37 30 / 4%), 0 8px 24px rgb(38 37 30 / 3%);--shadow-soft: 0 10px 30px rgb(38 37 30 / 6%);--shadow-lg: 0 16px 44px rgb(38 37 30 / 10%);--shell-max: 1680px;--text-xs: 11px;--text-sm: 12px;--text-md: 13px;--text-base: 14px;--text-lg: 16px;--text-xl: 20px;--text-2xl: 26px;--font-mono: "JetBrains Mono", "SFMono-Regular", Consolas, "Segoe UI Mono", monospace;--bg: var(--canvas);--border: var(--hairline);--accent: var(--orange);--accent-active: var(--orange-active);--danger: var(--red);--success: var(--green);--warning: var(--amber);--info: var(--blue);--text-muted: var(--muted);--surface-raised: var(--surface);--surface-hover: var(--surface-muted);--surface-sunken: var(--canvas-soft);--oc-tree-guide: 14px;color-scheme:light;font-family:Segoe UI,PingFang SC,Hiragino Sans GB,Noto Sans SC,system-ui,-apple-system,sans-serif;line-height:1.5}*{box-sizing:border-box}html{background:radial-gradient(1200px 480px at 8% -10%,rgb(241 216 203 / 45%),transparent 55%),radial-gradient(900px 420px at 100% 0%,rgb(216 234 213 / 35%),transparent 50%),var(--canvas);min-height:100%}body{margin:0;min-height:100vh;min-width:320px;background:transparent;color:var(--ink);font-size:14px}button,input,select,textarea{font:inherit}a{color:inherit}::selection{background:color-mix(in srgb,var(--orange) 18%,white)}.oc-messages,.oc-session-scroll,.oc-tree-scroll,.oc-process-timeline,.oc-tools-catalog-body,.oc-runtime-context-body,.inspect-split-panel{scrollbar-width:thin;scrollbar-color:var(--hairline-strong) transparent}.oc-messages::-webkit-scrollbar,.oc-session-scroll::-webkit-scrollbar,.oc-tree-scroll::-webkit-scrollbar,.oc-process-timeline::-webkit-scrollbar,.oc-tools-catalog-body::-webkit-scrollbar,.oc-runtime-context-body::-webkit-scrollbar,.inspect-split-panel::-webkit-scrollbar{width:8px;height:8px}.oc-messages::-webkit-scrollbar-thumb,.oc-session-scroll::-webkit-scrollbar-thumb,.oc-tree-scroll::-webkit-scrollbar-thumb,.oc-process-timeline::-webkit-scrollbar-thumb,.oc-tools-catalog-body::-webkit-scrollbar-thumb,.oc-runtime-context-body::-webkit-scrollbar-thumb,.inspect-split-panel::-webkit-scrollbar-thumb{background:var(--hairline-strong);border-radius:999px;border:2px solid transparent;background-clip:content-box}.oc-messages::-webkit-scrollbar-track,.oc-session-scroll::-webkit-scrollbar-track,.oc-tree-scroll::-webkit-scrollbar-track,.oc-process-timeline::-webkit-scrollbar-track,.oc-tools-catalog-body::-webkit-scrollbar-track,.oc-runtime-context-body::-webkit-scrollbar-track,.inspect-split-panel::-webkit-scrollbar-track{background:transparent}code,.cmd,.kv dd code{font-family:var(--font-mono)}.console-app{display:flex;flex-direction:column;min-height:100dvh}html:has(.console-app.is-chat-focus),body:has(.console-app.is-chat-focus){height:100%;overflow:hidden}.console-app.is-chat-focus{height:100dvh;max-height:100dvh;overflow:hidden}.console-shell{flex:1 1 auto;display:flex;flex-direction:column;min-height:0;width:min(100% - 48px,var(--shell-max));margin:0 auto;padding:var(--space-5) 0 var(--space-12, 48px)}.flash{display:flex;gap:var(--space-3);align-items:flex-start;margin:0 0 var(--space-4);padding:12px 16px;border:1px solid var(--hairline);border-left:3px solid var(--hairline-strong);border-radius:var(--radius-md);background:var(--surface);box-shadow:var(--shadow-panel);animation:flash-in .22s var(--ease-out)}.flash-error{border-color:color-mix(in srgb,var(--red) 35%,var(--hairline));border-left-color:var(--red);background:color-mix(in srgb,var(--red) 6%,var(--surface));color:var(--red)}.flash-info{border-color:color-mix(in srgb,var(--blue) 30%,var(--hairline));border-left-color:var(--blue);background:color-mix(in srgb,var(--blue) 6%,var(--surface));color:var(--body)}.flash-label{flex:0 0 auto;min-width:3.5rem;font-size:var(--text-xs);font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding-top:1px}.flash-body{margin:0;overflow-wrap:anywhere;font-size:var(--text-md);line-height:1.55}.muted{color:var(--muted)}.console-main{display:grid;gap:var(--space-4)}.console-main--fill{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:var(--space-3)}.console-shell.is-chat-focus{flex:1 1 auto;min-height:0;overflow:hidden;padding-bottom:var(--space-3)}.console-shell.is-chat-focus .console-main--fill{flex:1 1 auto;min-height:0;overflow:hidden}.panel--chat{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;padding:var(--space-3);overflow:hidden}.panel--chat .oc-shell{flex:1 1 auto;min-height:0;height:auto;overflow:hidden}.console-shell.is-chat-focus .panel--chat{padding:var(--space-2)}.console-shell.is-chat-focus .panel--chat .oc-shell{min-height:0;gap:var(--space-2);overflow:hidden}.inspect-split-panel{position:fixed;z-index:50;top:16px;right:16px;bottom:16px;width:min(420px,calc(100vw - 32px));display:grid;align-content:start;gap:var(--space-4);padding:var(--space-5);overflow:auto;border:1px solid var(--hairline);border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-soft)}.inspect-split-nodes{display:grid;gap:6px;margin:0;padding:0;list-style:none;max-height:220px;overflow:auto}.inspect-split-nodes li{display:grid;grid-template-columns:1fr auto;gap:var(--space-2);align-items:center;padding:6px 8px;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--canvas-soft);font-size:12px}.inspect-split-nodes li.is-active{border-color:var(--orange);background:var(--orange-soft)}.inspect-split-nodes code{color:var(--muted);font-size:11px}.inspect-split-header{display:flex;align-items:start;justify-content:space-between;gap:var(--space-3)}.inspect-split-header h2,.inspect-split-panel h3{margin:0}.inspect-split-kicker,.inspect-split-id{margin:0;color:var(--muted);font-size:12px}.inspect-split-facts{display:grid;gap:var(--space-2);margin:0}.inspect-split-facts div{display:grid;grid-template-columns:7rem 1fr;gap:var(--space-2);padding-bottom:var(--space-2);border-bottom:1px solid var(--hairline)}.inspect-split-facts dt{color:var(--muted)}.inspect-split-facts dd{margin:0;overflow-wrap:anywhere}.inspect-split-events{display:grid;gap:8px;margin:0;padding-left:18px;color:var(--body);font-size:12px}.panel{min-width:0;border:1px solid var(--hairline);background:color-mix(in srgb,var(--surface) 96%,transparent);border-radius:var(--radius-lg);padding:var(--space-6);box-shadow:var(--shadow-panel)}.panel h2{margin:0 0 var(--space-2);color:var(--ink);font-size:var(--text-xl);font-weight:700;letter-spacing:-.02em}.panel-lead{margin:0 0 var(--space-5);color:var(--muted);font-size:var(--text-md);line-height:1.55}.panel h3{margin:0 0 var(--space-3);color:var(--ink);font-size:15px;font-weight:700;letter-spacing:-.01em}.empty-state{border:1px dashed var(--hairline-strong);border-radius:var(--radius-lg);padding:var(--space-8) var(--space-6);background:var(--canvas-soft);color:var(--body);text-align:center}.empty-state .cta-row{justify-content:center}.empty-title{margin:0 0 var(--space-2);color:var(--ink);font-size:var(--text-lg);font-weight:700;letter-spacing:-.01em}.empty-state .muted{max-width:52ch;margin-left:auto;margin-right:auto}.checklist{margin:0 0 var(--space-4);padding:var(--space-4);border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--canvas-soft);list-style:none;color:var(--body)}.checklist li{position:relative;padding:6px 0 6px 22px}.checklist li:before{content:"";position:absolute;left:0;top:12px;width:8px;height:8px;border-radius:50%;background:var(--orange);opacity:.75}.kv{display:grid;gap:var(--space-1);margin:0}.kv div{display:grid;grid-template-columns:9rem 1fr;gap:var(--space-3);padding:10px 0;border-bottom:1px solid var(--hairline)}.kv div:last-child{border-bottom:none}.kv dt{color:var(--muted);font-size:var(--text-sm);font-weight:600}.kv dd{margin:0;color:var(--ink);overflow-wrap:anywhere;font-variant-numeric:tabular-nums}code{font-size:.85rem;color:var(--ink)}.cmd{display:block;width:100%;margin:0;padding:var(--space-3) var(--space-4);border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--canvas-soft);color:var(--ink);font-size:var(--text-sm);line-height:1.55;overflow-x:auto;white-space:pre-wrap;word-break:break-word;max-height:280px}.external-link{color:var(--orange);text-decoration:none;font-weight:600}.external-link:hover{text-decoration:underline}.error{color:var(--red)}.wizard-steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:var(--space-2);list-style:none;padding:0;margin:0 0 var(--space-5)}.wizard-steps li{position:relative;min-height:54px;display:grid;align-content:center;gap:3px;border:1px solid var(--hairline);border-radius:var(--radius-md);padding:9px 12px;background:var(--canvas-soft);color:var(--muted);font-size:var(--text-sm);font-weight:600;transition:border-color var(--transition-fast),background-color var(--transition-fast),color var(--transition-fast),box-shadow var(--transition-fast),transform var(--transition-fast)}.wizard-steps li[data-done=true]{border-color:color-mix(in srgb,var(--green) 35%,var(--hairline));background:color-mix(in srgb,var(--mint) 45%,var(--surface));color:var(--green)}.wizard-steps li[data-active=true]{border-color:var(--orange);background:var(--surface);color:var(--ink);box-shadow:0 0 0 3px var(--orange-soft),var(--shadow-panel);transform:translateY(-1px)}.wizard-steps .step-index{font-size:10px;font-weight:700;letter-spacing:.06em;font-variant-numeric:tabular-nums;color:var(--muted-soft)}.wizard-steps li[data-active=true] .step-index{color:var(--orange);opacity:1}.wizard-steps li[data-done=true] .step-index{color:inherit;opacity:.8}.wizard-steps .step-label{line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.field{display:grid;gap:var(--space-1);margin:0 0 var(--space-4);width:min(100%,520px)}.field span{font-size:12px;font-weight:600;color:var(--muted)}.field input,.field textarea,.field select{width:100%;border:1px solid var(--hairline-strong);border-radius:var(--radius-md);background:var(--surface);color:var(--ink);transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.field input{min-height:40px;padding:0 var(--space-3)}.field select{min-height:40px;padding:0 var(--space-3);cursor:pointer}.field textarea{min-height:120px;padding:var(--space-3);resize:vertical}.field input:focus-visible,.field textarea:focus-visible,.field select:focus-visible{outline:none;border-color:var(--orange);box-shadow:0 0 0 3px var(--orange-soft)}.field input:disabled,.field textarea:disabled,.field select:disabled{background:var(--surface-muted);color:var(--muted);cursor:not-allowed}.workflow-hint{margin:calc(var(--space-1) * -1) 0 var(--space-3);color:var(--muted);font-size:12px}.prd-drop-zone{position:relative;width:min(100%,720px);margin:0 0 var(--space-3);padding:var(--space-3);border:1px dashed var(--hairline-strong);border-radius:var(--radius-md);background:var(--surface);transition:border-color var(--transition-fast),background-color var(--transition-fast),box-shadow var(--transition-fast)}.prd-drop-zone.is-dragover{border-color:var(--orange);background:var(--orange-soft);box-shadow:0 0 0 3px var(--orange-soft)}.prd-upload-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2) var(--space-3);margin:0 0 var(--space-3)}.prd-upload-btn{display:inline-flex;align-items:center;cursor:pointer;margin:0}.prd-upload-btn.is-disabled,.prd-upload-btn:has(input:disabled){opacity:.55;cursor:not-allowed;pointer-events:none}.prd-upload-btn:focus-within{outline:none;box-shadow:0 0 0 3px var(--orange-soft)}.prd-upload-hint{font-size:12px}.prd-file-meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2) var(--space-3);margin:0 0 var(--space-3);padding:var(--space-2) var(--space-3);border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface-muted);font-size:12px;color:var(--ink)}.prd-file-list{list-style:none;margin:0 0 var(--space-3);padding:0;display:grid;gap:var(--space-2)}.prd-file-list li{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--space-2);padding:var(--space-2) var(--space-3);border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface-muted);font-size:12px}.prd-file-list li.is-primary{border-color:color-mix(in srgb,var(--orange) 40%,var(--hairline-strong))}.prd-file-item-name{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--space-1);min-width:0}.prd-file-primary-tag{display:inline-flex;align-items:center;padding:0 6px;border-radius:999px;background:var(--orange-soft);color:var(--orange);font-size:11px;font-weight:700}.prd-file-item-actions{display:inline-flex;flex-wrap:wrap;gap:var(--space-1)}.prd-file-item-actions .btn,.prd-file-clear{min-height:28px;padding:0 var(--space-2);font-size:12px}.prd-drop-hint{margin:0 0 var(--space-3);font-size:13px;font-weight:600;color:var(--orange)}.prd-text-field{width:100%;margin-bottom:0}.prd-file-error{width:min(100%,720px);margin:0 0 var(--space-3);padding:var(--space-2) var(--space-3);border:1px solid color-mix(in srgb,var(--orange) 45%,var(--hairline-strong));border-radius:var(--radius-md);background:var(--orange-soft);color:var(--ink);font-size:13px}.meta-row{display:grid;gap:6px;margin:0 0 var(--space-4);padding:var(--space-3) var(--space-4);border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--canvas-soft);font-size:13px}.meta-row code{font-size:12px}.cta-row,.recovery-cta-row{display:flex;flex-wrap:wrap;gap:var(--space-2);align-items:center}.cta-row{margin-top:var(--space-3)}.cta-row .cmd{flex:1 1 100%}.wizard-body{display:grid;gap:var(--space-3);justify-items:stretch;animation:panel-in .24s ease}.wizard-body>div{display:grid;gap:var(--space-3);justify-items:stretch}.wizard-body .field{width:100%;max-width:640px}.wizard-body .cta-row{margin-top:var(--space-1)}.interview-card{width:100%;padding:var(--space-5);border:1px solid var(--hairline);border-radius:var(--radius-lg);background:linear-gradient(180deg,var(--surface),var(--canvas-soft));box-shadow:var(--shadow-panel)}.interview-q{margin:0 0 var(--space-2);color:var(--ink)}.btn,button{appearance:none;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:34px;padding:0 14px;border:1px solid var(--hairline-strong);border-radius:var(--radius-md);background:var(--surface);color:var(--ink);font-size:var(--text-md);font-weight:600;letter-spacing:.01em;cursor:pointer;transition:border-color var(--transition-fast),background-color var(--transition-fast),color var(--transition-fast),box-shadow var(--transition-fast),transform var(--transition-fast)}.btn:hover:not(:disabled),button:hover:not(:disabled){border-color:color-mix(in srgb,var(--ink) 18%,var(--hairline-strong));background:var(--canvas-soft);outline:none}.btn:focus-visible:not(:disabled),button:focus-visible:not(:disabled){outline:none;box-shadow:0 0 0 3px var(--orange-soft)}.btn:active:not(:disabled),button:active:not(:disabled){transform:translateY(1px)}.btn:disabled,button:disabled{opacity:.48;cursor:not-allowed;transform:none}.btn-primary,button.btn-primary{border-color:var(--orange);background:linear-gradient(180deg,var(--orange),var(--orange-active));color:#fff;font-weight:650;box-shadow:inset 0 1px #ffffff2e,0 6px 16px #f54e002e}.btn-primary:hover:not(:disabled),button.btn-primary:hover:not(:disabled){border-color:var(--orange-active);background:var(--orange-active);color:#fff;box-shadow:inset 0 1px #ffffff1f,0 8px 20px #f54e003d;transform:translateY(-1px)}.btn-primary:active:not(:disabled),button.btn-primary:active:not(:disabled){transform:translateY(0);box-shadow:0 2px 8px #f54e0033}.btn-secondary,button.btn-secondary{border-color:var(--hairline-strong);background:var(--surface);color:var(--ink);box-shadow:var(--shadow-sm)}.btn-secondary:hover:not(:disabled),button.btn-secondary:hover:not(:disabled){background:var(--canvas-soft)}.btn-ghost,button.btn-ghost{border-color:transparent;background:transparent;color:var(--body)}.btn-ghost:hover:not(:disabled),button.btn-ghost:hover:not(:disabled){background:var(--surface-muted);color:var(--ink)}.recovery-cta-row{margin-top:var(--space-3)}.recovery-confirm{margin-top:var(--space-3);padding:var(--space-4);border:1px solid color-mix(in srgb,var(--orange) 40%,var(--hairline));border-radius:var(--radius-md);background:color-mix(in srgb,var(--orange-soft) 55%,var(--surface))}.recovery-confirm h3{margin:0 0 var(--space-2);font-size:15px}.recovery-confirm .cta-row{margin-top:var(--space-3)}.recovery-report{margin-top:var(--space-3);padding:var(--space-4);border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface)}.recovery-report-header{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);margin-bottom:var(--space-3)}.recovery-report-header h3{margin:0;font-size:15px}.recovery-report-facts{display:grid;gap:var(--space-2);margin:0 0 var(--space-3)}.recovery-report-facts div{display:grid;grid-template-columns:5.5rem 1fr;gap:var(--space-2)}.recovery-report-facts dt{color:var(--muted);font-size:12px}.recovery-report-facts dd{margin:0;overflow-wrap:anywhere}.recovery-steps{margin:.5rem 0 .75rem 1.25rem;padding:0;line-height:1.5}.recovery-lane{margin-top:var(--space-4);padding-top:var(--space-3);border-top:1px solid var(--hairline)}.recovery-lane h3{margin:0 0 .35rem;font-size:1rem}.recovery-cta-row button[disabled]{opacity:.4}@keyframes flash-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}@keyframes panel-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}@media(max-width:800px){.console-shell{width:min(100% - 24px,var(--shell-max));padding-top:var(--space-5)}.inspect-split-panel{inset:auto 0 0;width:100%;max-height:min(76vh,680px);border-radius:var(--radius-lg) var(--radius-lg) 0 0}.kv div{grid-template-columns:1fr}.wizard-steps{grid-template-columns:repeat(2,minmax(0,1fr))}}.oc-shell{display:flex;flex-direction:column;height:100%;min-height:0;gap:var(--space-3);position:relative;overflow:hidden}.oc-workspace{display:flex;flex:1 1 auto;min-height:0;gap:0;position:relative;overflow:hidden}.oc-main{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.oc-sidebar{flex:0 0 280px;width:280px;min-width:0;display:flex;flex-direction:column;gap:var(--space-2);margin-right:var(--space-3);padding:var(--space-2);overflow:hidden;border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface)}.oc-sidebar.is-collapsed{flex:0 0 36px;width:36px;padding:6px;overflow:visible;align-items:center}.oc-sidebar-header-actions{display:inline-flex;align-items:center;gap:2px}.oc-sidebar-toggle{appearance:none;display:inline-grid;place-items:center;width:28px;height:28px;padding:0;border:1px solid transparent;border-radius:var(--radius-sm);background:transparent;color:var(--muted);cursor:pointer;transition:background-color var(--transition-fast),color var(--transition-fast),border-color var(--transition-fast)}.oc-sidebar-toggle:hover{color:var(--ink);background:color-mix(in srgb,var(--orange) 8%,var(--surface));border-color:var(--hairline)}.oc-sidebar-toggle:focus-visible{outline:2px solid color-mix(in srgb,var(--orange) 45%,transparent);outline-offset:1px}.oc-sidebar-toggle.is-collapsed{width:100%;height:28px}.oc-sidebar-section{display:flex;flex-direction:column;gap:6px;min-height:0}.oc-sidebar-section>header{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:13px;flex-shrink:0}.oc-sessions{flex:0 1 auto;max-height:min(34vh,320px)}.oc-session-scroll{min-height:0;max-height:min(28vh,260px);overflow:auto;padding:4px;border:1px solid var(--hairline);border-radius:var(--radius-md);background:linear-gradient(180deg,color-mix(in srgb,var(--canvas-soft) 70%,white),var(--canvas-soft))}.oc-session-group{display:flex;flex-direction:column;gap:2px}.oc-session-group>small{margin:4px 6px 2px;color:var(--muted-soft);font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase}.oc-session-item{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px;align-items:center;border-radius:6px;transition:background-color var(--transition-fast)}.oc-session-item:hover,.oc-session-item:focus-within{background:color-mix(in srgb,var(--orange) 8%,var(--surface))}.oc-session-item.is-active{background:var(--orange-soft)}.oc-session-main{min-width:0;padding:5px 6px;border:0;background:transparent;color:inherit;text-align:left;cursor:pointer}.oc-session-main strong,.oc-session-main span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.oc-session-main strong{font-size:12.5px;font-weight:650;line-height:1.25}.oc-session-main span{color:var(--muted-soft);font-size:10px;margin-top:1px;font-variant-numeric:tabular-nums}.oc-session-actions{display:none;align-items:center;gap:0;padding-right:2px}.oc-session-item:hover .oc-session-actions,.oc-session-item:focus-within .oc-session-actions,.oc-session-item.is-active .oc-session-actions{display:inline-flex}.oc-session-menu{padding:3px 4px;border:0;background:transparent;color:var(--muted);cursor:pointer;font:inherit;font-size:10px;white-space:nowrap}.oc-session-menu:hover{color:var(--ink)}.oc-session-delete{color:color-mix(in srgb,var(--red) 75%,var(--muted))}.oc-session-delete:hover{color:var(--red)}.oc-explorer{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:6px;border-top:1px solid var(--hairline);padding-top:var(--space-3);margin-top:2px}.oc-explorer-hint{margin:0 2px;color:var(--muted-soft);font-size:10.5px;letter-spacing:.01em}.oc-tree-scroll{flex:1 1 auto;min-height:120px;max-height:min(52vh,560px);overflow:auto;padding:4px;border:1px solid var(--hairline);border-radius:var(--radius-md);background:linear-gradient(180deg,color-mix(in srgb,var(--canvas-soft) 70%,white),var(--canvas-soft))}.oc-tree,.oc-tree ul{list-style:none;padding:0;margin:0}.oc-tree-node{position:relative}.oc-tree-row{appearance:none;display:grid;grid-template-columns:12px 14px minmax(0,1fr);align-items:center;gap:6px;width:100%;min-height:28px;margin:0;padding:3px 8px;border:0;border-radius:6px;background:transparent;color:var(--ink);text-align:left;cursor:pointer;transition:background-color var(--transition-fast),color var(--transition-fast)}.oc-tree-row:hover{background:color-mix(in srgb,var(--orange) 8%,var(--surface))}.oc-tree-row:focus-visible{outline:2px solid color-mix(in srgb,var(--orange) 45%,transparent);outline-offset:1px}.oc-tree-chevron{color:var(--muted-soft);transition:transform var(--transition-fast);transform:rotate(0)}.oc-tree-chevron.is-open{transform:rotate(90deg)}.oc-tree-chevron-spacer{width:12px;height:12px}.oc-tree-glyph{flex-shrink:0;color:var(--amber)}.oc-tree-glyph.is-folder-open{color:color-mix(in srgb,var(--orange) 70%,var(--amber))}.oc-tree-glyph.is-file{color:var(--muted)}.oc-tree-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;line-height:1.25;font-variant-numeric:tabular-nums}.oc-tree-dir .oc-tree-label{font-weight:650;color:var(--ink)}.oc-tree-file .oc-tree-label{font-family:JetBrains Mono,SFMono-Regular,Consolas,Segoe UI Mono,monospace;font-size:11.5px;font-weight:500;color:var(--body)}.oc-tree-node.is-dot .oc-tree-label{color:var(--muted-soft);font-weight:500}.oc-tree-node.is-dot .oc-tree-glyph{opacity:.7}.oc-tree-children{position:relative;margin:0}.oc-tree-children:before{content:"";position:absolute;top:0;bottom:2px;left:var(--oc-tree-guide, 14px);width:1px;background:color-mix(in srgb,var(--hairline-strong) 55%,transparent);pointer-events:none}.oc-tree-empty,.oc-sidebar-error{margin:8px 6px;color:var(--muted);font-size:12px;line-height:1.45}.oc-sidebar-error{color:var(--red)}.oc-governance{margin-top:auto;border-top:1px solid var(--hairline);padding-top:8px}.oc-governance-links{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:4px}.oc-governance-links button{min-height:28px;padding:4px 6px;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--surface);color:var(--muted);cursor:pointer;font:inherit;font-size:11px;font-weight:600;text-align:center}.oc-governance-links button:hover{color:var(--ink);border-color:color-mix(in srgb,var(--ink) 18%,var(--hairline))}.oc-governance-links button.is-active{color:var(--orange-active);border-color:color-mix(in srgb,var(--orange) 35%,var(--hairline))}.oc-resource-backdrop{position:fixed;inset:0;z-index:40;display:flex;align-items:center;justify-content:center;padding:var(--space-4);background:#26251e52}.oc-resource-panel{width:min(880px,calc(100vw - 32px));max-height:min(80vh,760px);display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--hairline);border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-lg);color:var(--ink)}.oc-resource-panel-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--space-3);flex-shrink:0;padding:12px 14px;border-bottom:1px solid var(--hairline);background:var(--surface-raised)}.oc-resource-panel-head-copy{display:grid;gap:2px;min-width:0}.oc-resource-panel-head strong{font-size:14px}.oc-resource-panel-meta{color:var(--muted);font-size:11px;line-height:1.5}.oc-resource-panel-body{flex:1;min-height:0;overflow:auto;display:grid;gap:12px;padding:14px}.oc-resource-panel-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;flex-shrink:0;padding:10px 14px;border-top:1px solid var(--hairline);background:var(--surface-raised)}.oc-resource-panel-actions{display:flex;align-items:center;gap:6px}.oc-resource-error{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 10px;border:1px solid color-mix(in srgb,var(--red) 35%,var(--hairline));border-radius:var(--radius-sm);background:color-mix(in srgb,var(--red) 6%,var(--surface));color:var(--red);font-size:var(--text-sm)}.oc-resource-empty{margin:0;padding:14px 10px;border:1px dashed var(--hairline-strong);border-radius:var(--radius-md);color:var(--muted);font-size:var(--text-sm);text-align:center}.oc-resource-lists{display:grid;gap:14px}.oc-resource-group{display:grid;gap:6px}.oc-resource-group-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;padding-bottom:4px;border-bottom:1px solid var(--hairline);font-size:var(--text-sm);text-transform:capitalize}.oc-resource-group-head span{color:var(--muted);font-size:11px}.oc-resource-list,.oc-resource-sub-list{display:grid;gap:6px;margin:0;padding:0;list-style:none}.oc-resource-item{display:grid;gap:4px;padding:10px 12px;border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface)}.oc-resource-item-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.oc-resource-item-head span{color:var(--muted);font-size:11px}.oc-resource-item p{margin:0;color:var(--body);font-size:var(--text-sm)}.oc-resource-item code{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--body);font-size:11px}.oc-resource-sub-list li{display:grid;gap:2px;padding:6px 8px;border-radius:var(--radius-sm);background:var(--surface-muted)}.oc-resource-diagnostic{color:var(--amber);font-size:11px}.oc-package-status{display:inline-block;padding:1px 8px;border-radius:999px;font-size:11px;line-height:1.7;text-transform:uppercase;background:var(--surface-muted);color:var(--muted);white-space:nowrap}.oc-package-status.is-installed{background:color-mix(in srgb,var(--green) 12%,var(--surface));color:var(--green)}.oc-package-status.is-error{background:color-mix(in srgb,var(--red) 12%,var(--surface));color:var(--red)}.oc-package-status.is-disabled{opacity:.7}.oc-package-meta{display:flex;align-items:baseline;gap:8px;margin:0;font-size:11px;min-width:0}.oc-package-meta span{color:var(--muted);min-width:96px;flex:none}.oc-package-meta code{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0}.oc-package-resources{display:grid;gap:8px}.oc-package-group{display:grid;gap:4px}.oc-package-diagnostics{display:grid;gap:4px;padding:6px 8px;border-radius:var(--radius-sm);background:color-mix(in srgb,var(--amber) 6%,var(--surface));color:var(--amber);font-size:11px;overflow-wrap:anywhere}.oc-resource-diagnostics{display:grid;gap:6px;margin-bottom:12px;padding:8px 10px;border:1px solid color-mix(in srgb,var(--amber) 35%,var(--hairline));border-radius:var(--radius-sm);background:color-mix(in srgb,var(--amber) 5%,var(--surface))}.oc-resource-diagnostic.is-error{color:var(--red)}.oc-resource-panel button:disabled{cursor:wait;opacity:.55}.oc-model-provider{border-bottom:1px solid var(--hairline);padding-bottom:8px}.oc-model-provider-head,.oc-model-row,.oc-model-row-actions{display:flex;align-items:center;justify-content:space-between;gap:8px}.oc-model-provider-head{padding:6px 0}.oc-model-provider-head span{color:var(--muted);font-size:11px}.oc-model-row{padding:4px 0 4px 10px;font-size:var(--text-sm)}.oc-model-row-actions button,.oc-model-provider-remove{border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--surface);color:var(--muted);cursor:pointer;font:inherit;font-size:11px;padding:3px 8px}.oc-model-row-actions button:hover,.oc-model-provider-remove:hover{color:var(--ink);border-color:var(--hairline-strong)}.oc-model-provider-remove{margin:6px 0 6px 10px}.oc-model-form{display:grid;gap:6px;margin-top:10px}.oc-model-form input{min-width:0;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--surface);color:var(--ink);padding:6px 8px;font:inherit;font-size:12px}.oc-model-form button{justify-self:start}.oc-model-test-result{padding:6px 8px;border-radius:var(--radius-sm);background:var(--surface-muted);font-size:12px}@media(max-width:800px){.oc-resource-backdrop{padding:var(--space-2)}.oc-resource-panel{width:100%;max-height:calc(100vh - 32px)}.oc-resource-panel-body{padding:10px}}@media(max-width:640px){.oc-resource-panel-foot{flex-direction:column;align-items:stretch}.oc-resource-panel-actions{justify-content:flex-end}.oc-resource-item-head,.oc-model-row{flex-direction:column;align-items:flex-start}.oc-model-row-actions{justify-content:flex-start}}.oc-file-preview{position:absolute;z-index:4;inset:0;display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface);box-shadow:var(--shadow-panel)}.oc-file-preview header{display:flex;align-items:center;gap:var(--space-2);padding:10px 12px;border-bottom:1px solid var(--hairline)}.oc-file-tabs{display:flex;min-width:0;gap:2px;overflow-x:auto}.oc-file-tabs button{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--muted);padding:4px 7px;cursor:pointer}.oc-file-tabs button.is-active{background:var(--surface-muted);color:var(--ink);font-weight:650}.oc-file-preview header span{flex:1;color:var(--muted);font-size:11px}.oc-file-preview pre{flex:1;min-height:0;margin:0;padding:var(--space-3);overflow:auto;font-size:12px;line-height:1.5;white-space:pre-wrap}.oc-file-preview-loading{position:absolute;z-index:5;right:12px;top:12px;padding:6px 8px;border-radius:var(--radius-sm);background:var(--surface-muted);color:var(--muted);font-size:12px}.oc-process{display:flex;flex-direction:column;flex:0 0 min(360px,42%);width:min(360px,42%);min-width:280px;min-height:0;border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface);box-shadow:var(--shadow-panel);margin-left:var(--space-3);overflow:hidden;animation:panel-in .18s ease}.oc-turn-group{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}.oc-process-inline{margin:0 0 4px 44px;max-width:min(720px,100%)}.oc-process-inline-summary{display:inline-flex;align-items:center;gap:6px;min-height:24px;padding:2px 0;border:none;background:transparent;color:var(--muted);cursor:pointer;font-size:12px;text-align:left}.oc-process-inline-summary:hover,.oc-process-inline-summary.is-open{color:var(--ink)}.oc-process-inline-summary.is-running{color:var(--accent, #2563eb)}.oc-process-inline-summary.is-error{color:var(--danger, #dc2626)}.oc-process-inline-summary .oc-tool-chevron{width:12px;height:12px;flex-shrink:0;transition:transform .15s ease}.oc-process-inline-summary.is-open .oc-tool-chevron{transform:rotate(90deg)}.oc-process-inline-body{display:flex;flex-direction:column;gap:8px;margin-top:8px}.oc-tool-duration{flex-shrink:0;font-size:11px;font-variant-numeric:tabular-nums;color:var(--muted)}.oc-tool-expand-result{margin-top:4px;font-size:12px}.oc-process-head{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:10px 12px;border-bottom:1px solid var(--hairline);flex-shrink:0}.oc-process-title{display:inline-flex;align-items:center;gap:8px;font-weight:700;font-size:13px;color:var(--ink)}.oc-process-title-mark{width:8px;height:8px;border-radius:50%;background:var(--orange)}.oc-process-count{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:18px;padding:0 6px;border-radius:var(--radius-pill);background:var(--surface-muted);color:var(--muted);font-size:11px;font-weight:650}.oc-process-close{font-size:12px}.oc-process-timeline{flex:1 1 auto;min-height:0;overflow-y:auto;padding:var(--space-3);display:flex;flex-direction:column;gap:0}.oc-process-empty{padding:var(--space-4);color:var(--muted);font-size:12.5px;line-height:1.5;text-align:center}.oc-process-item{display:grid;grid-template-columns:20px 52px minmax(0,1fr);gap:8px;align-items:start;padding:4px 0}.oc-process-rail{position:relative;display:grid;place-items:center;min-height:34px}.oc-process-rail:before{position:absolute;top:0;bottom:0;width:1px;background:var(--hairline);content:""}.oc-process-item.is-first .oc-process-rail:before{top:50%}.oc-process-item.is-last .oc-process-rail:before{bottom:50%}.oc-process-dot{position:relative;z-index:1;width:8px;height:8px;border-radius:50%;background:var(--surface);border:2px solid color-mix(in srgb,var(--orange) 55%,var(--hairline))}.oc-process-item.is-running .oc-process-dot{border-color:var(--orange-active);background:var(--orange-soft)}.oc-process-item.is-error .oc-process-dot{border-color:var(--red);background:color-mix(in srgb,var(--red) 18%,var(--surface))}.oc-process-time{padding-top:10px;font-size:11px;color:var(--muted-soft);font-variant-numeric:tabular-nums;white-space:nowrap}.oc-process-body{min-width:0}.oc-process-toggle{cursor:pointer;font:inherit}.oc-process-toggle.is-active{border-color:color-mix(in srgb,var(--orange) 40%,var(--hairline));background:color-mix(in srgb,var(--orange) 10%,var(--surface));color:var(--orange-active)}.oc-process-toggle.is-running{border-color:color-mix(in srgb,var(--orange) 40%,var(--hairline));color:var(--orange-active)}.oc-process-toggle.is-error{border-color:color-mix(in srgb,var(--red) 35%,var(--hairline));background:color-mix(in srgb,var(--red) 8%,var(--surface));color:var(--red)}.oc-process-summary{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border:1px solid var(--hairline);border-radius:var(--radius-pill);background:var(--surface);color:var(--muted);font-size:11.5px;font-weight:650;cursor:pointer;font:inherit;max-width:min(420px,100%);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.oc-process-summary:hover,.oc-process-summary.is-open{border-color:color-mix(in srgb,var(--orange) 35%,var(--hairline));color:var(--orange-active)}.oc-process-summary.is-running{color:var(--orange-active);border-color:color-mix(in srgb,var(--orange) 40%,var(--hairline));background:var(--orange-soft)}.oc-process-summary.is-error{color:var(--red);border-color:color-mix(in srgb,var(--red) 35%,var(--hairline));background:color-mix(in srgb,var(--red) 8%,var(--surface))}.oc-process-summary.is-ok{color:var(--green);border-color:color-mix(in srgb,var(--green) 30%,var(--hairline))}@media(max-width:900px){.oc-sidebar{position:absolute;inset:0 auto 0 0;z-index:7;width:min(300px,calc(100% - 24px));margin-right:0;box-shadow:var(--shadow-soft)}.oc-sidebar.is-collapsed{position:static;width:auto}.oc-workspace.has-process .oc-process{position:absolute;top:0;right:0;bottom:0;z-index:8;width:min(360px,calc(100% - 24px));min-width:0;flex:none;margin-left:0;border-radius:var(--radius-md) 0 0 var(--radius-md);box-shadow:var(--shadow-soft)}}.oc-banner{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2) var(--space-3);padding:10px 14px;border:1px solid var(--hairline);border-radius:var(--radius-md);background:color-mix(in srgb,var(--surface) 94%,transparent);box-shadow:var(--shadow-panel)}.oc-shell>.oc-alert:first-child{margin-top:0}.oc-banner-title{display:inline-flex;align-items:center;gap:6px;font-weight:700;color:var(--ink)}.oc-banner-dot{width:8px;height:8px;border-radius:50%;background:var(--orange)}.oc-banner-body{color:var(--body);font-size:12.5px;line-height:1.45}.oc-banner-body strong{color:var(--ink)}.oc-cap-chips{display:inline-flex;flex-wrap:wrap;gap:6px;margin-left:auto;align-items:center}.oc-cap-chip{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border:1px solid color-mix(in srgb,var(--green) 30%,var(--hairline));border-radius:var(--radius-pill);background:color-mix(in srgb,var(--mint) 50%,var(--surface));color:var(--green);font-size:11.5px;font-weight:650}button.oc-cap-chip{font:inherit;cursor:pointer}.oc-tools-menu{position:relative}.oc-tools-menu--composer .oc-tools-catalog{top:auto;bottom:calc(100% + 8px)}.oc-tools-toggle.is-active{border-color:color-mix(in srgb,var(--orange) 40%,var(--hairline));background:color-mix(in srgb,var(--orange) 10%,var(--surface));color:var(--orange-active)}.oc-tools-catalog{position:absolute;top:calc(100% + 8px);right:0;z-index:20;width:min(420px,calc(100vw - 48px));max-height:min(480px,60vh);display:flex;flex-direction:column;border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface);box-shadow:var(--shadow-soft);animation:panel-in .16s ease;overflow:hidden}.oc-tools-catalog-head{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid var(--hairline);flex-shrink:0}.oc-tools-catalog-head strong{font-size:13px;color:var(--ink)}.oc-tools-catalog-meta{font-size:11.5px;color:var(--muted-soft)}.oc-tools-catalog-close{margin-left:auto;font-size:12px}.oc-tools-catalog-body{overflow-y:auto;padding:var(--space-3);display:flex;flex-direction:column;gap:var(--space-3)}.oc-tools-group-title{margin:0 0 8px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted-soft)}.oc-tools-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.oc-tools-item{padding:8px 10px;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:color-mix(in srgb,var(--surface-muted) 55%,var(--surface))}.oc-tools-item-head{display:flex;align-items:center;gap:8px;margin-bottom:4px}.oc-tools-name{font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace;font-size:12.5px;font-weight:650;color:var(--ink)}.oc-tools-kind{margin-left:auto;font-size:10.5px;font-weight:650;padding:1px 7px;border-radius:var(--radius-pill);color:var(--muted);background:var(--surface);border:1px solid var(--hairline)}.oc-tools-kind.is-explore,.oc-tools-kind.is-read{color:var(--green);border-color:color-mix(in srgb,var(--green) 28%,var(--hairline));background:color-mix(in srgb,var(--mint) 45%,transparent)}.oc-tools-kind.is-mutation{color:var(--orange-active);border-color:color-mix(in srgb,var(--orange) 35%,var(--hairline));background:var(--orange-soft)}.oc-tools-kind.is-long-running{color:var(--amber);border-color:color-mix(in srgb,var(--amber) 35%,var(--hairline));background:color-mix(in srgb,var(--amber) 10%,var(--surface))}.oc-tools-desc{margin:0;font-size:12px;line-height:1.45;color:var(--body)}.oc-tools-cli{margin:4px 0 0;font-size:11px;color:var(--muted-soft)}.oc-tools-cli code{font-size:11px;color:var(--muted)}.oc-alert{padding:10px 14px;border-radius:var(--radius-md);font-size:var(--text-md);line-height:1.5;animation:panel-in .18s var(--ease-out)}.oc-alert-warning{border:1px solid color-mix(in srgb,var(--amber) 35%,var(--hairline));border-left:3px solid var(--amber);background:color-mix(in srgb,var(--amber) 8%,var(--surface));color:var(--amber)}.oc-alert-error{border:1px solid color-mix(in srgb,var(--red) 35%,var(--hairline));border-left:3px solid var(--red);background:color-mix(in srgb,var(--red) 7%,var(--surface));color:var(--red)}.oc-messages{flex:1 1 auto;min-height:0;overflow-y:auto;padding:var(--space-3) var(--space-3) var(--space-2);display:flex;flex-direction:column;gap:var(--space-3)}.oc-messages.has-thread:before{content:"";flex:1 1 auto;min-height:0}.oc-messages.is-empty{justify-content:center;align-items:center;padding:var(--space-5) var(--space-4) var(--space-4);background:radial-gradient(640px 300px at 50% 28%,rgb(241 216 203 / 34%),transparent 68%),radial-gradient(520px 240px at 78% 78%,rgb(216 234 213 / 26%),transparent 62%)}.oc-main.is-landing{position:relative}.oc-day-sep{display:flex;align-items:center;gap:var(--space-3);color:var(--muted-soft);font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;margin:var(--space-2) 0}.oc-day-sep:before,.oc-day-sep:after{content:"";flex:1;height:1px;background:var(--hairline)}.oc-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;flex:0 1 auto;gap:var(--space-5);text-align:center;width:min(100%,680px);padding:var(--space-2) var(--space-2) var(--space-4);animation:oc-empty-in .28s ease both}@keyframes oc-empty-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@media(prefers-reduced-motion:reduce){.oc-empty{animation:none}}.oc-empty-hero{display:flex;flex-direction:column;align-items:center;gap:var(--space-3)}.oc-empty-icon{display:grid;place-items:center;width:56px;height:56px;border-radius:50%;background:linear-gradient(145deg,color-mix(in srgb,var(--orange) 18%,white),color-mix(in srgb,var(--mint) 35%,white));color:var(--orange-active);font-size:26px;font-weight:800;font-family:var(--font-mono, "JetBrains Mono", monospace);box-shadow:0 0 0 1px color-mix(in srgb,var(--orange) 18%,transparent)}.oc-empty-copy{display:flex;flex-direction:column;align-items:center;gap:var(--space-2)}.oc-empty h3{margin:0;font-size:20px;font-weight:700;letter-spacing:-.02em;color:var(--ink)}.oc-empty p{margin:0;max-width:48ch;color:var(--muted);font-size:13px;line-height:1.55}.oc-suggestions{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-2);width:100%}@media(max-width:560px){.oc-suggestions{grid-template-columns:1fr}}.oc-suggestion{appearance:none;display:flex;flex-direction:column;gap:5px;text-align:left;min-height:0;padding:14px 16px;border:1px solid var(--hairline);border-radius:var(--radius-lg);background:color-mix(in srgb,var(--surface) 88%,transparent);color:var(--ink);cursor:pointer;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);transition:border-color var(--transition-fast),background var(--transition-fast),transform var(--transition-fast),box-shadow var(--transition-fast)}.oc-suggestion:hover:not(:disabled){border-color:color-mix(in srgb,var(--orange) 45%,var(--hairline));background:var(--surface);box-shadow:0 0 0 3px var(--orange-soft),var(--shadow-panel);transform:translateY(-1px)}.oc-suggestion:disabled{opacity:.55;cursor:not-allowed}.oc-suggestion strong{font-size:var(--text-md);font-weight:700}.oc-suggestion span{color:var(--muted);font-size:var(--text-sm);line-height:1.45}.oc-empty-tips{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--space-2) var(--space-3);margin:0;padding:0;list-style:none;color:var(--muted-soft);font-size:12px}.oc-empty-tips li{display:inline-flex;align-items:center;gap:6px}.oc-empty-tips kbd{display:inline-grid;place-items:center;min-width:1.4em;padding:1px 6px;border:1px solid var(--hairline);border-radius:5px;background:var(--surface);color:var(--body);font-family:var(--font-mono, "JetBrains Mono", monospace);font-size:11px;font-weight:600;line-height:1.4}.oc-empty-cta{margin-top:calc(var(--space-1) * -1)}.oc-row{display:flex;flex-direction:column;gap:4px;max-width:100%}.oc-row.is-user{align-self:flex-end;align-items:flex-end;width:auto;max-width:min(100%,720px)}.oc-row.is-assistant,.oc-row.is-tool{align-self:stretch;align-items:stretch;width:100%;max-width:100%}.oc-meta{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--muted-soft)}.oc-row.is-assistant .oc-meta,.oc-row.is-user .oc-meta{width:100%;max-width:100%}.oc-message-copy{appearance:none;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:26px;height:26px;margin-left:auto;padding:0;border:1px solid transparent;border-radius:var(--radius-sm);background:transparent;color:var(--muted-soft);cursor:pointer;opacity:0;transition:opacity var(--transition-fast),background var(--transition-fast),color var(--transition-fast),border-color var(--transition-fast)}.oc-row:hover .oc-message-copy,.oc-row:focus-within .oc-message-copy,.oc-message-copy:focus-visible{opacity:1}.oc-message-copy:hover{color:var(--body);background:color-mix(in srgb,var(--ink) 6%,transparent);border-color:var(--hairline)}.oc-message-copy.is-copied{opacity:1;color:var(--green, #1a7f37);border-color:color-mix(in srgb,var(--green, #1a7f37) 35%,transparent)}@media(hover:none){.oc-message-copy{opacity:.5}}.oc-avatar{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;font-size:11px;font-weight:700;flex-shrink:0}.oc-avatar-user{background:var(--ink);color:#fff}.oc-avatar-assistant{background:var(--orange);color:#fff}.oc-role{font-weight:700;color:var(--body)}.oc-time{font-variant-numeric:tabular-nums}.oc-bubble{padding:10px 14px;border-radius:var(--radius-lg);font-size:14px;line-height:1.6;overflow-wrap:anywhere;word-break:break-word;animation:bubble-in .16s ease}@keyframes bubble-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.oc-bubble-user{background:var(--ink);color:#fff;border-bottom-right-radius:4px;box-shadow:0 4px 14px #26251e1f}.oc-bubble-assistant{background:var(--surface);color:var(--ink);border:1px solid var(--hairline);border-bottom-left-radius:4px}.oc-bubble-tool{background:var(--canvas-soft);color:var(--body);border:1px solid var(--hairline)}.oc-bubble-error{border-color:color-mix(in srgb,var(--red) 40%,var(--hairline));background:color-mix(in srgb,var(--red) 7%,var(--surface));color:var(--red)}.oc-md{display:flex;flex-direction:column;gap:var(--space-2)}.oc-md>*:first-child{margin-top:0}.oc-md>*:last-child{margin-bottom:0}.oc-md p{margin:0}.oc-md h1,.oc-md h2,.oc-md h3,.oc-md h4{margin:var(--space-3) 0 var(--space-2);color:inherit;font-weight:700;line-height:1.3}.oc-md h1{font-size:1.3em}.oc-md h2{font-size:1.18em}.oc-md h3{font-size:1.06em}.oc-md h4{font-size:1em}.oc-md ul,.oc-md ol{margin:0;padding-left:1.4em;display:flex;flex-direction:column;gap:2px}.oc-md li{margin:0}.oc-md blockquote{margin:0;padding:2px 0 2px 12px;border-left:3px solid var(--hairline-strong);color:var(--body)}.oc-bubble-user .oc-md blockquote{border-left-color:#ffffff59;color:#ffffffd1}.oc-md code{font-size:.86em;padding:1px 5px;border-radius:4px;background:color-mix(in srgb,var(--ink) 8%,transparent)}.oc-bubble-user .oc-md code{background:#ffffff29}.oc-md pre{margin:0;padding:0;background:transparent}.oc-codeblock{margin:0;border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface-muted);overflow:hidden}.oc-bubble-assistant .oc-codeblock{background:#f3efe8}.oc-codeblock-head{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:5px 6px 5px 10px;border-bottom:1px solid var(--hairline);font-size:11px;color:var(--muted);background:color-mix(in srgb,var(--ink) 4%,transparent)}.oc-codeblock-lang{font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace;font-weight:600;text-transform:lowercase}.oc-codeblock-copy{appearance:none;display:inline-flex;align-items:center;gap:5px;min-height:26px;padding:0 9px;border:1px solid transparent;border-radius:var(--radius-sm);background:transparent;color:var(--muted);font-size:11px;font-weight:600;cursor:pointer;transition:background var(--transition-fast),color var(--transition-fast),border-color var(--transition-fast)}.oc-codeblock-copy:hover{background:var(--surface);color:var(--ink);border-color:var(--hairline-strong)}.oc-codeblock pre{margin:0;padding:var(--space-3) var(--space-4);overflow-x:auto;font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace;font-size:12.5px;line-height:1.55;white-space:pre;color:var(--ink)}.oc-md a{color:var(--orange);text-decoration:none;border-bottom:1px solid color-mix(in srgb,var(--orange) 40%,transparent)}.oc-md a:hover{border-bottom-color:var(--orange)}.oc-md hr{margin:var(--space-2) 0;border:none;border-top:1px solid var(--hairline-strong)}.oc-stream{white-space:pre-wrap;font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace;font-size:13px;line-height:1.55}.oc-cursor{display:inline-block;width:7px;height:1.05em;margin-left:2px;vertical-align:text-bottom;background:var(--orange);animation:oc-blink 1s steps(2,start) infinite;border-radius:1px}@keyframes oc-blink{to{opacity:0}}.oc-tool{border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--canvas-soft);overflow:hidden;animation:panel-in .16s ease;transition:border-color var(--transition-fast),background-color var(--transition-fast)}.oc-tool:hover{border-color:var(--hairline-strong);background:var(--surface)}.oc-tool.is-error{border-color:color-mix(in srgb,var(--red) 38%,var(--hairline))}.oc-tool.is-running{border-color:color-mix(in srgb,var(--orange) 40%,var(--hairline))}.oc-tool-head{display:flex;align-items:center;gap:var(--space-2);width:100%;padding:7px 12px;border:none;background:transparent;text-align:left;cursor:pointer;font:inherit;color:var(--ink)}.oc-tool-head:hover{background:var(--surface-muted)}.oc-tool-chevron{flex-shrink:0;width:12px;height:12px;transition:transform var(--transition-fast)}.oc-tool-head[aria-expanded=true] .oc-tool-chevron{transform:rotate(90deg)}.oc-tool-icon{display:grid;place-items:center;width:22px;height:22px;border-radius:6px;background:var(--surface-muted);color:var(--orange-active);flex-shrink:0}.oc-tool-name{font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace;font-size:12.5px;font-weight:650;flex-shrink:0}.oc-tool-args{color:var(--muted);font-size:11.5px;font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-right:8px;min-width:0}.oc-tool-status{margin-left:auto;flex-shrink:0;font-size:11px;font-weight:650;padding:2px 8px;border-radius:var(--radius-pill)}.oc-tool-waiting{color:var(--muted);font-size:12px;animation:pulse 1.5s infinite}.oc-tool-status.is-running{color:var(--orange-active);background:var(--orange-soft)}.oc-tool-status.is-ok{color:var(--green);background:color-mix(in srgb,var(--mint) 60%,transparent)}.oc-tool-status.is-error{color:var(--red);background:color-mix(in srgb,var(--red) 10%,transparent)}.oc-tool-body{padding:0 var(--space-4) var(--space-3);border-top:1px solid var(--hairline)}.oc-tool-section{display:flex;flex-direction:column;gap:4px;padding-top:var(--space-3)}.oc-tool-label{font-size:11px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--muted-soft)}.oc-tool-code{margin:0;padding:var(--space-3);border-radius:var(--radius-sm);background:var(--surface-muted);font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace;font-size:12px;line-height:1.5;white-space:pre-wrap;word-break:break-word;max-height:260px;overflow-y:auto;color:var(--body)}.oc-composer{flex:0 0 auto;flex-shrink:0;border-top:1px solid var(--hairline);padding:var(--space-2) var(--space-3) var(--space-3);background:color-mix(in srgb,var(--canvas) 92%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);z-index:2}.oc-composer-inner{display:flex;flex-direction:column;gap:var(--space-2);max-width:1080px;margin:0 auto;position:relative}.console-shell.is-chat-focus .oc-composer-inner{max-width:none}.oc-composer-shell{position:relative;display:flex;align-items:flex-end;gap:var(--space-2);padding:8px 8px 8px 14px;border:1px solid var(--hairline-strong);border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-panel);transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.oc-composer-shell:focus-within{border-color:var(--orange);box-shadow:0 0 0 3px var(--orange-soft)}.oc-composer-shell.is-busy{opacity:.78}.oc-textarea{flex:1 1 auto;min-width:0;min-height:52px;max-height:280px;padding:8px 0;border:none;background:transparent;color:var(--ink);font:inherit;font-size:14px;line-height:1.5;resize:none;outline:none}.oc-textarea::placeholder{color:var(--muted-soft)}.oc-send{appearance:none;display:grid;place-items:center;width:38px;height:38px;flex-shrink:0;padding:0;border:1px solid var(--orange);border-radius:50%;background:linear-gradient(180deg,var(--orange),var(--orange-active));color:#fff;cursor:pointer;box-shadow:inset 0 1px #fff3,0 4px 12px #f54e003d;transition:background var(--transition-fast),border-color var(--transition-fast),box-shadow var(--transition-fast),transform var(--transition-fast)}.oc-send:hover:not(:disabled){background:var(--orange-active);border-color:var(--orange-active);box-shadow:0 6px 16px #f54e004d;transform:translateY(-1px)}.oc-send:active:not(:disabled){transform:scale(.94)}.oc-send:disabled{opacity:.4;cursor:not-allowed}.oc-stop{appearance:none;display:grid;place-items:center;width:38px;height:38px;flex-shrink:0;padding:0;border:1px solid var(--hairline-strong);border-radius:50%;background:var(--surface);color:var(--red);cursor:pointer;transition:background var(--transition-fast),border-color var(--transition-fast)}.oc-stop:hover{background:color-mix(in srgb,var(--red) 8%,var(--surface));border-color:var(--red)}.oc-composer-foot{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;font-size:11.5px;color:var(--muted)}.oc-composer-hint{display:inline-flex;align-items:center;gap:5px}.oc-kbd{display:inline-block;padding:1px 6px;border:1px solid var(--hairline-strong);border-bottom-width:2px;border-radius:4px;background:var(--surface);color:var(--body);font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace;font-size:10.5px;line-height:1.4}.oc-operation-card{width:min(720px,calc(100% - 42px));margin:var(--space-3) auto;padding:var(--space-4) var(--space-5);border:1px solid var(--hairline-strong);border-left:4px solid var(--blue);border-radius:var(--radius-lg);background:color-mix(in srgb,var(--surface) 96%,var(--blue) 4%);box-shadow:var(--shadow-panel)}.oc-operation-card.is-succeeded{border-left-color:var(--green)}.oc-operation-card.is-failed,.oc-operation-card.is-timed-out{border-left-color:var(--red)}.oc-operation-card.is-needs-reconcile{border-left-color:var(--orange)}.oc-operation-head,.oc-operation-actions{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3)}.oc-operation-kicker{margin-bottom:2px;color:var(--muted);font-size:10px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}.oc-operation-state{padding:3px 9px;border-radius:999px;background:color-mix(in srgb,var(--blue) 12%,transparent);color:var(--blue);font-size:11px;font-weight:700}.oc-operation-state.is-succeeded{background:color-mix(in srgb,var(--green) 12%,transparent);color:var(--green)}.oc-operation-state.is-failed,.oc-operation-state.is-timed-out{background:color-mix(in srgb,var(--red) 12%,transparent);color:var(--red)}.oc-operation-state.is-needs-reconcile{background:color-mix(in srgb,var(--orange) 14%,transparent);color:var(--orange-active)}.oc-operation-id,.oc-operation-controller{margin-top:var(--space-2);color:var(--muted);font-size:12px;overflow-wrap:anywhere}.oc-operation-identities{display:flex;flex-wrap:wrap;gap:6px;margin-top:var(--space-3)}.oc-operation-identities span{padding:3px 8px;border:1px solid var(--hairline);border-radius:var(--radius-pill);background:var(--surface);color:var(--muted);font-size:var(--text-xs);font-weight:600}.oc-operation-identities b{color:var(--body)}.oc-operation-preview{max-height:150px;margin:var(--space-3) 0 0;padding:var(--space-2) var(--space-3);overflow:auto;border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--canvas-soft);color:var(--body);font-family:var(--font-mono);font-size:var(--text-xs);line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere}.oc-operation-preview.is-error{border-color:color-mix(in srgb,var(--red) 30%,var(--hairline));color:var(--red)}.oc-operation-actions{justify-content:flex-start;margin-top:var(--space-3)}.oc-operation-actions time{margin-left:auto;color:var(--muted-soft);font-size:11px}.oc-interview-start{margin:var(--space-3) auto}.oc-draft-card{width:min(720px,calc(100% - 42px));margin:var(--space-3) auto;padding:var(--space-4) var(--space-5);border:1px solid var(--hairline);border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-panel)}.oc-draft-card>header{display:flex;align-items:center;gap:var(--space-3);justify-content:space-between}.oc-draft-card>header strong{font-size:15px;font-weight:700;letter-spacing:-.01em}.oc-draft-card>header span{padding:3px 10px;border-radius:var(--radius-pill);background:var(--surface-muted);color:var(--muted);font-size:var(--text-xs);font-weight:700;letter-spacing:.05em;text-transform:uppercase}.oc-interview-bubble{margin:var(--space-3) 0;padding:var(--space-3) var(--space-4);border-left:3px solid var(--orange);border-radius:0 var(--radius-md) var(--radius-md) 0;background:var(--canvas-soft)}.oc-interview-bubble textarea{width:100%;min-height:72px;margin:var(--space-2) 0;resize:vertical;border:1px solid var(--hairline-strong);border-radius:var(--radius-md);padding:var(--space-2) var(--space-3);font:inherit;font-size:var(--text-md)}.oc-interview-bubble textarea:focus-visible{outline:none;border-color:var(--orange);box-shadow:0 0 0 3px var(--orange-soft)}.oc-interview-bubble .btn+.btn{margin-left:var(--space-2)}.oc-draft-card pre{max-height:300px;overflow:auto;white-space:pre-wrap;font-size:var(--text-xs)}.oc-assessment{margin:var(--space-3) 0;color:var(--orange);font-size:var(--text-md)}.oc-assessment.is-fresh,.oc-task-kind-card.is-confirmed{color:var(--green)}.oc-task-kind-card{display:flex;align-items:center;gap:var(--space-3);justify-content:space-between;flex-wrap:wrap;margin-top:var(--space-3);padding:var(--space-3) var(--space-4);border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--canvas-soft)}.oc-task-kind-card small{color:var(--muted)}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-duration:.01ms!important}}.oc-human-gate-card{position:relative;width:min(720px,calc(100% - 42px));margin:var(--space-3) auto;padding:var(--space-4) var(--space-5);border:1px solid color-mix(in srgb,var(--orange) 38%,var(--hairline));border-left:4px solid var(--orange);border-radius:var(--radius-lg);background:linear-gradient(180deg,color-mix(in srgb,var(--orange) 4%,var(--surface)),var(--surface) 40%);box-shadow:var(--shadow-soft);animation:panel-in .2s var(--ease-out)}.oc-human-gate-card.is-prepared,.oc-human-gate-card.is-confirmed{position:sticky;bottom:var(--space-2);z-index:6}.oc-human-gate-card header,.oc-human-gate-card footer{display:flex;justify-content:space-between;align-items:center;gap:var(--space-3)}.oc-human-gate-card header div{display:grid;gap:3px}.oc-human-gate-card header small{color:var(--muted);font-size:var(--text-xs);font-weight:600;letter-spacing:.04em}.oc-human-gate-card header strong{font-size:15px;font-weight:700;letter-spacing:-.01em}.oc-human-gate-card header>span{flex-shrink:0;padding:3px 10px;border-radius:var(--radius-pill);font-size:var(--text-xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;border:1px solid transparent}.oc-human-gate-card.is-prepared header>span{color:var(--orange-active);background:var(--orange-soft);border-color:color-mix(in srgb,var(--orange) 35%,transparent)}.oc-human-gate-card.is-confirmed header>span,.oc-human-gate-card.is-dispatched header>span{color:var(--blue);background:color-mix(in srgb,var(--blue) 10%,transparent);border-color:color-mix(in srgb,var(--blue) 30%,transparent)}.oc-human-gate-card.is-consumed header>span{color:var(--green);background:color-mix(in srgb,var(--green) 10%,transparent);border-color:color-mix(in srgb,var(--green) 30%,transparent)}.oc-human-gate-card.is-stale header>span,.oc-human-gate-card.is-expired header>span,.oc-human-gate-card.is-error header>span{color:var(--red);background:color-mix(in srgb,var(--red) 8%,transparent);border-color:color-mix(in srgb,var(--red) 30%,transparent)}.oc-human-gate-card>p{margin:var(--space-3) 0 0;color:var(--body);font-size:var(--text-md);line-height:1.55}.oc-human-gate-card>div:has(>b){margin-top:var(--space-3);padding:var(--space-2) var(--space-3);border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--canvas-soft);font-size:var(--text-sm)}.oc-human-gate-challenges,.oc-human-gate-card ol{max-height:160px;overflow:auto}.oc-human-gate-card ul,.oc-human-gate-card ol{margin:var(--space-2) 0 0;padding-left:1.3em;color:var(--body)}.oc-human-gate-card footer{margin-top:var(--space-4);padding-top:var(--space-3);border-top:1px solid var(--hairline)}.oc-human-gate-card footer a{color:var(--muted);font-size:var(--text-sm);font-weight:600;text-decoration:none}.oc-human-gate-card footer a:hover{color:var(--orange-active);text-decoration:underline}.oc-human-gate-error{margin-top:var(--space-2);padding:var(--space-2) var(--space-3);border-radius:var(--radius-sm);background:color-mix(in srgb,var(--red) 8%,transparent);color:var(--red);font-size:var(--text-sm);font-weight:600}.oc-human-gate-card.is-confirmed{border-left-color:var(--blue)}.oc-human-gate-card.is-consumed{border-left-color:var(--green);box-shadow:var(--shadow-panel)}.oc-human-gate-card.is-stale,.oc-human-gate-card.is-expired,.oc-human-gate-card.is-error{border-left-color:var(--red)}.oc-task-context{margin:var(--space-3);padding:var(--space-3);border:1px solid var(--hairline);border-radius:var(--radius-lg);background:var(--surface)}.oc-task-context>header{display:flex;justify-content:space-between;gap:var(--space-2);align-items:center}.oc-task-context>header div{display:grid;gap:2px}.oc-task-context>header small,.oc-task-context>header span,.oc-task-context-empty{color:var(--muted)}.oc-task-context-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--space-2);margin-top:var(--space-3)}.oc-task-context-grid>div{display:grid;gap:4px;min-width:0;padding:var(--space-2);border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--canvas-soft)}.oc-task-context-grid span,.oc-task-context-grid small{overflow-wrap:anywhere}.oc-shortcut-palette{position:absolute;z-index:12;left:0;right:0;bottom:calc(100% + 8px);display:grid;padding:6px;border:1px solid var(--hairline);border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-soft)}.oc-shortcut-palette button{display:flex;gap:var(--space-3);align-items:center;border:0;border-radius:var(--radius-md);padding:9px 10px;color:inherit;background:transparent;text-align:left;min-height:0}.oc-shortcut-palette button:hover,.oc-shortcut-palette button.is-active{background:var(--surface-muted)}.oc-shortcut-palette button span{display:grid;gap:2px}.oc-shortcut-palette button small{color:var(--muted)}.oc-composer-inner{position:relative}@media(max-width:800px){.oc-task-context-grid{grid-template-columns:1fr}}.oc-file-palette{position:absolute;z-index:13;left:0;right:0;bottom:calc(100% + 8px);display:grid;max-height:240px;overflow:auto;padding:6px;border:1px solid var(--hairline);border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-soft)}.oc-file-palette button{border:0;padding:8px 10px;color:inherit;background:transparent;text-align:left;border-radius:var(--radius-md);min-height:0}.oc-file-palette button:hover{background:var(--surface-muted)}.oc-runtime-controls{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:8px}.oc-select-menu{position:relative;flex-shrink:0}.oc-select-trigger{display:inline-flex;align-items:center;gap:6px;max-width:min(260px,calc(100vw - 32px));min-height:30px;padding:0 var(--space-2);border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface);color:var(--body);font:inherit;font-size:var(--text-sm);cursor:pointer;transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.oc-select-trigger:hover{border-color:var(--hairline-strong)}.oc-select-trigger:focus-visible{outline:none;border-color:var(--orange);box-shadow:0 0 0 3px var(--orange-soft)}.oc-select-trigger:disabled{cursor:not-allowed;opacity:.55}.oc-select-trigger-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-mono)}.oc-select-panel{position:absolute;left:0;bottom:calc(100% + 8px);z-index:18;width:min(340px,calc(100vw - 24px));max-height:min(60vh,440px);display:flex;flex-direction:column;gap:2px;padding:6px;border:1px solid var(--hairline);border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-soft);overflow:auto}.oc-select-search{flex-shrink:0;margin-bottom:4px;padding:7px 9px;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--surface-muted);color:var(--ink);font:inherit;font-size:var(--text-sm);outline:none}.oc-select-search:focus{border-color:var(--orange);box-shadow:0 0 0 3px var(--orange-soft)}.oc-select-status{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 10px;color:var(--muted);font-size:var(--text-sm)}.oc-select-status.is-error{color:var(--red)}.oc-model-group-head{padding:6px 10px 2px;color:var(--muted);font-size:11px;font-weight:650;letter-spacing:.04em;text-transform:uppercase}.oc-select-option{display:flex;align-items:center;gap:8px;width:100%;border:0;border-radius:var(--radius-md);padding:8px 10px;background:transparent;color:inherit;font:inherit;font-size:var(--text-sm);text-align:left;cursor:pointer;min-height:0}.oc-select-option.is-highlighted{background:var(--surface-muted)}.oc-select-option.is-active{color:var(--orange-active)}.oc-model-option-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-mono)}.oc-model-option-check{flex-shrink:0;color:var(--orange)}.oc-thinking-option-label{flex:1}.oc-thinking-badge{flex-shrink:0;padding:1px 7px;border:1px solid var(--hairline);border-radius:var(--radius-pill);color:var(--muted);font-size:11px}.oc-reload-status{color:var(--muted);font-size:var(--text-sm)}.oc-reload-banner{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 10px;border:1px solid color-mix(in srgb,var(--red) 35%,var(--hairline));border-radius:var(--radius-sm);background:color-mix(in srgb,var(--red) 6%,var(--surface));color:var(--red);font-size:var(--text-sm)}.oc-runtime-controls .oc-process-summary{margin-left:0}.oc-usage{color:var(--muted);font-size:var(--text-sm);font-variant-numeric:tabular-nums}.oc-runtime-context-menu{position:relative;margin-left:auto;flex-shrink:0}.oc-runtime-context-toggle.is-active{color:var(--orange-active);background:color-mix(in srgb,var(--orange) 10%,var(--surface))}.oc-runtime-context-panel{position:absolute;right:0;bottom:calc(100% + 8px);z-index:16;width:min(760px,calc(100vw - 24px));max-height:min(72vh,720px);display:flex;flex-direction:column;border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface);box-shadow:var(--shadow-soft);animation:panel-in .16s ease;overflow:hidden}.oc-runtime-context-head{display:flex;align-items:flex-start;gap:12px;flex-shrink:0;padding:12px 14px;border-bottom:1px solid var(--hairline);background:var(--surface-raised)}.oc-runtime-context-head-copy{flex:1;min-width:0;display:grid;gap:2px}.oc-runtime-context-head strong{font-size:14px}.oc-runtime-context-meta{font-size:11px;color:var(--muted)}.oc-runtime-context-close{flex-shrink:0;min-height:28px;padding:0 8px;font-size:12px}.oc-runtime-context-body{flex:1;min-height:0;overflow:auto;padding:12px 14px 14px;display:grid;gap:14px}.oc-runtime-context-chips{display:flex;flex-wrap:wrap;gap:6px}.oc-runtime-context-chip{display:inline-flex;align-items:center;gap:4px;max-width:100%;padding:4px 8px;border:1px solid var(--hairline);border-radius:999px;background:var(--canvas-soft);color:var(--body);font-size:11px;line-height:1.3}.oc-runtime-context-chip code{font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.oc-runtime-context-section{display:grid;gap:8px;min-width:0}.oc-runtime-context-section-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.oc-runtime-context-section-head strong{font-size:12px}.oc-runtime-context-hash{display:inline-block;margin-left:6px;font-size:11px;color:var(--muted)}.oc-runtime-context-text{margin:0;max-height:min(36vh,360px);overflow:auto;padding:10px 12px;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--canvas-soft);color:var(--body);font-size:12px;line-height:1.55;white-space:pre-wrap;overflow-wrap:anywhere}.oc-runtime-context-empty{margin:0;color:var(--muted);font-size:12px}.oc-runtime-context-tools{margin:0;padding:0;list-style:none;display:flex;flex-wrap:wrap;gap:6px}.oc-runtime-context-tools li code{display:inline-block;padding:3px 7px;border:1px solid var(--hairline);border-radius:6px;background:var(--canvas-soft);font-size:11px}.oc-runtime-context-raw{padding-top:10px;border-top:1px solid var(--hairline);font-size:12px;color:var(--muted)}.oc-runtime-context-raw summary{cursor:pointer;-webkit-user-select:none;user-select:none}.oc-runtime-context-raw pre{margin:8px 0 0;max-height:180px;overflow:auto;padding:8px;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--canvas-soft);font-size:11px;line-height:1.45;white-space:pre-wrap;overflow-wrap:anywhere}.oc-runtime-context-resources{display:grid;gap:6px;margin:0;padding:0;list-style:none}.oc-runtime-context-resources li{display:grid;gap:3px;padding:7px 9px;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--surface);font-size:var(--text-sm)}.oc-runtime-context-resources code{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--body);font-size:11px}.oc-runtime-context-resolved{white-space:normal!important;overflow-wrap:anywhere}.oc-runtime-context-diag{color:var(--amber);font-size:11px;line-height:1.5}.oc-runtime-context-diag.is-error{color:var(--red);display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.oc-runtime-context-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;flex-shrink:0;padding:10px 14px;border-top:1px solid var(--hairline);background:var(--surface-raised)}@media(max-width:800px){.oc-runtime-context-menu{margin-left:0;width:100%}.oc-runtime-context-panel{left:0;right:0;width:auto;max-height:min(78vh,680px)}}.oc-artifact-card{max-width:100%;margin:var(--space-3) 0;padding:var(--space-4);overflow:hidden;border:1px solid var(--hairline);border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-panel)}.oc-artifact-card header{display:flex;justify-content:space-between;align-items:center;gap:var(--space-3)}.oc-artifact-card header strong{font-size:var(--text-md);font-weight:700}.oc-artifact-card header>span{padding:2px 9px;border-radius:var(--radius-pill);background:var(--surface-muted);color:var(--muted);font-size:var(--text-xs);font-weight:700;letter-spacing:.05em;text-transform:uppercase}.oc-artifact-card pre{max-height:320px;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--text-xs)}.oc-artifact-card iframe{width:100%;min-height:280px;border:1px solid var(--hairline);border-radius:var(--radius-md);background:#fff}.oc-load-history{display:block;margin:var(--space-2) auto}.oc-attach-button{display:inline-flex;min-height:26px;align-items:center;padding:0 var(--space-2);border:1px solid var(--hairline);border-radius:var(--radius-pill);background:var(--surface);color:var(--muted);cursor:pointer;font-size:var(--text-sm);font-weight:600;transition:border-color var(--transition-fast),color var(--transition-fast),background-color var(--transition-fast)}.oc-attach-button:hover{border-color:var(--orange);color:var(--orange-active);background:var(--orange-soft)}.oc-composer-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:10px 14px;margin-bottom:6px;font-size:11.5px;color:var(--muted)}.oc-composer-toolbar .oc-attach-button{margin-bottom:0}.oc-attachment-list{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}.oc-attachment-list span{display:inline-flex;align-items:center;gap:5px;max-width:100%;padding:4px 8px;border:1px solid var(--hairline);border-radius:999px;overflow-wrap:anywhere}.oc-attachment-list button{border:0;color:inherit;background:transparent;cursor:pointer}@media(max-width:640px){.oc-runtime-controls,.oc-composer-foot{align-items:stretch;flex-direction:column}.oc-runtime-controls .oc-select-trigger,.oc-runtime-controls button{width:100%;max-width:100%;min-height:44px}.oc-runtime-controls .oc-select-menu{width:100%}.oc-select-panel{width:min(340px,calc(100vw - 24px))}.oc-artifact-card{width:100%}.oc-composer{padding-inline:8px}}.night-workbench .night-health{margin:var(--space-4) 0;padding:var(--space-3) var(--space-4);border:1px solid var(--hairline);border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-sm)}.night-health-main{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2) var(--space-3)}.night-health-pill{display:inline-flex;align-items:center;gap:6px;padding:3px 12px;border-radius:var(--radius-pill);background:color-mix(in srgb,var(--green) 14%,white);color:var(--green);font-size:var(--text-sm);font-weight:700;letter-spacing:.02em}.night-health-pill:before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}.night-health-pill.is-bad{background:color-mix(in srgb,var(--red) 14%,white);color:var(--red)}.night-health-hint{margin:var(--space-2) 0 0}.night-aux-ticker{margin-top:var(--space-3);padding-top:var(--space-3);border-top:1px solid var(--hairline)}.night-aux-ticker-label{margin:0;color:var(--body);font-size:var(--text-md)}.night-doctor-list{margin:var(--space-2) 0 0;padding-left:1.2em;color:var(--body);font-size:12.5px}.night-kpi{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:var(--space-3) 0;color:var(--body);font-size:var(--text-md)}.night-kpi span{padding:4px 12px;border:1px solid var(--hairline);border-radius:var(--radius-pill);background:var(--canvas-soft);font-variant-numeric:tabular-nums;font-weight:600}.night-board,.night-wizard,.night-discard-card,.night-advanced{margin-top:var(--space-5)}.night-schedule-list{list-style:none;margin:0;padding:0;display:grid;gap:var(--space-3)}.night-schedule-row{padding:var(--space-3) var(--space-4);border:1px solid var(--hairline);border-radius:var(--radius-lg);background:var(--surface);transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.night-schedule-row:hover{border-color:var(--hairline-strong);box-shadow:var(--shadow-panel)}.night-schedule-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--space-2)}.night-status-chip{padding:2px 10px;border-radius:var(--radius-pill);background:color-mix(in srgb,var(--blue) 10%,transparent);border:1px solid color-mix(in srgb,var(--blue) 25%,transparent);font-size:var(--text-xs);color:var(--blue);font-weight:700;letter-spacing:.03em}.night-schedule-row[data-status=failed] .night-status-chip,.night-schedule-row[data-status=human_required] .night-status-chip{color:var(--red);background:color-mix(in srgb,var(--red) 8%,transparent);border-color:color-mix(in srgb,var(--red) 28%,transparent)}.night-schedule-row[data-status=succeeded] .night-status-chip{color:var(--green);background:color-mix(in srgb,var(--green) 10%,transparent);border-color:color-mix(in srgb,var(--green) 28%,transparent)}.night-schedule-row[data-status=running] .night-status-chip,.night-schedule-row[data-status=dispatching] .night-status-chip{color:var(--orange-active);background:var(--orange-soft);border-color:color-mix(in srgb,var(--orange) 30%,transparent)}.night-next-action{font-size:var(--text-sm);overflow-wrap:anywhere}.night-review-card,.night-discard-card{padding:var(--space-4) var(--space-5);border:1px solid var(--hairline-strong);border-radius:var(--radius-lg);background:var(--canvas-soft)}.night-writeset{margin:0;padding-left:1.1em}.night-advanced{border-top:1px solid var(--hairline);padding-top:var(--space-3)}.night-advanced summary{cursor:pointer;color:var(--body);font-weight:600}.checkbox-field{display:flex;align-items:center;gap:var(--space-2)}.models-page .panel-lead{margin-top:var(--space-2)}.models-header{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2) var(--space-3)}.models-header h2{margin:0}.models-status-pill{display:inline-flex;align-items:center;gap:6px;padding:4px 14px;border-radius:var(--radius-pill);font-size:var(--text-sm);font-weight:700;letter-spacing:.02em}.models-status-pill:before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}.models-status-pill[data-state=ready]{background:color-mix(in srgb,var(--green) 14%,white);color:var(--green)}.models-status-pill[data-state=setup-required]{background:color-mix(in srgb,var(--red) 14%,white);color:var(--red)}.models-status-pill[data-state=degraded]{background:color-mix(in srgb,var(--orange) 16%,white);color:var(--orange-active)}.models-status-pill[data-state=unknown]{background:var(--canvas-soft);color:var(--muted)}.models-subtitle{margin:0 0 var(--space-2);font-size:var(--text-md);font-weight:700;color:var(--ink)}.models-probe-card,.models-setup-card,.models-blocked-card{margin-top:var(--space-4);padding:var(--space-3) var(--space-4);border:1px solid var(--hairline);border-radius:var(--radius-md);background:color-mix(in srgb,var(--surface) 55%,transparent)}.models-probe-list{list-style:none;margin:0;padding:0;display:grid;gap:var(--space-2)}.models-probe-item{display:grid;grid-template-columns:28px 1fr;gap:var(--space-2);align-items:start;padding:var(--space-2) 0;border-bottom:1px solid var(--hairline)}.models-probe-item:last-child{border-bottom:none;padding-bottom:0}.models-probe-badge{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;font-size:14px;font-weight:800;line-height:1;background:var(--canvas-soft);color:var(--muted)}.models-probe-item.is-ok .models-probe-badge{background:color-mix(in srgb,var(--green) 16%,white);color:var(--green)}.models-probe-item.is-bad .models-probe-badge{background:color-mix(in srgb,var(--red) 14%,white);color:var(--red)}.models-probe-body{display:grid;gap:2px;min-width:0}.models-probe-body strong{font-size:var(--text-md);color:var(--ink)}.models-probe-body .muted{font-size:var(--text-sm)}.models-probe-detail{margin:var(--space-1) 0 0;font-size:var(--text-sm);color:var(--body);line-height:1.45}.models-setup-steps{margin:0 0 var(--space-3);padding-left:1.25em;color:var(--body);font-size:var(--text-md);line-height:1.5}.models-setup-steps li+li{margin-top:var(--space-2)}.models-degraded-note{margin:var(--space-4) 0 0;padding:var(--space-3) var(--space-4);border-radius:var(--radius-md);border:1px solid color-mix(in srgb,var(--orange) 35%,var(--hairline));background:color-mix(in srgb,var(--orange) 8%,white);color:var(--body);font-size:var(--text-md)}.models-blocked-list{margin:0;padding:0;list-style:none;display:flex;flex-wrap:wrap;gap:var(--space-2)}.models-blocked-list li{padding:4px 10px;border-radius:var(--radius-pill);border:1px solid var(--hairline);background:var(--surface)}.models-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-3);margin-top:var(--space-4)}.models-generated-at{font-size:var(--text-sm)}.models-raw-details{margin-top:var(--space-4);font-size:var(--text-sm);color:var(--muted)}.models-raw-details summary{cursor:pointer;font-weight:600;color:var(--body)}.models-raw-json{margin-top:var(--space-2);max-height:240px;overflow:auto}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
7
|
<link rel="stylesheet" href="/inspect/operator-chrome.css" />
|
|
8
8
|
<title>Loop 操作台 · Operator Console</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-BUOLppPr.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-C1KzazY5.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
|
@@ -9,20 +9,47 @@ import { requestId } from "./format.js";
|
|
|
9
9
|
* original request would have. `assistantId` is the in-flight assistant
|
|
10
10
|
* message id to patch for streaming text; reconnect creates one if none. */
|
|
11
11
|
export function createChatSseEventApplier(deps) {
|
|
12
|
-
const { refs, setError, setProcessOpen, setInterview, setHumanGateCards, setArtifactCards, setStreaming, setStreamingAssistantId, setUnread, setOperationCards, setUsage, setMessages, setToolCalls, } = deps;
|
|
12
|
+
const { refs, onReconcile, setError, setProcessOpen, setInterview, setHumanGateCards, setArtifactCards, setStreaming, setStreamingAssistantId, setUnread, setOperationCards, setUsage, setMessages, setToolCalls, } = deps;
|
|
13
13
|
return (eventName, data, assistantId, options) => {
|
|
14
|
+
if (options?.generation !== undefined &&
|
|
15
|
+
refs.sessionGenerationRef.current !== options.generation)
|
|
16
|
+
return;
|
|
14
17
|
const replay = options?.replay === true;
|
|
18
|
+
if (eventName === "stream_ready")
|
|
19
|
+
return;
|
|
15
20
|
if (eventName === "reconcile") {
|
|
16
|
-
// Cursor aged out of the server ring
|
|
17
|
-
//
|
|
21
|
+
// Cursor aged out of the server ring (or the turn changed under us):
|
|
22
|
+
// retained history may be incomplete. Surface recovery via the callback
|
|
23
|
+
// (reload canonical facts -> new cursor -> reconnect) instead of a fatal
|
|
24
|
+
// global error; the snapshot replay frames that follow are still applied
|
|
25
|
+
// as ordinary blocks (deduped by eventId, replay text skipped).
|
|
18
26
|
const reason = typeof data.reason === "string" ? data.reason : "cursor_expired";
|
|
19
|
-
|
|
27
|
+
onReconcile?.({
|
|
28
|
+
reason,
|
|
29
|
+
...(typeof data.minSeq === "number" ? { minSeq: data.minSeq } : {}),
|
|
30
|
+
...(typeof data.epoch === "string" ? { epoch: data.epoch } : {}),
|
|
31
|
+
...(typeof data.latestTurnId === "string"
|
|
32
|
+
? { latestTurnId: data.latestTurnId }
|
|
33
|
+
: {}),
|
|
34
|
+
...(typeof data.latestEventId === "string"
|
|
35
|
+
? { latestEventId: data.latestEventId }
|
|
36
|
+
: data.latestEventId === null
|
|
37
|
+
? { latestEventId: null }
|
|
38
|
+
: {}),
|
|
39
|
+
});
|
|
20
40
|
return;
|
|
21
41
|
}
|
|
22
42
|
const payload = data.data && typeof data.data === "object"
|
|
23
43
|
? data.data
|
|
24
44
|
: data;
|
|
25
45
|
const turnId = typeof data.turnId === "string" ? data.turnId : undefined;
|
|
46
|
+
if (turnId &&
|
|
47
|
+
(eventName === "agent_start" || eventName === "message_update") &&
|
|
48
|
+
refs.settledTurnIdsRef.current.has(turnId)) {
|
|
49
|
+
// A delayed start/text frame from a completed turn must not create a
|
|
50
|
+
// ghost bubble or set streaming again (M5.4).
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
26
53
|
let resolvedAssistantId = assistantId;
|
|
27
54
|
if (turnId) {
|
|
28
55
|
const mapped = refs.turnAssistantIdsRef.current.get(turnId);
|
|
@@ -102,16 +129,26 @@ export function createChatSseEventApplier(deps) {
|
|
|
102
129
|
turnId !== refs.activeTurnIdRef.current) {
|
|
103
130
|
return;
|
|
104
131
|
}
|
|
105
|
-
if (turnId)
|
|
132
|
+
if (turnId) {
|
|
133
|
+
refs.settledTurnIdsRef.current.delete(turnId);
|
|
106
134
|
refs.activeTurnIdRef.current = turnId;
|
|
135
|
+
}
|
|
136
|
+
refs.streamingRef.current = true;
|
|
107
137
|
setStreaming(true);
|
|
108
138
|
setStreamingAssistantId(resolvedAssistantId);
|
|
109
139
|
return;
|
|
110
140
|
}
|
|
111
141
|
if (eventName === "agent_settled" || eventName === "agent_end") {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
//
|
|
142
|
+
if (turnId)
|
|
143
|
+
refs.settledTurnIdsRef.current.add(turnId);
|
|
144
|
+
// `agent_end` means the SDK run ended, but the Console keeps the only
|
|
145
|
+
// GET stream through an idle grace for agent_settled/state reconciliation
|
|
146
|
+
// (M4.4). It must not prematurely close the visible turn.
|
|
147
|
+
if (eventName === "agent_end")
|
|
148
|
+
return;
|
|
149
|
+
if (turnId === refs.activeTurnIdRef.current)
|
|
150
|
+
refs.activeTurnIdRef.current = null;
|
|
151
|
+
refs.streamingRef.current = false;
|
|
115
152
|
setStreaming(false);
|
|
116
153
|
setStreamingAssistantId(null);
|
|
117
154
|
if (eventName === "agent_settled" &&
|
|
@@ -7,6 +7,9 @@ export function useChatRefs() {
|
|
|
7
7
|
const inlineStreamSessionIdsRef = useRef(new Set());
|
|
8
8
|
const assistantIdRef = useRef(null);
|
|
9
9
|
const lastEventIdRef = useRef(null);
|
|
10
|
+
const sessionGenerationRef = useRef(0);
|
|
11
|
+
const eventsStreamStateRef = useRef(null);
|
|
12
|
+
const settledTurnIdsRef = useRef(new Set());
|
|
10
13
|
const seenEventIdsRef = useRef(new Set());
|
|
11
14
|
const turnAssistantIdsRef = useRef(new Map());
|
|
12
15
|
const reconnectAbortRef = useRef(null);
|
|
@@ -24,6 +27,9 @@ export function useChatRefs() {
|
|
|
24
27
|
inlineStreamSessionIdsRef,
|
|
25
28
|
assistantIdRef,
|
|
26
29
|
lastEventIdRef,
|
|
30
|
+
sessionGenerationRef,
|
|
31
|
+
eventsStreamStateRef,
|
|
32
|
+
settledTurnIdsRef,
|
|
27
33
|
seenEventIdsRef,
|
|
28
34
|
turnAssistantIdsRef,
|
|
29
35
|
reconnectAbortRef,
|
|
@@ -39,6 +45,9 @@ export function useChatRefs() {
|
|
|
39
45
|
inlineStreamSessionIdsRef,
|
|
40
46
|
assistantIdRef,
|
|
41
47
|
lastEventIdRef,
|
|
48
|
+
sessionGenerationRef,
|
|
49
|
+
eventsStreamStateRef,
|
|
50
|
+
settledTurnIdsRef,
|
|
42
51
|
seenEventIdsRef,
|
|
43
52
|
turnAssistantIdsRef,
|
|
44
53
|
reconnectAbortRef,
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator Chat — runtime snapshot store (design §2.2 / AC-04/05/06).
|
|
3
|
+
*
|
|
4
|
+
* A pure, framework-free state machine for the unified Live Runtime Snapshot.
|
|
5
|
+
* The browser treats the current Pi session as the single source of truth:
|
|
6
|
+
*
|
|
7
|
+
* status: idle → loading → ready | error
|
|
8
|
+
* reload: reloading (single-flight; no silent queueing) → success | failure
|
|
9
|
+
*
|
|
10
|
+
* `createRuntimeSnapshotStore` is intentionally side-effect free so it can be
|
|
11
|
+
* unit-tested in a plain Node environment; the React binding (fetch + attach)
|
|
12
|
+
* lives in `useRuntimeSnapshot.ts`.
|
|
13
|
+
*/
|
|
14
|
+
export const runtimeSnapshotInitialState = {
|
|
15
|
+
status: "idle",
|
|
16
|
+
snapshot: null,
|
|
17
|
+
error: null,
|
|
18
|
+
reloading: false,
|
|
19
|
+
reloadError: null,
|
|
20
|
+
mutationBusy: false,
|
|
21
|
+
mutationToken: null,
|
|
22
|
+
};
|
|
23
|
+
/** Parse a failed HTTP body into a structured snapshot error (never empty). */
|
|
24
|
+
export function parseRuntimeSnapshotError(body) {
|
|
25
|
+
if (!body || typeof body !== "object") {
|
|
26
|
+
return { message: "runtime snapshot request failed" };
|
|
27
|
+
}
|
|
28
|
+
const record = body;
|
|
29
|
+
const err = record.error;
|
|
30
|
+
if (err && typeof err === "object") {
|
|
31
|
+
const e = err;
|
|
32
|
+
return {
|
|
33
|
+
...(typeof e.code === "string" && e.code ? { code: e.code } : {}),
|
|
34
|
+
...(typeof record.status === "number" ? { status: record.status } : {}),
|
|
35
|
+
message: typeof e.message === "string" && e.message
|
|
36
|
+
? e.message
|
|
37
|
+
: "runtime snapshot request failed",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
...(typeof record.status === "number" ? { status: record.status } : {}),
|
|
42
|
+
message: typeof record === "object" && "message" in record &&
|
|
43
|
+
typeof record.message === "string" &&
|
|
44
|
+
record.message
|
|
45
|
+
? record.message
|
|
46
|
+
: "runtime snapshot request failed",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function runtimeSnapshotReducer(state, action) {
|
|
50
|
+
switch (action.type) {
|
|
51
|
+
case "load-start":
|
|
52
|
+
// Stale replies are dropped by the fetch layer (sessionId guard); the
|
|
53
|
+
// reducer stays a deterministic state machine. The previous snapshot
|
|
54
|
+
// remains visible during a background refresh.
|
|
55
|
+
return {
|
|
56
|
+
...state,
|
|
57
|
+
sessionId: action.sessionId,
|
|
58
|
+
status: "loading",
|
|
59
|
+
error: null,
|
|
60
|
+
};
|
|
61
|
+
case "load-success":
|
|
62
|
+
// RF-04: monotonic revision guard — a stale (lower-revision) reply for
|
|
63
|
+
// the current session must never overwrite a newer snapshot, so a slow
|
|
64
|
+
// refresh cannot reorder-overwrite the current session selection. The
|
|
65
|
+
// fetch layer already drops replies for other sessions (sessionId guard).
|
|
66
|
+
if (state.snapshot &&
|
|
67
|
+
state.sessionId === action.sessionId &&
|
|
68
|
+
action.snapshot.revision < state.snapshot.revision) {
|
|
69
|
+
return state;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
...state,
|
|
73
|
+
sessionId: action.sessionId,
|
|
74
|
+
status: "ready",
|
|
75
|
+
snapshot: action.snapshot,
|
|
76
|
+
error: null,
|
|
77
|
+
reloadError: null,
|
|
78
|
+
reloading: false,
|
|
79
|
+
};
|
|
80
|
+
case "load-failure":
|
|
81
|
+
// AC-06: a failed load is a STRUCTURED error state — never folded
|
|
82
|
+
// into a fake empty snapshot.
|
|
83
|
+
return {
|
|
84
|
+
...state,
|
|
85
|
+
sessionId: action.sessionId,
|
|
86
|
+
status: "error",
|
|
87
|
+
error: action.error,
|
|
88
|
+
snapshot: null,
|
|
89
|
+
};
|
|
90
|
+
case "reload-start":
|
|
91
|
+
// Single-flight: a reload already in progress is NOT queued (AC-06).
|
|
92
|
+
if (state.reloading)
|
|
93
|
+
return state;
|
|
94
|
+
return {
|
|
95
|
+
...state,
|
|
96
|
+
reloading: true,
|
|
97
|
+
reloadError: null,
|
|
98
|
+
...(state.status === "error" ? { error: null } : {}),
|
|
99
|
+
};
|
|
100
|
+
case "reload-success":
|
|
101
|
+
// Atomic replacement: the new snapshot supersedes the old one and
|
|
102
|
+
// every open panel reads the same revision.
|
|
103
|
+
return {
|
|
104
|
+
...state,
|
|
105
|
+
status: "ready",
|
|
106
|
+
snapshot: action.snapshot,
|
|
107
|
+
reloading: false,
|
|
108
|
+
reloadError: null,
|
|
109
|
+
error: null,
|
|
110
|
+
sessionId: action.snapshot.sessionId || state.sessionId,
|
|
111
|
+
};
|
|
112
|
+
case "reload-failure":
|
|
113
|
+
return {
|
|
114
|
+
...state,
|
|
115
|
+
reloading: false,
|
|
116
|
+
reloadError: action.error,
|
|
117
|
+
};
|
|
118
|
+
case "mutation-start":
|
|
119
|
+
return {
|
|
120
|
+
...state,
|
|
121
|
+
mutationBusy: true,
|
|
122
|
+
mutationToken: action.token,
|
|
123
|
+
};
|
|
124
|
+
case "mutation-adopt":
|
|
125
|
+
// RF2-02: atomic adoption of a mutation response snapshot. Three
|
|
126
|
+
// guards are evaluated BEFORE any write:
|
|
127
|
+
// - mutationToken: only the LATEST mutation may adopt — a stale
|
|
128
|
+
// out-of-order response is dropped entirely;
|
|
129
|
+
// - sessionId: never adopt a mutation response into a state that
|
|
130
|
+
// belongs to a different session;
|
|
131
|
+
// - revision monotonic: equal/higher revisions adopt, lower reject
|
|
132
|
+
// (a stale reply can never reorder-overwrite the selection).
|
|
133
|
+
// The action NEVER settles mutationBusy (mutation-settle owns that)
|
|
134
|
+
// and never passes through an intermediate stale/loading state.
|
|
135
|
+
if (state.mutationToken !== action.token)
|
|
136
|
+
return state;
|
|
137
|
+
if (state.sessionId !== undefined &&
|
|
138
|
+
state.sessionId !== action.sessionId)
|
|
139
|
+
return state;
|
|
140
|
+
if (state.snapshot &&
|
|
141
|
+
action.snapshot.revision < state.snapshot.revision)
|
|
142
|
+
return state;
|
|
143
|
+
return {
|
|
144
|
+
...state,
|
|
145
|
+
sessionId: action.sessionId,
|
|
146
|
+
status: "ready",
|
|
147
|
+
snapshot: action.snapshot,
|
|
148
|
+
error: null,
|
|
149
|
+
reloadError: null,
|
|
150
|
+
reloading: false,
|
|
151
|
+
};
|
|
152
|
+
case "mutation-settle":
|
|
153
|
+
// RF-04: only the LATEST request token settles the busy state; a
|
|
154
|
+
// stale (earlier) settle must not clear busy while a newer mutation
|
|
155
|
+
// is still in flight.
|
|
156
|
+
if (state.mutationToken !== action.token)
|
|
157
|
+
return state;
|
|
158
|
+
return {
|
|
159
|
+
...state,
|
|
160
|
+
mutationBusy: false,
|
|
161
|
+
mutationToken: null,
|
|
162
|
+
};
|
|
163
|
+
case "mutation-reset":
|
|
164
|
+
// RF3-02: a session switch immediately abandons the in-flight
|
|
165
|
+
// mutation lifecycle — the new session must not inherit the old
|
|
166
|
+
// session's busy state. The adopted snapshot/status stay untouched
|
|
167
|
+
// (a load-start for the new session follows); stale adopt/settle
|
|
168
|
+
// actions then become no-ops via the token + sessionId guards.
|
|
169
|
+
return {
|
|
170
|
+
...state,
|
|
171
|
+
mutationBusy: false,
|
|
172
|
+
mutationToken: null,
|
|
173
|
+
};
|
|
174
|
+
case "reset":
|
|
175
|
+
return { ...runtimeSnapshotInitialState };
|
|
176
|
+
default:
|
|
177
|
+
return state;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Derive the session/runtimeContext selection patch from an ADOPTED snapshot
|
|
182
|
+
* (RF3-01). The hydrate effect in `useRuntimeControls` feeds this patch into
|
|
183
|
+
* `mergeSessionRuntime` / `runtimeContext`, so derived state only ever comes
|
|
184
|
+
* from a snapshot the reducer guards accepted — the mutation success branch
|
|
185
|
+
* never writes session/runtimeContext directly. Missing fields are omitted;
|
|
186
|
+
* `auto` resolves to the Pi effective thinking level.
|
|
187
|
+
*/
|
|
188
|
+
export function runtimeSelectionFromSnapshot(snapshot) {
|
|
189
|
+
const selection = snapshot.selection;
|
|
190
|
+
return {
|
|
191
|
+
...(selection.model
|
|
192
|
+
? {
|
|
193
|
+
model: {
|
|
194
|
+
provider: selection.model.provider,
|
|
195
|
+
modelId: selection.model.id,
|
|
196
|
+
},
|
|
197
|
+
}
|
|
198
|
+
: {}),
|
|
199
|
+
...(selection.thinkingSelection === "auto"
|
|
200
|
+
? selection.effectiveThinkingLevel
|
|
201
|
+
? { thinkingLevel: selection.effectiveThinkingLevel }
|
|
202
|
+
: {}
|
|
203
|
+
: { thinkingLevel: selection.thinkingSelection }),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* RF4: merge an incoming `/runtime-context` response with the ADOPTED snapshot
|
|
208
|
+
* (coordination seam genuinely called by the production `useRuntimeControls`
|
|
209
|
+
* context hydration effect; tests import this same symbol — no copied logic).
|
|
210
|
+
*
|
|
211
|
+
* Content fields (systemPrompt/resources/suffix/readOnly) always come from the
|
|
212
|
+
* response. model/thinkingLevel NEVER come from the response body — the
|
|
213
|
+
* backend returns them without revision semantics, so a late reply for the
|
|
214
|
+
* same session could carry a stale selection. Selection is derived exclusively
|
|
215
|
+
* from the adopted snapshot when it matches the current session (same
|
|
216
|
+
* derivation as the snapshot hydration effect: `runtimeSelectionFromSnapshot`,
|
|
217
|
+
* `auto` resolves to the Pi effective level); otherwise the current derived
|
|
218
|
+
* values are preserved, or omitted when absent. The store is never written
|
|
219
|
+
* here, so the snapshot hydration effect never re-runs.
|
|
220
|
+
*/
|
|
221
|
+
export function mergeRuntimeContextWithSnapshot(current, context, snapshot, sessionId) {
|
|
222
|
+
const selection = snapshot && snapshot.sessionId === sessionId
|
|
223
|
+
? runtimeSelectionFromSnapshot(snapshot)
|
|
224
|
+
: {};
|
|
225
|
+
const model = selection.model ?? current?.model;
|
|
226
|
+
const thinkingLevel = selection.thinkingLevel ?? current?.thinkingLevel;
|
|
227
|
+
return {
|
|
228
|
+
readOnly: context.readOnly,
|
|
229
|
+
systemPrompt: context.systemPrompt,
|
|
230
|
+
resources: context.resources,
|
|
231
|
+
suffix: context.suffix,
|
|
232
|
+
...(model ? { model } : {}),
|
|
233
|
+
...(thinkingLevel ? { thinkingLevel } : {}),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/** Create a fresh snapshot store (framework-free; React binding adds fetch). */
|
|
237
|
+
export function createRuntimeSnapshotStore(initial = runtimeSnapshotInitialState) {
|
|
238
|
+
let state = initial;
|
|
239
|
+
const listeners = new Set();
|
|
240
|
+
return {
|
|
241
|
+
getState: () => state,
|
|
242
|
+
dispatch: (action) => {
|
|
243
|
+
state = runtimeSnapshotReducer(state, action);
|
|
244
|
+
for (const listener of listeners)
|
|
245
|
+
listener();
|
|
246
|
+
return state;
|
|
247
|
+
},
|
|
248
|
+
subscribe: (listener) => {
|
|
249
|
+
listeners.add(listener);
|
|
250
|
+
return () => listeners.delete(listener);
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/** True when the given error is the busy 409 (PI_SESSION_BUSY). */
|
|
255
|
+
export function isSnapshotBusyError(error) {
|
|
256
|
+
return error?.code === "PI_SESSION_BUSY";
|
|
257
|
+
}
|
|
@@ -17,12 +17,23 @@ export function useChatSessions(params) {
|
|
|
17
17
|
// newer user choice.
|
|
18
18
|
const sessionSelectionVersionRef = useRef(0);
|
|
19
19
|
const resetActiveProjection = useCallback(() => {
|
|
20
|
+
// M4.5/M5.2: selection is a hard ownership boundary. Abort every request
|
|
21
|
+
// owned by the old projection, then invalidate its async callbacks.
|
|
22
|
+
refs.sessionGenerationRef.current += 1;
|
|
23
|
+
refs.abortRef.current?.abort();
|
|
24
|
+
refs.reconnectAbortRef.current?.abort();
|
|
25
|
+
for (const controller of refs.sessionAbortControllersRef.current.values())
|
|
26
|
+
controller.abort();
|
|
27
|
+
refs.sessionAbortControllersRef.current.clear();
|
|
28
|
+
refs.reconnectAbortRef.current = null;
|
|
29
|
+
refs.eventsStreamStateRef.current = null;
|
|
20
30
|
refs.streamingRef.current = false;
|
|
21
31
|
refs.abortRef.current = null;
|
|
22
32
|
refs.assistantIdRef.current = null;
|
|
23
33
|
refs.lastEventIdRef.current = null;
|
|
24
34
|
refs.seenEventIdsRef.current = new Set();
|
|
25
35
|
refs.turnAssistantIdsRef.current = new Map();
|
|
36
|
+
refs.settledTurnIdsRef.current = new Set();
|
|
26
37
|
refs.activeTurnIdRef.current = null;
|
|
27
38
|
resetForNewSession();
|
|
28
39
|
setInput("");
|
|
@@ -124,11 +135,13 @@ export function useChatSessions(params) {
|
|
|
124
135
|
sessionId: body.session.sessionId,
|
|
125
136
|
...runtimeSelectionFromRecord(body.session),
|
|
126
137
|
};
|
|
138
|
+
resetActiveProjection();
|
|
127
139
|
refs.sessionRef.current = next;
|
|
128
140
|
refs.streamingRef.current = Boolean(body.session.activeTurnId);
|
|
129
141
|
refs.activeTurnIdRef.current = body.session.activeTurnId ?? null;
|
|
130
142
|
refs.seenEventIdsRef.current = new Set();
|
|
131
143
|
refs.turnAssistantIdsRef.current = new Map();
|
|
144
|
+
refs.settledTurnIdsRef.current = new Set();
|
|
132
145
|
refs.lastEventIdRef.current = null;
|
|
133
146
|
setSession(next);
|
|
134
147
|
refs.assistantIdRef.current = null;
|
|
@@ -159,6 +172,7 @@ export function useChatSessions(params) {
|
|
|
159
172
|
setPreviews,
|
|
160
173
|
setInput,
|
|
161
174
|
applyActivatedSession,
|
|
175
|
+
resetActiveProjection,
|
|
162
176
|
setActivePreviewPath,
|
|
163
177
|
setError,
|
|
164
178
|
]);
|
|
@@ -189,6 +203,7 @@ export function useChatSessions(params) {
|
|
|
189
203
|
sessionId: body.session.sessionId,
|
|
190
204
|
...runtimeSelectionFromRecord(body.session),
|
|
191
205
|
};
|
|
206
|
+
refs.sessionGenerationRef.current += 1;
|
|
192
207
|
refs.sessionRef.current = restored;
|
|
193
208
|
// Do not seed Last-Event-ID here: session messages are restored
|
|
194
209
|
// from the durable store, but tool/gate/operation projections live
|
|
@@ -197,6 +212,7 @@ export function useChatSessions(params) {
|
|
|
197
212
|
refs.activeTurnIdRef.current = body.session.activeTurnId ?? null;
|
|
198
213
|
refs.seenEventIdsRef.current = new Set();
|
|
199
214
|
refs.turnAssistantIdsRef.current = new Map();
|
|
215
|
+
refs.settledTurnIdsRef.current = new Set();
|
|
200
216
|
refs.lastEventIdRef.current = null;
|
|
201
217
|
setSession(restored);
|
|
202
218
|
setInput(body.session.composerDraft ?? "");
|