@tea-agent/loop-agent 0.22.0 → 0.23.1
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/AGENTS.md +42 -108
- package/CHANGELOG.md +68 -0
- package/README.md +7 -4
- package/dist/application/context-usage/skill-resolution-stats.js +263 -0
- package/dist/application/dag/generate-task-dag.js +17 -3
- package/dist/cli/command-definitions.js +8 -7
- package/dist/cli/program.js +17 -15
- package/dist/commands/doctor.js +269 -18
- package/dist/commands/init.js +101 -86
- package/dist/commands/stats.js +40 -11
- package/dist/shared/operator/capabilities.js +11 -1
- package/dist/worker/console/app-data.js +4 -0
- package/dist/worker/console/chat/instruction-skills.js +217 -0
- package/dist/worker/console/chat/model-resolver.js +106 -0
- package/dist/worker/console/chat/pi-runtime.js +605 -0
- package/dist/worker/console/chat/resource-loader.js +66 -0
- package/dist/worker/console/chat/routes.js +357 -0
- package/dist/worker/console/chat/session-store.js +238 -0
- package/dist/worker/console/chat/tool-adapter.js +238 -0
- package/dist/worker/console/chat/tools.js +171 -0
- package/dist/worker/console/server.js +55 -0
- package/dist/worker/console/static/assets/index-DVl7Jxt5.js +25 -0
- package/dist/worker/console/static/assets/index-lVcIr9Ju.css +1 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/feature/profile-schema.js +1 -1
- package/dist/workflows/dag/init-hybrid.js +69 -21
- package/dist/workflows/dag/node-execution.js +38 -1
- package/dist/workflows/dag/output-protocol.js +89 -0
- package/dist/workflows/dag/prompt.js +35 -1
- package/dist/workflows/dag/recovery-recommendation.js +45 -0
- package/dist/workflows/dag/report.js +28 -1
- package/dist/workflows/dag/rerun-task.js +1 -1
- package/dist/workflows/dag/scheduler.js +9 -0
- package/dist/workflows/dag/types.js +12 -0
- package/dist/workflows/dag/validate.js +55 -0
- package/docs/README.md +73 -156
- package/docs/architecture/README.md +3 -2
- package/docs/architecture/dag-execution.md +2 -2
- package/docs/architecture/evolution.md +14 -12
- package/docs/architecture/system-overview.md +1 -1
- package/docs/architecture/worker-and-feature.md +3 -3
- package/docs/governance/README.md +15 -0
- package/docs/{harness-methodology-debugging.md → governance/harness-methodology-debugging.md} +27 -3
- package/docs/init-surface.manifest.json +22 -4
- package/docs/operations/README.md +12 -0
- package/docs/{local-development-environment.md → operations/local-development-environment.md} +1 -1
- package/docs/skills/vetted-skill-registry.md +23 -3
- package/docs/templates/README.md +55 -0
- package/docs/templates/evaluation/agents-map-slim-v1.candidate.json +9 -0
- package/docs/templates/evaluation/agents-map-slim-v1.md +87 -0
- package/docs/templates/evaluation/agents-map-verbose-v0.candidate.json +9 -0
- package/docs/templates/evaluation/agents-map-verbose-v0.md +153 -0
- package/docs/templates/hybrid-dag.json +1 -1
- package/docs/templates/progress-log.md +9 -2
- package/harness.json +5 -5
- package/package.json +5 -5
- package/scripts/kb-bootstrap-init-skeleton.sh +2 -2
- package/skills/agent-worker/SKILL.md +1 -1
- package/skills/grill-with-docs/SKILL.md +44 -52
- package/skills/grill-with-docs/adr-format.md +37 -26
- package/skills/grill-with-docs/context-format.md +18 -26
- package/skills/loop-agent/SKILL.md +28 -112
- package/skills/loop-agent/references/command-reference.md +9 -3
- package/skills/loop-agent/references/harness-policy.md +3 -3
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/skills/loop-agent/references/orchestrator-and-interventions.md +1 -1
- package/skills/loop-agent/references/task-workflow.md +2 -0
- package/skills/systematic-debugging/SKILL.md +20 -4
- package/skills/test-driven-development/SKILL.md +10 -3
- package/dist/worker/console/static/assets/index-CUDke82y.js +0 -18
- package/dist/worker/console/static/assets/index-wSEksVSO.css +0 -1
- /package/docs/{harness-methodology-tdd.md → governance/harness-methodology-tdd.md} +0 -0
- /package/docs/{harness-methodology-verification.md → governance/harness-methodology-verification.md} +0 -0
|
@@ -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;--radius-sm: 6px;--radius-md: 8px;--radius-lg: 12px;--radius-pill: 999px;--transition-fast: .16s ease;--transition-base: .22s ease;--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%);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}code,.cmd,.kv dd code{font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace}.embedded-operator-nav{position:sticky;top:0;z-index:40;display:flex;gap:6px;align-items:center;margin:0;padding:8px max(24px,calc((100vw - 1120px)/2));border-bottom:1px solid var(--hairline);background:color-mix(in srgb,var(--canvas) 92%,transparent);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);font-size:13px;font-weight:700}.embedded-operator-nav a,.embedded-operator-nav span{padding:6px 12px;border-radius:var(--radius-pill);text-decoration:none;color:var(--ink)}.embedded-operator-nav a:hover,.embedded-operator-nav a:focus-visible{background:var(--surface);color:var(--orange-active);outline:none}.embedded-operator-nav span[aria-current=page]{background:var(--ink);color:#fff}.console-shell{width:min(100% - 48px,1120px);margin:0 auto;padding:var(--space-5) 0 var(--space-12, 48px)}.console-header{display:grid;gap:var(--space-4);grid-template-columns:1fr auto;align-items:center;margin-bottom:var(--space-4);padding-bottom:var(--space-4);border-bottom:1px solid var(--hairline)}.brand-block{display:grid;gap:2px;align-content:start;min-width:0}h1{margin:0;color:var(--ink);font-size:clamp(20px,2.2vw,24px);font-weight:650;line-height:1.2;letter-spacing:-.02em}.subtitle{max-width:58ch;margin:0;color:var(--body);font-size:13px;line-height:1.4}.top-nav{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);margin:0 0 var(--space-5);padding:6px;border:1px solid var(--hairline);border-radius:var(--radius-pill);background:color-mix(in srgb,var(--surface) 88%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:var(--shadow-panel)}.nav-link{appearance:none;display:inline-flex;align-items:center;min-height:34px;padding:0 14px;border:1px solid transparent;border-radius:var(--radius-pill);background:transparent;color:var(--body);font-size:13px;font-weight:600;cursor:pointer;transition:background-color var(--transition-fast),color var(--transition-fast),border-color var(--transition-fast),transform var(--transition-fast)}.nav-link:hover:not(:disabled),.nav-link:focus-visible:not(:disabled){background:var(--surface);color:var(--ink);outline:none}.nav-link-active{background:var(--ink);color:#fff}.nav-link-active:hover:not(:disabled),.nav-link-active:focus-visible:not(:disabled){background:var(--ink);color:#fff}.nav-link-external{margin-left:auto;border-color:var(--hairline);background:var(--surface);color:var(--orange-active)}.nav-link-external:hover:not(:disabled){border-color:var(--orange);background:var(--orange-soft);color:var(--orange-active)}.readiness{position:relative;display:grid;gap:2px;min-width:140px;max-width:280px;border:1px solid var(--hairline);background:var(--surface);border-radius:var(--radius-md);padding:8px 32px 8px 12px;box-shadow:var(--shadow-panel)}.readiness:before{content:"";position:absolute;top:50%;right:12px;width:8px;height:8px;border-radius:50%;transform:translateY(-50%);background:var(--muted-soft)}.readiness[data-state=setup-required]{border-color:var(--hairline);background:var(--surface)}.readiness[data-state=setup-required]:before{background:var(--amber)}.readiness[data-state=ready]{border-color:var(--hairline);background:var(--surface)}.readiness[data-state=ready]:before{background:color-mix(in srgb,var(--green) 72%,white);box-shadow:0 0 0 3px color-mix(in srgb,var(--green) 12%,transparent)}.readiness[data-state=degraded]{border-color:var(--hairline);background:var(--surface)}.readiness[data-state=degraded]:before{background:var(--amber);box-shadow:0 0 0 3px color-mix(in srgb,var(--amber) 12%,transparent)}.readiness-label{display:block;margin:0;color:var(--muted);font-size:11px;font-weight:600;line-height:1.2}.readiness strong{color:var(--ink);font-size:14px;font-weight:700;line-height:1.2}.readiness[data-state=ready] strong,.readiness[data-state=setup-required] strong,.readiness[data-state=degraded] strong{color:var(--ink)}.readiness .muted{margin:2px 0 0;font-size:12px;line-height:1.35}.flash{display:flex;gap:var(--space-3);align-items:flex-start;margin:0 0 var(--space-4);padding:12px 14px;border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface);box-shadow:var(--shadow-panel);animation:flash-in .22s ease}.flash-error{border-color:color-mix(in srgb,var(--red) 35%,var(--hairline));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));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:12px;font-weight:700;letter-spacing:.02em}.flash-body{margin:0;overflow-wrap:anywhere}.muted{color:var(--muted)}.console-main{display:grid;gap:var(--space-4)}.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:18px;font-weight:700;letter-spacing:-.01em}.panel-lead{margin:0 0 var(--space-5);color:var(--muted);font-size:13px}.panel h3{margin:0 0 var(--space-3);color:var(--ink);font-size:15px;font-weight:700}.empty-state{border:1px dashed var(--hairline-strong);border-radius:var(--radius-md);padding:var(--space-6);background:var(--canvas-soft);color:var(--body)}.empty-title{margin:0 0 var(--space-2);color:var(--ink);font-size:16px;font-weight:700}.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:12px;font-weight:600}.kv dd{margin:0;color:var(--ink);overflow-wrap:anywhere}code{font-size:.85rem;color:var(--ink)}.cmd{display:block;width:100%;margin:0;padding:var(--space-4);border:1px solid var(--hairline);border-radius:var(--radius-md);background:#f3efe8;color:var(--ink);font-size:12px;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(108px,1fr));gap:var(--space-2);list-style:none;padding:0;margin:0 0 var(--space-5)}.wizard-steps li{position:relative;min-height:52px;display:grid;align-content:center;gap:2px;border:1px solid var(--hairline);border-radius:var(--radius-md);padding:8px 10px;background:var(--canvas-soft);color:var(--muted);font-size:12px;font-weight:650;transition:border-color var(--transition-fast),background-color var(--transition-fast),color var(--transition-fast),box-shadow 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)}.wizard-steps .step-index{font-size:10px;font-weight:700;letter-spacing:.04em;color:var(--muted-soft)}.wizard-steps li[data-active=true] .step-index,.wizard-steps li[data-done=true] .step-index{color:inherit;opacity:.8}.wizard-steps .step-label{line-height:1.25}.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}.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:40px;padding:0 16px;border:1px solid var(--hairline-strong);border-radius:var(--radius-md);background:var(--surface);color:var(--ink);font-size:13px;font-weight:650;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:var(--orange);color:#fff;box-shadow: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}.btn-secondary,button.btn-secondary{border-color:var(--hairline-strong);background:var(--surface);color:var(--ink)}.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(--border, #ddd)}.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,1120px);padding-top:var(--space-5)}.console-header{grid-template-columns:1fr}.nav-link-external{margin-left:0}.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}.oc-workspace{display:flex;flex:1 1 auto;min-height:0;gap:0;position:relative}.oc-main{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0}.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-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-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-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-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:8px 12px;border-radius:var(--radius-md);font-size:12.5px;line-height:1.45;animation:panel-in .18s ease}.oc-alert-warning{border:1px solid color-mix(in srgb,var(--amber) 35%,var(--hairline));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));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-4) var(--space-3);display:flex;flex-direction:column;gap:var(--space-4)}.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:1;gap:var(--space-4);text-align:center;padding:var(--space-6) var(--space-4)}.oc-empty-icon{display:grid;place-items:center;width:64px;height:64px;border-radius:50%;background:var(--orange-soft);color:var(--orange-active);font-size:30px;font-weight:800;font-family:var(--font-mono, "JetBrains Mono", monospace)}.oc-empty h3{margin:0;font-size:18px;color:var(--ink)}.oc-empty p{margin:0;max-width:46ch;color:var(--muted);font-size:13px;line-height:1.5}.oc-suggestions{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-2);width:100%;max-width:640px;margin-top:var(--space-2)}@media(max-width:560px){.oc-suggestions{grid-template-columns:1fr}}.oc-suggestion{appearance:none;display:flex;flex-direction:column;gap:4px;text-align:left;padding:12px 14px;border:1px solid var(--hairline);border-radius:var(--radius-md);background:var(--surface);color:var(--ink);cursor:pointer;transition:border-color var(--transition-fast),background var(--transition-fast),transform var(--transition-fast),box-shadow var(--transition-fast)}.oc-suggestion:hover{border-color:var(--orange);background:var(--surface);box-shadow:0 0 0 3px var(--orange-soft);transform:translateY(-1px)}.oc-suggestion strong{font-size:13px;font-weight:700}.oc-suggestion span{color:var(--muted);font-size:12px}.oc-row{display:flex;flex-direction:column;gap:4px;max-width:min(100%,900px)}.oc-row.is-user{align-self:flex-end;align-items:flex-end}.oc-row.is-assistant,.oc-row.is-tool{align-self:flex-start;align-items:stretch;width:min(100%,760px)}.oc-meta{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--muted-soft)}.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}.oc-bubble-assistant{background:var(--surface);color:var(--ink);border:1px solid var(--hairline);border-bottom-left-radius:4px;box-shadow:var(--shadow-panel)}.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(--surface);overflow:hidden;box-shadow:var(--shadow-panel);animation:panel-in .16s ease}.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:9px 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-shrink:0;border-top:1px solid var(--hairline);padding:var(--space-3) var(--space-4) var(--space-4);background:color-mix(in srgb,var(--canvas) 86%,transparent)}.oc-composer-inner{display:flex;flex-direction:column;gap:var(--space-2);max-width:1080px;margin:0 auto}.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:var(--orange);color:#fff;cursor:pointer;transition:background var(--transition-fast),border-color var(--transition-fast),transform var(--transition-fast)}.oc-send:hover:not(:disabled){background:var(--orange-active);border-color:var(--orange-active)}.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-session-info{display:inline-flex;align-items:center;gap:8px;margin-left:auto;font-size:11px;color:var(--muted-soft)}.oc-session-info code{font-size:11px;color:var(--muted)}.oc-new-session{margin-left:auto}@media(max-width:600px){.oc-session-info{width:100%;margin-left:0}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-duration:.01ms!important}}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Loop 操作台 · Operator Console</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-DVl7Jxt5.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-lVcIr9Ju.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
@@ -5,7 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
* `fullstack-v1` is NOT a new DAG or taskKind. It is `agent-worker`'s
|
|
6
6
|
* deterministic structural validation + read-model projection policy over a
|
|
7
7
|
* Feature Packet. See
|
|
8
|
-
* `docs/design/agent-worker-fullstack-workflow-integration.md` §9.
|
|
8
|
+
* `docs/design/active/agent-worker-fullstack-workflow-integration.md` §9.
|
|
9
9
|
*
|
|
10
10
|
* A Feature Packet declares its profile via an optional `feature.yaml` next to
|
|
11
11
|
* `acceptance.yaml`. A packet without `feature.yaml` (or one that does not set
|
|
@@ -18,7 +18,8 @@ import { discoverProjectGovernancePresence } from "./project-governance-context.
|
|
|
18
18
|
import { getTaskPaths, loadTaskConfig } from "../../task/runtime.js";
|
|
19
19
|
import { materializeTaskReferenceDocs } from "../../task/source-references.js";
|
|
20
20
|
import { observeTaskContract } from "../../task/contract/observe.js";
|
|
21
|
-
import {
|
|
21
|
+
import { dagHasWriterExecution } from "./task-contract-binding.js";
|
|
22
|
+
import { DEFAULT_VERIFY_TIMEOUT_MS, resolveVerifyPreset, } from "../../executors/shell-verification.js";
|
|
22
23
|
import { resolveExecutorModelMatrices } from "../../executors/model-routing.js";
|
|
23
24
|
import { normalizeTaskRequirementText, resolveTaskDagTemplateSelection, } from "./task-demand-routing.js";
|
|
24
25
|
import { BACKEND_TEST_EXECUTION_DEFAULT_TEST_ROOT, buildBackendTestExecutionPreflightShellSnippet, } from "./backend-test-execution-contract.js";
|
|
@@ -556,7 +557,7 @@ export function hasApiDependency(sources) {
|
|
|
556
557
|
/**
|
|
557
558
|
* Resolve frontend Mock mode from capability seed, task config, and interface dependency analysis.
|
|
558
559
|
*
|
|
559
|
-
* Decision matrix (from docs/design/frontend-mock-data-workflow.md):
|
|
560
|
+
* Decision matrix (from docs/design/active/frontend-mock-data-workflow.md):
|
|
560
561
|
*
|
|
561
562
|
* | 接口/异步数据依赖 | 既有 Mock 服务 | policy | 结果 |
|
|
562
563
|
* |---|---|---|---|
|
|
@@ -871,21 +872,29 @@ function buildExplicitFrontendVerifyCommands(taskConfig, repoRoot) {
|
|
|
871
872
|
function verifyCommandKey(command) {
|
|
872
873
|
return `${command.cwd}\0${command.args.join("\0")}`;
|
|
873
874
|
}
|
|
874
|
-
function resolveDagVerifyStrategy(taskConfig) {
|
|
875
|
+
function resolveDagVerifyStrategy(taskConfig, defaultIntermediateQuotaWhenFull = "full") {
|
|
876
|
+
const explicitIntermediateQuota = taskConfig.dagVerifyStrategy?.intermediateQuota;
|
|
875
877
|
return {
|
|
876
|
-
intermediateQuota:
|
|
878
|
+
intermediateQuota: explicitIntermediateQuota ??
|
|
879
|
+
(taskConfig.verifyQuota === "full"
|
|
880
|
+
? defaultIntermediateQuotaWhenFull
|
|
881
|
+
: taskConfig.verifyQuota),
|
|
877
882
|
finalQuota: "full",
|
|
878
883
|
focusedCommandSource: taskConfig.dagVerifyStrategy?.focusedCommandSource ?? "adapter",
|
|
879
884
|
};
|
|
880
885
|
}
|
|
881
886
|
function buildVerifyEvidence(input) {
|
|
887
|
+
const selectedCommands = input.commands && input.commands.length > 0 ? input.commands : undefined;
|
|
888
|
+
const commandCount = selectedCommands?.length ?? input.fallbackCommands.length;
|
|
882
889
|
return {
|
|
883
890
|
phase: input.phase,
|
|
884
891
|
quota: input.quota,
|
|
885
892
|
commandSource: input.commandSource,
|
|
886
|
-
commandCount
|
|
887
|
-
commandLabels:
|
|
893
|
+
commandCount,
|
|
894
|
+
commandLabels: selectedCommands?.map((command) => command.label) ?? input.fallbackCommands,
|
|
888
895
|
commandTexts: input.commandTexts ?? [],
|
|
896
|
+
commandTimeoutMs: input.commandTimeoutMs,
|
|
897
|
+
totalTimeoutBudgetMs: commandCount * input.commandTimeoutMs,
|
|
889
898
|
finalFullRequired: input.finalFullRequired,
|
|
890
899
|
};
|
|
891
900
|
}
|
|
@@ -1369,9 +1378,10 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
1369
1378
|
commands: finalVerifyCommands,
|
|
1370
1379
|
fallbackCommands: [],
|
|
1371
1380
|
finalFullRequired: true,
|
|
1381
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
1372
1382
|
}),
|
|
1373
1383
|
cwd: ".",
|
|
1374
|
-
timeoutMs:
|
|
1384
|
+
timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
1375
1385
|
},
|
|
1376
1386
|
},
|
|
1377
1387
|
]
|
|
@@ -1677,9 +1687,10 @@ function buildFrontendMockVerifyNode(sources, implementId, readOnlyPaths, forbid
|
|
|
1677
1687
|
commands: verifyCommands.length > 0 ? verifyCommands : undefined,
|
|
1678
1688
|
fallbackCommands: [],
|
|
1679
1689
|
commandTexts: commands,
|
|
1690
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
1680
1691
|
}),
|
|
1681
1692
|
cwd: ".",
|
|
1682
|
-
timeoutMs:
|
|
1693
|
+
timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
1683
1694
|
},
|
|
1684
1695
|
};
|
|
1685
1696
|
}
|
|
@@ -2017,6 +2028,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
2017
2028
|
commands: partitionedStaticVerifyCommands.static.commands,
|
|
2018
2029
|
fallbackCommands: staticFallbackCommands,
|
|
2019
2030
|
commandTexts: staticShellCommands,
|
|
2031
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
2020
2032
|
});
|
|
2021
2033
|
const lintVerifyEvidence = lintShellCommands.length > 0
|
|
2022
2034
|
? buildVerifyEvidence({
|
|
@@ -2026,6 +2038,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
2026
2038
|
commands: partitionedStaticVerifyCommands.lint.commands,
|
|
2027
2039
|
fallbackCommands: [],
|
|
2028
2040
|
commandTexts: lintShellCommands,
|
|
2041
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
2029
2042
|
})
|
|
2030
2043
|
: undefined;
|
|
2031
2044
|
const behaviorVerifyEvidence = buildVerifyEvidence({
|
|
@@ -2036,6 +2049,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
2036
2049
|
fallbackCommands: behaviorFallbackCommands,
|
|
2037
2050
|
commandTexts: behaviorShellCommands,
|
|
2038
2051
|
finalFullRequired: true,
|
|
2052
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
2039
2053
|
});
|
|
2040
2054
|
const mockVerifyTemplate = mockMode === "required" && hasMockVerifyCommands
|
|
2041
2055
|
? buildFrontendMockVerifyNode(frontendSources, implementId, readOnlyPaths, forbiddenPaths)
|
|
@@ -2273,7 +2287,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
2273
2287
|
lintEvidence: lintVerifyEvidence,
|
|
2274
2288
|
},
|
|
2275
2289
|
cwd: ".",
|
|
2276
|
-
timeoutMs:
|
|
2290
|
+
timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
2277
2291
|
},
|
|
2278
2292
|
},
|
|
2279
2293
|
]
|
|
@@ -2345,7 +2359,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
2345
2359
|
mode: "initial",
|
|
2346
2360
|
},
|
|
2347
2361
|
cwd: ".",
|
|
2348
|
-
timeoutMs:
|
|
2362
|
+
timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
2349
2363
|
},
|
|
2350
2364
|
},
|
|
2351
2365
|
{
|
|
@@ -2406,7 +2420,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
2406
2420
|
mode: "repair",
|
|
2407
2421
|
},
|
|
2408
2422
|
cwd: ".",
|
|
2409
|
-
timeoutMs:
|
|
2423
|
+
timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
2410
2424
|
},
|
|
2411
2425
|
},
|
|
2412
2426
|
{
|
|
@@ -3122,9 +3136,10 @@ function buildExecuteBackendPytestNode(sources, options = {}) {
|
|
|
3122
3136
|
commandSource: "inline",
|
|
3123
3137
|
fallbackCommands: [pytestCommand],
|
|
3124
3138
|
finalFullRequired: true,
|
|
3139
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
3125
3140
|
}),
|
|
3126
3141
|
cwd: ".",
|
|
3127
|
-
timeoutMs:
|
|
3142
|
+
timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
3128
3143
|
},
|
|
3129
3144
|
};
|
|
3130
3145
|
}
|
|
@@ -3241,6 +3256,7 @@ function buildBackendTestOutcomeGateNode(sources) {
|
|
|
3241
3256
|
commandSource: "inline",
|
|
3242
3257
|
fallbackCommands: [gateCommand],
|
|
3243
3258
|
finalFullRequired: true,
|
|
3259
|
+
commandTimeoutMs: 60_000,
|
|
3244
3260
|
}),
|
|
3245
3261
|
cwd: ".",
|
|
3246
3262
|
timeoutMs: 60000,
|
|
@@ -3981,6 +3997,7 @@ function buildMultiPerspectiveReviewNodes(input) {
|
|
|
3981
3997
|
commandSource: "inline",
|
|
3982
3998
|
fallbackCommands: [aggregateScript],
|
|
3983
3999
|
finalFullRequired: true,
|
|
4000
|
+
commandTimeoutMs: 60_000,
|
|
3984
4001
|
}),
|
|
3985
4002
|
cwd: ".",
|
|
3986
4003
|
timeoutMs: 60000,
|
|
@@ -4165,6 +4182,7 @@ function buildKnowledgeSyncValidateNode(sources, featureId) {
|
|
|
4165
4182
|
commandSource: "inline",
|
|
4166
4183
|
fallbackCommands: [validateScript],
|
|
4167
4184
|
finalFullRequired: true,
|
|
4185
|
+
commandTimeoutMs: 120_000,
|
|
4168
4186
|
}),
|
|
4169
4187
|
cwd: ".",
|
|
4170
4188
|
timeoutMs: 120000,
|
|
@@ -4385,6 +4403,7 @@ function buildKgBootstrapPreflightNode(sources) {
|
|
|
4385
4403
|
quota: "full",
|
|
4386
4404
|
commandSource: "inline",
|
|
4387
4405
|
fallbackCommands: [script],
|
|
4406
|
+
commandTimeoutMs: 60_000,
|
|
4388
4407
|
}),
|
|
4389
4408
|
cwd: ".",
|
|
4390
4409
|
timeoutMs: 60000,
|
|
@@ -4432,6 +4451,7 @@ function buildKgBootstrapInventoryNode(sources) {
|
|
|
4432
4451
|
quota: "full",
|
|
4433
4452
|
commandSource: "inline",
|
|
4434
4453
|
fallbackCommands: [script],
|
|
4454
|
+
commandTimeoutMs: 120_000,
|
|
4435
4455
|
}),
|
|
4436
4456
|
cwd: ".",
|
|
4437
4457
|
timeoutMs: 120000,
|
|
@@ -4526,6 +4546,7 @@ function buildKgBootstrapValidateNode(sources) {
|
|
|
4526
4546
|
commandSource: "inline",
|
|
4527
4547
|
fallbackCommands: [script],
|
|
4528
4548
|
finalFullRequired: true,
|
|
4549
|
+
commandTimeoutMs: 120_000,
|
|
4529
4550
|
}),
|
|
4530
4551
|
cwd: ".",
|
|
4531
4552
|
timeoutMs: 120000,
|
|
@@ -4653,6 +4674,7 @@ function buildKgBootstrapPromoteNode(sources) {
|
|
|
4653
4674
|
commandSource: "inline",
|
|
4654
4675
|
fallbackCommands: [script],
|
|
4655
4676
|
finalFullRequired: true,
|
|
4677
|
+
commandTimeoutMs: 120_000,
|
|
4656
4678
|
}),
|
|
4657
4679
|
cwd: ".",
|
|
4658
4680
|
timeoutMs: 120000,
|
|
@@ -4718,6 +4740,7 @@ function buildKgBootstrapMaterializeNode(sources) {
|
|
|
4718
4740
|
commandSource: "inline",
|
|
4719
4741
|
fallbackCommands: [script],
|
|
4720
4742
|
finalFullRequired: true,
|
|
4743
|
+
commandTimeoutMs: 120_000,
|
|
4721
4744
|
}),
|
|
4722
4745
|
cwd: ".",
|
|
4723
4746
|
timeoutMs: 120000,
|
|
@@ -4774,7 +4797,22 @@ export async function requireManagedTaskContractBinding(input) {
|
|
|
4774
4797
|
taskId: input.taskId,
|
|
4775
4798
|
});
|
|
4776
4799
|
if (state.effectiveStatus !== "managed" || !state.ref) {
|
|
4777
|
-
const
|
|
4800
|
+
const expectedRevision = state.ref?.revision ?? 0;
|
|
4801
|
+
const observedCanonicalHash = state.observedCanonicalHash ?? "<observedCanonicalHash-from-show>";
|
|
4802
|
+
const recovery = state.effectiveStatus === "transaction-incomplete"
|
|
4803
|
+
? `Recover the unfinished transaction first: loop-agent task contract recover --task ${input.taskId} --json`
|
|
4804
|
+
: [
|
|
4805
|
+
"After reviewing the show JSON, either adopt the current on-disk facts:",
|
|
4806
|
+
`loop-agent task contract adopt --task ${input.taskId} --expected-revision ${expectedRevision} --expected-observed-hash ${observedCanonicalHash} --request-id <unique-request-id> --request-payload-sha256 <sha256-of-this-adopt-request> --json`,
|
|
4807
|
+
"or apply a reviewed draft:",
|
|
4808
|
+
`loop-agent task contract apply --task ${input.taskId} --input <reviewed-draft.json> --expected-revision ${expectedRevision} --expected-observed-hash ${observedCanonicalHash} --request-id <unique-request-id> --request-payload-sha256 <sha256-of-input-file> --json`,
|
|
4809
|
+
].join(" ");
|
|
4810
|
+
const err = new Error([
|
|
4811
|
+
`dag generate requires managed Task Contract for task ${input.taskId} (effectiveStatus=${state.effectiveStatus})`,
|
|
4812
|
+
`Inspect current state: loop-agent task contract show ${input.taskId} --json`,
|
|
4813
|
+
recovery,
|
|
4814
|
+
"Do not auto-adopt or auto-apply before confirming the Task Contract facts and concurrency fields.",
|
|
4815
|
+
].join("; "));
|
|
4778
4816
|
err.code =
|
|
4779
4817
|
state.effectiveStatus === "externally-modified"
|
|
4780
4818
|
? "EXTERNALLY_MODIFIED"
|
|
@@ -4828,10 +4866,6 @@ function synthesizeInMemoryTaskContractBinding(sources) {
|
|
|
4828
4866
|
};
|
|
4829
4867
|
}
|
|
4830
4868
|
async function resolveTaskContractBindingForGenerate(sources) {
|
|
4831
|
-
// Real CLI generation freezes the managed ref. Direct builder unit tests
|
|
4832
|
-
// may provide repoRoot-shaped fixture paths without a persisted task; keep
|
|
4833
|
-
// their deterministic synthetic binding path so topology/prompt tests remain
|
|
4834
|
-
// isolated from contract storage.
|
|
4835
4869
|
if (sources.repoRoot) {
|
|
4836
4870
|
const state = await observeTaskContract({
|
|
4837
4871
|
repoRoot: sources.repoRoot,
|
|
@@ -4986,7 +5020,12 @@ function buildReviewVerdictRecoveryNode(sources) {
|
|
|
4986
5020
|
return {
|
|
4987
5021
|
id: "review-verdict-recovery-pi",
|
|
4988
5022
|
depends_on: ["review-pi"],
|
|
5023
|
+
// AC3: recovery is a read-only failure-aware node. It runs after review-pi
|
|
5024
|
+
// settles, consuming either normal FINISHED output or an explicitly tolerated
|
|
5025
|
+
// ERROR (for example protocol-invalid after retries). It must not bypass the
|
|
5026
|
+
// writer, write guard, or final shell gate, which remain fail-closed.
|
|
4989
5027
|
role: "reviewer",
|
|
5028
|
+
failureAwareDependsOn: ["review-pi"],
|
|
4990
5029
|
executor: "pi",
|
|
4991
5030
|
complexity: "LOW",
|
|
4992
5031
|
writePolicy: "read-only",
|
|
@@ -5271,7 +5310,7 @@ function buildWriteSetGateNode(sources) {
|
|
|
5271
5310
|
}
|
|
5272
5311
|
function buildSoftVerifyNode(sources) {
|
|
5273
5312
|
const implementId = implementationNodeId();
|
|
5274
|
-
const strategy = resolveDagVerifyStrategy(sources.taskConfig);
|
|
5313
|
+
const strategy = resolveDagVerifyStrategy(sources.taskConfig, "1");
|
|
5275
5314
|
const fallbackCommands = ["npm run typecheck"];
|
|
5276
5315
|
const commands = buildVerifyShellCommands({
|
|
5277
5316
|
repoRoot: sources.repoRoot,
|
|
@@ -5297,9 +5336,10 @@ function buildSoftVerifyNode(sources) {
|
|
|
5297
5336
|
commandSource: sources.verifyCommands ? "adapter" : "inline",
|
|
5298
5337
|
commands: sources.verifyCommands?.intermediate,
|
|
5299
5338
|
fallbackCommands,
|
|
5339
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
5300
5340
|
}),
|
|
5301
5341
|
cwd: ".",
|
|
5302
|
-
timeoutMs:
|
|
5342
|
+
timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
5303
5343
|
},
|
|
5304
5344
|
};
|
|
5305
5345
|
}
|
|
@@ -5309,6 +5349,7 @@ function buildProcessSupervisorNode(sources) {
|
|
|
5309
5349
|
return {
|
|
5310
5350
|
id: "process-supervisor-pi",
|
|
5311
5351
|
depends_on: ["soft-verify-shell", implementId],
|
|
5352
|
+
failureAwareDependsOn: ["soft-verify-shell"],
|
|
5312
5353
|
role: "supervisor",
|
|
5313
5354
|
executor: "pi",
|
|
5314
5355
|
complexity: "HIGH",
|
|
@@ -5415,9 +5456,10 @@ function buildHardVerifyNode(sources) {
|
|
|
5415
5456
|
commands: sources.verifyCommands?.final,
|
|
5416
5457
|
fallbackCommands,
|
|
5417
5458
|
finalFullRequired: true,
|
|
5459
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
5418
5460
|
}),
|
|
5419
5461
|
cwd: ".",
|
|
5420
|
-
timeoutMs:
|
|
5462
|
+
timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
5421
5463
|
},
|
|
5422
5464
|
};
|
|
5423
5465
|
}
|
|
@@ -5466,7 +5508,7 @@ function buildSupervisedHybridDag(standard, sources) {
|
|
|
5466
5508
|
objective: `${standard.objective ?? ""}\n\nRoute: supervised implementation DAG selected by workflowPolicy/governanceProfile or explicit CLI profile.`.trim(),
|
|
5467
5509
|
globalConstraints: supervisedConstraints,
|
|
5468
5510
|
convergence: sources.taskConfig.convergence,
|
|
5469
|
-
verifyStrategy: resolveDagVerifyStrategy(sources.taskConfig),
|
|
5511
|
+
verifyStrategy: resolveDagVerifyStrategy(sources.taskConfig, "1"),
|
|
5470
5512
|
tasks: [
|
|
5471
5513
|
cloneTask(contract),
|
|
5472
5514
|
cloneTask(scoutSrc),
|
|
@@ -5536,6 +5578,12 @@ export async function writeHybridDagDraft(sources, outputPath, options = {}) {
|
|
|
5536
5578
|
? await prepareFrontendMockSources(sources)
|
|
5537
5579
|
: sources;
|
|
5538
5580
|
const spec = await buildHybridDagForTemplate(preparedSources, template);
|
|
5581
|
+
if (preparedSources.repoRoot && dagHasWriterExecution(spec)) {
|
|
5582
|
+
await requireManagedTaskContractBinding({
|
|
5583
|
+
repoRoot: preparedSources.repoRoot,
|
|
5584
|
+
taskId: preparedSources.taskId,
|
|
5585
|
+
});
|
|
5586
|
+
}
|
|
5539
5587
|
await writeFile(outputPath, `${JSON.stringify(spec, null, 2)}\n`, "utf-8");
|
|
5540
5588
|
return {
|
|
5541
5589
|
taskId: sources.taskId,
|
|
@@ -9,7 +9,7 @@ import { buildDagNodePromptEnvelope } from "./prompt.js";
|
|
|
9
9
|
import { persistLongNodeOutputArtifacts } from "./upstream-artifacts.js";
|
|
10
10
|
import { computeBackoffDelayMs, isRetryablePiFailureCategory, isSafeReadOnlyPiRetryCandidate, } from "./retry-policy.js";
|
|
11
11
|
import { applyNodeActivity, evaluateNodeLiveness, resolveLivenessPolicy, } from "./liveness-policy.js";
|
|
12
|
-
import { buildProtocolRetryInstruction, validateOutputProtocol, } from "./output-protocol.js";
|
|
12
|
+
import { buildProtocolRetryInstruction, normalizeReviewVerdictAfterRetries, validateOutputProtocol, } from "./output-protocol.js";
|
|
13
13
|
import { writeDagNodeJsonArtifact } from "../../infrastructure/harness/artifact-store.js";
|
|
14
14
|
import { buildProjectGovernanceContext, readCompletedWriterChangeManifests, writeProjectGovernanceContext, } from "./project-governance-context.js";
|
|
15
15
|
import { assertSkillSnapshotCoversSpec, buildNodePromptFromSnapshot, isDagSkillSnapshotIntegrityError, readSkillSnapshot, } from "./skill-snapshot.js";
|
|
@@ -483,6 +483,43 @@ export async function executeDagNode(input) {
|
|
|
483
483
|
await input.persistState();
|
|
484
484
|
}
|
|
485
485
|
}
|
|
486
|
+
// AC2: after retries are exhausted on a canonical review verdict protocol
|
|
487
|
+
// node, attempt one deterministic normalization of the full terminal output.
|
|
488
|
+
// This ONLY re-wraps output so the first non-empty line is the single
|
|
489
|
+
// supported verdict; it never changes the verdict value, and missing /
|
|
490
|
+
// duplicated / conflicting / illegal verdicts remain protocol-invalid.
|
|
491
|
+
// Shell, write-guard, timeout, and model failures are never normalized.
|
|
492
|
+
if (terminalResult &&
|
|
493
|
+
!terminalResult.ok &&
|
|
494
|
+
terminalResult.failureCategory === "protocol-invalid" &&
|
|
495
|
+
task.outputProtocol) {
|
|
496
|
+
const fullText = terminalResult.assistantText && terminalResult.assistantText.trim().length > 0
|
|
497
|
+
? terminalResult.assistantText
|
|
498
|
+
: terminalResult.stdout ?? "";
|
|
499
|
+
const normalized = normalizeReviewVerdictAfterRetries(task.outputProtocol, fullText);
|
|
500
|
+
if (normalized.ok) {
|
|
501
|
+
const normalizedText = normalized.normalizedOutput;
|
|
502
|
+
terminalResult = {
|
|
503
|
+
...terminalResult,
|
|
504
|
+
ok: true,
|
|
505
|
+
failureCategory: undefined,
|
|
506
|
+
assistantText: normalizedText,
|
|
507
|
+
stdout: normalizedText,
|
|
508
|
+
stderr: [
|
|
509
|
+
terminalResult.stderr,
|
|
510
|
+
`verdict normalized after retries exhausted: ${normalized.verdictLine}`,
|
|
511
|
+
]
|
|
512
|
+
.filter(Boolean)
|
|
513
|
+
.join("\n"),
|
|
514
|
+
};
|
|
515
|
+
// Sync the observable node fields so downstream gates and reports see the
|
|
516
|
+
// normalized first-line verdict while the original evidence is preserved.
|
|
517
|
+
node.assistantText = normalizedText;
|
|
518
|
+
node.stdout = normalizedText;
|
|
519
|
+
node.failureCategory = undefined;
|
|
520
|
+
node.stderr = terminalResult.stderr;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
486
523
|
const result = terminalResult;
|
|
487
524
|
const started = Date.now();
|
|
488
525
|
try {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { normalizeVerdictCandidateLine } from "./dynamic-runtime/shared.js";
|
|
2
3
|
/**
|
|
3
4
|
* Machine-readable output protocol for safe read-only Pi nodes.
|
|
4
5
|
*
|
|
@@ -74,3 +75,91 @@ export function buildProtocolRetryInstruction(protocol, reason) {
|
|
|
74
75
|
"</retry_instruction>",
|
|
75
76
|
].join("\n");
|
|
76
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* AC2: deterministic review-verdict normalization applied ONLY after retries
|
|
80
|
+
* are exhausted on a review verdict protocol node.
|
|
81
|
+
*
|
|
82
|
+
* The canonical review verdict protocol accepts exactly one of
|
|
83
|
+
* {@link REVIEW_VERDICT_OUTPUT_PROTOCOL}.validLines. This helper scans the
|
|
84
|
+
* FULL output (assistantText + stdout style text) for verdict lines using the
|
|
85
|
+
* same whole-line Markdown-emphasis normalization the verdict gate uses.
|
|
86
|
+
*
|
|
87
|
+
* Normalization succeeds if and only if the full output contains exactly one
|
|
88
|
+
* supported, unambiguous verdict line. It produces a normalized output whose
|
|
89
|
+
* first non-empty line is that verdict, preserving the original evidence text
|
|
90
|
+
* afterwards. It never changes the verdict value.
|
|
91
|
+
*
|
|
92
|
+
* It fails (returns ok:false) for:
|
|
93
|
+
* - no verdict line anywhere;
|
|
94
|
+
* - multiple verdict lines, even if identical (duplicated protocol ambiguity);
|
|
95
|
+
* - conflicting verdict lines;
|
|
96
|
+
* - any unsupported/illegal verdict value (e.g. `VERDICT: approve`);
|
|
97
|
+
* - malformed lines that are not a supported verdict after normalization;
|
|
98
|
+
* - natural-language approval prose without a real verdict token.
|
|
99
|
+
*
|
|
100
|
+
* This helper only operates on the canonical review verdict protocol; any
|
|
101
|
+
* other protocol is left untouched (ok:false) so decision envelopes, shell
|
|
102
|
+
* output, and structured protocols are never loosely tolerated.
|
|
103
|
+
*/
|
|
104
|
+
export function normalizeReviewVerdictAfterRetries(protocol, text) {
|
|
105
|
+
const isReviewVerdictProtocol = protocol.type === REVIEW_VERDICT_OUTPUT_PROTOCOL.type &&
|
|
106
|
+
Array.from(protocol.validLines).every((line, index) => line === REVIEW_VERDICT_OUTPUT_PROTOCOL.validLines[index]) &&
|
|
107
|
+
protocol.validLines.length ===
|
|
108
|
+
REVIEW_VERDICT_OUTPUT_PROTOCOL.validLines.length;
|
|
109
|
+
if (!isReviewVerdictProtocol) {
|
|
110
|
+
return {
|
|
111
|
+
ok: false,
|
|
112
|
+
reason: "deterministic verdict normalization only applies to the canonical review verdict protocol",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const supported = new Set(REVIEW_VERDICT_OUTPUT_PROTOCOL.validLines);
|
|
116
|
+
const candidates = [];
|
|
117
|
+
let seenIllegalVerdictPrefixedLine = false;
|
|
118
|
+
for (const rawLine of String(text).split(/\r?\n/)) {
|
|
119
|
+
const normalized = normalizeVerdictCandidateLine(rawLine);
|
|
120
|
+
if (!normalized)
|
|
121
|
+
continue;
|
|
122
|
+
// Detect any line that looks like a verdict token (VERDICT: ...) but is
|
|
123
|
+
// not a supported value, so a single valid + single illegal line cannot
|
|
124
|
+
// be silently normalized to the valid one.
|
|
125
|
+
if (/^VERDICT:/.test(normalized) && !supported.has(normalized)) {
|
|
126
|
+
seenIllegalVerdictPrefixedLine = true;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (supported.has(normalized)) {
|
|
130
|
+
candidates.push(normalized);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (seenIllegalVerdictPrefixedLine) {
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
reason: "output contains an illegal or unsupported VERDICT value; cannot normalize",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (candidates.length === 0) {
|
|
140
|
+
return {
|
|
141
|
+
ok: false,
|
|
142
|
+
reason: "no supported verdict line found in full output; cannot normalize",
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const unique = new Set(candidates);
|
|
146
|
+
if (unique.size !== 1 || candidates.length !== 1) {
|
|
147
|
+
return {
|
|
148
|
+
ok: false,
|
|
149
|
+
reason: "output contains multiple or conflicting verdict lines; normalization requires exactly one supported verdict",
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const verdictLine = candidates[0];
|
|
153
|
+
const preservedEvidence = String(text)
|
|
154
|
+
.split(/\r?\n/)
|
|
155
|
+
.filter((line) => {
|
|
156
|
+
const normalized = normalizeVerdictCandidateLine(line);
|
|
157
|
+
return !normalized || !supported.has(normalized);
|
|
158
|
+
})
|
|
159
|
+
.join("\n")
|
|
160
|
+
.trim();
|
|
161
|
+
const normalizedOutput = preservedEvidence
|
|
162
|
+
? `${verdictLine}\n${preservedEvidence}`
|
|
163
|
+
: verdictLine;
|
|
164
|
+
return { ok: true, verdictLine, normalizedOutput };
|
|
165
|
+
}
|
|
@@ -58,13 +58,47 @@ export function formatOutputLanguageBlock(language = DEFAULT_DAG_OUTPUT_LANGUAGE
|
|
|
58
58
|
}
|
|
59
59
|
export function buildUpstreamContext(task, upstream, maxChars = MAX_UPSTREAM_CHARS) {
|
|
60
60
|
const sections = [];
|
|
61
|
+
const toleratedErrors = new Set(task.failureAwareDependsOn ?? []);
|
|
61
62
|
for (const depId of task.depends_on) {
|
|
62
63
|
const record = upstream[depId];
|
|
63
64
|
const stdout = record?.stdout?.trim() ? record.stdout : "";
|
|
64
65
|
const assistantText = record?.assistantText?.trim()
|
|
65
66
|
? record.assistantText
|
|
66
67
|
: "";
|
|
67
|
-
const
|
|
68
|
+
const stderr = record?.stderr?.trim() ? record.stderr : "";
|
|
69
|
+
const upstreamText = stdout || assistantText || stderr;
|
|
70
|
+
// AC1: a failure-aware recovery/diagnosis/closeout node may consume an
|
|
71
|
+
// upstream ERROR as settled failure evidence. Surface that evidence
|
|
72
|
+
// explicitly so the node can read the failure without the upstream being
|
|
73
|
+
// rewritten to FINISHED.
|
|
74
|
+
if (record?.status === "ERROR" &&
|
|
75
|
+
toleratedErrors.has(depId) &&
|
|
76
|
+
upstreamText) {
|
|
77
|
+
const { preview, truncated } = formatStdoutPreview(upstreamText, maxChars);
|
|
78
|
+
let section = `## Upstream failure evidence: ${depId} (status=ERROR${record.failureCategory ? `, failureCategory=${record.failureCategory}` : ""})\n${preview}`;
|
|
79
|
+
const artifactKind = stdout
|
|
80
|
+
? "stdout"
|
|
81
|
+
: assistantText
|
|
82
|
+
? "assistant"
|
|
83
|
+
: "stderr";
|
|
84
|
+
const artifactPath = artifactKind === "stdout"
|
|
85
|
+
? record.stdoutArtifactPath
|
|
86
|
+
: artifactKind === "assistant"
|
|
87
|
+
? record.assistantArtifactPath
|
|
88
|
+
: undefined;
|
|
89
|
+
if (truncated && artifactPath) {
|
|
90
|
+
section += formatUpstreamArtifactPointerMap(record, artifactKind === "stdout" ? "stdout" : "assistant");
|
|
91
|
+
}
|
|
92
|
+
else if (truncated && record.nodeRecordPath) {
|
|
93
|
+
section += [
|
|
94
|
+
"",
|
|
95
|
+
"Full failure record (read-only runner evidence — use read tool to fetch; do not edit):",
|
|
96
|
+
`- node record: ${record.nodeRecordPath}`,
|
|
97
|
+
].join("\n");
|
|
98
|
+
}
|
|
99
|
+
sections.push(section);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
68
102
|
if (!record || record.status !== "FINISHED" || !upstreamText)
|
|
69
103
|
continue;
|
|
70
104
|
const artifactKind = stdout ? "stdout" : "assistant";
|